Vue Router summary (from official website)

Keywords: Front-end Vue Programming Webpack React

Vue Router summary (from official website)

1. Introduction to Vue router function

  • Nested route / view tables
  • Modular, component-based routing configuration
  • Routing parameters, queries, wildcards
  • View transition effect based on Vue.js transition system
  • Fine grained navigation control
  • Links with automatically activated CSS class es
  • HTML 5 history mode or hash mode, automatically degraded in IE9
  • Custom scroll bar behavior
  • Three routing modes (hash,history,abstract)

2. Dynamic routing configuration

General parameters:

We often need to map all the routes that a pattern matches to the same component. For example, we have a User component that we use to render for all users with different ID s. Then, we can use "dynamic segment" in the route path of Vue router to achieve this effect:

const User = {
  template: '<div>User</div>'
}

const router = new VueRouter({
  routes: [
    // Dynamic path parameters start with a colon
    { path: '/user/:id', component: User }
  ]
})
Pattern Match path $route.params
/user/:username /user/evan { username: 'evan' }
/user/:username/post/:post_id /user/evan/post/123 { username: 'evan', post_id: '123' }
be careful:

When routing parameters are used, such as navigating from / user/foo to / user/bar, the original component instance is reused. Because both routes render the same component, reuse is more efficient than destroy and recreate. However, this also means that the component's lifecycle hook is no longer called.

You can use watch for route monitoring
const User = {
  template: '...',
  watch: {
    '$route' (to, from) {
      // Respond to route changes
    }
  }
}
Or use the beforeRouteUpdate navigation guard introduced in 2.2
const User = {
  template: '...',
  beforeRouteUpdate (to, from, next) {
    // react to route changes...
    // don't forget to call next()
  }
}

Unconventional parameters

The general parameters only match the characters in the URL fragment that is / delimited. If you want to match any path, we can use the wildcard (*):

{
  // Will match all paths
  path: '*'
}
{
  // Will match any path starting with '/ user -'
  path: '/user-*'
}

3. Nested route

const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User,
      children: [
        {
          // When / user/:id/profile is matched successfully,
          // UserProfile will be rendered in User's < router View >
          path: 'profile',
          component: UserProfile
        },
        {
          // When / user/:id/posts match successfully
          // UserPosts will be rendered in User's < router View >
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]
})

be careful:

Nested paths starting with / are treated as root paths. This allows you to make full use of nested components without setting nested paths. At this time, based on the above configuration, when you access / user/foo, the User exit will not render anything, because there is no matching to the appropriate sub route. If you want to render something, you can provide an empty sub route:

const router = new VueRouter({
  routes: [
    {
      path: '/user/:id', component: User,
      children: [
        // When the / user/:id matches successfully,
        // UserHome will be rendered in User's < router View >
        { path: '', component: UserHome },

        // ... other sub routes
      ]
    }
  ]
})

4. Program navigation

1).router.push(location, onComplete?, onAbort?)

When you click < router link >, this method will be called internally, so clicking < router link: to = "..." > is equivalent to calling router.push(...).

Declarative Programming
<router-link :to="..."> router.push(...)
// character string
router.push('home')

// object
router.push({ path: 'home' })

// Named route
router.push({ name: 'user', params: { userId: '123' }})

// With query parameter, / register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

be careful:

If path is provided, params will be ignored Note: if the destination is the same as the current route and only the parameters have changed (for example, from one user profile to another / users / 1 - > / users / 2), you need to use the beforeRouteUpdate To respond to this change (such as grabbing user information).

2).router.replace(location, onComplete?, onAbort?)

Like router.push, the only difference is that it doesn't add new records to history, but it's the same as its method name - replacing the current history record.

Declarative Programming
<router-link :to="..." replace> router.replace(...)

3).router.go(n)

The parameter of this method is an integer, which means how many steps forward or backward in the history record, similar to window.history.go(n).

// Take a step forward in the browser record, which is equivalent to history.forward()
router.go(1)

// One step back recording, equivalent to history.back()
router.go(-1)

// 3 steps forward record
router.go(3)

// If the history record is not enough, it will fail silently
router.go(-100)
router.go(100)

Summary:

You may notice that router.push, router.replace and router.go follow window.history.pushState, window.history.replaceState and window.history.go As if, in fact, they do follow the window. History API.

5. Named view

Sometimes you want to display multiple views at the same time (at the same level), rather than nested display. For example, you can create a layout with two views: sidebar (side navigation) and main (main content). At this time, named views are useful. Instead of having a single exit, you can have multiple views named individually in the interface. If the router view does not have a name set, the default is default.

<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})

be careful:

A view is rendered with one component, so multiple views require multiple components for the same route. Ensure the correct use of * * components * * configuration (with s):

6.HTML5 History mode

history mode requires background configuration support. Because our application is a single page client application, if there is no correct configuration in the background, when users directly access the browser, they will return 404, which is not good-looking. To add a candidate resource covering all situations on the server side: if the URL does not match any static resources, the same index.html page should be returned

Background configuration example:

Apache
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>
nginx
location / {
  try_files $uri $uri/ /index.html;
}
Native Node.js
const http = require('http')
const fs = require('fs')
const httpPort = 80

http.createServer((req, res) => {
  fs.readFile('index.htm', 'utf-8', (err, content) => {
    if (err) {
      console.log('We cannot open "index.htm" file.')
    }

    res.writeHead(200, {
      'Content-Type': 'text/html; charset=utf-8'
    })

    res.end(content)
  })
}).listen(httpPort, () => {
  console.log('Server listening on: http://localhost:%s', httpPort)
})

warning

Give a warning, because after you do this, your server will not return the 404 error page, because the index.html file will be returned for all paths. To avoid this situation, you should cover all routing situations in Vue application, and then give a 404 page.

const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '*', component: NotFoundComponent }
  ]
})

7. Navigation guard

Keep in mind that changes in parameters or queries do not trigger in / out navigational guards. You can go through Observe $route Object To cope with these changes, or use the component guard of beforeRouteUpdate.

  • Global front guard beforeEach

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
  // Proceed to the next hook in the pipe. If all hooks are completed, the navigation status is * * confirmed * *.
  next();
  // Interrupt the current navigation. If the URL of the browser changes (maybe the user manually or the browser Back button), the URL address will be reset to the address corresponding to the 'from' route.
  next(false): 
  // Jump to a different address. The current navigation is interrupted and a new navigation is performed. You can pass any location object to 'next', and you can set options such as' replace: true ',' name: 'home', and any 'to ` prop' (https://router.vuejs.org/zh/api/ #) or [` router. Push '(https://router.vuejs.org/zh/api/ #) used in [` router link].
  next('/')`perhaps`next({ path: '/' })
  // If the parameter passed in 'next' is an instance of 'Error', the navigation will be terminated and the Error will be passed to the registered callback of [` router. Onerror() '] (https://router.vuejs.org/zh/api/ #router-onerror).
  next(error)
})
  • Global resolution guard beforeResolve

At 2.5.0 +, you can register a global guard with router.beforeResolve. This is similar to router.beforeEach. The difference is that before the navigation is confirmed, and after all the internal guard and asynchronous route components are resolved, the resolution guard is called.

const router = new VueRouter({ ... })

router.beforeResolve((to, from, next) => {
  // ...
  next();
})
  • Global after hook after each

const router = new VueRouter({ ... })
// Post hook does not accept next parameter
router.afterEach((to, from) => {
  // ...
})
  • Route exclusive guard before enter

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
  • Guard in component beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // Before rendering the component, the corresponding route is called before confirm.
    // No Yes! Get component instance ` this`
    // Because before the guard executes, the component instance has not been created
  },
  beforeRouteUpdate (to, from, next) {
    // Called when the current route changes, but the component is reused
    // For example, for a path with dynamic parameters / foo/:id, when you jump between / foo/1 and / foo/2,
    // Because the same Foo component is rendered, the component instance is reused. And the hook will be called in this case.
    // Can access component instance ` this`
  },
  beforeRouteLeave (to, from, next) {
    // Called when the navigation leaves the corresponding route of the component
    // Can access component instance ` this`
  }
}

8. Complete navigation analysis process

1. Navigation is triggered.
2. Call the leave guard in the deactivated component.
3. Call the global 'beforeEach' guard.
4. Call 'beforeRouteUpdate' guard (2.2 +) in the reused component.
5. Call 'beforeEnter' in routing configuration.
6. Resolve asynchronous routing components.
7. Call 'beforeRouteEnter' in the activated component.
8. Call the global 'beforeResolve' guard (2.5 +).
9. Navigation confirmed.
10. Call the global 'afterEach' hook.
11. Trigger DOM update.
12. Call the callback function passed to 'next' in the 'beforeRouteEnter' guard with the created instance.

9. Transition dynamic effect

<transition name="fade">
  <router-view></router-view>
</transition>
.fade-enter-active, .fade-leave-active { 
     transition: opacity .5s; 
} 
.fade-enter, .fade-leave-to { 
     opacity: 0; 
}

10. Data acquisition

  • Get data after navigation

export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  created () {
    // Get the data after the component is created,
    // At this time, the data has been observed
    this.fetchData()
  },
  watch: {
    // If the route changes, the method is executed again
    '$route': 'fetchData'
  },
  methods: {
    fetchData () {
      this.error = this.post = null
      this.loading = true
      // replace getPost with your data fetching util / API wrapper
      getPost(this.$route.params.id, (err, post) => {
        this.loading = false
        if (err) {
          this.error = err.toString()
        } else {
          this.post = post
        }
      })
    }
  }
}
  • Get data before navigation is complete

export default {
  data () {
    return {
      post: null,
      error: null
    }
  },
  beforeRouteEnter (to, from, next) {
    getPost(to.params.id, (err, post) => {
      next(vm => vm.setData(err, post))
    })
  },
  // Before the route changes, the component has finished rendering
  // Slightly different logic
  beforeRouteUpdate (to, from, next) {
    this.post = null
    getPost(to.params.id, (err, post) => {
      this.setData(err, post)
      next()
    })
  },
  methods: {
    setData (err, post) {
      if (err) {
        this.error = err.toString()
      } else {
        this.post = post
      }
    }
  }
}

11. Rolling behavior

Note: this feature is only available in browsers that support history.pushState.

const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return where do you want to scroll
    // When you press the back / forward button, it's like the browser's native behavior
    if (savedPosition) {
       return savedPosition
    } else {
        return { x: 0, y: 0 }
    }
    // Or enable anchors
    if (to.hash) {
        return {
            selector: to.hash
        }
    }
  }
})

12. Route lazy loading

be careful: If you are using Babel, you will need to add syntax-dynamic-import Plug in to enable Babel to parse the syntax correctly.

Combining the asynchronous component of Vue and the code division function of Webpack, the lazy loading of route is realized

1) An asynchronous component is defined as a factory function that returns a Promise (the Promise returned by this function should resolve the component itself):
const Foo = () => Promise.resolve({ /* Component definition object */ })
2) In Webpack 2, we can use the Dynamic import Syntax to define a code split point:
import('./Foo.vue') // Back to Promise
Combine 1) and 2) to achieve lazy loading of routes, as follows:
const Foo = () => import('./Foo.vue')

Posted by sotusotusotu on Sat, 25 Apr 2020 03:00:26 -0700