Router如何实现的?不用import {vue-router} from 'vue' 你还会吗?

使用过vue的小伙伴们都知道,路由使用起来非常简单,只用引入,定义,配置,输出还能需要的地方引入使用,实现单页应用的页面跳转,今天我们就来说一说,我不import,也能实现<router-link>、<router-view>的路由效果。来到了我们最熟悉的手写环节。

一、知识储备

(1)、全局组件

  • 在vue中,路由是在很多地方都要使用的组件,所以在手写的实现过程,我们将它定义成全局组件,一旦被注册,在任何需要使用的模板组件中,都不需要引入,直接可以使用。
  • 使用app.component()方法注册一个全局组件,这里的app实际上是Vue实例。接受两个参数:name:组件名、options:组件定义对象,既可以是Vue组件,也可以是自定义组件。

(2)、provide和inject

  • 用于祖先-后代关系通信的特性,这就意味着允许所有的后代可以访问到祖先组件注入的数据或者服务,不需要通过组件逐层的传递props。在跨多个组件层级传递数据或服务时多使用。

(3)、<slot>元素,也叫插槽

  • 它允许父组件向子组件的特定位置插入内容,这个机制叫做内容分发。它让组件的设计更为灵活,允许组件的使用者决定某些部分的具体内容,不再仅仅是组件的设计者。

使用示例:

父组件

xml 复制代码
<template>
    <div>
        <child-component>
            <p>子组件中显示的内容</p>
        </child-component>
    </div>
</template>

子组件

xml 复制代码
<template>
    <div>
        <h1>我是子组件</h1>
        <slot></slot> <!-- 显示的位置 -->
    </div>
</template>

(4)<component>元素,动态组件标签

  • 根据一个表达式来动态的渲染不同的组件,需要一个is特性,绑定到一个表示组件名称的字符串,或者是一个组件选项对象。这个值通常是响应式的数据属性,决定了渲染哪个组件。

二、实现过程

1、router-link的实现

  • 在使用vue的原生route-link时,会有一个to参数,值为跳转的目标路由。这里我们定义一个router-link组件,接收父组件,也就是使用router-link的组件传来的参数,并使用slot显示跳转信息。
xml 复制代码
<template>
    <div>
        <a :href="'#' + props.to">
            <slot></slot>
        </a>
    </div>
</template>

<script setup>
const props = defineProps({
    to:{
        type:String,
        required:true
    }        
})
</script>

<style lang="css" scoped>

</style>
ini 复制代码
      <router-link to="/">首页</router-link>
      <router-link to="/about">About</router-link>

2、定义Router并单例输出

javascript 复制代码
import RouterLink from './RouterLink.vue'
import RouterView from './RouterView.vue'
import {ref,inject} from 'vue' 
// 单例的责任
export const createRouter = (options) => {
    return new Router(options)
}

export const createWebHashHistory = () =>{
    //history 对象
    function bindEvents (fn){
        window.addEventListener('hashchange',fn)
    }
    return {
        url:window.location.hash.slice(1) || '/',
        bindEvents
    }
}

//标记一下 router
const ROUTER_KEY = '__router__'
export const useRouter  = () =>{
    return inject(ROUTER_KEY)
}

class Router{
    constructor(options){
        this.history = options.history
        this.routes = options.routes
        this.current = ref(this.history.url)
        this.history.bindEvents(() => {
            this.current.value = window.location.hash.slice(1)
        })
    }
    // use方法会调用 插件的install方法
    install(app){
        //全局声明有一个router 全局使用的对象
        app.provide(ROUTER_KEY,this) 
        app.component('router-link',RouterLink) 
        app.component('router-view',RouterView)
    }
}
  • 输出单例路由示例,接收配置对象作为参数,例如Vue中router的routes和history
  • 定义监听hashchange事件的函数,将当前路径和这个函数返回,接着Router类中跟踪和响应URL hash的变化。
  • 将ROUTER_KEY常量作为Vue中provide和inject API中的标识符,用于在组件树中注入和获取路由实例,用于祖先-后代关系通信的特性。
  • 使用inject方法,从组件树中获取路由实例,让任何组件都可以通过调用这个方法来访问路由对象,从而实现对路由状态的查询和修改。
  • Router类,构造函数接收option对象(配置信息routes和history),将当前路径设置为响应式,存储当前的路由状态,初始值为createWebHashHistory中返回的url。并且在路径发生改变时,自动响应更新对应的值,这是响应式在路由中的高级用法。
  • install(app),将当前Router注入到Vue应用(app)中,让整个组件树中的后代组件都能通过inject方法来访问这个路由实例。对RouterLink、RouterView组件的全局注册。

3、router-view的实现

xml 复制代码
<template>
    <component :is="component">
    </component>
</template>
<script setup>
import { useRouter } from './index'
import { computed } from 'vue';
const router = useRouter()
console.log(router);

//router-view 动态组件展示,依赖于 url 的变化
//响应式 router.current 设置为ref

const component = computed(() => {
    const route = router.routes.find((route)=>route.path == router.current.value)
    return route ? route.component:null
})
</script>

<style lang="css" scoped>

</style>
  • 使用计算属性computed,在当前路由变化时,计算component的值,并显示配置信息中的对应组件,route ? route.component:null并且做了防止用户输入错误路由。

使用Router

javascript 复制代码
import {createRouter,createWebHashHistory} from './grouter/index'
import Home from '../pages/Home.vue'
import About from '../pages/About.vue'

const routes = [
    {
        path:'/',
        name:'Home',
        component:Home
    },
    {
        path:'/about',
        name:'About',
        component: About
    },

]

const router = createRouter({
    history:createWebHashHistory(),
    routes
})

export default router

将from 'vue' 改为我们自己手写的,我们来看一下实现效果。在这Home和About中只写了相应的名称来代表页面,模拟实现效果。

相关推荐
腾讯TNTWeb前端团队44 分钟前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
uhakadotcom4 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰4 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪4 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪4 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy5 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom6 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom6 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom6 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github