WEB架構師學習筆記_Vue_02 手寫一個路由

知識導航:

  • 路由重要知識點
  • 手寫一個路由

知識點(建議最後看,放到是爲了方便複習)
在這裏插入圖片描述

1. 路由重要知識點

vue-router對於寫過vue的人想必是很熟悉了,所以這裏忽略它的一些基本用法了。

1.1 動態路由

所謂動態路由就是類似那種restful接口對接形式,即以/demo/:id,即可匹配到demo路由同時可在demo路由下的組件中拿到傳過來的id值。
示例:

路由配置中:
{
path: "/",
component: Home,
children: [
{ path: "", name: "home", component: List },
{ path: "/demo/:id", component: Demo},
]
}
發出鏈接組件中的router-link
<router-link to="/demo/1">...</router-link>

接收組件
{{$route.params.id}}
或者
將路由routes配置中更改爲{ path: "/demo/:id", component: Demo,props:true}
這時候接收組件便可以通過props來接收參數了,例如{ props: ['id'] }

1.2 路由守衛

1.2.1 全局守衛

就是一些鉤子,通過運行這些鉤子的執行時期來做一些我們想要的控制。即每次發生路由的跳轉變化執行一下我們所設置的某些邏輯

1.2.1.1 前置鉤子(即前置守衛)

//to:即要進入的路由
//from:即要離開的路由
//next:它是一個函數,即可控制是否能夠進入下個路由
//它的參數形式(1)next(); //默認路由(2)next(false); //阻止路由跳轉(3)next({path:'/'}); //阻止默認路由,跳轉到指定路徑
router.beforeEach((to, from, next) = {
    if (to.path == "/login") {
        next();
    } else {
        next("/login")
    }
})

1.2.1.1 後置鉤子(即後置守衛)

後置鉤子用法和前置鉤子基本一樣,只不過它所接受的函數參數中沒有了next

router.afterEach((to, from) => {
  //邏輯代碼
})

1.2.2 路由獨享

使用和全局類似。
示例:

        //登錄模塊
        path: '/login',
        component:Login,
        beforeEnter: (to, from, next) => {
            if (to.meta.needLogin && !$store.state.isLogin) {
        }

1.2.3 組件內的守衛

直接放進組件中的路由鉤子。
即:(說明放在代碼中)

export default {
  //出去時觸發
  beforeRouteLeave(to, from, next) {
    next();
  },
 //進來時觸發
  beforeRouteEnter(to, from, next) {
    next();
  },
   //它的場景是那種動態路由,如此組件匹配的是/demo/:id,即雖路由發生了變化但還是進到當前組件
  beforeRouteUpdate(to, from, next) {
    next();
  },
  data: {},
  method: {}
};

2. 手寫一個路由插件

思路:回想我們在使用路由的時候進行了哪些配置。(ps只是基礎實現功能)
觀察

路由配置js文件中:
在這裏插入圖片描述

入口main.js文件:
在這裏插入圖片描述
通過觀察可以知道我們接下來要做什麼了。

  1. 寫一個路由類
  2. 構造函數需要接受傳過來的參數
  3. 要有一個path和組件信息字典即需要把參數整成{path:組件名}
  4. 需要有個響應式的東西用於存放url中的哈希值
  5. 需要監聽url中哈希值的變化
  6. 需要做兩個組件,router-link和 router-view
  7. 需要把它做成插件

1.開始

current用於存放哈希值借用vue的響應式數據,map用於存放path和組件信息的映射

class MyRouter {
    constructor(routes) {
        this.$routes = routes;
        this.map = {};
        this.app = new Vue({
            data: {
                current: '/'
            }
        })

    }

2.接下來先把它整成一個組件吧
首先我們要理解在路由配置js文件中Vue.use()。它的作用是安裝插件,如果插件是一個對象則該對象必須提供install方法。如果插件是一個方法,那麼這個方法便會被做爲install方法。並且在調用install方法時,Vue會作爲參數傳進去。

即我們想要把上面寫的東西搞成一個組件,就得實現一個install方法。

下面我們用了一下混入,即Vue.mixin({})。簡單說一下它,它的作用是全局註冊一個混入,即它會影響註冊之後創建的每一個vue實例。既然可以影響每個vue實例,那我們可以搞一個vue的鉤子。控制vue實例在它的生命週期中做些我們讓它做的事情

MyRouter.install = function(_vue) {
    Vue = _vue;
    Vue.mixin({
        beforeCreate() {
        //目前,在vue根據組件實例時初始化路由,只有根組件實例化時有router。看上面入口文件
            if (this.$options.router) {
                this.$options.router.init();
            }
        }
    })
}

3.實現init方法

init() {
        // 初始化要做的事情:
        // 1.進行哈希值的監聽
      	this.addEvent();
        // 2.做好字典映射
        this.creatMap(this.$routes);
        // 3.整好路由的兩個專有組件
        this.creatCom();
    }

4.實現監聽addEvent

  addEvent() {
  			哈希值給到current,current是響應式的即哈希值一變,所有的用到current的地方便 跟着變化
        window.addEventListener("hashchange", function() {
            // 哈希值一改變對應的current便需要改變,注意這裏的this指向爲window
            this.app.current = window.location.hash.slice(1) || "/";
        }.bind(this));
        window.addEventListener('load', function() {
            this.app.current = window.location.hash.slice(1) || "/";
        }.bind(this));

    }

5.實現path與組件信息的映射creatMap(routes);

   creatMap(routes) {
        routes.routes.forEach(item => {
            this.map[item.path] = item;
        });

    }

6.創鍵兩個專有組件creatCom()

creatCom() {

        Vue.component("router-link", {


            props: {
                to: String
            },
            //就是渲染一個a標籤
            render(h) {
                return h("a", {
                    attrs: {
                        href: "#" + this.to
                    }
                }, this.$slots.default)
            },
        });
        
        Vue.component("router-view", {
        //將組件渲染成真正的DOM視圖
            render: (h) => {
                return h(this.map[this.app.current].component)
            }
        })
    }

測試:引用我們寫的路由
在這裏插入圖片描述
效果:

在這裏插入圖片描述
在這裏插入圖片描述
路由完整代碼:

// 思路
// 1.接收一個map的配置信息
// 2.監聽哈希值的變化
// 3.哈希值一旦法師變化,重新渲染頁面
let Vue;
class MyRouter {
    constructor(routes) {
        this.$routes = routes;
        this.map = {};
        this.app = new Vue({
            data: {
                current: '/'
            }
        })

    }
    init() {
        // 初始化要做的事情:
        // 1.進行哈希值的監聽
        // 2.做好字典
        // 3.整好路由的兩個專有組件
        this.addEvent();
        this.creatMap(this.$routes);
        this.creatCom();
    }
    addEvent() {
        window.addEventListener("hashchange", function() {
            // 哈希值一改變對應的current便需要改變,注意這裏的this指向爲window
            this.app.current = window.location.hash.slice(1) || "/";
        }.bind(this));
        window.addEventListener('load', function() {
            this.app.current = window.location.hash.slice(1) || "/";
        }.bind(this));

    }


    creatMap(routes) {
        routes.routes.forEach(item => {
            this.map[item.path] = item;
        });

    }
    creatCom() {

        Vue.component("router-link", {


            props: {
                to: String
            },
            render(h) {
                return h("a", {
                    attrs: {
                        href: "#" + this.to
                    }
                }, this.$slots.default)
            },
        });
        Vue.component("router-view", {
            render: (h) => {
                return h(this.map[this.app.current].component)
            }
        })
    }
}


// 寫成一個插件
// 即實現一個install方法

MyRouter.install = function(_vue) {
    Vue = _vue;
    Vue.mixin({
        beforeCreate() {
            if (this.$options.router) {          
                this.$options.router.init();
            }
        }
    })
}

export default MyRouter
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章