手寫vue-router源碼 實現屬於本身的路由

先簡單說一說,咱們前端路由的實現主要是爲了SPA應用的框架,框架開發都是單頁應用,單頁應用的特色就是跳轉頁面的時候的不刷新瀏覽器,那想要實現跳轉頁面不刷新瀏覽器的方式有兩種:一種是經過hash的方式、另外一種就是經過H5的history的api實現javascript

路由實現的核心原理

  • 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>
複製代碼
  • history api的實現原理
<a onclick="go(/home)">首頁</a>
<a onclick="go(/about)">關於</a>
<div id="html"></div>

<script > function go(pathname) { history.pushState({},null,pathname) html.innerHTML = pathname; } window.addEventListener('popstate',()=>{ go(location.pathname); }) </script>

複製代碼

源碼實現

  • $options下的router對象是咱們在實例化Vue的時候掛載的那個vue-router實例;
  • _route是一個響應式的路由route對象,這個對象會存儲咱們路由信息,它是經過Vue提供的Vue.util.defineReactive來實現響應式的,下面的get和set即是對它進行的數據劫持;
  • _router存儲的就是咱們從$options中拿到的vue-router對象;
  • _root指向咱們的Vue根節點;
  • route和$router是定義在Vue.prototype上的兩個getter。前者指向_root下的_route,後者指向_root下的_router
class HistoryRoute{
    constructor(){
        this.current = null;
    }
}
class vueRouter {
  constructor(options){
      this.mode = options.mode || "hash";
      this.routes = options.routes || [];
      // 傳遞的路由表是數組 須要裝換成{'/home':Home,'/about',About}格式
      this.routesMap = this.createMap(this.routes);
      // 路由中須要存放當前的路徑 須要狀態
      this.history = new HistoryRoute;
      this.init();//開始初始化操做
  }
  init(){
      if(this.mode == 'hash'){
          // 先判斷用戶打開時有沒有hash,沒有就跳轉到#/
          location.hash?'':location.hash = '/';
          window.addEventListener('load',()=>{
              this.history.current = location.hash.slice(1);
          });
          window.addEventListener('hashchange',()=>{
              this.history.current = location.hash.slice(1);
          })
      }else {
          location.pathname?'':location.pathname = '/';
          window.addEventListener('load',()=>{
              this.history.current = location.pathname;
          });
          window.addEventListener('popstate',()=>{
              this.history.current = location.pathname;
          })
          
      }
  }
  createMap(routes){
      return routes.reduce((memo,current)=>{
          memo[current.path] = current.component
          return memo
      },{})
  }
}
//使用vue.use就會調用install方法
vueRouter.install = function(Vue,opts) {
    //每一個組件都有 this.$router / this.$route 因此要mixin一下
    Vue.mixin({
        beforeCreate(){ //混合方法
            if(this.$options && this.$options.router){//定位跟組件
                this._root = this;//把當前實例掛載在_root上
                this._router = this.$options.router // 把router實例掛載在_router上
                //history中的current變化也會觸發
                Vue.util.defineReactive(this,'xxx',this._router.history);
            }else {
                // vue組件的渲染順序 父 -> 子 -> 孫子
                this._root =  this.$parent._root;//獲取惟一的路由實例
            }
            Object.defineProperty(this,'$router',{//Router的實例
                get(){
                    return this._root._router;
                }
            });
            Object.defineProperty(this,'$route',{
                get(){
                    return {
                        //當前路由所在的狀態
                        current:this._root._router.history.current
                    }
                }
            })
        }
    });
    // 全局註冊 router的兩個組件
    Vue.component('router-link',{
        props:{
            to:String,
            tag:String
        },
        methods:{
            handleClick(){跳轉方法
                
            }
        },
        render(h){
            let mode = this._self._root._router.mode;
            let tag = this.tag;
            return <tag on-click={this.handleClick} href={mode === 'hash'?`#${this.to}`:this.to}>{this.$slots.default}</tag>
        }
    })
    Vue.component('router-view',{//根據當前的狀態 current 對應相應的路由
        render(h){
            //將current變成動態的 current變化應該會影響視圖刷新
            //vue實現雙向綁定 重寫Object.defineProperty
            let current = this._self._root._router.history.current;
            let routeMap = this._self._root._router.routesMap
            return h(routeMap[current])
        }
    })
}
export  default VueRouter;


複製代碼
相關文章
相關標籤/搜索