虽然最近需求着实很多,可是感受本身学习劲头仍是蛮足的,并无被需求压垮。今天,带来Vue-Router源码解析系列的第二篇文章:index.js。html
系列文章:前端
Vue-Router 源码学习之咱们从API中看些门道vue
index.js是vue-router这个类的主构造函数,因此内容上算是比较关键的:面试
constructor (options: RouterOptions = {})
,在vue-router中使用了flow.js作了类型的检查,
什么是flow.js?flow.js怎么使用呢?由于篇幅缘由,这里就暂时先不作涉及。各位小伙伴,能够参看官网:flow.org/en/docs/typ…ajax
首先咱们来看一下constructor内的代码,vue-router
constructor (options: RouterOptions = {}) {
this.app = null
this.apps = []
this.options = options
this.beforeHooks = []
this.resolveHooks = []
this.afterHooks = []
this.matcher = createMatcher(options.routes || [], this)
//默认为hash锚点
let mode = options.mode || 'hash'
//固然使用的是history模式 h5的pushState的方式来实现路由跳转的,对options设置fallback属性为true时会回退到hash模式
// 是否支持回退
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
if (this.fallback) {
mode = 'hash'
}
if (!inBrowser) {
mode = 'abstract'
}
//没有fallback的话选择锚点模式,node环境选择abstract模式
this.mode = mode
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}`)
}
}
}
复制代码
咱们声明一个vue-router实例的时候是怎么作的?编程
let router = new Router({
base : '/',
mode : 'history',
routes : [{
component : xxx,
path : xxx
},xxx]
})
复制代码
constructor (options: RouterOptions = {})
options就是咱们刚才上面的一个对象,里面有base、mode、routes等属性
复制代码
这时候咱们知道options是个什么东西了,咱们来看看内部对options进行了哪些处理。api
首先咱们说一下vue-router最核心的内容之一:
咱们知到vue-router的路由有两种方式,一种是#锚点性的,第二种是和正常路径同样的,但是vue构建的应用是一个但页面应用如何让他像正常的多页面应用同样,是在不停的改变路径呢? 这里面就使用了html5的history的pushState与replaceState(让页面看起来无刷新的改变路径),具体内容你们能够看一下官网文档和大神张鑫旭的博客(www.zhangxinxu.com/wordpress/2…
在vue-router源码中有一个工具类专门作了这个事情:
// vue-router默认使用hash模式
let mode = options.mode || 'hash';
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
// 若是选择了history可是pushState方法并不能使用而且设置了
// 在当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式的状况下。
// (options.fallback默认就是true)
// 若是发现须要回退了,就回到hash锚点模式
if (this.fallback) {
mode = 'hash'
}
// 不在浏览器环境就选择abstract模式(在node环境)
if (!inBrowser) {
mode = 'abstract'
}
this.mode = mode
// 根据三种状况是成不一样的路由转换实例。
// 若是没有mode不是这三种状况就报错。
// HTML5History、HTML5History、HTML5History三个类都是继承与一个base类
// 里面有这三种模式对于路径转换时作的事情进行了必定的封装。
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HTML5History(this, options.base, this.fallback)
break
case 'abstract':
this.history = new HTML5History(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
复制代码
到这里你们应该对咱们写的mode模式有一点的了解了吧。
下面说一下init方法,上一章咱们讲了在根节点的beforeCreate生命周期钩子中,使用了init方法,若是忘记了能够翻看上一篇install方法的学习来回归一下
因此app就是根组件,init在执行前要判断一下,vue-router是否是被vue成功use了,由于成功use以后,会把install方法的installed属性设置为true:
init (app: any /* Vue component instance */) {
process.env.NODE_ENV !== 'production' && assert(
install.installed,
`not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
`before creating root instance.`
)
this.apps.push(app)
// main app already initialized. (根组件已经被初始化)
if (this.app) {
return
}
this.app = app
const history = this.history
// 当咱们的根组件完成了对vue-router的init的时候咱们就要完成第一次路由的跳转了
// 当咱们的项目启动的时候确定会有一个路径,这个路径是什么不重要
// 咱们在第一次进入这个路径的时候,会进行vue-router的初始化
// 初始化以后要开始展现对应的组件,但是咱们vue-router那些popState的事件确定没有绑定,不会触发啊,
// 怎么办?? 那就手动触发一下这个事情,
// 第一次进入确定没有对应的事件,不会完成跳转时该作的事情。
if (history instanceof HTML5History) {
history.transitionTo(history.getCurrentLocation())
} else if (history instanceof HashHistory) {
const setupHashListener = () => {
history.setupListeners()
}
history.transitionTo(
history.getCurrentLocation(),
setupHashListener,
setupHashListener
)
}
// 针对设置的不一样mode(模式)
// 将mode的时候,每种模式的history实例来源于三个不一样的类,因此instanceof足够判断是哪一种模式。
复制代码
用过vue-router的同窗们都知道路由守卫的概念,这是在路由跳转的先后等三个地方设置了不一样的钩子,帮助咱们在进入离开路由前作一些事情。这个钩子是怎么作的呢?
vue-router全局级别的beforeEach、beforeResolve、afterEach作了什么?
function registerHook (list: Array<any>, fn: Function): Function {
list.push(fn)
// 返回值是一个function
return () => {
const i = list.indexOf(fn)
if (i > -1) list.splice(i, 1)
}
}
复制代码
接收一个生命周期的钩子数组,将咱们要执行的函数传到数组内就能够完成注册了,我还没看到这三个数组的内容,可是直觉告诉我颇有可能就是,观察者模式(之后就探索去)。注册到这应该就OK了,
为何还有个返回值呢? 返回值的内容一看就是要清楚钩子内的函数呀,咱们调用这个registerHook函数后,能够获得注册函数的清除函数,清除的是钩子数组中对应的函数,还有这么一手,牛的一匹。(让代码教你如何熟练使用闭包~)
到了这里咱们vue-router的主线流程咱们已经进行了一个梳理,不知道你们对这一块内容感受满意吗? 不满意就请不要邮寄刀片哈。
1:mode是设置模式的,有hash、history、abstract三种模式、每一个模式会致使vue-router实例的history不一样,来自三个不一样的类、每一个类又继承于总的base类。 2:init方法会初始化整个组件、而且在vue-router的实例中设置了app属性存放根组件(这个确实颇有用)、手动的完成初始化后的第一次路由跳转。 3:beforeEach、beforeREsolve、afterEach三个全局的钩子都有对应的钩子函数数组,存放每一个周期钩子内要作的事情、使用registerHook方法来完成钩子函数的注册,registerHook也能够清除钩子内对应的函数。 4:push、replace、go等方法都是使用history方法内的对应方法。
真的没出息的总结。
vue-router类中还有一部分对options.routes的处理
options.routes 就是 [{path : 'xxx',components : 'xxx'}] 这个数组
复制代码
生成一个根据options.routes的一份比对程序,完成程序的比对。
下一期的内容要在继续学习index.js和开荒history中进行一个抉择,具体是什么内容你们能够积极留言,给个方向呀。
每个前端er(boy and girl) 大家都不是一我的在战斗。
安排!!!!