最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue-Router

    正文概述 掘金(泽能)   2020-11-28   609

    思维导图

    Vue-Router

    开始

    先看平时使用的 Vue-Router ,引入Router , Vue.use 注册插件。直接从这里开始入手

    使用场景

    import Vue from 'vue'
    import Router from "../vue-router"
    import routes from './routes'
    Vue.use(Router)
    let router =  new Router({
      routes
    })
    

    index

    先看vue-router.js文件,先生成一个VueRouter类,然后导入install方法,因为Vue-Routerinstall方法比Vuex复杂一些,所以将install单独作为一个文件。

    import install from './install';
    
    class VueRouter {
        constructor(options) {
          
        }
    
    }
    VueRouter.install = install;
    
    export default VueRouter
    

    Vue.use()

    来先看 Vue.use()的源码中的一部分,这里面判断注册的插件里的install是不是一个函数,有就执行插件里的install函数。或者判断插件本身是不是一个函数,有就执行插件本身。这里本质上是没有区别,有没有install都可以。而VueRouter使用了install,目的是为了将install作为入口函数,方便封装,同时也将install和其他代码分开。

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

    install

    上述已经将vue-router的类构建好,现在VueRouter实例已经有了,然后执行vue.use(),然后会执行VueRouter类里的install函数,那来看install.js
    install里使用了Vue.mixin,混入代码,下面代码是在当前组件生命周期beforeCreate里混入代码,代码逻辑是判断当前组件是否为根组件,如果是则将_routerRoot作为键放入当前组件中,值为Vue实例。再将_router作为键放入当前组件中,值为VueRouter实例。然后执行初始化操作。如果不为当前组件不是根组件,则该组件为根组件的子组件。将_routerRoot作为键放入当前组件中,值为父组件的_routerRoot,从父亲身上获取.
    $route$router是利用Object.definePropert代理_routerRoot里的_router_route,访问到的。
    接着注册全局组件router-viewrouter-link

    import RouterView from '../components/router-view'
    import RouterLink from '../components/router-link'
    
    const install = (_Vue, s) => {
        _Vue.mixin({
            beforeCreate() {
                if (this.$options.router) {      // 判断是不是根组件            
                    this._routerRoot = this;              // 把vue实例放在当前组件的——routerRoot属性上
                    this._router = this.$options.router   // 这里直接获取根实例
                    this._router.init(this);      // 初始化     
                    _Vue.util.defineReactive(this, '_route', this._router.history.current)  // 将属性_route成为响应式属性
                } else {
                    this._routerRoot = this.$parent && this.$parent._routerRoot   // 这里的是从父亲最顶层获取Router实例
                }
            }
        })
        Object.defineProperty(_Vue.prototype, '$route', {    // 代理$route
            get() {
                return this._routerRoot._route
            }
        })
        Object.defineProperty(_Vue.prototype, '$router', {  // 代理$router
            get() {
                return this._routerRoot._router
            }
        })
        _Vue.component('router-view', RouterView)   // 注册组件router-view
        _Vue.component('router-link', RouterLink)   // 注册组件router-view
    }
    export default install
    

    init

    然后回到VueRouter类中,此时多了个init函数。目前做的路由方式是hash方式,还有另外俩种方式,historyabstract。因为有三种方式,所以Vue-Router做了一个父类base执行同样的逻辑,子类三种方式继承父类base,再独自执行自己方式的代码。
    通过new HashHistory 获取 history实例,初始化init执行history实例对应函数。将目光放到history实例上,这些函数来自于base.jshash.js

    import install from './install';
    
    class VueRouter {
        constructor(options) {
            this.history = new HashHistory(this);
        }
    
    	
    
        init(app) {
            const history = this.history;
    
            const setupHashLister = () => {
                history.setupLister(); // hash的跳转
            }
    
            history.transitionTo(
                history.getCurrentLocation(),
                setupHashLister
            )
    
            history.listen((route) => {
                app._route = route
            })
    
        }
    
    }
    VueRouter.install = install;
    
    export default VueRouter
    

    base.js

    先看构造函数construcror,将router作为键放在自身实例上,值为VueRouter实例,curent为当前导航正要离开的路由,也就是路由守卫的参数里的from
    1.transitionTo()为跳转后立即执行的函数,传入当前路径和回调函数,r$route,是扁平化后的配置,也就是即将要进入的目标 路由对象
    2.cbHistorylisten()函数,将$route放入当前组件上供用户使用。
    3.callback是执行HashHistorysetupHashLister()函数,是给当前window添加监听事件onhashChangeonhashChange后续通过hash变化执行transitionTo进行更新。
    4.最后将r赋值给current,更新路由信息。

    class History {
        constructor(router) {
            this.router = router;
            this.current = createRoute(null, {
                path: '/'
            })
        }
    
        transitionTo(location, callback) {
    
            let r = this.router.match(location)
            if (location == this.current.path && r.matched.length == this.current.matched.length) { // 防止重复跳转
                return
            }
    
    
         
            this.cb && this.cb(r);
            callback && callback();
            this.current = r;
        }
    
    
    
        listen(cb) {
    
            this.cb = cb;
        }
    
    }
    

    hash.js

    hash方式的函数就简单介绍一下,看构造函数constructor,跟父类一样赋值router。执行ensureSlash函数,因为hash相比其他函数,一进入页面就会多个#。所以就初始化的时候处理一下。getCurrentLocation函数是获取当前路径的,pushhash方式的跳转,setupLister函数是刚刚所述的监听函数hashchange

    import Histroy from './base';
    
    function ensureSlash() {
        if (window.location.hash) {
            return
        }
        window.location.hash = '/';
    }
    
    
    class HashHistory extends Histroy {
        constructor(router) {
            super();
            this.router = router;
            ensureSlash();
        }
        getCurrentLocation() {
            return window.location.hash.slice(1);
        }
        push(location){
            this.transitionTo(location,()=>{
                window.location.hash = location
            })
        }
        setupLister() {
            window.addEventListener('hashchange', () => {
                this.transitionTo(window.location.hash.slice(1));
            })
    
        }
    
    }
    export default HashHistory
    

    扁平化

    刚刚base.js里执行的this.router.match(location)以及createRoute(),都是需要建立在扁平化配置基础之上的。 平时配置的路由是这样的,需要将配置进行扁平化,才能用得上。

     [
        {
            path: '/',
            name: 'home',
            component: Home
        },
        {
            path: '/about',
            name: 'about',
            component: About,
            children: [
                {
                    path: 'add',
                    name: 'add',
                    component: Add
                },
                {
                    path: 'bull',
                    name: 'bull',
                    component: Bull
                }
            ]
        }
    ]
    

    扁平化后是这样的

    /: {path: "/", component: {…}, parnent: undefined}
    /about: {path: "about", component: {…}, parnent: {…}}
    /about/add: {path: "add", component: {…}, parnent: {…}}
    /about/bull: {path: "bull", component: {…}, parnent: {…}}
    

    接着看扁平化函数createMatcher以及createRouteMap

    createMatcher

    createMatcher返回一个match函数,match方法是匹配路径,根据路径拿扁平化对象里的配置,然后执行createRoute方法,将其转化为route,返回。pathMapcreateRouteMap生成

    import createRouteMap from './create-route-map'
    import { createRoute } from './history/base';
    
    export default function createMatcher(routes) {
        let { pathList, pathMap } = createRouteMap(routes);
    
    
        function match(location) {
            console.log(pathMap)
            let record = pathMap[location];
            
            return createRoute(record,{
                path: location
            })
        }
    
        return {
            match
        }
    }
    
    

    createRouteMap

    routes配置传入createRouteMap中,遍历routes,进行扁平化操作 pathMap以路径为键名,值为一个对象包裹着路径,组件,父组件。 将路径匹配上父组件的路径和自身的路径 如果有子组件就进行递归,全部转为扁平化返回。

    export default function createRouteMap(routes, oldPathList, oldpathMap) {
    
        let pathList = oldPathList || [];
        let pathMap = oldpathMap || Object.create(null);
        routes.forEach(route => {
            addRouteRecord(route, pathList, pathMap);
        })
    
        return {
            pathList,
            pathMap
        }
    }
    
    function addRouteRecord(route, pathList, pathMap,parnent) {
        let path = parnent ? `${parnent.path}/${route.path}`: route.path;
        let record = {
            path: route.path,
            component: route.component,
            parnent
        }
        if (!pathMap[path]) {
            pathList.push(path);
            pathMap[path] = record;
        }
        if(route.children){
            route.children.forEach(route=>{
                addRouteRecord(route, pathList, pathMap,record)
            })
        }
    }
    
    

    createRoute

    createRoute是生成$route的函数,传入参数为扁平化配置,路径。将res作为空数组,如果传进来的扁平化配置有值,则进行while循环,将自己从数组头部插入,取出父组件再从头部插入,如此反复,得到一个含着层次关系的数组。将loaction和数组包裹为对象返回。

    export function createRoute(record, location) {
        let res = [];
    
        if (record) {
            while (record) {
                res.unshift(record)
                record = record.parnent
            }
        }
    
        return {
            ...location,
            matched: res
        }
    }
    
    

    router-view

    然后在来看看routerview 是一个函数式组件, 返回render方法 进行while循环,遍历出嵌套的routerviewdepth作为深度,也是matchedindex. 每遍历一次,就在$vnode.data.rouView 改为true,将深度加1 返回对应的组件即可

    export default {
        name:'routerView',
        functional: true,
        render(h,{parent,data}) {
            let route = parent.$route
    
            let depth = 0;
            while (parent) {  
                if (parent.$vnode && parent.$vnode.data.routeView) {
                    depth++;
                }
                parent = parent.$parent;
            }
            data.routeView = true;
            let record = route.matched[depth];
            if (!record) {
                return h();
            }
            return h(record.component, data);
        }
    }
    

    router-link

    再来看看routerlink 没什么东西就返回一个a标签,用插槽把对应的文本显示出来,在添加的跳转事件 调用$routerpush方法,也就是Router类上的push

    export default {
        name: 'routerLink',
        props: {
            to: {
                type: String,
                required: true
            },
            tag: {
                type: String,
                default: 'a'
            }
        },
        methods: {
            handler(to) {
                this.$router.push(to)   // 路由跳转
            }
        },
        render() {
        
            return <a onClick={this.handler.bind(this,this.to)}>{this.$slots.default[0].text}</a>
        }
    }
    

    Vue-Router草稿 :https://shimo.im/docs/TqKywdwhwV9Jrqg8


    起源地下载网 » Vue-Router

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元