vue-router设计思路分析

发布者: xiaozhimn

第一步:编写模版

<body>
<div id="app">
    <div>
        <!--
            router-link to属性就是指向某个具体的链接,链接的内容会被渲染到router-view标签中
            router-link会被渲染成a标签,例如第一个会变成<a href="#/first">第一个页面</a>,前面加了个#
        -->
        <router-link  to="/first">第1个页面</router-link>
        <router-link  to="/second">第2个页面</router-link>
        <router-link  to="/third">第3个页面</router-link>
    </div>
    <router-view></router-view>
</div>
</body>

第二步:编写脚本

    /*
    * 申明三个模板
    */
    var first = { template: '<p>this is first page</p>' };
    var second = { template: '<p>this is second page</p>' };
    var third = { template: '<p>this is third page</p>' };
    /*
    * 定义路由,component属性是通过 Vue.extend() 创建的组件构造器,或者,只是一个组件配置对象。
    */
    var routes = [
        { path: '/first', component: first },
        { path: '/second', component: second },
        { path: '/third', component: third }
    ];
    /*
    * 创建VueRouter实例
    */
    var router = new VueRouter({
        routes:routes
    });
    /*
    * 给vue对象绑定路由
    * .$mount("#app")手动挂载,用来延迟挂载,跟
    *  const app = new Vue({
    *   el:"#app"
    *   router
    * });
    * 效果是一样的
    */
    const app = new Vue({
        router
    }).$mount("#app");

redirct重定向

在routes数组里面添加
{ path: ‘/‘, redirect: ‘/first’},
当打开页面时候,会自动重定向到第一个中.

tag

router-link默认会被渲染成a标签
我们可以加上tag=”li”就会被渲染成li标签

active-class

router-link标签被选中时候会默认给选中的元素添加.router-link-active属性,我们可以通过设置active-class设置被选中后添加的class样式

路由拦截,以登录验证为例

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    user: false
  },
  mutations: {
    // 登录
    login (state, user) {
      state.user = user
    },
    // 退出
    logout (state, user) {
      state.user = false
    }
  }
})

路由验证:
路由验证用 router.beforeEach( (to, from, next) => { }
这里的to代表要去的路由指向,from是指从哪个路由跳转过来的,next是判断操作,如果为空,表示放行。
比如:下一跳的路由为‘/watchHouse’或者‘/AgentMsg’ ,如果没有登录的话,通过代码 next ({path: ‘/login’})
跳转到登录的界面。代码如下:

if (!store.state.user && (to.path === '/watchHouse' || to.path === '/AgentMsg')) {
  next({ path: '/login' })
}

vue-router设计思路分析

vue的单页面应用分为两种设计思路:
将所有的视图和脚本打包成一个大的js文件,首屏一次性加载所有页面所需要的内容,通过路由的切换来更新视图,这样设计的优缺点如下:
优点:底层设计简单暴力,无需分片加载,由于js文件包很大,所以首次加载比较慢,以后基本是本地切换,只需要获取动态数据填充
     即可。
缺点:所有页面的内容打包在一起,增加了脚本以及页面之间的耦合度,需要做好沙盒分离,否则容易造成互相影响,这个对于编程人
     员能力有一定的要求,其次代码柔和在一起晦涩难懂不易阅读理解,不适合特大项目开发。

单页面vue-router的整体路由切换思路为:监听window的haschange事件,将浏览器hash值与router中的path进行关联,获取到对应的component,使用vue的compiler进行动态编译渲染生成最终的html字符串,替换到router-view中,从而实现无刷新页面切换。 路由的拦截就更为简单了, 在window的hashchange中使用aop的方式织入 beforeEach和afterEach的事件为开发者提供业务介入。

另一种实现单页面应用的方式为,分片加载也就是按需加载,这种设计的优点大于缺点,对资源的利用相对比较合理,从from页面切换到to页面,只需要加载to页面所需要的资源同时回收from页面的资源。下来分享一段自己实现按需加载的单页面应用路由代码,如下:
route.js
var Route = {
    isNeedReLoad: false,
    computeJsRelyCount: function(mainJsPath, callback) {
        $.ajax({
            type: "get",
            url: "/computeJsRelationCount",
            data: {
                mainJsPath: mainJsPath
            },
            dataType: "json",
            success: function(result) {
                callback(result);
            }
        });
    },
    gc: function() {
        $("#route-view").html("");
        $("#route-view").html("<div id='app'></div>");
        for(var index = 0; index < flux.ev.length; index++) {
            flux.ev[index].$destroy();//清除掉上一个页面的组件
        }
        function destroyService() {
            var paths = location.href.split("/");
            var pathMark = paths[3];
            var pageName = "index";
            if(pathMark == "#") {
                pageName = paths[4].indexOf("?") > -1 ? paths[4].substring(0, paths[4].indexOf("?")) : paths[4];
            }
            window[pageName + "Service"] = null;
        }
        destroyService();
    },
    insertMainCss: function(cssContent) {
        var styleEle = $("<style></style>");
        styleEle.html(cssContent);
        $("#route-view").append(styleEle);
    },
    loadMainComponent: function(mainJsPath) {
        var self = this;
        var jsContent = cacheStore.get(mainJsPath) ? cacheStore.get(mainJsPath).url : "";
        var mainCssPath = mainJsPath.replaceAll(".js", ".css");
        var cssContent = cacheStore.get(mainCssPath) ? cacheStore.get(mainCssPath).url : "";
        if(!cssContent) {
            easyVue.getTplContent(mainCssPath, function(result) {
                cssContent = result;
                self.insertMainCss(cssContent);
                loadJsContent();
            });
        } else {
            this.insertMainCss(cssContent);
            loadJsContent();
        }
        function loadJsContent() {
            if(!jsContent) {
                easyVue.getTplContent(mainJsPath, function(result) {
                    jsContent = result;
                    $.globalEval(jsContent);
                });
            } else {
                $.globalEval(jsContent);
            }
        }
    },
    getMainJsPath: function() {
        var paths = location.href.split("/");
        var pathMark = paths[3];
        var pageName = "index";
        if(pathMark != "#") {
            return routeMap[pageName];
        } else {
            pageName = paths[4].indexOf("?") > -1 ? paths[4].substring(0, paths[4].indexOf("?")) : paths[4];
            return routeMap[pageName];
        }
    },
    setJsUseCount: function(mainJsPath, callback) {
        var useCount = cacheStore.get(mainJsPath + "_useCount") ? cacheStore.get(mainJsPath + "_useCount").useCount : undefined;
        if(!useCount) {
            this.computeJsRelyCount(mainJsPath, function (result) {
                storage["useCount"] = result.useCount;
                cacheStore.set(mainJsPath + "_useCount", {
                    useCount: result.useCount,
                    timestamp: storage.timestamp
                });
                callback();
            });
        } else {
            storage["useCount"] =  useCount;
            callback();
        }
    },
    loadOtherPage: function() {
        var self = this;
        var pathMark = location.href.split("/")[3];
        if(pathMark == "#") {
            self.gc();//回收上一个页面的内存
            var routeItem = this.getMainJsPath();
            if(routeItem.isReload && self.isNeedReLoad) {
                location.reload();
                return;
            }
            var mainJsPath = routeItem.path;
            this.setJsUseCount(mainJsPath, function() {
                self.loadMainComponent(mainJsPath);
            });
        }
    },
    loadIndexPage: function() {
        var self = this;
        var pathMark = location.href.split("/")[3];
        if(pathMark != "#") {
            var mainJsPath = this.getMainJsPath().path;
            this.setJsUseCount(mainJsPath, function() {
                self.loadMainComponent(mainJsPath);
            });
        } else {
            self.isNeedReLoad = false;
            this.loadOtherPage();
        }
    },
    bindEvent: function() {
        var self = this;
        window.onhashchange = function() {
            self.isNeedReLoad = true;
            self.loadOtherPage();
        }
    },
    init: function() {
        this.bindEvent();
        this.loadIndexPage();
    }
}
Route.init();

1赞