preface

This is xiao Lang’s summary of learning Vue Router. Some of the notes are not very good. Please give me more advice

  • Here is the basic use of Vue Router
  • Handwriting a basic Vue Router feeling under the basic implementation
  • With the previous basis, then according to the official source code to write a Vue Router

I wrote detailed comments on both handwritten Vue routers

Hope this article is helpful to you, I suggest you manually to knock down

Finally: Xiao Lang online for praise

Vue Router based

Let’s first understand the simple use of Vue Router, first understand how to use, and then to figure out how to achieve

1. Introduction

Routing: Is essentially a correspondence relationship

There are front-end routes and back-end routes

The back-end routing

For example, node.js routes are URL request addresses that correspond to server resources, and return different resources according to different request addresses

The front-end routing

In SPA (single page application), the URL changes according to the event triggered by the user to display different page contents without refreshing the premise, such as the Vue Router in a moment

2. Basic steps for using vue-router

1. Import the vue-router file

<! Vue router -->
<script src="./js/vue.js"></script>
<! -- Import vue-router file -->
<script src=". / js/vue - router_3. 0.2 js. ""></script>
Copy the code

2. Add router-link and router-view on the page

<! -- Add route -->
<! <a href="#/home"></a> -->
<router-link to="/home">Home</router-link>
<router-link to="/login">Login</router-link>
<! -- Display routing content -->
<router-view></router-view>
Copy the code

3. Create a routing component

// Create a routing component
const home = {
    template: Welcome to {{name}} '.data() {
        return {
            name: 'home',}}}const login = {
    template: Welcome to the login page  ',}Copy the code

4. Configure routing rules

// Configure routing rules
const router = new VueRouter({
    routes: [
        // Each routing rule is an object
        // Path Hash address of the route
        // Component Routes the displayed component
        {
            path: '/'.// Route redirects to new address '/home' when accessing '/'
            redirect: '/home'}, {path: '/home'.component: home,
        },
        {
            path: '/login'.component: login,
        },
    ],
})
Copy the code

5. Mount routes

 let vm = new Vue({
        el: '#app'.data: {},
        methods: {},
        // Mount to vue
        router,
 })
Copy the code

3. Set routine by

The nested routines here are based on the example above

1. Add child route links and placeholders to the route

// Create a routing component
const home = {
    template: Welcome to the home page <br> <! -- Child router-link --> <router-link to="/tab1"> tab1 </router-link> <router-link to="/tab2"> tab2 </router-link> <! --> <router-view></router-view> </div>Copy the code

2. Add routing components

// Create two child routing components
const tab1 = {
    template: '
      
child route 1
'
,}const tab2 = { template: '
child route 2
'
,}Copy the code

3. Configure routing rules

// Configure routing rules
const router = new VueRouter({
    routes: [{path: '/home'.component: home,
            // Children indicates the child routing rule
            children: [{path: '/tab1'.component: tab1 },
                { path: '/tab2'.component: tab2 },
            ],
        },
    ],
})
Copy the code

4. Dynamic routing

The path attribute plus /:id uses the params.id of the route object to get the dynamic parameter

For example, now there are so many routes, if you configure multiple routes, wouldn’t it be a bit… redundant

<div id="app">
    <! -- Add route -->
    <! <a href="#/home"></a> -->
    <router-link to="/goods/1">goods1</router-link>
    <router-link to="/goods/2">goods2</router-link>
    <router-link to="/goods/3">goods3</router-link>
    <router-link to="/goods/4">goods4</router-link>
    <! -- Display routing content -->
    <router-view></router-view>
</div>
Copy the code

This can then be resolved using dynamic routing

<script>
    // Create a routing component
    const goods = {
        // this.$route.parms.id can omit this
        template: Welcome to the commodity {{$route.params.id}} page  ',}// Configure routing rules
    const router = new VueRouter({
        routes: [{/ / add ` / : id `
                path: '/goods/:id'.component: goods,
            },
        ],
    })
    let vm = new Vue({
        el: '#app'.data: {},
        methods: {},
        // Mount to vue
        router,
    })
</script>
Copy the code

Finally, you can also pass parameters using query.

/ / such as<router-link to="/goods? id=1">goods</router-link>
Copy the code

Then use this.$route.query.id to get the ID in the routing component

Adding a Dynamic Route

This.$router.addroutes ([]) is used to add a dynamic route, which is passed as an array just like the routes

5. Route transmission parameters

We can use props to pass values

Why use props for the value, the route is not sweet, does the route are flexible enough

There are three scenarios for props

1. Boolean value type

// Create a routing component
const goods = {
    // Use props to receive
    props: ['id'].template: Welcome to the merchandise {{id}} page  ',}// Configure routing rules
const router = new VueRouter({
    routes: [{path: '/goods/:id'.component: goods,
            //props for true, rout. params will be set as a component property
            props: true,}]})Copy the code

2. Object type

But you can’t get the ID here, so you get an error

Here the id needs to be obtained by $route.params.id

const goods = {
    // Use props to receive
    props: ['name'.'info'.'id'].// The id is not available
    template: '
      
{{info}} goes to the {{name}} {{id}} page
'
,}// Configure routing rules const router = new VueRouter({ routes: [{path: '/goods/:id'.component: goods, //props for this object // The routing component uses props to receive props: { name: 'goods'.info: 'welcome',},},],})Copy the code

3. The function

const goods = {
    // Use props to receive
    props: ['name'.'info'.'id'].template: '
      
{{info}} goes to the {{name}} {{id}} page
'
,}// Configure routing rules const router = new VueRouter({ routes: [{path: '/goods/:id'.component: goods, //prop is a function props: (route) = > { return { name: 'goods'.info: 'welcome'.id: route.params.id, } }, }, ], }) Copy the code

6. The route and the router

What’s the difference between a route and a router

  • Route indicates that path, params, hash, query, fullPath, matched, and name can be obtained from the current Router jump object
  • routerforVueRouter instancewithnew VueRouterCreates an instance that wants to navigate to differentURL, use therouter.pushmethods
  • Routes are router routing instances used to configure routing objects (by the way)

7. Name the route

Routing component

// Create a routing component
const goods = {
    // Use props to receive
    props: ['id'].template: '
      
commodity {{id}} page
'
,}Copy the code

The routing configuration

// Configure routes
const router = new VueRouter({
    routes: [{path: '/goods/:id'.// Name the route
            name: 'goods'.component: goods,
        },
    ],
})
Copy the code

Binding: To finds the route defined by name and can also pass parameters using Params

<router-link :to="{name: 'goods', params: { id: 1 } }">goods1</router-link>
<! -- Display routing content -->
<router-view></router-view>
Copy the code

8. Programmatic navigation

1. Declarative navigation

While we’re on the subject of programmatic navigation, let’s briefly talk about declarative navigation

All shown above are declared navigation such as router-link

<router-link to="/goods/1">goods1</router-link>

And the A label

<a href="#/goods/1">goods1</a>

2. Programmatic navigation

Use javaScript to control routing hops

Use loaction.href window.open and so on to jump to normal web pages

Now I’m going to introduce programmatic navigation in the Vue Router

Router.push () **router.go(n)**

/ / string
this.$router.push('/home')

/ / object
this.$ruter.push({path:'/home'})

// Such as this /goods? id=1
this.$router.push({path:'/goods'.query: {id:'1'}})

// Name the route /goods/1
this.$router.push({name:'goods'.params: {id:1}})

/ / back
this.$router.go(-1)
Copy the code

9. Route guards

1. Global guard

Router. beforeEach The global guard takes effect for all routes

router.beforeEach((to, from, next) = > { 
      next();// Do not omit next!!
    }).catch(() = >{
      // The failure page is displayed
      next({ path: '/error'.replace: true.query: { back: false}})})Copy the code

Three parameters for the global guard

To: Indicates the destination route object to be entered

From: The current navigation is leaving the routing object

Next: Different parameters do different things

  • Next () goes straight to the next hook
  • Next (false) Stops the current navigation
  • Next (‘/ path ‘) jumps to the routing address of the path, which can also be written as an object next({path: ‘/ path ‘})
  • next(error): if the passed parameter is aErrorInstance, the navigation is terminated and the error is passed torouter.onError()

2. Route exclusive guards

BeforeEnter The guard exclusively owned by the route object is written in routes

const router = new VueRouter({
  routes: [{path: '/goods'.component: goods,
      beforeEnter: (to, from, next) = > {
        // The same usage}}}])Copy the code

3. Guards inside components (understand)

The guards inside the component are written inside the component as an official introduction

  • BeforeRouteEnter, the component hasn’t been instantiated yet so we can’t get this here
  • BeforeRouteUpdate (2.2) This phase gets this and is triggered when routing the same component over
  • The beforeRouteLeave phase retrives this, which can be used to save data, initialize data, turn off the timer, and so on, when leaving the component’s route
const goods = {
  template: `<div>goods</div>`,
  beforeRouteEnter (to, from, next) {
    // Concrete logic
  },
  beforeRouteUpdate (to, from, next) {
    // Concrete logic
  },
  beforeRouteLeave (to, from, next) {
    // Concrete logic}}Copy the code

10. Component cachekeep-alive

When the page is reloaded it will rerender the page and so on and so forth, we have a component that’s not active and we don’t want it to be rerendered, so we can wrap it around
, so it doesn’t trigger the created hook

Application scenario: Get the details of an item and then roll back and use the cache while going forward, improving performance

1. Do not use the keep-alive example

Here the home component prints the current time at created

<div id="app">
    <router-link to="/home">home</router-link>
	<router-link to="/login">login</router-link>

	<router-view></router-view>
</div>
Copy the code
<script>
      const login = {
        template: ` 
      
Login
`
,}const home = { template: `
Home
`
.created() { console.log(new Date()}},const router = new VueRouter({ routes: [{path: '/'.redirect: '/home'}, {path: '/home'.component: home, }, { path: '/login'.component: login, }, ], }) let vm = new Vue({ el: '#app'.data: {}, methods: {}, router, }) </script> Copy the code

As above, every time you switch home, the routing component is rerendered, printing the current time

What happens if you use keep-alive

2. Use the keep alive

It’s just a simple package here

<div id="app">
    <router-link to="/home">home</router-link>
    <router-link to="/login">login</router-link>

    <keep-alive>
        <router-view></router-view>
    </keep-alive>
</div>
Copy the code

What you can see is that it only prints once, indicating that the routing is switched and it doesn’t re-render the component

The component can be cached by adding include to the keep-alive tag

const login = {
    name: login,
    template: ` 
      
Login
`
,}const home = { name: home, template: `
Home
`
.created() { console.log(new Date()}},Copy the code
<div id="app">
    <router-link to="/home">home</router-link>
    <router-link to="/login">login</router-link>

    <keep-alive include="login,home">
        <router-view></router-view>
    </keep-alive>
</div>
Copy the code

3. The activated and deactivated

Keep-alive Life cycle execution sequence

When accessing the route for the first time:

  • created–>mounted –>activated
  • deactivatedTriggered after exit

Later entry will only trigger Activated

11. Hash and History modes

1. The hash pattern

The default hash mode is used on vue-router

The hash is the anchor point in the URL, which is called **#. With the anchor as the routing address, we usually change the following part of the #** to enable the browser to render the specified component. The change of the anchor point triggers the onHashChange event

2. The history mode

History mode is the normal address, the use of the server needs to support

If the access path resource is not directly 404

Two new apis are added after HTML5

PushState (): Supported after IE10

replaceState()

If you want to use the History mode on the VUe-Router, specify this

const router = new VueRouter({
  mode: 'history'
})
Copy the code

Implement a basic Vue Router

Just to review the basics of routing, let’s write a Vue Router

The Vue Router is implemented based on the History mode

All steps are commented in the code, and each line is commented

This simple does not according to the Vue Router source code to write is mainly some basic function of the implementation

For the following in accordance with the source code to write a foundation

1. Register the global Vue Router

The first step is to register your own Vue Router

Determines whether the component is registered

Register the Vue instance after it is created

// Save a global variable Vue
let _Vue = null

// By default, export your own VueRouter
export default class MyVueRouter {
  MyVueRouter vue provides install for us to develop new plug-ins and global registration components
  // Pass Vue
  static install(Vue) {
    // Define a flag to determine if MyVueRouter is registered. If so, no further action is required
    if (MyVueRouter.install.installed) return
    // If not, change the identifier to true
    MyVueRouter.install.installed = true
    // Save the global variable _Vue
    _Vue = Vue
    // In order to get this in Vue we use mixin here
    _Vue.mixin({
      // Do this when the Vue instance is created
      beforeCreate() {
        // To determine whether an instance or component is created, you can determine whether the router is mounted
        if (this.$options.router) {
          // Register router with _Vue
          _Vue.prototype.$router = this.$options.router
        }
      },
    })
  }
}
Copy the code

2. Implement the constructor

Optoins saves incoming rules

RouterMap Determines the relationship between an address and a component

Current indicates that the current address is responsive and later the rendering component is associated with it

export default class MyVueRouter {...// Implement the construct
  constructor(optoins) {
    // This saves routes
    this.optoins = optoins
    // routerMap Stores the relationship between routes and components
    this.routerMap = {}
    // It is used to record data
    this.data = _Vue.observable({
      // The IP address of the current route
      current: '/',}}}Copy the code

3. Parse the routing rules

The incoming routing rule takes an object address and matches the component one by one

export default class MyVueRouter {...// Parse routing rules
  createRouterMap() {
    // Loop through the routes rule passed in the previous constructor
    this.optoins.routes.forEach((item) = > {
      // Add the mapping between routes and components to the routerMap
      this.routerMap[item.path] = item.component
    })
  }
}
Copy the code

4. Implement the router-link component

Router-link is the route link displayed on the page

Because the general use of the basic version of the running version of Vue, so their own components into the virtual DOM

Then there’s the issue of links refreshing

Write your own function to jump to prevent the default event

Also pay attention to the component that the corresponding route is rendering

export default class MyVueRouter {...// Implement the component
  initComponents(Vue) {
    // Implement the router-link component
    Vue.component('router-link', {
      props: {
        // The address that the to attribute on the router-link will access
        to: String,},}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
      // template: `<a :href="to"><slot></slot></a>`,
      render(h) {
        // The first argument is the tag
        return h(
          'a'.// The second argument is that the object is an attribute in the tag
          {
            // Set the properties
            attrs: {
              href: this.to,
            },
            // Bind the event
            on: {
              // Re-copy the click event. If not, it will be clicked and send a request to the server to refresh the page
              click: this.myClick,
            },
          },
          // This is the content inside the tag. Rendering is the default slot
          [this.$slots.default]
        )
      },
      methods: {
        // Router-link click event
        myClick(e) {
          // Push state is used for hash routing. Push state is used for hash routing
          // Use history to change the browser address
          // pushState The first argument is the passed argument, the second is the title, and the third is the link
          history.pushState({}, ' '.this.to)
          // Render the corresponding component
          // The render page also needs to be changed. The current in data is responsive. The router-view is rendered according to current
          this.$router.data.current = this.to
          // Prevent default jump events
          e.preventDefault()
        },
      },
    })
Copy the code

5. Implement the router-view component

Here we get the current corresponding component from the previously parsed rule and convert it into the virtual DOM

Finally, the router-view placeholder is rendered to the page

export default class MyVueRouter {...// Implement the component
  initComponents(Vue) {
    // Implement the router-view component
    Vue.component('router-view', {
      render(h) {
        // Obtain the component corresponding to the current path
        $router = MyVueRouter; // This.$router = MyVueRouter
        const component = this.$router.routerMap[this.$router.data.current]
        // Convert to the virtual Dom
        return h(component)
      },
    })
  }
}
Copy the code

6. Forward and backward

It’s not enough to write before you’re done, because the browser’s back and forward buttons change the browser’s address, but the component doesn’t refresh. Let’s fix that

export default class MyVueRouter {...// Initialize the event
  initEvent() {
    // Listen for browser address changes
    window.addEventListener('popstate'.() = > {
      // Re-render the component by changing VueRouter's current address
      this.data.current = window.location.pathname
    })
  }
}
Copy the code

7. Initialize the router after the router is mounted

And then I write a function to initialize it

The router is initialized after it is registered with Vue

export default class MyVueRouter {
  / / initialization
  init() {
    // Parse routing rules
    this.createRouterMap()
    // Initialize the component
    this.initComponents(_Vue)
    // Initialize the event
    this.initEvent()
  }
    
  static install(Vue) {
    if (MyVueRouter.install.installed) return
    MyVueRouter.install.installed = true
    _Vue = Vue
    _Vue.mixin({
      beforeCreate() {
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router
          // Initialize the router after registering the router
          this.$options.router.init()
        }
      },
    })  
  }
  ...
}

Copy the code

8. Put the full index.js

// Save a global variable Vue
let _Vue = null

export default class MyVueRouter {
  MyVueRouter vue provides install for us to develop new plug-ins and global registration components
  // Pass Vue
  static install(Vue) {
    // Define a flag to determine if MyVueRouter is registered. If so, no further action is required
    if (MyVueRouter.install.installed) return
    // If not, change the identifier to true
    MyVueRouter.install.installed = true
    // Save the global variable _Vue
    _Vue = Vue
    // In order to get this in Vue we use mixin here
    _Vue.mixin({
      // Do this when the Vue instance is created
      beforeCreate() {
        // To determine whether an instance or component is created, you can determine whether the router is mounted
        if (this.$options.router) {
          // Register router with _Vue
          _Vue.prototype.$router = this.$options.router
          // Initialize the router after registering the router
          this.$options.router.init()
        }
      },
    })
    // Determine whether to mount
  }
  // Implement the constructor
  constructor(optoins) {
    // This saves routes
    this.optoins = optoins
    // routerMap Stores the relationship between routes and components
    this.routerMap = {}
    // It is used to record data
    this.data = _Vue.observable({
      // The IP address of the current route
      current: '/'})},// Parse routing rules
  createRouterMap() {
    // Loop through the routes rule passed in the previous constructor
    this.optoins.routes.forEach((item) = > {
      // Routes {path: '/XXX', component: XXX}
      // Add the mapping between routes and components to the routerMap
      this.routerMap[item.path] = item.component
    })
  }
  // Implement the component
  initComponents(Vue) {
    // Implement the router-link component
    Vue.component('router-link', {
      props: {
        // The address that the to attribute on the router-link will access
        to: String,},}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
      // template: `<a :href="to"><slot></slot></a>`,
      render(h) {
        // The first argument is the tag
        return h(
          'a'.// The second argument is that the object is an attribute in the tag
          {
            // Set the properties
            attrs: {
              href: this.to,
            },
            // Bind the event
            on: {
              // Re-copy the click event. If not, it will be clicked and send a request to the server to refresh the page
              click: this.myClick,
            },
          },
          // This is the content inside the tag. Rendering is the default slot
          // For example 
      
          // This is just an example
          [this.$slots.default]
        )
      },
      methods: {
        // Router-link click event
        myClick(e) {
          // Push state is used for hash routing. Push state is used for hash routing
          // Use history to change the browser address
          // pushState The first argument is the passed argument, the second is the title, and the third is the link
          history.pushState({}, ' '.this.to)
          // Render the corresponding component
          // The render page also needs to be changed. The current in data is responsive. The router-view is rendered according to current
          this.$router.data.current = this.to
          // Prevent default jump events
          e.preventDefault()
        },
      },
    })
    // Implement the router-view component
    Vue.component('router-view', {
      render(h) {
        // Obtain the component corresponding to the current path
        $router = MyVueRouter; // This.$router = MyVueRouter
        const component = this.$router.routerMap[this.$router.data.current]
        // Convert to the virtual Dom
        return h(component)
      },
    })
  }
  // Initialize the event
  initEvent() {
    // Listen for browser address changes
    window.addEventListener('popstate'.() = > {
      // Re-render the component by changing VueRouter's current address
      this.data.current = window.location.pathname
    })
  }

  / / initialization
  init() {
    // Parse routing rules
    this.createRouterMap()
    // Initialize the component
    this.initComponents(_Vue)
    // Initialize the event
    this.initEvent()
  }
}
Copy the code

Here the basic implementation of the function is almost, the above example is for the following foundation, all the functions are basically under a file is not rigorous, the following in strict accordance with Vue Router source code to achieve their own Vue Router

Vue Router to achieve

After the above simple implementation, we are now in accordance with the Vue Router source code for writing

1. First, the Vue Router is constructed

/* index.js */

// Export the VueRouter written by yourself
export default class VueRouter {
  // Implement the constructor function
  constructor(options) {
    // Select routes from options where the routing rule is not in place
    this._options = options.routes || []
  }
  / / initialization
  init(Vue){}}Copy the code

2. Register the install component

Register the vue-router globally in install.js

Router ∗∗∗router** **router∗∗∗ route is also created here

And register router-link router-view

/* install.js */

// Define a global Vue
export let _Vue = null

// Export the install method
export default function install(Vue) {
  // Save to global Vue
  _Vue = Vue
  / / with
  _Vue.mixin({
    // Do this after the Vue instance is created
    beforeCreate() {
      // This is new Vue
      if (this.$options.router) {
        / / save the Vue
        this._routerRoot = this
        // Save an instance of the Vue Router and some methods that can be constructed from the Vue Router later
        this._router = this.$options.router
        // Call Vue Router init(Vue)
        this._router.init(this)}else {
        // Here is the component that creates the Vue, etc
        // Determine if there is a parent component, and if there is, give the parent component's _roterRoot(Vue) to the child component
        // If there is no parent, give this, Vue, and Vue to the child
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this}}})}Copy the code

Then import Install in index.js to add install for the build

/ / import install
import install from './install'

// Export the VueRouter written by yourself
export default class VueRouter {... }// Add the install method for VueRouter
VueRouter.install = install
Copy the code

3. Write the create – the route – map. Js

The main purpose of this is to resolve the route that is passed to you and then export it for use in create-matcher.js

The details are all commented out

/* create-route-map.js */

// Export the specific route resolution
/ * * * *@param {*} Routes Routes rule *@param {*} OldPathList Route list *@param {*} OldPathMap Mapping between routes and components */
export default function createRouteMap(routes, oldPathList, oldPathMap) {
  // If the route is passed in, the dynamic route is added
  const pathList = oldPathList || []
  const pathMap = oldPathMap || []

  // Iterate over the rule operation
  routes.forEach((route) = > {
    // The recording route is also the core of the parsing route for division of labor clearly written outside
    addRouteRecord(route, pathList, pathMap)
  })

  // Return the new route list and route mapping
  return {
    pathList,
    pathMap,
  }
}

/ * * * *@param {*} Route Route rule *@param {*} PathList Route list *@param {*} PathMap Mapping between routes and components *@param {*} ParentRecord Parent route */
function addRouteRecord(route, pathList, pathMap, parentRecord) {
  // Route address Check whether there is a parent route. If there is no path between the parent route and the current route, the current route
  const path = parentRecord ? `${parentRecord.path}/${route.path}` : route.path
  // Record is used as a route record to record the routing address, component, and parent route for the routing relationship to correspond to the corresponding path
  const record = {
    path,
    component: route.component,
    parent: parentRecord,
  }
  // Check whether the current route exists in the route list. If the current route does not exist, add the current route and update the route list
  if(! pathList[path]) {// Add a route to the route list
    pathList.push(path)
    // Add the record corresponding to the path to the routing relationship
    pathMap[path] = record
  }
  // Determine whether the current route has children, if so, recurse
  if (route.children) {
    route.children.forEach((childRoute) = > {
      // The last parameter is the parent route record
      addRouteRecord(childRoute, pathList, pathMap, record)
    })
  }
}
Copy the code

4. Write the create – the matcher. Js

And the purpose of this module is also to parse the route but this is the conductor, so this is the actual parsing

Call the route resolution method in this module

With the above specific route resolution, creation-matcher.js is easy to implement, simply calling it

This module returns two methods

  • Match: Create a routing rule object based on the routing path. Then you can obtain all routing information and obtain all components through the rule object for creation
  • AddRoutes: adds dynamic routes
/* create-matcher.js */

// Import specific route resolution rules
import createRouteMap from './create-route-map'

// Export route parsing rules
export default function createMatcher(router) {
  // pathList Indicates the list of routes. PathMap indicates the mapping between routes and components
  // The specific parsing rule is createRouteMap
  const { pathList, pathMap } = createRouteMap(router)
  // Match is to obtain the corresponding route record from pathMap according to the path
  function match(path) {
      / / implementation
  }
  // Add a dynamic route
  function addRoutes(router) {
    // Adding dynamic routes must also resolve routing rules
    createRouteMap(router, pathList, pathMap)
  }
  // Return match and addRoutes
  return {
    match,
    addRoutes,
  }
}
Copy the code

Then use createMatcher in index.js, which is the Vue Router. Receive using this.matcher

/ / import install
import install from './install'
// Import the resolved route
import createMatcher from './create-matcher'

// Export the VueRouter written by yourself
export default class VueRouter {
  // Implement the constructor function
  constructor(options) {
    // Select routes from options where the routing rule is not in place
    this._routes = options.routes || []
    // We also return two match methods. AddRoutes uses matcher to receive a match
    this.matcher = createMatcher(this._routes)
  }
  / / initialization
  init(Vue){}}// Add the install method for VueRouter
VueRouter.install = install

Copy the code

5. Write createMatcher

Do you see that it defines a match in createMatcher,

Match is to obtain the corresponding route record from pathMap according to the path

I haven’t done it yet, so let’s do it now

To implement this, you need to write a createRoute method, which I write here in the uITL /route.js module

/* util/route.js */

/ / export createRoute
/ * * * *@param {*} Record The record sent by *@param {*} Path Route address *@returns* /
export default function createRoute(record, path) {
  [parentRecord,childRecord] [parentRecord,childRecord]
  const matched = []
  // Check whether it is a child route
  // Record = record. Parent ()
  // Return the object below
  while (record) {
    // Loop the resulting record to the front of the array
    matched.unshift(record)
    // Add the parent record to the current record to continue the loop
    record = record.parent
  }
  // Return path and matched for later router-view rendering
  return {
    path,
    matched,
  }
}
Copy the code

With the createRoute method written above, we can call create-mathcer.js to retrieve the record

The match method is then refined in create-mathcer.js

/* create-matcher.js */

// Import specific route resolution rules
import createRouteMap from './create-route-map'
/ / import createRoute
import createRoute from './util/route'

// Export route parsing rules
export default function createMatcher(router) {
  // pathList Indicates the list of routes. PathMap indicates the mapping between routes and components
  // The specific parsing rule is createRouteMap
  const { pathList, pathMap } = createRouteMap(router)
  // Match is to obtain the corresponding route record from pathMap according to the path
  function match(path) {
    // Retrieve the record corresponding to path
    const record = pathMap[path]
    // Check whether the record exists
    if (record) {
      return createRoute(record, path)
    }
    return createRoute(null, path)
  }
  // Add a dynamic route
  function addRoutes(router) {
    // Adding dynamic routes must also resolve routing rules
    createRouteMap(router, pathList, pathMap)
  }
  // Return match and addRoutes
  return {
    match,
    addRoutes,
  }
}
Copy the code

6. Processing History

Create a new base module in the history directory to write the parent class

This superclass has methods shared by the hash mode and the History (HTML5) mode

Here is the main demonstration of hash mode code

/* history/base.js */

// Import the createRoute we wrote above
import createRoute from '.. /util/route'

/ / export History
export default class History {
  // Router is an instance of the ue-router
  constructor(router) {
    // Assign a value to your own router
    this.router = router
    // The default current path is /
    this.current = createRoute(null.'/')}// The link to jump to
  // Path is the address of the route and onComplete is a callback
  transitionTo(path, onComplete) {
    // To get the current route to jump to, call the match method received in this.matcher in vue-router
    // In this case this.router is an instance of vue-router
    // this.router.matcher.match(path)
    this.current = this.router.matcher.match(path)
    // The callback has a trigger callback
    onComplete && onComplete()
  }
}
Copy the code

Write the HashHistory schema to inherit History

/* /history/hash */

// Import History from base
import History from './base'

// Inherit History
export default class HashHistory extends History {
  constructor(router) {
    super(router)
    // Make sure the route is #/ on the first access
    ensuerSlash()
  }
  // Listen for URL changes to set the current current
  setUpListener() {
    // Listen for hash changes
    window.addEventListener('hashchange'.() = > {
      / / change this. The current
      this.transitionTo(this.getCurrentLocation())
    })
  }
  // Get the hash of the current URL
  getCurrentLocation() {
    / / here it is not recommended as the return window. The location. The hash. Slice (1) have compatibility problems
    let href = window.location.href
    const index = href.indexOf(The '#')
    // Return an empty string when there is no #
    if (index < 0) return ' '
    // Get the address after #
    href = href.slice(index + 1)
    return href
  }
}

// Make sure to add #/ the first time
function ensuerSlash() {
  // You cannot add/if there is a hash
  if (window.location.hash) {
    return
  }
  // If there is no hash value, just add a/to the hash and it will automatically add /#/
  window.location.hash = '/'
}
Copy the code

The HTML5 mode is not covered here

Then go back to the index.js is written by their own Vue Router to continue to write pattern judgment

And finally, we’re going to initialize the init method

/* index.js */

/ / import install
import install from './install'
// Import the resolved route
import createMatcher from './create-matcher'
/ / import HashHistory
import HashHistory from './history/hash'
/ / import HTML5History
import HTML5History from './history/html5'

// Export the VueRouter written by yourself
export default class VueRouter {
  // Implement the constructor function
  constructor(options) {
    // Select routes from options where the routing rule is not in place
    this._routes = options.routes || []
    // We also return two match methods. AddRoutes uses matcher to receive a match
    this.matcher = createMatcher(this._routes)
    // If the fetch mode is not available, the default hash mode is used
    this.mode = options.mode || 'hash'
    // Use if or branch to perform different route hops according to different modes, etc
    switch (this.mode) {
      case 'history':
        this.history = new HTML5History(this)
        break
      case 'hash':
        // An instance of the pattern uses this.history to receive the next one
        // This is passed to VueRouter
        this.history = new HashHistory(this)
        break
      default:
        throw new Error('This mode does not exist')}}/ / initialization
  init(Vue) {
    // Get an example of the pattern
    const history = this.history
    The first argument is path, and the second is a callback function
    history.transitionTo(history.getCurrentLocation, () = >
      // Listen for URL changes to set current this.current
      history.setUpListener()
    )
  }
}
// Add the install method for VueRouter
VueRouter.install = install
Copy the code

7. Define a response value _route

A prerequisite for rendering different routing pages is to have an attribute that represents the response of the current route

So let’s go to install.js and add a responsive property **_route**

Code unrelated to this… omit

/* install.js */

export let _Vue = null

export default function install(Vue) {
  _Vue = Vue
  Vue.mixin({
    beforeCreate() {
      if (this.$options.router) {
        ...
        // Create a value _route that represents the current route response
        // It is not recommended to create directly using defineReactive.
        // The first argument is who to bind to, the second is the name of the value, and the second is the value
        Vue.util.defineReactive(this.'_route'.this._router.history.current)
      } else{... }}})}Copy the code

Then you go back to the base below history and add a callback this.cb that changes the value of the responder _route

/* history/base.js */

import createRoute from '.. /util/route'

export default class History {
  constructor(router){...// cb a callback function that changes the value of the responsive route _route and then refreshes the view
    this.cb = null
  }
  // Use listen to change the cb value
  listen(cb) {
    this.cb = cb
  }

  transitionTo(path, onComplete){...// Change the value of the responsive route if the CB exists
    this.cb && this.cb(this.current) ... }}Copy the code

Finally, init in index.js calls the Listen method, passing in the callback to change the response value **_route**

/* index.js */.export default class VueRouter {...init(Vue){...// Modify the response route
    history.listen((route) = > {
      Vue._route = route
    })
  }
}
...

Copy the code

Add 8.$router$route

We know that the Vue Router provides $Router (which is an instance of the Vue Router**) and $route(routing rule object).

You can add these properties yourself by going to install.js

/* install.js */.export default function install(Vue) {...// Add the $router route Object object. defineProperty parameters are who to add, attribute name, attribute value
  Object.defineProperty(Vue.prototype, '$router', {
    get() {
      // this._routerRoot stands for Vue, and its _router is an instance of Vue Router
      // Go back to the second point
      return this._routerRoot._router
    },
  })
  / / add $route
  Object.defineProperty(Vue.prototype, '$route', {
    get() {
      // His _route is the current route that just added the response
      return this._routerRoot._route
    },
  })
}
Copy the code

9.router-link

The basic introduction is not much to say, there is also an introduction before. And now let’s do it again

Create a new link.js under the Components file

/* ./components/link.js */

/ / export link
export default {
  props: {
    to: {
      type: String.required: true,}},/ / rendering
  render(h) {
    // Convert to the virtual DOM
    return h(
      / / tag name
      'a'.// Tag attributes
      {
        domProps: {
          href: The '#' + this.to,
        },
      },
      // The content inside the tag is the default slot
      [this.$slots.default]
    )
  },
}
Copy the code

10.router-view

I’m going to create view.js under the Components file and I’m going to comment out what I did

/* ./components/link.js */

/ / export the view
export default {
  render(h) {
    // Get the routing rule object
    const route = this.$route
    // define a variable to wait for the value in matched
    let depth = 0
    // The component is router-view
    this.routerView = true
    // Try to get the parent component
    let parent = this.$parent
    // Check whether there is a parent component
    while (parent) {
      // Check whether the component is a routerView
      if (parent.routerView) {
        depth++
      }
      // Continue to check that there are no parent components
      parent = parent.$parent
    }
    $route = '_route'; $route = '_route'
    // Current is returned with matched and path
    [parentRecord,childRecord] [parentRecord]
    ['/login','/login/ TAB '] ['/login/TAB ']
    // Because unshif is used to add after the parent component is added before
    // Add the depth to the bottom
    const record = route.matched[depth]
    // Render directly without recording
    if(! record) {return h()
    }
    // Retrieve the component in the record
    const component = record.component
    // Finally render the component
    return h(component)
  },
}

Copy the code

Well, here is the Vue Router second preparation is completed, although and the official gap is very big. Well, because this is a simplification

11. File directory

Forgot the directory where the last file was pasted

This simulated VueRouter demo is put on Github, there is a need can here MyVueRouter

Up here, we have only implemented a small part of the functions of VueRouter

But in general, the functionality is pretty much done, and the embedding is done by adding dynamic routing

In fact, I think this is ok, but I still have to continue to study