Vue源码学习(十九):router基本原理

发布时间 2024-01-11 20:24:46作者: 养肥胖虎

好家伙,

 

0.什么是路由?

路由就是匹配到对应路径显示对应的组件

那么我们要如何去实现?

 

我们来回忆一下这router怎么用的

1. 声明式路由配置:在路由配置对象中,定义路径与组件的映射关系。例如:
import AboutComponent from '../views/AboutComponent.vue'

const routes = [
  { path: '/', component: HomeComponent },
  { path: '/about', component: AboutComponent },
  { path: '/user/:id', component: UserComponent },
];

 

2. 安装路由插件:在 Vue 根实例中,使用 `Vue.use()` 方法安装 vue-router 插件,
并将路由实例注入到根实例中。
import Vue from 'vue';
import VueRouter from 'vue-router';


Vue.use(VueRouter);

 

3. 创建 router 实例:根据路由配置对象创建一个 VueRouter 实例。
const router = new VueRouter({
  routes,
  mode: 'history',
  base: '',
});

 

4. 挂载路由:将创建的 router 实例挂载到 Vue 应用上。
new Vue({
  router,
  render: (h) => h(App),
}).mount('#app');

 

5. 在组件中使用 `<router-view>`:在需要显示路由组件的页面中,
使用 `<router-view>` 标签。该标签会根据当前路由自动渲染对应的组件。
<template>
  <div>
    <router-link to="/about">about</router-link>
    <router-view></router-view>
  </div>
</template>

 


6. 导航:通过调用 router 实例的方法(如 `router.push()`)实现页面跳转。当用户访问不同的路径时,vue-router 会根据路由配置自动渲染对应的组件。

图例:

 也就是说如果我点击了about,就对应跳到/about,并在下方展示这个路径对应的组件

于是我们目标明确了,事实上,我们只需要解决三个问题

1.router-link的实现

2.router-view的实现

3.路径到组件的映射关系的实现

 

 

 目录结构如下:

1.router-link的实现

routerLink.jsx

  //组件
  export default{
    props:{ //组件的属性
      to:{
        type:String,
        required:true
      },
      tag:{
        type:String
      }
    },
    
  //jsx  
  render(){
    let tag = this.tag || 'a'
    //跳转
    console.log(tag,666)
    let handler = ()=>{
       this.$router.push(this.to)
    }
      return <tag  onclick={handler}>{this.$slots.default}</tag >//jsx  {变量}
  }
}

 

2.router-view的实现

routerView.js

export default {
    functional: true,
    // 函数式组件
    render(h, { parent, data }) { // 1 h  2 属性
        //1 获取到组件
        let route = parent.$route // 获取到route
        // this match  component
        // 2问题  嵌套  /about/a :[about, a]  routerView
        data.routerView = true
        let depath = 0
        while (parent) {
            // $vnode 相当于一个 占位符   
            if (parent.$vnode && parent.$vnode.data.routerView) {
                depath++
            }
            parent = parent.$parent //一直寻找父亲
        }
        let recode = route.metched[0].metched[depath]

        if (!recode) {
            return h() //
        }
        return h(recode.component, data)
    }
}

 

3.路径到组件的映射关系的实现

index.js

export default class VueRouter {
    constructor(options = {}) {
        //   vue-router 核心 1 match核心  [{},{}]  => {'/':{组件相关信息},'/about':{}}
        console.log(options.routes,'this is options.routes')
        this.match = createMatch(options.routes || [])
        this.beforeHooks= []
        // 核心二:浏览器路由管理  
        // 1;获取模式
        options.mode = options.mode || 'hash'
        //进行判断
        switch (options.mode) {
            case 'hash':
                this.history = new HashHistory(this)
                break;
            case 'history':
                this.history = new HtmlHistory(this)
                break
        }
        console.log(this.history)
    }

 

createMatch.js

export function createMatch(routes) { //匹配器
   //1变成一个路由映射表   [{},{}]  => {'/':{组件相关信息},'/about':{},/about/c:{}}
   const  pathMap  = createRouterMap(routes)
   //2addRoutes 动态添加路由
   // addRoutes(routes)
   function addRoutes(routes) { // 1:用户自己动态的路由  2
      // 注意需要合并在一起

      createRouterMap(routes, pathMap)
   }

/.
.
.
./
}

 

createRouterMap.js

 

`createRouterMap` 函数用于创建路径映射对象

 

export function createRouterMap(routes,routerOptions={}) {
    // console.log(routes,5556)
    let pathMap = routerOptions
    routes.forEach(router => {
        //[{path:'/},{}]  => {'/':{组件相关信息},'/about':{},/about/a:{}}
        console.log(router, pathMap,'||this is router, pathMap')
        addRouterRecode(router, pathMap)
    })
    //  console.log(pathMap)
     //问题 路由嵌套 /about/a
     return pathMap
}


function addRouterRecode(router, pathMap,parent) {
    // 1路径 /  记录

    let path = parent ?`${parent.path}/${router.path}`:router.path
    let recode = {
        path: router.path,
        name: router.name,
        component: router.component,
        parent
    }
    //添加
    if (!pathMap[path]) {
        pathMap[path] = recode
    }
    //有没有儿子
    if(router.children){
        //递归
        router.children.forEach(child=>{
            //注意 parent 
            addRouterRecode(child, pathMap,recode)
        })
    }
}

对于以上createRouterMap方法,举个例子
const routes = [
  {
    path: '/',
    name: 'Home',
    component: HomeComponent
  },
  {
    path: '/about',
    name: 'About',
    component: AboutComponent,
    children: [
      {
        path: 'contact',
        name: 'Contact',
        component: ContactComponent
      }
    ]
  }
];

const routerOptions = {
  base: '/'
};

const pathMap = createRouterMap(routes, routerOptions);

得到结果

{
  '/': {
    path: '/',
    name: 'Home',
    component: HomeComponent,
    parent: null
  },
  '/about': {
    path: '/about',
    name: 'About',
    component: AboutComponent,
    parent: '/'
  },
  '/about/contact': {
    path: '/about/contact',
    name: 'Contact',
    component: ContactComponent,
    parent: '/about'
  }
}