github

vue-router Vue-routerVue.js官网的路由管理器。

它和Vue.js的外围深度集成,让构建单页面利用变得大海捞针。

装置

vue add router

外围步骤

  • 步骤一:应用vue-router插件

    //router.jsimport Router from 'vue-router';
  • VueRouter是一个插件
  • 1)实现并申明两个组件router-view router-link
  • 2)install: this.$router.push()
  • */
    Vue.use(Router); // 引入插件

  • 步骤二:创立Router实例

    // router.jsexport default new Router({...})   // 导出Router实例
  • 步骤三:在根组件增加该实例

    // main.jsimport router from './router';new Vue({    router   // 增加到配置项}).$mount("#app")
  • 步骤四:增加路由视图

    <!--  App.vue  --><router-view></router-view>
  • 步骤五:导航

    <router-link to="/">Home</router-link><router-link to="/about">About</router-link>
    this.$router.push('/');this.$router.push('/about')

    vue-router简略实现

    需要剖析

  • 单页面应用程序中,url发生变化时候,不能刷新,显示对应视图

    • hash:#/about
    • History api:/about
  • 依据url显示对应的内容

    • router-view
    • 数据响应式:current变量持有url地址,一旦变动,动静执行render

    工作

  • 实现一个插件

    • 实现VueRouter
    • 解决路由选项
    • 监控url变动
    • 响应变动
    • 实现install办法
    • $router注册
    • 两个全局组件

    实现

    创立新的插件

    Vue2.x我的项目中的src门路下,复制一份router文件,重命名为ou-router
    而后在ou-router门路下新建一个ou-vue-router.js文件,并将index.js文件中的VueRouter引入改为ou-vue-router.js

    import VueRouter from './ou-vue-router'

    同时将main.js中的router引入也批改一下。

    import router from './ou-router'

    创立Vue插件

    对于Vue插件的创立:

  • 能够应用function实现,也能够应用objectclass实现;
  • 要求必须有一个install办法,未来会被Vue.use()应用

    let Vue;   // 保留Vue的构造函数,插件中须要用到class VueRouter {}
  • 插件:实现install办法,注册$router
  • 参数1是Vue.use()肯定会传入
  • */
    VueRouter.install = function (_Vue) {

    Vue = _Vue;  // 援用构造函数,VueRouter中要应用

    }
    export default VueRouter;

    ### 挂载`$router`当咱们发现`vue-router`引入`vue`的时候,第一次是在`router/index.js`中应用了`Vue.use(Router)`,在这个时候也就会调用了`vue-router`的`install`办法;而第二次则是在`main.js`中,创立根组件实例的时候引入`router`,即`new Vue({router}).$mount("#app")`。也就是说,当调用`vue-router`的`install`办法的时候,我的项目还没有创立`Vue`的根组件实例。因而咱们须要在`vue-router`的`install`办法应用全局混入,提早到`router`创立结束才执行挂载`$router`。

    let Vue; // 保留Vue的构造函数,插件中须要用到
    class VueRouter {}
    /*

  • 插件:实现install办法,注册$router
  • 参数1是Vue.use()肯定会传入
  • */
    VueRouter.install = function (_Vue) {

    Vue = _Vue;  // 援用构造函数,VueRouter中要应用/* 挂载$router *//** 全局混入*   全局混入的目标是为了提早上面逻辑到router创立结束并且附加到选项上时才执行* */Vue.mixin({    beforeCreate() {    // 此钩子在每个组件创立实例时都会调用        /* this.$options即创立Vue实例的第一个参数 */        if(this.$options.router){   // 只在根组件领有router选项

    Vue.prototype.$router = this.$options.router; // vm.$router

            }    }})

    }
    export default VueRouter;

    ### 注册全局组件`router-link`和`router-view`首先在`install`办法中注册两个全局变量。

    let Vue;
    class VueRouter {}
    VueRouter.install = function (_Vue) {

    Vue = _Vue;Vue.mixin({    ...})/* 注册全局组件router-link和router-view */Vue.component('router-link',{    render(createElement){        return createElement('a','router-link');     // 返回虚构Dom    }});Vue.component('router-view',{    render(createElement){        return createElement('div','router-view');   // 返回虚构Dom    }})

    }
    export default VueRouter;

  • router-view是一个a标签
  • router-viewto属性设置到a标签的herf属性(先默认应用hash办法)
  • 获取router-view的插槽内容,插入a标签中

     Vue.component('router-link', {        props: {            to: {                type: String,                required: true            }        },        render(createElement) {      // 返回虚构Dom            return createElement('a',                {                    attrs: {href: '#' + this.to}    // 设置a标签的href属性                },                this.$slots.default    // 获取标签插槽内容            );        }    });

    实现router-view

    router-view本质上依据url的变动,实时响应渲染对应的组件,而createElement函数是能够传入一个组件参数的。
    因而,咱们不进行渲染任何内容,前面实现监听url变动后,从映射表获取到组件后,再来实现router-view

    Vue.component('router-view', {        render(createElement) {            let component = null;            return createElement(component);   // 返回虚构Dom        }    })

    监听url变动

    咱们在VueRouter类的constructor函数中监听url的变动,这里咱们默认应用hash形式。
    而且,咱们须要将存入url的变量设置为响应式数据,这样子当其发生变化的时候,router-viewrender函数才可能再次执行。

    class VueRouter {    /*    * options:    *   mode: 'hash'    *   base: process.env.BASE_URL    *   routes    * */    constructor(options) {        this.$options = options;        // 将current设置为响应式数据,即current变动时router-view的render函数可能再次执行 const initial = window.location.hash.slice(1) || '/';        Vue.util.defineReactive(this, 'current',initial);        // 监听hash变动 window.addEventListener('hashchange', () => {            this.current = window.location.hash.slice(1);        })    }}

    因而,咱们能够来实现router-view组件。
    render函数中,this.$router指向的是VueRouter创立的实例,因而咱们能够通过this.$router.$option.routes获取路由映射表,this.$router.current获取以后路由,而后通过遍历匹配获取组件。

    Vue.component('router-view', {   render(createElement) {          let component = null;           // 获取以后路由对应的组件 const route = this.$router.$options.routes             .find(route => route.path === this.$router.current);        if (route) {                component = route.component;        }        return createElement(component);   // 返回虚构Dom       }})

    实现history模式

    后面的实现都默认为hash模式,接下来简略实现一下history模式。
    首先将监听url的代码优化一下,并判断mode的值来设置current的初始值,而history模式下初始值为window.location.pathname

    class VueRouter {    /*    * options:    *   mode: 'hash'    *   base: process.env.BASE_URL    *   routes    * */    constructor(options) {        this.$options = options;        switch (options.mode) {            case 'hash':                this.hashModeHandle();                break;            case 'history':                this.historyModeHandle();        }    }    // Hash模式解决 hashModeHandle() {        // 将current设置为响应式数据,即current变动时router-view的render函数可能再次执行 const initial = window.location.hash.slice(1) || '/';        Vue.util.defineReactive(this, 'current', initial);        // 监听hash变动 window.addEventListener('hashchange', () => {            this.current = window.location.hash.slice(1);        })    }    // History模式解决 historyModeHandle() {        const initial = window.location.pathname || '/';        Vue.util.defineReactive(this, 'current', initial);    }}

    而后咱们来实现history模式下的router-link组件。
    history模式下,当咱们点击router-link时,即点下a标签时,页面会从新刷新。所以咱们须要设置一下其点击事件,勾销默认事件,而后通过history.pushState去批改url,而后重设current的值。

    Vue.component('router-link', {    render(createElement) {      // 返回虚构Dom        const self = this;        const route = this.$router.$options.routes            .find(route => route.path === this.to);        return createElement('a',            {                attrs: {href: this.to},    // 设置a标签的href属性 on: {                    click(e) {                        e.preventDefault();   // 勾销a标签的默认事件,即刷新页面 history.pushState({}, route.name, self.to);   // 通过history.pushState来扭转url                        self.$router.current = self.to;                    }                }            },            this.$slots.default    // 获取标签插槽内容        );    }})

    最初咱们将两种模式的router-link组件进行一个合并。

    Vue.component('router-link', {    props: {        to: {            type: String,            required: true        }    },    render(createElement) {      // 返回虚构Dom        if(this.$router.$options.mode === 'hash'){            return createElement('a',                {                    attrs: {href: '#' + this.to}    // 设置a标签的href属性                },                this.$slots.default    // 获取标签插槽内容            );        }else{            const self = this;            const route = this.$router.$options.routes                .find(route => route.path === this.to);            return createElement('a',                {                    attrs: {href: this.to},    // 设置a标签的href属性 on: {                        click(e) {                            e.preventDefault();   // 勾销a标签的默认事件,即刷新页面 history.pushState({}, route.name, self.to);   // 通过history.pushState来扭转url                            self.$router.current = self.to;                        }                    }                },                this.$slots.default    // 获取标签插槽内容            );        }    }});