首先导入 VueRouter 并使用 Vue.use() 注册这个插件。Vue.use 这个方法,可以传入函数或者对象,如果传入函数,Vue.use() 内部会直接调用这个函数,如果传入对象,Vue.ues() 会直接调用这个对象的 install 方法。Vue.use(VueRouter) 传入的是一个对象,所以后面要实现的就是这个对象的 install 方法。
接下来调用 new VueRouter() 即创建一个 Vue Router 的实例,所以 VueRouter 应该是一个构造函数或者是一个类,此处使用类的方式来创建实例。并且这个对象还有一个install 方法,因为我们前面将 VueRouter 直接传给了 Vue.use()。类本质上也是一个对象,所以 VueRouter 就是一个对象。之后再实现 VueRouter 的时候,就是要实现一个类,并且这个类里面有一个静态的 install 方法;VueRouter 的构造函数需要接收一个参数并且是一个对象的形式,里面传入了一些路由的规则,这些路由的规则主要记录的就是路由的地址和对应的组件。
最后再去创建一个 Vue 的实例,在这个 Vue 的实例中传入了创建好的 VueRouter 对象。
那接下来就是去实现这个类中的属性和方法。
(加号是对外公开的方法,下划线是静态方法)
Constructor : 构造函数。_install : 实现 Vue 的插件机制。init : 用来调用下面的三个方法。initEvent : 注册 popState 这个事件,监听浏览器历史的变化。createRouterMap : 初始化 routemap 属性的。把构造函数中传入的路由规则,转换成键值对的形式存储到 routemap 里面,routemap 是一个对象,键是路由的地址,值是路由对应的组件。initComponents : 创建 router-link 和 router-view 这个两个组件的。当 Vue.use() 的时候会调用 install
判断当前插件是否已经被安装,如果已经被安装就不需要重复安装了
把Vue的构造函数记录到全局变量中,因为当前的 install 是一个静态的方法,在这个静态方法中接受了一个参数Vue的构造函数,将来在 Vue 的一些实例方法中还要使用这个 Vue 的构造函数,比如说创建 router-link 、 router-view 这两个组件的时候要调用 Vue.component 来创建,所以需要把 Vue 的这个构造函数记录到全局变量中。
把创建 Vue 实例时传入的 router 对象注入到所有的 Vue 实例上。我们使用的 this.$router 就是注入到实例上的。
static install (Vue) { // 1、 if (VueRouter.install.installed) { return } VueRouter.install.installed = true // 2、 _Vue = Vue // 3、 // 混入 // 在插件中给所有的 Vue 实例混入一个选项,在选项里面设置一个 beforeCreate ,在这个钩子函数中可以获取到 Vue 实例,然后给他的原型上注入 $router 。 _Vue.mixin({ // 问题二: Vue.mixin 的使用 beforeCreate () { // 为了避免创建组件的时候都会注入一遍,所以这里需要加判断,让它只有在创建实例的时候采取注入(只注入一次) if (this.$options.router) { _Vue.prototype.$router = this.$options.router // 问题一:this.$options 是什么? this.$options.router.init() } } }) }遍历所有的路由规则并解析成键值对的形式存储到 routeMap 这个对象里面。
createRouteMap () { this.options.routes.forEach(route => { this.routeMap[route.path] = route.component }) }用来注册 popState 事件,当浏览器点击前进后退的时候触发事件,实现路由组件的切换
initEvent () { window.addEventListener('popstate', () => { // 因为箭头函数不会改变 this 的指向,所以这里的 this 是指向 initEvent 中的 this ,也就是 VueRouter 对象 this.data.current = window.location.pathname }) }vue-cli 创建的项目默认使用的是运行时版本,因为效率高。
想要切换成完整版的的 Vue 可以通过配置 vue.config.js 的方式。
module.exports = { runtimeCompiler: true }runtimeCompiler 的作用就是在运行是将 templte 属性,编译成 render 函数。
但是问题来了,当我们平时使用单文件组件的时候,一直都在些 template 模板,而且一直都正常工作,这是因为在打包的过程中,把单文件的 template 编译成了 render 函数,这叫做预编译。