實現一個簡單的vue-router

全部項目的源代碼都放在個人github上,歡迎你們start: https://github.com/Jasonwang911/my-vue-routerhtml

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

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項目的入口文件中:git

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 的方法集github

vue-router還區分兩種路由模式 mode: hash模式和history模式
1.hash模式的基本原理是在頁面加載完和hash發生變化的時候獲取 location.hash, 代碼放在個人github上: https://github.com/Jasonwang911/my-vue-router/blob/master/static/hash.html<router-view></router-view> // 用來顯示路由組件的視圖組件,須要註冊 Vue.use(VueRouter); <router-link to="/home"></router-link> // 連接全局組件,包括一些屬性 eg: to this.$route 屬性集 this.$router 方法集

<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.htmlvue-router

<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(只實現了部分功能和一層組件,若是須要實現子路由請自行遞歸):app

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-routerthis

相關文章
相關標籤/搜索