Vue-Router 源碼解析(五) router-link組件的用法及原理


該組件支持用戶在具有路由功能的應用中(點擊)導航,默認渲染成帶有正確鏈接的<a>標簽,可以通過tag屬性生成別的標簽。

它本質上是通過在生成的標簽上綁定了click事件,然后執行對應的VueRouter實例的push()實現的,對於router-link組件來說,可以傳入以下props:

  • to                                    表示目標路由的鏈接,當被點擊后,內部會立刻把to的值傳到router.push(),所以這個值可以是一個字符串或者是描述目標位置的對象
  • tag                                 router-link組件渲染的標簽名,默認為a
  • exact                              布爾類型,“是否激活”默認類名的依據是包含匹配
  • append                          布爾類型,設置append屬性后,則在當前(相對)路勁前添加基路勁
  • replace                          布爾類型,設置replace后,當點擊時會調用router.replace()而不是router.push(),這樣導航后不會留下history記錄
  • activeClass                   鏈接激活時使用的CSS類名
  • exactActiveClass          配置當鏈接被精確匹配的時候應該激活的 class
  • event                            聲明可以用來觸發導航的事件。可以是一個字符串或是一個包含字符串的數組

舉個栗子:

 writer by:大沙漠 QQ:22969969

    <div id="app">
        <router-link to="/info/1">詳情頁1</router-link>     
        <router-link to="/info/2" event="mouseover">詳情頁2</router-link>       
        <router-link to="/info/3" tag="p">詳情頁3</router-link>     
        <hr/>
        <router-view></router-view>
    </div>
    <script>                   
        const info  = { template:'<div>id:{{this.$route.params.id}}</div>'}
        const routes = [                                   
            {path:'/info/:id',component:info,name:'info'},
        ]
        const app = new Vue({                                                     
            el:'#app',
            router:new VueRouter({routes})
        })
    </script>

渲染后的DOM如下:

可以看到第二個routerlink組件渲染成了p標簽,我們點擊時效果如下:

可以看到詳情頁3上並沒有點擊,鼠標移動上去,就會自動切換路由了,這是因為我們傳入了event,在上面綁定了onmouseover事件。

router-link組件自定義了render函數,如下:

var Link = {
  name: 'RouterLink',
  props: {                          //可以傳入的props
    to: {
      type: toTypes,
      required: true
    },
    tag: {
      type: String,
      default: 'a'
    },
    exact: Boolean,
    append: Boolean,
    replace: Boolean,
    activeClass: String,
    exactActiveClass: String,
    event: {
      type: eventTypes,
      default: 'click'                      //默認為click事件
    }
  },
  render: function render (h) {       //router-link組件對應的render函數   h等於大vue上的$createElement函數
    var this$1 = this;

    var router = this.$router;                                //獲取$router實例
    var current = this.$route;                                //當前的路由對象 
    var ref = router.resolve(this.to, current, this.append);
    var location = ref.location;                              //要跳轉的地址,是一個對象    ;例如:{_normalized: true, path: "/login", query: {…}, hash: ""} 
    var route = ref.route;                                    //路由對象
    var href = ref.href;                                      //href屬性
 
    var classes = {};                                         //這段代碼和linkActiveClass、linkExactActiveClass的配置有關
    var globalActiveClass = router.options.linkActiveClass;
    var globalExactActiveClass = router.options.linkExactActiveClass;
    // Support global empty active class
    var activeClassFallback = globalActiveClass == null
      ? 'router-link-active'
      : globalActiveClass;
    var exactActiveClassFallback = globalExactActiveClass == null
      ? 'router-link-exact-active'
      : globalExactActiveClass;
    var activeClass = this.activeClass == null
      ? activeClassFallback
      : this.activeClass;
    var exactActiveClass = this.exactActiveClass == null
      ? exactActiveClassFallback
      : this.exactActiveClass;
    var compareTarget = location.path
      ? createRoute(null, location, null, router)
      : route;

    classes[exactActiveClass] = isSameRoute(current, compareTarget);
    classes[activeClass] = this.exact
      ? classes[exactActiveClass]
      : isIncludedRoute(current, compareTarget);

    var handler = function (e) {                          //綁定的事件
      if (guardEvent(e)) {                                  //先調用該函數過濾掉一些和導航無關的事件
        if (this$1.replace) {                               //如果設置了replace屬性
          router.replace(location);                           //則執行router.replace切換導航
        } else {
          router.push(location);                              //否則執行router.push更新導航
        }
      }
    };

    var on = { click: guardEvent };                               //<router-link>組件默認都支持的click事件,下面會重置的
    if (Array.isArray(this.event)) {
      this.event.forEach(function (e) { on[e] = handler; });
    } else {
      on[this.event] = handler;
    }

    var data = {                                              //一般情況下,這里會重置on.click為handler
      class: classes
    };

    if (this.tag === 'a') {                                   //如果tag是一個a標簽
      data.on = on;
      data.attrs = { href: href };
    } else {                                                  //如果是其它標簽          
      // find the first <a> child and apply listener and href
      var a = findAnchor(this.$slots.default);                  //查找第一個<a>標簽
      if (a) {                                                  //如果找到了一個a標簽
        // in case the <a> is a static node
        a.isStatic = false;
        var aData = a.data = extend({}, a.data);
        aData.on = on;
        var aAttrs = a.data.attrs = extend({}, a.data.attrs);
        aAttrs.href = href;
      } else {                                                  //如果沒有找到a標簽
        // doesn't have <a> child, apply listener to self
        data.on = on;                                             //則監聽自身
      }
    }

    return h(this.tag, data, this.$slots.default)             //最后調用$createElement去創建該Vnode
  }
}

如果是a標簽,在guardEvent函數內會執行preventDefault()函數通知瀏覽器不要執行與事件相關的動作,所有的動作比如設置hash都是通過VueRouter的內置代碼來實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM