Let’s cut the crap and get an article on how it works and how it works

Let’s review vue’s little brother MOE first.

I’ve been abused lately, and I’m not doing so well. If wrong, hope to advise, Thanksgiving!

The use and handwriting of vuex

Vue -router (Hash mode)

1. Start with use

1.1 basis

1.1.1 Dynamic Routing

Dynamic routing means that paths like good/001 and good/002 can all be mapped to the same component

Configure the route writing method

[{path: '/good/:id',}]Copy the code

When the path matches this route, the: argument is placed in $router.params of the current component instance, that is, we can get the: argument of the current path in the component

Chestnut :(note that the root component leaves a hole in the display of this component)

<template>
    <div>
        Good
       {{$route.params.id}}
    </div>
</template>
Copy the code

Of course, many times it is necessary to get the parameters passed in the URL. The back).

<template>
    <div>
        Good
       {{$route.params.id}}
       {{$route.query}}
       
    </div>
</template>
Copy the code

1.1.2 Nested routines by

So if we’ve been on the product page, now we’re going to go to the product details

Its path assumption is… good/01/details

const routes = [{
    path: '/good/:id'.component: Good,
    children: [{
        path: 'details'.component: Details
    }]
}]
Copy the code

1.1.3 Programmed navigation

Router

is used as a router link in components. It works better in templates, but what about js?

In this case, we will use an API=>this.$router.push.

The push method is usually named after a push operation. This is no exception, and it adds a new record to the history stack.

You can think of it as having a history stack of urls

When a route is matched, its address is pushed into the history stack. So when you click the back button, you will automatically go back to the previous page


also calls this API internally, and they are equivalent

Chestnut:

 this.$router.push('good/1')   //http://localhost:8080/#/good/1
 this.$router.push({path:'/good/2'})
//http://localhost:8080/#/good/2


 this.$router.push({name:'good'.params: {id:3}})
// Add a name to the routing rule configuration
const routes = [{
    name: 'good'.path: '/good/:id'.component: Good,
    children: [{
        path: 'details'.component: Details
    }]
}]
//http://localhost:8080/#/good/3
Copy the code

Note that params is ignored if path is provided

Let’s look at two more apis

router.replace

Router. push adds another record to history and reverses it. Router. replace replaces the current record at the top of the history stack.

The difference between them is that after using the push method to jump, click back can return; Have completely lost it

  this.$router.replace({path:'/good02'})
Copy the code
router.go(n)

This is easier. How many steps forward or backward along history?

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

1.1.4 Named Routes and Named Views

After routing

A named route is a route given a name when configuring rules.

const routes = [{
    name: 'good'.path: '/good/:id'.component: Good,
    children: [{
        path: 'details'.component: Details
    }]
}]

Copy the code

Two matches in a component

<router-link :to="{name:'good',params:{id:4}}"> jump < / router - the link >this.$router.push({name:'good'.params: {id:5}})

Copy the code
Named view

For example, if I match an INDEX route, the index page should at least have a header and a body. That means we have to put at least two pits in its parent component

Let’s look directly at chestnuts

 {
        path: '/'.components: {
            test01: Test01,// The key is the pit name
            test02: Test02,
            default: Test03// No name is default, matching the pit with no name attribute}}Copy the code

The component pit can be specified by name

<router-view name="test01"></router-view>
<router-view name="test02"></router-view>
<router-view />
Copy the code

1.1.5 Redirection and Alias

It’s easier to just look at chestnuts

redirect
// The simplest
{ path: '/good'.redirect: '/'Component: Good}// You can also specify a name
{ path: '/good'.redirect: { name: 'index'}, component: Good}// Can also be a method that takes a directory route as an argument
{ path: '/good'.redirect: to= > {
        const { params } = to
        if (params.id === 1) {
            return '/'}, Component: Good}Copy the code
The alias
 { path: '/good'.alias: '/goodAlias'Component: Good}Copy the code

1.1.6 Route Component Parameter Transfer

How did we pass in the reference?

We use dynamic routing, and route.params

{
     path: '/good/:id'.component: Good,
}
Copy the code
{{$route.params.id}}
Copy the code

But:

So let’s take a look at the new approach

Add a new props property to the configuration. Note that the props value can be more than booleans

{
        name: 'good'.path: '/good/:id'.component: Good,
        props: true
    },
Copy the code

Component, using parent-child communication. In the props

<template>
    <div>Commodity {{id}}<router-view/>
    </div>
</template>
<script>
export default {
    props: ['id']}</script>
Copy the code

1.2 the guards

1.2.1 Global Guard

Global front guard

Each guard method receives three arguments

  • To: indicates the route to be entered

  • From: indicates the outgoing route

  • Next: It is a method that must be called. It takes the following parameters

    • Not pass, that is, next(). After the current hook executes, proceed to the next hook (if any)

    • Next (‘/’) equivalent month next({path:’/’}), similar to programmatic navigation, go to the root path (or other address)

    • Next (error), (note that this error is an instance of error) the navigation is terminated and the error is passed to the callback registered with router.onerror ().

A chestnut is not allowed to enter good/1

router.beforeEach((to, from, next) = > {
    if (to.name === 'good' && to.params.id === '1') {
        console.log('You can't come to this place.');
        next({ path: '/'})}else {
        next()
    }
})
Copy the code
Global post-hook

Unlike the front guard, it does not accept the next function in its methods

router.afterEach((to, from) = > {
  // ...
})
Copy the code

1.2.2 Route Exclusive Guard

When configuring routing rules, you can directly define them

{
        name: 'good'.path: '/good/:id'.component: Good,
        props: true.beforeEnter: (to, from, next) = > {
            if (to.name === 'good' && to.params.id === '2') {
                console.log('Neither 2')
                next({ path: '/'})}else {
                next()
            }

        }
    },
Copy the code

1.2.3 Guards within components

beforeRouteEnter
<template>
    <div>
   
    </div>
</template>
<script>
export default {
    beforeRouteEnter (to, from, next) {
        if(to.name==='good'&&to.params.id==='3') {console.log('3 doesn't work either. ')
            next({path:'/'})}else{
            next()
        }
    }
}
</script>
Copy the code

Here are some things to note:

This method is called before the navigation is confirmed, i.e. the current route has not been entered. The component for the current route has not yet been created, so you can’t get the component instance in this method

However, if you must take the current component instance in this method. There is a way

You can pass a callback to the next method that takes the current component instance. Of course, since the instance is available, the callback method must be called during navigation validation. The return value of this callback is taken as the argument to next

chestnuts

beforeRouteEnter (to, from, next) {
       next(vm= >{
           console.log(vm); })}Copy the code

As long as the guard’s next method supports the pass-through callback, the component is already created when the next two methods are called and there is no need to bother fetching instances. The next two are chestnuts from the official website

beforeRouteUpdate
beforeRouteUpdate (to, from, next) {
  this.name = to.params.name
  next()
}
Copy the code
beforeRouteLeave
beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes! ')
  if (answer) {
    next()
  } else {
    next(false)}}Copy the code
Navigation is important

Let’s look at some of the order in which these hooks are called

Let’s verify that

The above is the case of routing from one route to another, so the starting point is the exit hook within the component.

For simplicity here, I’ll go straight into a route as chestnut

Let’s get the hooks set up

/ / configuration
{
        name: 'good'.path: '/good/:id'.component: Good,
        props: true.beforeEnter: (to, from, next) = > {
            console.log('Configuring beforeEnter')
            next()

        }
    },
// Global hook
  
router.beforeEach((to, from, next) = > {
    console.log('Global Guard')
    next()
})
router.beforeResolve((to, from, next) = > {
    console.log('Global resolution')
    next()
})
router.afterEach((to, from) = > {
    console.log('Global post' + to + from)// HERE I also don't want to print to, esLint can't pass without printing
})      

/ / components
 beforeRouteEnter(to, from, next) {
    console.log("Component in front");
    next((vm) = > {
      console.log("Component front callback");
      console.log(vm);
    });
  },
  beforeRouteUpdate(to, from, next) {
    console.log("Updates within components");
    next();
  },
  beforeRouteLeave(to, from, next) {
    console.log("Component in-back");
    next();
  },

Copy the code

start

The URL is changed from http://localhost:8080/#/good/2 to http://localhost:8080/#/good/3. For the trigger beforeRouteUpdate

The global parse hook is not described earlier, and it is just the same as the global guard

1.3 To explain some other common knowledge

1.3.1 Lazy Route Loading

The traditional way of writing this is that whether or not the component definition is used is to come in first. This will increase the volume of the packaging (because some of it may not be used)

Lazy loading writing:

{
        name: 'good'.path: '/good/:id'.component: () = >
            import ('.. /components/test01.vue'),
        // props: true,
        // beforeEnter: (to, from, next) => {
        // console.log(' configuring beforeEnter')
        // next()

        // }
    },
Copy the code

1.3.2 Other tips

Things like data acquisition, and scrolling behavior can be taken a look at

The timing of the data retrieval, whether it’s in the lifecycle hook of the current component after entering the route or in the route guard before entering the route, is something that we usually use

The scrolling action is triggered by the browser’s forward/back buttons

2. Implement a simplified version of routing

Let’s first analyze what’s going on in the routing configuration file

First, vue.use (VueRouter), indicating that VueRouter is a plug-in. So it needs to provide an install method internally (in the case of an object) (if it is a function, this function is called Install). The internal install method is also called, passing Vue as an argument

Here we create a routing configuration rule, which is an array passed in as a construction parameter when new VueRouter is created

Take a look at the entry file main.js

Place one of the route objects instantiated above as an option in the new Vue constructor option

On the whole

class MyRouter {
    constructor(options) {
        this.$options = options
    }
}

// An install method needs to be implemented
MyRouter.install = function(vue) {}Copy the code

What we need to do in the install method is to put the routing instance we made during the routing configuration into the prototype of the Vue when new Vue

This is definitely a mix-up, but we only want to tie the routing instance to the Vue prototype when the root instance is created, so now we have to compare the root instance with the normal component instance.

As you can see at a glance, the root instance has a Router object in its construction options, which is our Router instance

Then we can get the Router instance with $options.router in a lifecycle hook and put it into the Vue prototype

 vue.mixin({
        beforeCreate() {
            if (this.$options.router) {
                vue.prototype.$router = this.$options.router
            }
        },
    })
Copy the code

Create two routing componentsrouter-linkandrouter-view

Note that the template syntax can only be used under the compiler version; only the JSX or H functions can be used here

Router-link creates an A link. It has a feature href, so it requires the parent component to pass the parameter. At the same time, the content of the slot is also indispensable

vue.component('router-link', {
        props: ['to'].render(h) {
            return h('a', {
                attrs: {
                    href: The '#' + this.to
                }
            }, this.$slots.default)
        },
    })
Copy the code

Router-view: after the HASH value of the URL changes, the router will re-match the new hash value with each path in the route configuration, and render its corresponding component once the match is found

The component instance takes the Router instance (vue prototype), the Router instance takes its $options (constructor passed), and finally the routes rule

Note that the hash value this.$router.current is ugly because it is intended to be reactive. Since we want to make sure that this function is called every time the URL hash changes, I’ll just dump the update to vUE

 vue.component('router-view', {
        render(h) {
            let component = null

            this.$router.$options.routes.forEach(route= > {
                if (route.path === this.$router.current.current) {
                    console.log(111)
                    component = route.component
                }
            })
            return h(component)
        }
    })
Copy the code

Save the hash value and listen for hash change events

Note that you can get the vue from the install method, but you can’t get it from the MyRouter class.

Declare a variable Vue at install

Reactive problem, we use Vue’s data option, the data option of the initialization of the data is reactive (regardless of the array)

Another simple note is window.addeventListener (‘hashchange’, this.onhashchange.bind (this))

Without this, it is obvious that this.onHashChange is a default binding that points to undefined (in strict mode), so the following onHashChange methods cannot store hashes through this.current.current

let Vue
class MyRouter {
    constructor(options) {
        this.$options = options
        let currentHash = window.location.hash.slice(1) | |'/'
        this.current = new Vue({
            data: {
                current: currentHash
            }
        })

        window.addEventListener('hashchange'.this.onHashChange.bind(this))}onHashChange() {
        this.current.current = window.location.hash.slice(1)}}Copy the code

A simple route to this point is done

Wrote last

Hope this article can give you a little help.

Looking forward to our next encounter