1.前言
vue-router 通过浏览器 HTML5 hashchange 事件 和 History API 实现了前端路由的接管。通过自定义函数式组件 router-link 和 router-view 来实现路由切换交互和渲染的承载。
2.router-link
函数式组件,实现用户点击交互劫持,并手动触发 router.push 和 router.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);
};