zoukankan      html  css  js  c++  java
  • 实现一个简单的vue-router

    所有项目的源代码都放在我的github上,欢迎大家start: https://github.com/Jasonwang911/my-vue-router

    首先来看下vue-router的使用: 

    import Vue from 'vue';
    import VueTouter from 'vue-router'; // VueRouter 是一个类,用来new 一个实例

    // 注册vue-router
    Vue.use(VueRouter); new VueRouter({ mode: 'hash', // hash 或者是 history routes: [ {path: '/home', component: Home }, ] })

    vue项目的入口文件中:

    import Vue from "vue";
    import App from "./App.vue";
    // 引入配置好的router import router from "@/router"; Vue.config.productionTip = false; new Vue({
     //将路由注入根组件 router, render: h => h(App) }).$mount("#app");

    同时视图文件展示在全局组件  router-view 上,并使用全局组件  router-link 进行跳转 , 并且在所有组件上都会有一个 $route 的属性集和 一个 $router 的方法集

    <router-view></router-view>    // 用来显示路由组件的视图组件,需要注册  Vue.use(VueRouter); 
    <router-link to="/home"></router-link>   // 链接全局组件,包括一些属性 eg: to
    this.$route    属性集
    this.$router   方法集  

    vue-router还区分两种路由模式 mode: hash模式和history模式
    1.hash模式的基本原理是在页面加载完和hash发生变化的时候获取 location.hash, 代码放在我的github上: https://github.com/Jasonwang911/my-vue-router/blob/master/static/hash.html
    <body>
        <!-- hash -->
        <a href="#/home">首页</a>
        <a href="#/about">关于我们</a>
        <div id="html"></div>
    
    <script>
        window.addEventListener('load', () => {
            html.innerHTML = location.hash.slice(1);
        })
        window.addEventListener('hashchange', () => {
            html.innerHTML = location.hash.slice(1);
        })
    </script>
    

      2.history的基本原来是使用浏览器的 history API    演示代码放在我的github上: https://github.com/Jasonwang911/my-vue-router/blob/master/static/history.html

    <body>
        <a  onclick="go('/home')">首页</a>
        <a onclick="go('/about')">关于</a>
        <div id="html"></div>
    
    <script>
        function go(pathname) {
            // 传入的数据  标题null  真正的路径  --- 页面不会刷新,后退按钮有问题
            history.pushState({}, pathname, pathname);
            html.innerHTML = pathname;
        }
        // 浏览器的后退按钮
        window.addEventListener('popstate', () => {
            go(location.pathname);
        })
    </script>
    

      

    简单介绍一下注册插件 vue.use 这个方法: vue要求在插件上添加一个 install 的方法,这个方法接收两个参数,第一个参数是vue, 第二个参数是options, options用来给插件传值

    import Vue from 'vue';
    import VueRouter from '@/router/vue-router.js';
    
    // 注册组件
    Vue.use(VueRouter, {name: 'jason', age: 18});
    
    
    // VueRouter 的类  (/router/vue-router.js)
    class VueRouter {
    
    }
    
    // 使用vue.use 就会调用 install 方法, 方法上有一个参数是vue实例
    VueRouter.install = function(Vue, options) {
        console.log(Vue, options);
    }
    
    export default VueRouter;
    

      

    根据上面的思路来简单实现一下vue-router(只实现了部分功能和一层组件,如果需要实现子路由请自行递归):

    import Vue from 'vue';
    // 路由的history属性类
    class HistoryRoute {
        constructor() {
            this.current = null;
        }
    }
    
    // VueRouter 的类
    class VueRouter {
        // options 中包含 mode 和 routes
        constructor(options) {
            this.mode = options.mode || 'hash';
            this.routes = options.routes || [];
            // 把routes改成 路径:组件 的键值对对象,方便拿到路径直接渲染组件
            this.routesMap = this.createMap(this.routes);
            console.log('收敛后的路由表===>',this.routesMap);
            // 路由中history属性存放当前路径  创建一个history类,方便扩展属性  {currnet: null}
            this.history = new HistoryRoute;
            // 初始化操作
            this.init();
        }
    
        init() {
            console.log('执行了初始化的操作')
            // 判断路由模式
            if(this.mode === 'hash') {
                // 先判断用户打开时有没有hash,没有就跳转到 #/
                location.hash ? '' : location.hash = '/';
                // 页面加载完成当前路径写入this.history
                window.addEventListener('load', () => {
                    this.history.current = location.hash.slice(1);
                });
                // 监听 hash 变化并把当前路径存入 this.history
                window.addEventListener('hashchange', () => {
                    this.history.current = location.hash.slice(1);
                });
            }else if(this.mode === 'history'){
                // 判断用户打开页面的 pathname
                location.pathname ? '' : location.pathname = '/';
                // 页面加载完成当前路径写入this.history
                window.addEventListener('load', () => {
                    this.history.current = location.pathname;
                });
                // 监听 history 变化并把当前路径存入 this.history
                window.addEventListener('popstate', () => {
                    this.history.current = location.pathname;
                });
            }else {
                throw new Error(`vue-router mode error, can no font router mode: ${this.mode}`);
            }
        }
    
        // 收敛路由表 this.routes
        createMap(routes) {
            return routes.reduce((prev, next, index, arr) => {
                prev[next.path] = next.component;
                return prev;
            }, {});
        }
    }
    
    // 使用vue.use 就会调用 install 方法, 方法上有一个参数是vue实例
    VueRouter.install = function(Vue) {
        // 混合到每个组件中路由属性和路由方法  每个组件都有 this.$router / this.$toute  this是当前的组件 组件中所有属性都在 this.$options上
        Vue.mixin({
            beforeCreate () {
                // this.$router 是 vue-router 的实例, 即 VueRouter, 在 main.js中实例化vue的时候传入的 vue-router 实例,需要在所有组件中拿到这个路由实例
                // vue 组件是从上到下渲染的 
                if(this.$options && this.$options.router) {
                    // 根组件
                    this._root = this;
                    this._router = this.$options.router;
                    // vue.util.defineReactive 是vue的一个核心库, 接收三个参数, 监听谁,第二个参数是一个别名,第三个参数如果是对象会深度监听,给对象中的每个属性加上set方法
                    // hostoryzhong de current发生变化会令视图发生变化
                    Vue.util.defineReactive(this, 'xxx' , this._router.history );
                }else {
                    this._root = this.$parent._root;
                }
                Object.defineProperty(this, '$router', {
                        get() {
                            return this._root._router;
                        }
                });
                // this.$route 是路由实例的属性
                Object.defineProperty(this, '$route', {
                        get() {
                            return {
                                current: this._root.history.current
                            }
                        }
                    });
            }
        });
        // 注册全局组件
        Vue.component('router-link', {
            props: {
                to: {
                    type: String,
                    default: ''
                },
                tag: String
            },
            methods: {
                // <tag on-click={this.handleClick.bind(this)}></tag>
                handleClick() {
                    
                }
            },
            // h 表示 createElement 
            render(h) {
                let mode = this._self._root._router.mode;
                let tag = this.tag;
                // return h('a', {}, '首页');
                return <a href={mode === 'hash' ? `#${this.to}` : this.to}>{this.$slots.default}</a>
            }
        });
        // 根据当前的状态 history.current 匹配 收敛后的路由表
        Vue.component('router-view', { 
            // this 是 proxy   
            // h 表示 createElement 
            render(h) {
                // console.log(this);  先注册组件然后才页面加载完成执行 onload , 需要 currnet 变化 视图更新  --- vue 双向绑定  Object.defineProperty
                 console.log('====>', this._root)
                 let current = this._root._router.history.current;
                 let routeMap = this._self._root._router.routesMap;
                 console.log(current)
                return h(routeMap[current]);
            }
        });
    }
    
    export default VueRouter;
    

      

    所有项目的源代码都放在我的github上,欢迎大家start: https://github.com/Jasonwang911/my-vue-router



  • 相关阅读:
    吴裕雄--天生自然 诗经:天净沙·秋思
    阿里云Kubernetes服务
    一探究竟:善用 MaxCompute Studio 分析 SQL 作业
    MaxCompute Studio 使用入门
    AI 一体机,满足新时代的新需求
    OSS重磅推出OSS Select——使用SQL选取文件的内容
    如何将DynamoDB的数据增量迁移到表格存储
    多隆:淘宝第一行代码撰写者的程序世界
    专访阿里巴巴量子实验室:最强量子电路模拟器“太章”到底强在哪?
    饿了么CTO张雪峰:允许90后的技术人员“浮躁“一点
  • 原文地址:https://www.cnblogs.com/jasonwang2y60/p/10448542.html
Copyright © 2011-2022 走看看