资讯专栏INFORMATION COLUMN

Vue源码解析(六)-vue-router

苏丹 / 2453人阅读

摘要:是响应式的,当浏览器路由改变时,的值也会相应的改变的作用是清楚了,但页面内容的变化是怎么实现的呢下面再介绍下的作用。

先上一段简单的demo,本文根据此demo进行解析

Vue.use(VueRouter)
const router = new VueRouter({
    routes: [
        { path: "/home", component: {template: "
home
"}} ] }) new Vue({ "el":"#app", router, template: `

Basic

` })

vue源码解析(五)中介绍过,Vue.use(VueRouter)其实主要调用了VueRouter.install(Vue)方法

function install (Vue) {
  //挂载全局的钩子函数到Vue,vue对象初始化会调用下面的函数
  Vue.mixin({
    beforeCreate: function beforeCreate () {
      if (isDef(this.$options.router)) {
        // _routerRoot为当前vue对象
        this._routerRoot = this;
        // _router为new Vue传入的VueRouter对象
        this._router = this.$options.router;
        //调用 VueRouter.protoype.init,后面介绍
        this._router.init(this);
        // 设置响应式的_route,this._router.history.current为当前页面的路由信息
        Vue.util.defineReactive(this, "_route", this._router.history.current);
      } else {
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this;
      }
    }
  });
  //全局注册组件
  Vue.component("router-view", View);
  Vue.component("router-link", Link);
}
// 全局组件router-view的参数
var View = {
  name: "router-view",
  functional: true,
  props: {
    name: {
      type: String,
      default: "default"
    }
  },
  render: function render (_, ref){}
}

install方法主要是挂载钩子函数和全局注册组件,全局注册的组件router-view的值如下,

再看一下router = new VueRouter的过程

var VueRouter = function VueRouter (options) {
  this.options = options;
  //默认使用hash进行前端路由
  var mode = options.mode || "hash";
  switch (mode) {
    case "history":
      this.history = new HTML5History(this, options.base);
      break
    case "hash":
      this.history = new HashHistory(this, options.base, this.fallback);
      break
  }
}
//  HashHistory 和 HTML5History都是继承History
var HashHistory = (function (History$$1) {
  function HashHistory (router, base, fallback) {
    History$$1.call(this, router, base);
  }

  if ( History$$1 ) HashHistory.__proto__ = History$$1;
  HashHistory.prototype = Object.create( History$$1 && History$$1.prototype );
  HashHistory.prototype.constructor = HashHistory;
  return HashHistory;
}(History));

var History = function History (router, base) {
  // VueRouter实例对象
  this.router = router;
  //base路径
  this.base = normalizeBase(base);
  //当前路由信息,此时是一个空值
  this.current = START;
};

new Vue的过程中会触发挂载的beforeCreate函数,主要是调用了this._router.init(this);
为了更清晰的解析整个流程,假定我们现在访问的页面路径是/home,并且是hash的方式进行路由

VueRouter.prototype.init = function init (app /* Vue component instance */) {
  var history = this.history;

  if (history instanceof HTML5History) {
    history.transitionTo(history.getCurrentLocation());
  } else if (history instanceof HashHistory) {
    var setupHashListener = function () {
      //监听浏览器地址的变更,并调用transitionTo“跳转”到新的路由页面
      history.setupListeners();
    };
    //初始化时调用一次transitionTo,根据当前浏览器地址栏里的path(/home)来激活对应的路由
    history.transitionTo(
      // 初始化值为“/home”
      history.getCurrentLocation(),
      setupHashListener    
    );
  }
};
HashHistory.prototype.setupListeners = function setupListeners () {
   //监听浏览器地址的变更
   window.addEventListener(supportsPushState ? "popstate" : "hashchange", function () {
      //实现页面内容的变更,getHash()为变更后的hash路径
      this$1.transitionTo(getHash())
   }
}
//将页面转换到当前真实的路由
History.prototype.transitionTo = function transitionTo (location, onComplete, onAbort) {
  var this$1 = this;
  // 根据location("/home")得到route对象{name: undefined, meta: {…}, path: "/home", hash: "", query: {…}, …}
  var route = this.router.match(location, this.current);
  //confirmTransition实现比较复杂,本文不做介绍,主要会执行下面的回调函数
  this.confirmTransition(route, function () {
    //将histoty.current值更新为route
    this$1.updateRoute(route);
    //执行onComplete(setupHashListener)
    onComplete && onComplete(route);
    //更新浏览器url地址
    this$1.ensureURL();
  }
};

function match (){
  遍历路由配置(本文只有一项配置{ path: "/home", component: {template: "
home
"}}) for (var i = 0; i < pathList.length; i++) { var path = pathList[i]; var record$1 = pathMap[path]; //判断当前路径是否有匹配的路由配置 if (matchRoute(record$1.regex, location.path, location.params)) { return _createRoute(record$1, location, redirectedFrom) } } // no match return _createRoute(null, location) } function createRoute (record){ var route = { name: location.name || (record && record.name), meta: (record && record.meta) || {}, path: location.path || "/", hash: location.hash || "", query: query, params: location.params || {}, fullPath: getFullPath(location, stringifyQuery$$1), //当前路径匹配的路由配置 matched: record ? formatMatch(record) : [] }; return Object.freeze(route) }

根据上面的代码逻辑可以分析得出,vue对象初始化时会挂载属性vm._router(记录了整个应用的路由配置信息)和vm._route(记录了当前的路由信息)。vm._route是响应式的,当浏览器路由改变时,vm._route的值也会相应的改变
vm._route的作用是清楚了,但页面内容的变化是怎么实现的呢?下面再介绍下router-view的作用。
Vue源码解析(四)-components组件介绍过,vue初始化时根据template函数生成render函数,本文render函数会调用vm._c("router-view"),_createElement判断router-view是注册过的组件,因此以组件的方式生成vnode,但是router-view生成vnode的过程与Vue源码解析(四)中的方法又有区别

function _createElement(){
    //本例tag=‘router-view’,‘router-view’在components属性中注册过,因此以组件的方式生成vnode
    if (isDef(Ctor = resolveAsset(context.$options, "components", tag))) {
      //Ctor是router-view的构造函数VueComponent(Vue.component("router-view", View)注册)
      vnode = createComponent(Ctor, data, context, children, tag);
    }
}

function createComponent (Ctor){
    //Ctor 此时已经是构造函数 , 不需要再调用Vue.extend生成
    var baseCtor = context.$options._base;
    if (isObject(Ctor)) {
      Ctor = baseCtor.extend(Ctor);
    }
    // router-view是functional component(见上文图中view的option的值),与用户自定义的component的vnode生成方法有区别
    if (isTrue(Ctor.options.functional)) {
      return createFunctionalComponent(Ctor, propsData, data, context, children)
    }
    //用户自定义component的vnode构造方法
    var vnode = new VNode(
      ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : "")),
      data, undefined, undefined, undefined, context,
      { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children:         children } );
}

function createFunctionalComponent (Ctor){
   var options = Ctor.options;
   //主要是调用View.render方法,前文提到过
   var vnode = options.render.call(null, renderContext._c, renderContext);
   return vnode;
}

var View = {
  name: "router-view",
  functional: true,
  props: {
    name: {
      type: String,
      default: "default"
    }
  },
  render: function render (_, ref) {
    //Vue实例化对象vm
    var parent = ref.parent;
    // vm._route
    var route = parent.$route;

    var depth = 0;
    //上文提到的createRoute中生成的路由匹配信息
    var matched = route.matched[depth];
    
    // _createElement方法
    var h = parent.$createElement;
    // render empty node if no matched route
    if (!matched) {
      return h()
    }
    // 本文component为{template: "
home
", _Ctor: {…}, inject: {…}} var component = matched.components[name]; //重新调用_createElement,这次是以常规方式生成vnode,后续vnode将渲染成template中的内容 return h(component, data, children) } };

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90405.html

相关文章

  • 关于Vue2一些值得推荐的文章 -- 五、月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    sutaking 评论0 收藏0
  • 关于Vue2一些值得推荐的文章 -- 五、月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    khs1994 评论0 收藏0
  • 【转】使用Vue-Router 2实现路由功能

    摘要:请输入代码注意只适用于版本,下面我们是基于讲的如何使用实现路由功能。一使用路由在中,需要明确安装路由功能定义组件,这里使用从其他文件进来定义路由创建实例,然后传配置创建和挂载根实例。路由记录就是配置数组中的对象副本还有在数组。 请输入代码注意:vue-router 2只适用于Vue2.x版本,下面我们是基于vue2.0讲的如何使用vue-router 2实现路由功能。推荐使用npm安装...

    seanlook 评论0 收藏0
  • Vue.js资源分享

    摘要:中文官网英文官网组织发出一个问题之后,不要暂时的离开电脑,如果没有把握先不要提问。珍惜每一次提问,感恩每一次反馈,每个人工作还是业余之外抽出的时间有限,充分准备好应有的资源之后再发问,有利于问题能够高效质量地得到解决。 Vue.js资源分享 更多资源请Star:https://github.com/maidishike... 文章转自:https://github.com/maid...

    vpants 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<