博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Vue源码解析(六)-vue-router
阅读量:6268 次
发布时间:2019-06-22

本文共 6713 字,大约阅读时间需要 22 分钟。

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

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

Basic

`})

中介绍过,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的值如下,

clipboard.png

再看一下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都是继承Historyvar 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初始化时根据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) }};

转载地址:http://kyvpa.baihongyu.com/

你可能感兴趣的文章
Laravel的三种安装方法总结
查看>>
SpringMVC加载配置Properties文件的几种方式
查看>>
C#设计模式总结 C#设计模式(22)——访问者模式(Vistor Pattern) C#设计模式总结 .NET Core launch.json 简介 利用Bootstrap Paginat...
查看>>
java 项目相关 学习笔记
查看>>
numpy opencv matlab eigen SVD结果对比
查看>>
WPF获取某控件的位置,也就是偏移量
查看>>
Boost C++ 库 中文教程(全)
查看>>
solr查询优化(实践了一下效果比较明显)
查看>>
jdk目录详解及其使用方法
查看>>
说说自己对RESTful API的理解s
查看>>
通过layout实现可拖拽自动排序的UICollectionView
查看>>
服务器错误码
查看>>
javascript中的面向对象
查看>>
Splunk作为日志分析平台与Ossec进行联动
查看>>
yaffs文件系统
查看>>
Mysql存储过程
查看>>
NC营改增
查看>>
Lua
查看>>
Mysql备份系列(3)--innobackupex备份mysql大数据(全量+增量)操作记录
查看>>
postgresql 获取刚刚插入的数据主键id
查看>>