vue router 源码概览案例分析

源码这个东西对于实际的工作其实没有立竿见影的效果,不会像那些针对性极强的文章一样看了之后就立马可以运用到实际项目中,产生什么样的效果,源码的作用是一个潜移默化的过程,它的理念、设计模式、代码结构等看了之后可能不会立即知识变现(或者说变现很少),而是在日后的工作过程中悄无声息地发挥出来,你甚至都感觉不到这个过程

另外,优秀的源码案例,例如 vue 、 react 这种,内容量比较庞大,根本不是三篇五篇十篇八篇文章就能说完的,而且写起来也很难写得清楚,也挺浪费时间的,而如果只是分析其中一个点,例如 vue 的响应式,类似的文章也已经够多了,没必要再 repeat

所以我之前没专门写过源码分析的文章,只是自己看看,不过最近闲来无事看了 vue-router 的源码,发现这种插件级别的东西,相比 vue 这种框架级别的东西,逻辑简单清晰,没有那么多道道,代码量也不多,但是其中包含的理念等东西却很精炼,值得一写,当然,文如其名,只是概览,不会一行行代码分析过去,细节的东西还是要自己看看的

vue.use

vue 插件必须通过 vue.use 进行注册, vue.use 的代码位于 vue 源码的 src/core/global-api/use.js 文件中,此方法的主要作用有两个:

对注册的组件进行缓存,避免多次注册同一个插件

if (installedPlugins.indexOf(plugin) > -1) { return this }

调用插件的 install 方法或者直接运行插件,以实现插件的 install

if (typeof plugin.install === 'function') { plugin.install.apply(plugin, args) } else if (typeof plugin === 'function') { plugin.apply(null, args) }

路由安装

vue-router 的 install 方法位于 vue-router 源码的 src/install.js 中 主要是通过 vue.minxin 混入 beforeCreate 和 destroyed 钩子函数,并全局注册 router-view 和 router-link 组件

// src/install.js Vue.mixin({ beforeCreate () { if (isDef(this.$options.router)) { this._routerRoot = this this._router = this.$options.router this._router.init(this) Vue.util.defineReactive(this, '_route', this._router.history.current) } else { this._routerRoot = (this.$parent && this.$parent._routerRoot) || this } registerInstance(this, this) }, destroyed () { registerInstance(this) } }) ... // 全局注册 `router-view` 和 `router-link`组件 Vue.component('RouterView', View) Vue.component('RouterLink', Link)

路由模式

vue-router 支持三种路由模式( mode ): hash 、 history 、 abstract ,其中 abstract 是在非浏览器环境下使用的路由模式,例如 weex

路由内部会对外部指定传入的路由模式进行判断,例如当前环境是非浏览器环境,则无论传入何种 mode ,最后都会被强制指定为 abstract ,如果判断当前环境不支持 HTML5 History ,则最终会被降级为 hash 模式

// src/index.js let mode = options.mode || 'hash' this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false if (this.fallback) { mode = 'hash' } if (!inBrowser) { mode = 'abstract' }

最后会对符合要求的 mode 进行对应的初始化操作

// src/index.js switch (mode) { case 'history': this.history = new HTML5History(this, options.base) break case 'hash': this.history = new HashHistory(this, options.base, this.fallback) break case 'abstract': this.history = new AbstractHistory(this, options.base) break default: if (process.env.NODE_ENV !== 'production') { assert(false, `invalid mode: ${mode}`) } }

路由解析

通过递归的方式来解析嵌套路由

// src/create-route-map.js function addRouteRecord ( pathList: Array<string>, pathMap: Dictionary<RouteRecord>, nameMap: Dictionary<RouteRecord>, route: RouteConfig, parent?: RouteRecord, matchAs?: string ) { ... route.children.forEach(child => { const childMatchAs = matchAs ? cleanPath(`${matchAs}/${child.path}`) : undefined addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs) }) ... }

解析完毕之后,会通过 key-value 对的形式对解析好的路由进行记录,所以如果声明多个相同路径( path )的路由映射,只有第一个会起作用,后面的会被忽略

// src/create-route-map.js if (!pathMap[record.path]) { pathList.push(record.path) pathMap[record.path] = record }

例如如下路由配置,路由 /bar 只会匹配 Bar1 , Bar2 这一条配置会被忽略

const routes = [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar1 }, { path: '/bar', component: Bar2 }, ];

路由切换

当访问一个 url 的时候, vue-router 会根据路径进行匹配,创建出一个 route 对象,可通过 this.$route 进行访问

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/844fe7505bb0859b422083ffe7702598.html