一张图了解 vue-router 路由变化到页面渲染过程

2,350 阅读2分钟

1.前言

vue-router 通过浏览器 HTML5 hashchange 事件 和 History API 实现了前端路由的接管。通过自定义函数式组件 router-linkrouter-view 来实现路由切换交互和渲染的承载。

2.router-link

函数式组件,实现用户点击交互劫持,并手动触发 router.pushrouter.replace 操作。

3.router-view

函数式组件,渲染的时候,会根据当前路由 path 以及 router map (初始化路由传入的配置,vue-router 根据这个生成 path 和 渲染组件的映射关系), 匹配对应的组件来渲染。

4.路由变化到组件渲染大致流程

图示基于懒加载路由和 css 提取。

vue-router 会在初始化 vue 根实例的时候,通过 mixin 的方式混入一个方法,方法如下所示:

具体源码

beforeCreate () {
    // 省略部分代码
    Vue.util.defineReactive(this, '_route', this._router.history.current)
   // 省略部分代码
},

具体源码

给 vue 根实例定义了一个可观察属性,在路由变化的时候,重新设置 _router 属性:

 history.listen(route => {
  this.apps.forEach((app) => {
    app._route = route
  })
})

从而触发 vue 重新渲染的操作,进一步可以渲染不同的组件。

查看文章更多详情

ps:
有个疑惑的点,在懒加载的路由中,在进行异步组件加载的时候,通过 webpack 自带的 import 懒加载方式引入异步 chunk, 这部分代码没看到有 onload 的时候进行 resolve, 那么如何判断异步 chunk 加载完成了?

/******/ 	__webpack_require__.e = function requireEnsure(chunkId) {
/******/ 		var promises = [];
/******/
/******/
/******/ 		// JSONP chunk loading for javascript
/******/
/******/ 		var installedChunkData = installedChunks[chunkId];
/******/ 		if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ 			// a Promise means "currently loading".
/******/ 			if(installedChunkData) {
/******/ 				promises.push(installedChunkData[2]);
/******/ 			} else {
/******/ 				// setup Promise in chunk cache
/******/ 				var promise = new Promise(function(resolve, reject) {
/******/ 					installedChunkData = installedChunks[chunkId] = [resolve, reject];
/******/ 				});
/******/ 				promises.push(installedChunkData[2] = promise);
/******/
/******/ 				// start chunk loading
/******/ 				var script = document.createElement('script');
/******/ 				var onScriptComplete;
/******/
/******/ 				script.charset = 'utf-8';
/******/ 				script.timeout = 120;
/******/ 				if (__webpack_require__.nc) {
/******/ 					script.setAttribute("nonce", __webpack_require__.nc);
/******/ 				}
/******/ 				script.src = jsonpScriptSrc(chunkId);
/******/
/******/ 				// create error before stack unwound to get useful stacktrace later
/******/ 				var error = new Error();
/******/ 				onScriptComplete = function (event) {
/******/ 					// avoid mem leaks in IE.
/******/ 					script.onerror = script.onload = null;
/******/ 					clearTimeout(timeout);
/******/ 					var chunk = installedChunks[chunkId];
/******/ 					if(chunk !== 0) {
/******/ 						if(chunk) {
/******/ 							var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/ 							var realSrc = event && event.target && event.target.src;
/******/ 							error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/ 							error.name = 'ChunkLoadError';
/******/ 							error.type = errorType;
/******/ 							error.request = realSrc;
/******/ 							chunk[1](error);
/******/ 						}
/******/ 						installedChunks[chunkId] = undefined;
/******/ 					}
/******/ 				};
/******/ 				var timeout = setTimeout(function(){
/******/ 					onScriptComplete({ type: 'timeout', target: script });
/******/ 				}, 120000);
/******/ 				script.onerror = script.onload = onScriptComplete;
/******/ 				document.head.appendChild(script);
/******/ 			}
/******/ 		}
/******/ 		return Promise.all(promises);
};