Vue源码学习(十九):router基本原理
好家伙,
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'
}
}