This is the 25th day of my participation in the August Genwen Challenge.More challenges in August


One, foreword

$route, $router and router-link

  • Define the prototype method$route$router;
  • <router-link>Component functionality and implementation;

This article introduces the implementation of

component.


Two, the previous review

  • When VueRouter is instantiated, the route configuration is flattened by createMatcher.
  • The match method in createMatcher matches routing records and creates routing objects.
  • Create the current matching routing object, current, as responsive data;
  • Click router-link component to switch path, call transitionTo to match new route record and update responsive data current, trigger view update;

When clicking the router-link component to switch paths, all routing rules need to be matched according to the latest routing address, and the page is updated and rendered recursively.


Third, functional components

Router-view is a functional component that has the following features:

  • High performance, render without this;
  • Functional: true stands for functional components;
export default {
  name: 'routerView'.functional: true.// Functional components
  render() {
      / / this is null
      return <div></div>}}Copy the code

$mount(); new Ctor().$mount();

Function components that can be used without creating an instance (equivalent to function components in React);

The only difference is that there is no this in the render function, i.e. no component state (no data, props, etc.)

Vue official website: an introduction to functional components


Four,<router-view>Component implementation

1. Obtain render record data

The $route attribute exists on the parent of the current component.

$router = _route; parent.$router = _route;

/** * Router-view functional components, multi-layer rendering * Functional components features: high performance, no need to create instances, no this */
export default {
    name: 'routerView'.functional: true.// Functional components
    render(h, { parent }) {
        // Get the relevant route record that needs to be rendered currently, namely: this.current
        letroute = parent.$route; }}Copy the code

2. Record the depth of recursion

After obtaining $route attribute, route.matched is all routing matching records.

Processing logic:

  • App. vue contains router-View component and Home page rendering component;
  • When clicking “Jump to Mine” for route switching, router-view in app.vue renders Mine component;
  • When clicking “Mys-Personal Information” for route switching, the components corresponding to router-view rendering /mine/ User routing in mine.vue;

Therefore, when accessing /mine/user, three routing rules will be matched: /, /mine, /mine/user;

In this case, render operations need to be carried out as described above.

Therefore, it is necessary to record the depth of each layer of router-view to render the content;


3. Handle the first layer of rendering

When the Router-View component renders, the routerView component’s Render method is called.

Add the custom data.routerView = true to the data attribute of the current layer (layer 1).

export default {
    name: 'routerView'.functional: true.render(h, { parent, data }) {
        let route = parent.$route;
        let depth = 0;// Record the level depth
        data.routerView = true; // Custom attributes}}Copy the code

The first rendering, according to the matching result and the depth of the hierarchy, needs to render the 0 item in Route. matched;

Let Record = route.matched[0] to obtain the records required by the corresponding level of rendering;

export default {
    name: 'routerView'.functional: true.render(h, { parent, data }) {
        let route = parent.$route;
        let depth = 0;
        data.routerView = true;

        // Router-view renders the first record and the second router-view renders the second record
        let record = route.matched[depth]; // Get the corresponding level of records}}Copy the code
  • Router-view may have only one layer, which can be removed directly.
  • If no record is matched, the record does not exist, and an empty virtual node called empty-vNode is returnedh()
  • If a record is matched, the record has a value and the component is renderedh(record.component), get components from record.component;
  • H (record.component): Renders the current component. When the component renders, it passes in data data containing the previously identified routerView property.
export default {
    name: 'routerView'.functional: true.render(h, { parent, data }) {
      let route = parent.$route;
      let depth = 0;
      data.routerView = true;

      // Obtain the routing record corresponding to the current layer depth for view rendering
      let record = route.matched[depth];
      // No routing record was matched, render empty virtual node (empty-vNode), also called comment node
      if(! record) {return h();
      }
      returnh(record.component, data); }}Copy the code

This completes the rendering of the first layer of router-view tags;


4. Handle multi-level router-view rendering

  • Each time a Router-View is rendered, the routerView attribute is set as a marker in the current data.
  • The parent in the render function is the parent tag of the current router-View.
  • Check by loop parentparent.$vnode.data.routerViewCalculates the depth of the current render level.
  • Obtain routing records of corresponding levels and render views;

Related instructions:

  • Parent.$vnode: represents the placeholder vnode; That is, virtual nodes with component label names.
  • Parent._vnode refers to the contents of the component; That is, the actual virtual node to render;
/** * Router-view functional components, multi-layer rendering * Functional components features: high performance, no need to create instances, no this */
export default {
  name: 'routerView'.functional: true.// Functional components
  render(h, { parent, data }) {
    // Get the relevant route record that needs to be rendered currently, namely: this.current
    let route = parent.$route;
    let depth = 0;// Record the level depth
    data.routerView = true; // Custom attributes

    // App.vue calls the Render function when the app. vue component is rendered without a data.routerView attribute
    // Add the routerView=true flag when rendering the first layer
    while (parent) { // parent is the parent label of router-view
      // parent.$vnode: placeholder vnode; That is, virtual nodes with component label names.
      // parent._vnode refers to the contents of the component; That is, the actual virtual node to render;
      if (parent.$vnode && parent.$vnode.data.routerView) {
        depth++;
      }
      parent = parent.$parent; // Update the parent for the next processing of the loop
    }

    // Obtain the routing record corresponding to the current layer depth for view rendering
    let record = route.matched[depth];
    // No routing record was matched, render empty virtual node (empty-vNode), also called comment node
    if(! record) {return h();
    }
    returnh(record.component, data); }}Copy the code

This implements router-view component view update operation;


Five, the end

This article introduces the implementation of router-View component, mainly involving the following contents:

  • Introduction to functional components;
  • Implementation of router-view component:
    • Get render record;
    • Mark router-view hierarchy depth;
    • Router-view rendering based on depth;

In the next article, vue-Router hook functions are introduced.