Table of Contents
Example
History
Router-View
Home Web Front-end JS Tutorial Detailed explanation of vue-router source code examples

Detailed explanation of vue-router source code examples

Feb 07, 2018 pm 02:47 PM
vue-router Source code Detailed explanation

This article mainly shares with you the reading and learning of vue-router source code. Just like analyzing the vuex source code, we first understand how vue-router is used through a simple example, and then analyze how it is implemented in the source code. I hope Can help everyone.

Example

The following example comes from example/basica/app.js

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const Home = { template: &#39;<div>home</div>&#39; }
const Foo = { template: &#39;<div>foo</div>&#39; }
const Bar = { template: &#39;<div>bar</div>&#39; }

const router = new VueRouter({
  mode: &#39;history&#39;,
  base: __dirname,
  routes: [
    { path: &#39;/&#39;, component: Home },
    { path: &#39;/foo&#39;, component: Foo },
    { path: &#39;/bar&#39;, component: Bar }
  ]
})

new Vue({
  router,
  template: `    <div id="app">      <h1>Basic</h1>      <ul>        <li><router-link to="/">/</router-link></li>        <li><router-link to="/foo">/foo</router-link></li>        <li><router-link to="/bar">/bar</router-link></li>        <router-link tag="li" to="/bar" :event="[&#39;mousedown&#39;, &#39;touchstart&#39;]">          <a>/bar</a>        </router-link>      </ul>      <router-view class="view"></router-view>    </div>
  `
}).$mount(&#39;#app&#39;)
Copy after login

First of all Calling Vue.use(VueRouter), the Vue.use() method is the method used by Vue to install plug-ins. It is mainly used to install VueRouter. Then VueRouter is instantiated. Let's take a look at what the VueRouter constructor does.
Start from the source code entry file src/index.js

import type { Matcher } from &#39;./create-matcher&#39;export default class VueRouter {  constructor (options: RouterOptions = {}) {    this.app = null    this.apps = []    this.options = options    this.beforeHooks = []    this.resolveHooks = []    this.afterHooks = []    this.matcher = createMatcher(options.routes || [], this)    let mode = options.mode || &#39;hash&#39;    this.fallback = mode === &#39;history&#39; && !supportsPushState && options.fallback !== false    if (this.fallback) {
      mode = &#39;hash&#39;
    }    if (!inBrowser) {
      mode = &#39;abstract&#39;
    }    this.mode = mode    switch (mode) {      case &#39;history&#39;:        this.history = new HTML5History(this, options.base)        break      case &#39;hash&#39;:        this.history = new HashHistory(this, options.base, this.fallback)        break      case &#39;abstract&#39;:        this.history = new AbstractHistory(this, options.base)        break      default:        if (process.env.NODE_ENV !== &#39;production&#39;) {
          assert(false, `invalid mode: ${mode}`)
        }
    }
  }

  init (app: any /* Vue component instance */) {    this.apps.push(app)    // main app already initialized.    if (this.app) {      return
    }    this.app = app    const history = this.history    if (history instanceof HTML5History) {
      history.transitionTo(history.getCurrentLocation())
    } else if (history instanceof HashHistory) {      const setupHashListener = () => {
        history.setupListeners()
      }
      history.transitionTo(
        history.getCurrentLocation(),
        setupHashListener,
        setupHashListener
      )
    }

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

  getMatchedComponents (to?: RawLocation | Route): Array<any> {    const route: any = to
      ? to.matched
        ? to
        : this.resolve(to).route
      : this.currentRoute    if (!route) {      return []
    }    return [].concat.apply([], route.matched.map(m => {      return Object.keys(m.components).map(key => {        return m.components[key]
      })
    }))
  }

}
Copy after login

Look at the code step by step, starting with the implementation of the constructor function , first initialize. Let’s take a look at what these initialization conditions represent.

  • this.app represents the current Vue instance

  • this.apps Represents all app components

  • this.options represents the options of the incoming VueRouter

  • this.resolveHooks represents the array of resolve hook callback functions, resolve Used to parse the target location

  • this.matcher creates a matching function

There is a createMatcher() function in the code, let’s take a look at its implementation

       
function createMatcher (
  routes,
  router
) {  var ref = createRouteMap(routes);  var pathList = ref.pathList;  var pathMap = ref.pathMap;  var nameMap = ref.nameMap;  
  function addRoutes (routes) {
    createRouteMap(routes, pathList, pathMap, nameMap);
  }   function match (
    raw,
    currentRoute,
    redirectedFrom
  ) {    var location = normalizeLocation(raw, currentRoute, false, router);    var name = location.name;    // 命名路由处理    if (name) {      // nameMap[name]的路由记录      var record = nameMap[name];
      ...
        location.path = fillParams(record.path, location.params, ("named route \"" + name + "\""));        // _createRoute用于创建路由        return _createRoute(record, location, redirectedFrom)
    } else if (location.path) {      // 普通路由处理
    }    // no match    return _createRoute(null, location)
  }  return {
    match: match,
    addRoutes: addRoutes
  }
}
Copy after login

createMatcher() has two parameters, routes, which represent the routes configuration information passed in when creating VueRouter, and router, which represents the VueRouter instance. The function of createMatcher() is to create the corresponding map for the incoming routes through createRouteMap, and a method to create the map.
Let’s first look at the definition of the createRouteMap() method

function createRouteMap (
  routes,
  oldPathList,
  oldPathMap,
  oldNameMap) {  // 用于控制匹配优先级  var pathList = oldPathList || [];  // name 路由 map  var pathMap = oldPathMap || Object.create(null);  // name 路由 map  var nameMap = oldNameMap || Object.create(null);  // 遍历路由配置对象增加路由记录
  routes.forEach(function (route) {
    addRouteRecord(pathList, pathMap, nameMap, route);
  });  // 确保通配符总是在pathList的最后,保证最后匹配  for (var i = 0, l = pathList.length; i < l; i++) {    if (pathList[i] === &#39;*&#39;) {
      pathList.push(pathList.splice(i, 1)[0]);
      l--;
      i--;
    }
  }  return {    pathList: pathList,    pathMap: pathMap,    nameMap: nameMap
  }
}
Copy after login

createRouteMap() has 4 parameters: routes represented Configuration information, oldPathList contains an array of all paths for matching priorities, oldNameMap represents name map, and oldPathMap represents path map. createRouteMap is to update pathList, nameMap and pathMap. What does nameMap represent? It is an object containing routing records. Each attribute value name is the path attribute value of each record, and the attribute value is the routing record with this path attribute value. There is something called a routing record. What does this mean? The routing record is a copy of the object in the routes configuration array (and in the children array). The routing records are contained in the matched attribute, such as

               
const router = new VueRouter({  routes: [    // 下面的对象就是 route record
    { path: &#39;/foo&#39;, component: Foo,      children: [        // 这也是个 route record
        { path: &#39;bar&#39;, component: Bar }
      ]
    }
  ]
})
Copy after login

In the above code, a piece of code is used to add routing records to each route. So what is the implementation of routing records? The following is the implementation of addRouteReord()

function addRouteRecord (
  pathList,
  pathMap,
  nameMap,
  route,
  parent,
  matchAs
) {  var path = route.path;  var name = route.name;  var normalizedPath = normalizePath(
    path,    parent
  );  var record = {
    path: normalizedPath,
    regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
    components: route.components || { default: route.component },
    instances: {},
    name: name,    parent: parent,
    matchAs: matchAs,
    redirect: route.redirect,
    beforeEnter: route.beforeEnter,
    meta: route.meta || {},
    props: route.props == null
      ? {}
      : route.components
        ? route.props
        : { default: route.props }
  };  if (route.children) {
    route.children.forEach(function (child) {
      addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs);
    });
  }  if (route.alias !== undefined) {    // 如果有别名的情况
  }  if (!pathMap[record.path]) {
    pathList.push(record.path);
    pathMap[record.path] = record;
  }
}
Copy after login

I am too lazy to say what the parameters of the addRouteRecord() function mean. The newly added parent also represents the routing record. First get the path and name. Then standardize the format through normalizePath(), then create the record object, and then traverse the sub-elements of routes to add routing records. If there are aliases, you need to consider the aliases and update the path Map.

History

We are returning to the constructor of VueRouter. Looking down is the mode selection. There are three modes: history, hash and abstract. · Default hash: uses URL hash value as route, supports all browsers
· history: relies on HTML5 History API and server configuration
· abstract: supports all JavaScript runtime environments, such as Node.js server side. If no browser API is found, the router will automatically force into this mode.
The default is hash, and routes are separated by "#". However, if there are anchor links in the project or hash values ​​in the routes, the original "#" will have an impact on page jumps; so you need to use history mode.
What we commonly use in applications is basically history mode. Let’s take a look at the constructor of HashHistory

var History = function History (router, base) {  this.router = router;  this.base = normalizeBase(base);  this.current = START;  this.pending = null;  this.ready = false;  this.readyCbs = [];  this.readyErrorCbs = [];  this.errorCbs = [];
};
Copy after login

Because Hash and history have some similarities, so HashHistory will be expanded on the History constructor. The following is the meaning of each attribute:

  • this.router represents the VueRouter instance

  • this.base represents the base path of the application. For example, if the entire single-page application is served under /app/, then base should be set to
    "/app/". normalizeBase() is used to format base

  • this.current开始时的route,route使用createRoute()创建

  • this.pending表示进行时的route

  • this.ready表示准备状态

  • this.readyCbs表示准备回调函数

creatRoute()在文件src/util/route.js中,下面是他的实现

function createRoute (  record,
  location,
  redirectedFrom,
  router
) {
  var stringifyQuery$$1 = router && router.options.stringifyQuery;

  var query = location.query || {};
  try {
    query = clone(query);
  } catch (e) {}

  var route = {
    name: location.name || (record && record.name),
    meta: (record && record.meta) || {},
    path: location.path || &#39;/&#39;,
    hash: location.hash || &#39;&#39;,
    query: query,
    params: location.params || {},
    fullPath: getFullPath(location, stringifyQuery$$1),
    matched: record ? formatMatch(record) : []
  };  if (redirectedFrom) {
    route.redirectedFrom = getFullPath(redirectedFrom, stringifyQuery$$1);
  }  return Object.freeze(route)
}
Copy after login

createRoute有三个参数,record表示路由记录,location,redirectedFrom表示url地址信息对象,router表示VueRouter实例对象。通过传入的参数,返回一个冻结的route对象,route对象里边包含了一些有关location的属性。History包含了一些基本的方法,例如比较重要的方法有transitionTo(),下面是transitionTo()的具体实现。

History.prototype.transitionTo = function transitionTo (location, onComplete, onAbort) {    var this$1 = this;  var route = this.router.match(location, this.current);  this.confirmTransition(route, function () {    this$1.updateRoute(route);
    onComplete && onComplete(route);    this$1.ensureURL();    // fire ready cbs once    if (!this$1.ready) {      this$1.ready = true;      this$1.readyCbs.forEach(function (cb) { cb(route); });
    }
  }, function (err) {    if (onAbort) {
      onAbort(err);
    }    if (err && !this$1.ready) {      this$1.ready = true;      this$1.readyErrorCbs.forEach(function (cb) { cb(err); });
    }
  });
};
Copy after login

首先match得到匹配的route对象,route对象在之前已经提到过。然后使用confirmTransition()确认过渡,更新route,ensureURL()的作用就是更新URL。如果ready为false,更改ready的值,然后对readyCbs数组进行遍历回调。下面来看看HTML5History的构造函数

var HTML5History = (function (History$$1) {  function HTML5History (router, base) {    var this$1 = this;

    History$$1.call(this, router, base);    var initLocation = getLocation(this.base);    window.addEventListener(&#39;popstate&#39;, function (e) {      var current = this$1.current;      var location = getLocation(this$1.base);      if (this$1.current === START && location === initLocation) {        return
      }
    });
  }  if ( History$$1 ) HTML5History.__proto__ = History$$1;
  HTML5History.prototype = Object.create( History$$1 && History$$1.prototype );
  HTML5History.prototype.constructor = HTML5History;


  HTML5History.prototype.push = function push (location, onComplete, onAbort) {    var this$1 = this;    var ref = this;    var fromRoute = ref.current;    this.transitionTo(location, function (route) {
      pushState(cleanPath(this$1.base + route.fullPath));
      handleScroll(this$1.router, route, fromRoute, false);
      onComplete && onComplete(route);
    }, onAbort);
  };

  HTML5History.prototype.replace = function replace (location, onComplete, onAbort) {    var this$1 = this;    var ref = this;    var fromRoute = ref.current;    this.transitionTo(location, function (route) {
      replaceState(cleanPath(this$1.base + route.fullPath));
      handleScroll(this$1.router, route, fromRoute, false);
      onComplete && onComplete(route);
    }, onAbort);
  };  return HTML5History;
}(History))
Copy after login

在HTML5History()中代码多次用到了getLocation()那我们来看看他的具体实现吧

function getLocation (base) {  var path = window.location.pathname;  if (base && path.indexOf(base) === 0) {
    path = path.slice(base.length);
  }  return (path || &#39;/&#39;) + window.location.search + window.location.hash
}
Copy after login

用一个简单的地址来解释代码中各个部分的含义。例如http://example.com:1234/test/test.htm#part2?a=123,window.location.pathname=>/test/test.htm=>?a=123,window.location.hash=>#part2。
把我们继续回到HTML5History()中,首先继承history构造函数。然后监听popstate事件。当活动记录条目更改时,将触发popstate事件。需要注意的是调用history.pushState()或history.replaceState()不会触发popstate事件。我们来看看HTML5History的push方法。location表示url信息,onComplete表示成功后的回调函数,onAbort表示失败的回调函数。首先获取current属性值,replaceState和pushState用于更新url,然后处理滚动。模式的选择就大概讲完了,我们回到入口文件,看看init()方法,app代表的是Vue的实例,现将app存入this.apps中,如果this.app已经存在就返回,如果不是就赋值。this.history是三种的实例对象,然后分情况进行transtionTo()操作,history方法就是给history.cb赋值穿进去的回调函数。
下面看getMatchedComponents(),唯一需要注意的就是我们多次提到的route.matched是路由记录的数据,最终返回的是每个路由记录的components属性值的值。

Router-View

最后讲讲router-view

var View = {
  name: &#39;router-view&#39;,
  functional: true,
  props: {
    name: {
      type: String,      default: &#39;default&#39;
    }
  },
  render: function render (_, ref) {    var props = ref.props;    var children = ref.children;    var parent = ref.parent;    var data = ref.data;    // 解决嵌套深度问题
    data.routerView = true;    var h = parent.$createElement;    var name = props.name;    // route    var route = parent.$route;    // 缓存    var cache = parent._routerViewCache || (parent._routerViewCache = {});    // 组件的嵌套深度    var depth = 0;    // 用于设置class值    var inactive = false;    // 组件的嵌套深度    while (parent && parent._routerRoot !== parent) {      if (parent.$vnode && parent.$vnode.data.routerView) {
        depth++;
      }      if (parent._inactive) {
        inactive = true;
      }      parent = parent.$parent;
    }
    data.routerViewDepth = depth;    if (inactive) {      return h(cache[name], data, children)
    }    var matched = route.matched[depth];    if (!matched) {
      cache[name] = null;      return h()
    }    var component = cache[name] = matched.components[name];

    data.registerRouteInstance = function (vm, val) {      // val could be undefined for unregistration      var current = matched.instances[name];      if (
        (val && current !== vm) ||
        (!val && current === vm)
      ) {
        matched.instances[name] = val;
      }
    }

    ;(data.hook || (data.hook = {})).prepatch = function (_, vnode) {
      matched.instances[name] = vnode.componentInstance;
    };    var propsToPass = data.props = resolveProps(route, matched.props && matched.props[name]);    if (propsToPass) {
      propsToPass = data.props = extend({}, propsToPass);      var attrs = data.attrs = data.attrs || {};      for (var key in propsToPass) {        if (!component.props || !(key in component.props)) {
          attrs[key] = propsToPass[key];
          delete propsToPass[key];
        }
      }
    }    return h(component, data, children)
  }
};
Copy after login

router-view比较简单,functional为true使组件无状态 (没有 data ) 和无实例 (没有 this 上下文)。他们用一个简单的 render 函数返回虚拟节点使他们更容易渲染。props表示接受属性,下面来看看render函数,首先获取数据,然后缓存,_inactive用于处理keep-alive情况,获取路由记录,注册Route实例,h()用于渲染。很简单我也懒得一一再说。

相关推荐:

vue-router的权限控制代码分享

Vue-router结合transition实现app动画切换效果实例分享

三种Vue-Router实现组件间跳转的方法

The above is the detailed content of Detailed explanation of vue-router source code examples. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Chat Commands and How to Use Them
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Detailed explanation of obtaining administrator rights in Win11 Detailed explanation of obtaining administrator rights in Win11 Mar 08, 2024 pm 03:06 PM

Windows operating system is one of the most popular operating systems in the world, and its new version Win11 has attracted much attention. In the Win11 system, obtaining administrator rights is an important operation. Administrator rights allow users to perform more operations and settings on the system. This article will introduce in detail how to obtain administrator permissions in Win11 system and how to effectively manage permissions. In the Win11 system, administrator rights are divided into two types: local administrator and domain administrator. A local administrator has full administrative rights to the local computer

Detailed explanation of division operation in Oracle SQL Detailed explanation of division operation in Oracle SQL Mar 10, 2024 am 09:51 AM

Detailed explanation of division operation in OracleSQL In OracleSQL, division operation is a common and important mathematical operation, used to calculate the result of dividing two numbers. Division is often used in database queries, so understanding the division operation and its usage in OracleSQL is one of the essential skills for database developers. This article will discuss the relevant knowledge of division operations in OracleSQL in detail and provide specific code examples for readers' reference. 1. Division operation in OracleSQL

Detailed explanation of the role and usage of PHP modulo operator Detailed explanation of the role and usage of PHP modulo operator Mar 19, 2024 pm 04:33 PM

The modulo operator (%) in PHP is used to obtain the remainder of the division of two numbers. In this article, we will discuss the role and usage of the modulo operator in detail, and provide specific code examples to help readers better understand. 1. The role of the modulo operator In mathematics, when we divide an integer by another integer, we get a quotient and a remainder. For example, when we divide 10 by 3, the quotient is 3 and the remainder is 1. The modulo operator is used to obtain this remainder. 2. Usage of the modulo operator In PHP, use the % symbol to represent the modulus

Detailed explanation of the linux system call system() function Detailed explanation of the linux system call system() function Feb 22, 2024 pm 08:21 PM

Detailed explanation of Linux system call system() function System call is a very important part of the Linux operating system. It provides a way to interact with the system kernel. Among them, the system() function is one of the commonly used system call functions. This article will introduce the use of the system() function in detail and provide corresponding code examples. Basic Concepts of System Calls System calls are a way for user programs to interact with the operating system kernel. User programs request the operating system by calling system call functions

Detailed explanation of Linux curl command Detailed explanation of Linux curl command Feb 21, 2024 pm 10:33 PM

Detailed explanation of Linux's curl command Summary: curl is a powerful command line tool used for data communication with the server. This article will introduce the basic usage of the curl command and provide actual code examples to help readers better understand and apply the command. 1. What is curl? curl is a command line tool used to send and receive various network requests. It supports multiple protocols, such as HTTP, FTP, TELNET, etc., and provides rich functions, such as file upload, file download, data transmission, proxy

How to display the source code of PHP code in the browser without being interpreted and executed? How to display the source code of PHP code in the browser without being interpreted and executed? Mar 11, 2024 am 10:54 AM

How to display the source code of PHP code in the browser without being interpreted and executed? PHP is a server-side scripting language commonly used to develop dynamic web pages. When a PHP file is requested on the server, the server interprets and executes the PHP code in it and sends the final HTML content to the browser for display. However, sometimes we want to display the source code of the PHP file directly in the browser instead of being executed. This article will introduce how to display the source code of PHP code in the browser without being interpreted and executed. In PHP, you can use

Detailed analysis of C language learning route Detailed analysis of C language learning route Feb 18, 2024 am 10:38 AM

As a programming language widely used in the field of software development, C language is the first choice for many beginner programmers. Learning C language can not only help us establish the basic knowledge of programming, but also improve our problem-solving and thinking abilities. This article will introduce in detail a C language learning roadmap to help beginners better plan their learning process. 1. Learn basic grammar Before starting to learn C language, we first need to understand the basic grammar rules of C language. This includes variables and data types, operators, control statements (such as if statements,

Learn more about Promise.resolve() Learn more about Promise.resolve() Feb 18, 2024 pm 07:13 PM

Detailed explanation of Promise.resolve() requires specific code examples. Promise is a mechanism in JavaScript for handling asynchronous operations. In actual development, it is often necessary to handle some asynchronous tasks that need to be executed in sequence, and the Promise.resolve() method is used to return a Promise object that has been fulfilled. Promise.resolve() is a static method of the Promise class, which accepts a

See all articles