vue-router源码分析(一)__Vue.js
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利
1. 先分析下 vue-router 引用方式
import VueRouter from 'vue-router'
Vue.use(VueRouter)
发现是Vue.use(VueRouter)
引入。 Vue.use 会加载 当前对象的install方法, 所以VueRouter提供了一个install方法
// router.js
const routes = [
...
{
path: '/',
name: 'Home',
component: Home
}
...
]
const router = new VueRouter({
routes
})
export default router
// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
注入到根实例上
2. 用vue-cli创建工程,
-
- vue create my-vue-router(项目名)
-
- 选择以下配置
-
- 创建完项目后,新增及更改文件操作
- 3-1:新增 vue-router文件(自己写的vue-router文件目录)
- 3-2:更改 router/index.js, VueRouter替换成自己写的
这里跑起来会报错,不用急, 代码还没开始写呢
3. 开始手写vue-router
-
- 从 src/vue-router/index.js 入口开始
Vue.use 会加载 当前对象的install方法,所以给VueRouter类提供个install方法import install from './install'; export default class VueRouter { } VueRouter.install = install;
-
- 新建 src/vue-router/install.js
参数Vue就是vue的构造函数, 外部Vue.use调用install时,会传入// 安装这个插件, 这个插件应该依赖于Vue export let _Vue; export default function install(Vue) { // Vue就是vue的构造函数, 外部Vue.use调用install时,会传入 _Vue = Vue; }
4. 注入全局router属性
-
src/vue-router/install.js
现在只有main.js能获取到router, 所以需要通过Vue.mixin全局注入各个组件,具体代码及注释看以下:
// 安装这个插件, 这个插件应该依赖于Vue export let _Vue; export default function install(Vue) { // Vue就是vue的构造函数, 外部Vue.use调用install时,会传入 _Vue = Vue; // Vue,mixin 主要做了一件事:在所有组件上都增加了 _routerRoot 属性 // 通过Vue.mixin全局注入。 因为父子组件加载顺序是:父-beforeCreate => 子beforeCreate Vue.mixin({ beforeCreate() { if (this.$options.router) { // 一开始只有根组件有router(main.js) this._routerRoot = this; this._rooter = this.$options.router; } else { // 获取父组件的_routerRoot,然后赋值给自己 this._routerRoot = this.$parent && this.$parent._routerRoot; } } }) }
5. 初始化方法
- 5-1. 初始化方法,传入根实例
// src/vue-router/install.js
export let _Vue;
export default function install(Vue) {
_Vue = Vue;
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
this._routerRoot = this;
this._rooter = this.$options.router;
// init()
this._router,init(this) //初始化方法,传入根实例
} else {
this._routerRoot = this.$parent && this.$parent._routerRoot;
}
}
})
- 5-2. VueRouter初始化代码实现
- 新建 src/vue-router/create-matcher.js
// src/vue-router/index.js import install from './install'; export default class VueRouter { constructor(options) { // createMatcher返回 match和addRoutes // match 负责匹配路径{'/':'记录', 'about': '记录'} // addRoutes 动态添加路由配置 this.matcher = createMatcher(options.routes || []) } init(app) { //app指代根实例 } } VueRouter.install = install;
// src/vue-router/create-matcher.js import createRouteMap from "./create-route-map"; export default function createMatcher(routes) { // routes 外部用户当前传入的配置 // 扁平化用户传入的数据,创建路由映射表 // [/,/about/,/about/a, /about/b] // {/:'记录',/about:'记录', /about/a: '记录', /about/b: '记录'} let { pathList, pathMap } = createRouteMap(routes); //初始化配置 // 动态添加的方法 function addRoutes(routes) { //添加新的配置 createRouteMap(routes, pathList, pathMap) } // 匹配方法 function match(location) { } return { match, addRoutes } }
- 新建 src/vue-router/create-route-map.js
export default function createRouteMap(routes, oldPathList, oldPathMap) { // 将用户传入的数据 进行格式话 let pathList = oldPathList || []; let pathMap = oldPathMap || Object.create(null); routes.forEach(route => { addRouteRecord(route, pathList, pathMap); }) console.log(pathList,pathMap) return { pathList, pathMap } } function addRouteRecord(route, pathList, pathMap, parent) { let path = parent ? `${parent.path}/${route.path}` : route.path; let record = { //记录 path, component: route.component, parent } if (!pathMap[path]) { pathList.push(path); // 将路径添加到pathList中 pathMap[path] = record; } if (route.children) { route.children.forEach(child => { addRouteRecord(child, pathList, pathMap, record) }) } }
6. 创建路由系统
- 6-1. 根据模式 创建不同的路由对象
...
import HashHistory from "./history/hash";
export default class VueRouter {
constructor(options) {
...
// 创建路由系统 根据模式 创建不同的路由对象
this.mode = options.mode || 'hash';
this.history = new HashHistory(this);
}
init(app) { //app指代根实例
}
...
}
VueRouter.install = install;
- 6-2. 新建文件夹src/vue-router/history/base.js 及 hash.js(本章只实现hash模式路由)
// src/vue-router/history/base.js
export default class History {
constructor(router) {
this.router = router;
}
}
// src/vue-router/history/hash.js
import History from './base';
export default class HashHistory extends History {
constructor(router) {
super(router);
}
}
- 6-3. 初始化
// src/vue-router/index.js
import HashHistory from "./history/hash";
export default class VueRouter {
constructor(options) {
...
// 创建路由系统 根据模式 创建不同的路由对象
this.mode = options.mode || 'hash';
this.history = new HashHistory(this);
}
init(app) { //app指代根实例
// 初始化: 先根据当前路径 显示到指定的 组件
const history = this.history;
const setupHashListener = ()=> {
history.setupListener();
}
history.transitionTo(
history.getCurrentLocation(), // 后续要监听路径变化
setupHashListener
);
}
match (location) {
return this.matcher.match(location)
}
...
}
VueRouter.install = install;
// src/vue-router/history/base.js
export default class History {
constructor(router) {
this.router = router;
}
transitionTo(location, onComplete){
let route = this.router.match(location);
console.log('route',route)
if (this.current.path === location && route.matched.length ===
this.current.matched.length) {
return;
}
onComplete && onComplete();
}
}
7. 我太难了,直接附上 完整代码 地址
https://github.com/glihui/vue-router-01.git
代码有相应的注释
该文章只实现了vue-router核心代码
8.最后:分享下自己整理的部分知识点文章链接
-
webpack全方位由浅到深讲解,做到简历上真正所谓的“熟悉”(系列五)
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 小郭在深圳 原文链接:https://juejin.im/post/6955408084286046216