At the beginning

Finally, look at the two components of vue-Router

and

.

The two components can be used as follows:

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <! -- Use the router-link component to navigate.
    <! -- Specifies the link by passing in the 'to' attribute.
    <! -- <router-link> will be rendered as a '<a>' tag by default.
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <! -- Route exit -->
  <! -- Routing matching components will be rendered here -->
  <router-view></router-view>
</div>
Copy the code

Before opening the source code, think briefly about how components can be implemented.


can be a
tag, to is its props, and then bind a click event.

Remember programmatic navigation? In the previous source code, we also saw that this.history implements methods like push and replace, which trigger route changes by clicking on them.

/ / pseudo code
Vue.component('router-link', {
  props: {
    to: String
  },
  template: '<a v-on="handleClick"><slot></slot></a>'.methods: {
    handleClick () {
      this.$router.push(this.to)
    }
  }
})
Copy the code

$route = this.$route = this.$route = this.$route = this.$route = this.

/ / pseudo code
Vue.component('router-view', {
  render (h, { parent }) {
    const route = parent.$route

    // Route does not have to have component
    Component can be found by route
    const component = route.component

    returnh(component); }})Copy the code

This is just a simple guess, and the code to implement it is far more complex than the above. Let’s take a look at them in action.

router-link

The router-link component is written as a rendering function.

It mainly realizes several functions:

  1. Define the correct jump URL.
  2. Defines the highlighting style for the switch.
  3. Define click events.
  4. Set the data object.

The general framework of router-link looks like this:

export default {
  name: 'router-link'.props: {
    to: {
      type: toTypes,
      required: true
    },
    tag: {
      type: String.default: 'a'
    }
    // ...
  },
  render (h: Function) {
    // ...

    return h(this.tag, data, this.$slots.default)
  }
}
Copy the code

Render is the implementation of several pieces of function.

Define the correct jump URL

const router = this.$router
const current = this.$route
const to = normalizeLocation(this.to, current, this.append)
const resolved = router.match(to)
const fullPath = resolved.redirectedFrom || resolved.fullPath
const base = router.history.base
const href = base ? cleanPath(base + fullPath) : fullPath
Copy the code

You end up with href as the jump attribute for the tag, but the details are not expanded.

Defines the highlighting style for the switch

const classes = {}
const activeClass = this.activeClass || router.options.linkActiveClass || 'router-link-active'

// Omit a bunch of code
classes[activeClass] = true // or false
Copy the code

As you can see here, the highlighting style defaults to ‘router-link-active’.

Defining click events

const on = {
  click: (e) = > {
    // ...
    e.preventDefault()
    if (this.replace) {
      router.replace(to)
    } else {
      router.push(to)
    }
  }
}
Copy the code

The click event actually calls push or replace that triggers the router, similar to programmatic navigation.

Setting a Data object

const data: any = {
  class: classes
}

if (this.tag === 'a') {
  data.on = on
  data.attrs = { href }
} else {
  // find the first <a> child and apply listener and href
  const a = findAnchor(this.$slots.default)
  if (a) {
    const aData = a.data || (a.data = {})
    aData.on = on
    const aAttrs = aData.attrs || (aData.attrs = {})
    aAttrs.href = href
  } else {
    // doesn't have <a> child, apply listener to self
    data.on = on
  }
}
Copy the code

There is a judgment here that if the tag is not a tag, we recursively use the findAnchor function to find the tag from slots. If not, the router-link itself is assigned the on click event.

Finally, the createElement function is returned, completing the implementation of the router-link component.

return h(this.tag, data, this.$slots.default)
Copy the code

router-view

Router-view is a functional component that does only rendering work.

The primary task is to find the components to display, with cached components being selected first and nested components second.

The general framework of a router-view looks like this:

export default {
  name: 'router-view'.functional: true.props: {
    name: {
      type: String.default: 'default'
    }
  },
  render (h, { props, children, parent, data }) {
    // ...

    return h(component, data, children)
  }
}
Copy the code

Routing is triggered

$route is used.

const route = parent.$route

// ...

const matched = route.matched[depth]
Copy the code

Back to Install, $route is processed responsively using Vue:

Object.defineProperty(Vue.prototype, '$route', {
  get () { return this.$root._route }
})
Copy the code

Then remember the listening for route initialization:

this.history.listen(route= > {
  this.app._route = route
})
Copy the code

$route = $route = $route = $route = $route = $route = $route = $route = $route = $route

Nested routines are handled by

Route-view can find nested routes through depth and route.matched, so as to achieve the rendering of nested routes.

let depth = 0

while (parent) {
  if (parent.$vnode && parent.$vnode.data.routerView) {
    depth++
  }
  parent = parent.$parent
}

data.routerViewDepth = depth
const matched = route.matched[depth]
Copy the code

Keep alive – processing

When keep-alive is used, there will be a cache, so the component will use the cache directly.

const cache = parent._routerViewCache || (parent._routerViewCache = {})
let inactive = false

while (parent) {
  // ...

  if (parent._inactive) {
    inactive = true
  }
  parent = parent.$parent
}

const matched = route.matched[depth]

const name = props.name
const component = inactive
  ? cache[name]
  : (cache[name] = matched.components[name])
Copy the code

Finally, find the corresponding component to render and return createElement to complete the implementation of the router-View component.

return h(component, data, children)
Copy the code

The last

The above is just a brief interpretation of

and

, for a more in-depth understanding of the code implementation, you can move to vue-Router source code analysis – the overall process.

At this point, vue-Router source code interpretation comes to an end, applause.