Use of vue3 routing

use of routing

In the traditional web development process, when multiple in-site pages need to be implemented, many HTML pages need to be written before, and then jump to each other through tags.

Front-end development in today's engineering mode, like Vue projects, can easily implement the functions of rendering and jumping multiple pages in the site with only one HTML file. This is routing.

TIP

从这里开始,所有包含到 .vue 文件引入的地方,
可能会看到 @xx/xx.vue 这样的写法。

@views 是 src/views 的路径别名,
@cp 是 src/components 的路径别名。

路径别名可以在 vite.config.ts 等构建工具配置文件里添加 alias ,
点击了解:添加项目配置

Routing directory structure
Vue 3 imports routing in the same way as Vue 2, and routing management is also placed in the src/router directory:

src
│ # 路由目录
├─router
│   # 路由入口文件
├───index.ts
│   # 路由配置,如果路由很多,可以再拆分模块文件
├───routes.ts
│ # 项目入口文件
└─main.ts

Among them, index.ts is the entry file of the route. If there are few routes, it can only be maintained in this file. However, for complex projects, it is often necessary to configure the upper-level and third-level routes, and put the logic and configuration in one file. If so, it's too bulky.

So if the project is a little more complicated, it can be split into two files like the above structure: index.ts and routes.ts, maintain the structure of the routing tree in routes.ts, import the routing tree structure in index.ts and activate the routing , and routing hooks can be configured in this file.

If the project is more complex, such as an Admin background, you can split the routes into more detailed ones according to the business modules, such as game.ts / member.ts / order.ts and other business modules, and then uniformly import them into the index.ts file .

TIP

需要注意的是,与 Vue 3 配套的路由版本是 vue-router 4.x 以上才可以正确适配项目。

Import routes into the project

Regardless of whether it is Vue 2 or Vue 3, the imported route is in the src/router/index.ts file, but the version upgrade has brought about great changes. Since this book uses TypeScript for Vue 3, only one TypeScript is used here comparison of changes.

TIP

下文可能会出现多次 import.meta.env.BASE_URL 这个变量,
它是由 Vite 提供的环境变量,详见 Vite 官网关于 环境变量 的说明。

使用其他构建工具请自行替换为对应构建工具提供的环境变量,
例如使用 @vue/cli 创建的项目因为是基于 Webpack ,所以使用的是 process.env.BASE_URL

Reviewing Vue 2

The import method of Vue 2 is as follows (where RouteConfig is the TS type of the routing project).

import Vue from 'vue'
import VueRouter from 'vue-router'
import type {
    
     RouteConfig } from 'vue-router'

Vue.use(VueRouter)

const routes: Array<RouteConfig> = [
  // ...
]

const router = new VueRouter({
    
    
  mode: 'history',
  base: import.meta.env.BASE_URL,
  routes,
})

export default router

The function description of some options inside:

routes is the configuration of the routing tree. When there are many routes, it can be managed in routes.ts and then imported (see the routing configuration section below for specific configuration).

mode determines the access path mode, which can be configured as hash or history. The Hash mode is an address with # like http://abc.com/#/home, which supports all browsers. The History mode is http://abc. Com/home does not have #, which is not only beautiful, but also has a better experience, but requires some configuration support on the server side (see the server side configuration plan below for details), and it is only supported by mainstream browsers.

base is the base path of the History mode when switching routes. The default is the / root directory. If the project is not deployed in the root directory, but a multi-level directory such as a second-level directory, a third-level directory, etc., you must specify this base, otherwise Route switching will be problematic.

Understanding Vue 3
Vue 3 is introduced in the following way (where RouteRecordRaw is the TS type of the route item).

import {
    
     createRouter, createWebHistory } from 'vue-router'
import type {
    
     RouteRecordRaw } from 'vue-router'

const routes: Array<RouteRecordRaw> = [
  // ...
]

const router = createRouter({
    
    
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
})

export default router

In Vue 3 (that is, vue-router 4.x), routing simplifies some configuration items, and routes, like Vue 2, is the configuration of the routing tree.

But history is different from Vue 2. In Vue 3, history is used to replace the mode of Vue 2, but the function is the same, and it also determines whether the access path mode is Hash mode or History mode, and Vue 2 (that is, vue- router 3.x) as the input parameter of the mode function.

Just like when using Vue 2, Vue 3 can also configure some additional routing options, for example: specify router-link as the className matched by the currently active route:

const router = createRouter({
    
    
  history: createWebHistory(import.meta.env.BASE_URL),
  linkActiveClass: 'cur',
  linkExactActiveClass: 'cur',
  routes,
})

For more configuration items, please refer to the API reference chapter of the Vue Router official website.

The configuration of the routing tree
is mentioned in the import routing section. When there are many routes in the project, the article will become very long and difficult to maintain. At this time, you can concentrate on routes.ts or more modular file management, and then import into index.ts.

For the time being, the routes.ts file is called "routing tree", because it is like a big tree, which can not only grow with the first-level route as the trunk, but also add second-level, third-level and other multi-level routes to branch and disperse. Ye, let's see how to write routes.ts.

basic format

In TypeScript, the basic format of routing files consists of three parts: type declaration, array structure, and module export.

// src/router/routes.ts

// 使用 TypeScript 时需要导入路由项目的类型声明
import type {
    
     RouteRecordRaw } from 'vue-router'

// 使用路由项目类型声明一个路由数组
const routes: Array<RouteRecordRaw> = [
  // ...
]

// 将路由数组导出给其他模块使用
export default routes

Then it can be imported and used in index.ts.

So how to write the routing array inside? This involves the preparation of one-level routing and multi-level routing.

common base path

Before configuring routing, you need to understand the concept of "public basic path". When explaining the use of tools such as Vite to create projects, the management of a project configuration is mentioned. Take the configuration file vite.config.ts of the Vite project as an example. There is an option base, which is actually used to control the public base path of routing, so what is the use of it?

The default value of base is /, that is to say, if it is not configured, all resource files are read from the root directory of the domain name. It is of course good if the project is deployed in the root directory of the domain name, but what if it is not? Then you have to configure it.

The configuration is very simple, as long as the final address of the project to be launched is removed from the domain name, the remaining part is the value of the base. Assuming the project is deployed at https://example.com/vue3/, then the base can be set to /vue3/.

TIP

一级路由的 path 都必须是以 / 开头,比如: /home、/setting;

如果的项目首页不想带上 home 之类的尾巴,只想要 https://example.com/ 这样的域名直达 ,其实也是配置一级路由,只需要把路由的 path 指定为 / 即可。

name is the name of the route, which is not required, but it is generally configured, so that it is convenient to replace the path with the name to realize the jump of the route, because sometimes the paths of the development environment and the production environment are inconsistent, or the path changes , there is no need to adjust the name, but if you pass the path, you may have to modify the link jump targets in many files.

component is the template file of the route, pointing to a vue component, which is used to specify the view rendering of the route on the browser side. There are two ways to specify which component to use:

Synchronous component
The field component receives a variable, and the value of the variable is the corresponding template component.

When packaging, all the codes of the components will be packaged into one file. For large projects, this method of first-screen loading is a disaster, and you have to face the problem of longer waiting time caused by too large files.

import Home from '@views/home.vue'

const routes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/',
    name: 'home',
    component: Home,
  },
]

So now it is recommended to use the second method, which can realize lazy loading of routes .

The asynchronous component
field component receives a function, and returns the template component when it returns. At the same time, the code in the component will generate an independent file when it is packaged, and import it as needed when accessing the corresponding route.

const routes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/',
    name: 'home',
    component: () => import('@views/home.vue'),
  },
]

For more instructions on this part, see Lazy loading of routes.

Multi-level routing
In the Vue routing ecology, it supports the configuration of multi-level routing such as second-level, third-level, and fourth-level routing. Theoretically, there is no upper limit. The number of levels used in actual business is usually three to four levels.

For example, if you are making a gourmet website and plan to configure a "dumpling" column under the "Chinese food" category, then the address is:

https://example.com/chinese-food/dumplings

In this case, Chinese-food is the first-level route, and dumplings is the second-level route.

If you want to refine it further, add a subcategory of different fillings such as "leek" and "cabbage" under "dumplings":

https://example.com/chinese-food/dumplings/chives

The leek chives here are sub-routes of dumpling dumplings, which is the third-level route.

After understanding the concept of sub-routing, let's take a look at how to configure it and the precautions.

The relationship between parent and child routes is strictly in accordance with the hierarchical relationship of JSON. The information of child routes is configured in the children array of the parent level, and the grandson route is also configured in the children of child routes in the same format.

Here is an example of a simple subroute:

const routes: Array<RouteRecordRaw> = [
  // 注意:这里是一级路由
  {
    
    
    path: '/lv1',
    name: 'lv1',
    component: () => import('@views/lv1.vue'),
    // 注意:这里是二级路由,在 `path` 的前面没有 `/`
    children: [
      {
    
    
        path: 'lv2',
        name: 'lv2',
        component: () => import('@views/lv2.vue'),
        // 注意:这里是三级路由,在 `path` 的前面没有 `/`
        children: [
          {
    
    
            path: 'lv3',
            name: 'lv3',
            component: () => import('@views/lv3.vue'),
          },
        ],
      },
    ],
  },
]

In the above configuration, the access address of the final three-level route is as follows:

https://example.com/lv1/lv2/lv3

You can see that there is no / in front of the path field of the second-level and third-level routes in the comment, so that the path of its parent route will be in front of the path to reflect its hierarchical relationship, otherwise it will start from the root directory.

Routing lazy loading
As mentioned above, when routing configures synchronization components, the built files are all gathered together, and the files of large projects will become very large, which will affect page loading.

Therefore, based on the code splitting function of Webpack, Vue introduces asynchronous components, which can divide the components corresponding to different routes into different code blocks, and then load the corresponding components when the routes are accessed, so that loading on demand is very easy. It is convenient to implement lazy loading of routing components.

In this section of configuration:

const routes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/',
    name: 'home',
    component: () => import('@views/home.vue'),
  },
]

What plays the role of lazy loading configuration is the value received by component () => import('@views/home.vue'), where @views/home.vue is the component of the route.

After running npm run build on the command line to package and build, you will see the packaging results output by the console:

❯ npm run build

hello-vue3@0.0.0 build
vue-tsc --noEmit && vite build

vite v2.9.15 building for production...42 modules transformed.
dist/index.html                       0.42 KiB
dist/assets/home.03ad1823.js          0.65 KiB / gzip: 0.42 KiB
dist/assets/HelloWorld.1322d484.js    1.88 KiB / gzip: 0.96 KiB
dist/assets/about.c2af6d65.js         0.64 KiB / gzip: 0.41 KiB
dist/assets/login.e9d1d9f9.js         0.65 KiB / gzip: 0.42 KiB
dist/assets/index.60726771.css        0.47 KiB / gzip: 0.29 KiB
dist/assets/login.bef803dc.css        0.12 KiB / gzip: 0.10 KiB
dist/assets/HelloWorld.b2638077.css   0.38 KiB / gzip: 0.19 KiB
dist/assets/home.ea56cd55.css         0.12 KiB / gzip: 0.10 KiB
dist/assets/about.a0917080.css        0.12 KiB / gzip: 0.10 KiB
dist/assets/index.19d6fb3b.js         79.94 KiB / gzip: 31.71 KiB

You can see that the routing files are named according to the routing components in the views directory and the components in the components directory, and the corresponding JS files and CSS files are output. After the project is deployed, Vue will only load the required files according to the current routing, and other files will only do Preloading is very friendly to the access experience of large projects.

And if you don't use routing lazy loading, the packaged file looks like this:

❯ npm run build

hello-vue3@0.0.0 build
vue-tsc --noEmit && vite build

vite v2.9.15 building for production...41 modules transformed.
dist/index.html                  0.42 KiB
dist/assets/index.67b1ee4f.css   1.22 KiB / gzip: 0.49 KiB
dist/assets/index.f758ee53.js    78.85 KiB / gzip: 31.05 KiB

It can be seen that all components are packaged into a large JS file and CSS file without code segmentation. For large-scale projects, the files packaged in this way may be several megabytes. For the first screen loaded The speed can be imagined.

Rendering of routes
All routing components must have a router-view tag in the parent component to be rendered after access.

Where the router-view is, the code of the routing component is rendered on the node. The parent component of the first-level routing is the root component of src/App.vue.

The most basic configuration is to directly write a router-view inside, and the entire page is the routing component.

<template>
  <router-view />
</template>

If the site has global public components, such as a unified header and footer for the entire site, and only the middle area is the route, then it can be configured as follows:

<template>
  <!-- 全局页头 -->
  <Header />

  <!-- 路由 -->
  <router-view />

  <!-- 全局页脚 -->
  <Footer />
</template>

If some routes have public components and some do not, for example, most pages need to have sidebars, but login pages and registration pages do not, you can do this:

<template>
  <!-- 登录 -->
  <Login v-if="route.name === 'login'" />

  <!-- 注册 -->
  <Register v-else-if="route.name === 'register'" />

  <!-- 带有侧边栏的其他路由 -->
  <div v-else>
    <!-- 固定在左侧的侧边栏 -->
    <Sidebar />

    <!-- 路由 -->
    <router-view />
  </div>
</template>

These rules can also be managed through routing meta information.

Using route to obtain routing information
is different from Vue 2, which can directly use this.$route in components to obtain current routing information. In Vue 3 components, Vue instances have neither this nor $route.

One thing to keep in mind is that Vue 3 needs to import everything, so the correct way to get the current routing information is to import the routing API first:

import {
    
    useRoute} from 'vue-router'

Then define a variable in setup to get the current route:

const route =useRoute()

Next, you can get the current routing information through the defined variable route.
Of course, if you want to use routing in the template, remember to return the route in the setup.

// 获取路由名称
console.log(route.name)

// 获取路由参数
console.log(route.params.id)

The usage of Vue 3's route is basically the same as that of Vue 2, and it should be easy to get started in daily use.

WARNING

但是 Vue 3 的新路由也有一些小变化,有一些属性是被移除了,
比如之前获取父级路由信息,很喜欢用的 parent 属性,
现在已经没有了,可以在 Vue Router 官网的 从 Vue2 迁移 一章查看所有破坏性变化。

Similar to the removed parent, if you want to get the parent routing information (for example, when doing the breadcrumb function), you can change it to the following, and manually specify the penultimate one as the parent information:

// 获取路由记录
const matched = route.matched

// 获取该记录的路由个数
const max = matched.length

// 获取倒数第二个路由(也就是当前路由的父级路由)
const parentRoute = matched[max - 2]

If there is a parent route configured, then parentRoute is the parent route information, otherwise undefined will be returned.

Use router to operate routing
Like route, this.$router can no longer be used in Vue 3, and must be used by importing the routing API:

import {
    
     useRouter } from 'vue-router'

Like useRoute, useRouter is also a function, which needs to define a variable in setup to obtain routing information.

const router = useRouter()

Next, you can operate the route through the defined variable router:

// 跳转首页
router.push({
    
    
  name: 'home',
})

// 返回上一页
router.back()

Use the router-link label to jump

router-link is a global component that can be used directly in the template without importing. The basic usage is the same in Vue 2 and Vue 3.

By default, it will be converted into an a tag. Compared with hard-coded a href="...", using router-link will be more flexible.

Basic Jump
The most basic usage is to use it as an a tag with target="_self", but there is no need to refresh the page, because it is a routing jump, and its experience is exactly the same as using router for routing navigation.

<template>
  <router-link to="/home">首页</router-link>
</template>

Equivalent to router's push:

router.push({
    
    
  name: 'home',
})

You can write a div tag to bind the Click event to achieve the router-link effect:

<template>
  <div
    class="link"
    @click="
      router.push({
    
    
        name: 'home',
      })
    "
  >
    <span>首页</span>
  </div>
</template>

Understanding this usage comparison will be helpful for the learning of other jump methods below.

Jump with parameters
When using router, you can easily bring parameters to those content pages with IDs, user profile pages, column list pages, etc.

For example, if you want to access an article https://example.com/article/123, the way to use push is:

router.push({
    
    
  name: 'article',
  params: {
    
    
    id: 123,
  },
})

In the same way, from the basic jump writing, it is easy to guess how to write in router-link:

<template>
  <router-link
    class="link"
    :to="{
    
    
      name: 'article',
      params: {
    
    
        id: 123,
      },
    }"
  >
    这是文章的标题
  </router-link>
</template>

Do not generate a tag
router-link is converted to an a tag by default, but according to the business scenario, it can also be specified to generate other tags, such as span, div, li, etc., because these tags do not have the href attribute, so in When jumping, it is executed through the Click event.

In Vue 2, specifying as other tags only requires a tag attribute:

<template>
  <router-link tag="span" to="/home">首页</router-link>
</template>

But in Vue 3, the tag attribute has been removed, and it needs to be rendered as other tags through the cooperation of custom and v-slot.

For example, to render as another label with route navigation function:

<template>
  <router-link to="/home" custom v-slot="{ navigate }">
    <span class="link" @click="navigate"> 首页 </span>
  </router-link>
</template>

After rendering, it is an ordinary span tag. When the tag is clicked, it will jump to the specified routing page through routing navigation:

<!-- 渲染后的标签 -->
<span class="link">首页</span>

The parameters of these two properties are explained as follows:

1.custom , a Boolean value, is used to control whether to render as a tag, when custom is not included or custom is set to false, the a tag is still used for rendering.

2.v-slot is an object used to determine the behavior of the label, which contains:

insert image description here
Generally speaking, the only necessary v-slot is navigate, which is used to bind the click event of the element, otherwise there will be no response after the element is clicked, and others can be added according to actual needs.

TIP

要渲染为非 a 标签,切记两个点:

router-link 必须带上 custom 和 v-slot 属性

最终要渲染的标签,写在 router-link 里,包括对应的 className 和点击事件

Using routing in independent TS/JS files
In addition to using routing in .vue files, it can also be used in separate .ts and .js files.

For example, if you want to make a site with a user system, in addition to using the login.vue related codes, on the registration page register.vue, if the user registers successfully, you need to perform an automatic login for the user.

After the login is completed, the user's login information, Token, expiration time, etc. must be recorded. There is a lot of data to be processed, and it is necessary to help the user automatically cut the page before login. These are two different components. If you write two Almost the same code once, will greatly increase the maintenance cost.

In this case, you can extract the core code and encapsulate it into a login.ts file, and operate the routing in this independent ts file.

// 导入路由
import router from '@/router'

// 执行路由跳转
router.push({
    
    
  name: 'home',
})

Routing meta-information configuration
Sometimes projects require some personalized configuration, such as:

1. Each route is given an independent title;

2. Manage background routing, some pages need to restrict some access rights;

3. Automatically generate sidebars and breadcrumbs through routing;

4. The life cycle of some routes needs to be cached (Keep Alive);

5. Other more business scenarios...

There is no need to maintain many sets of configurations. Meta fields can be configured when defining the routing tree. For example, the following is a login route that contains various meta information:

const routes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/login',
    name: 'login',
    component: () => import('@views/login.vue'),
    meta: {
    
    
      title: '登录',
      isDisableBreadcrumbLink: true,
      isShowBreadcrumb: false,
      addToSidebar: false,
      sidebarIcon: '',
      sidebarIconAlt: '',
      isNoLogin: true,
    },
  },
]

These are some configurations that the author has used when working on background projects. The main functions are:
insert image description here
similar, if there are other needs, such as adding permission control to different user groups (such as administrators, ordinary users, and some pages Only the administrator allows access), can be used together with routing interception by configuring the fields in Meta.

Route redirection
For some offline pages, direct access to the original address will result in 404. In order to avoid this situation, redirection is usually configured to point it to a new page, or jump back to the home page.

Basic usage
Routing redirection is to use a redirect field to configure it into the corresponding route to achieve jumping:

const routes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/',
    name: 'home',
    component: () => import('@views/home.vue'),
    meta: {
    
    
      title: 'Home',
    },
  },
  // 访问这个路由会被重定向到首页
  {
    
    
    path: '/error',
    redirect: '/',
  },
]

Generally speaking, for a route configured with redirect, you only need to specify 2 fields, one is the path of the route itself, and the other is the path of the redirect target route, and other fields can be ignored.

The redirect field can receive three types of values:
insert image description here
Business scenario
Routing redirection can prevent users from accessing some invalid routing pages:

1. For example, after the project has been online for a period of time, a route needs to be renamed, or the route level needs to be adjusted. The old route can be redirected to the new one, so as to prevent the original users from finding it after coming in from favorites and other places.

2. Some addresses that are easy to type wrong, for example, usually the personal information page uses /profile, but the business website uses /account, then you can also redirect /profile to /account

3. For some sites with a membership system, they can be redirected according to user permissions, pointing to the pages they have access to respectively

4. The homepage of the official website has 3 sets of pages on the PC side, the mobile side, and the horizontal screen version embedded in the game, but it is hoped that different devices can be identified through the main domain name to help users automatically switch access

After understanding the business scenario, you can then have a clearer understanding of how to configure redirection.

Configured as path

The most commonly used scenario is probably the home page. For example, the home page address is https://example.com/home, but if you want the main domain name https://example.com/ to also jump to /home, you can configure it like this :

This is the simplest configuration method, just configure the path of the target route:

const routes: Array<RouteRecordRaw> = [
  // 重定向到 `/home`
  {
    
    
    path: '/',
    redirect: '/home',
  },
  // 真正的首页
  {
    
    
    path: '/home',
    name: 'home',
    component: () => import('@views/home.vue'),
  },
]

But the disadvantages are also obvious, only for those routes without parameters.

Configure as route
If you want to bring some parameters to the redirected routing address, you can configure it as route:

const routes: Array<RouteRecordRaw> = [
  // 重定向到 `/home` ,并带上一个 `query` 参数
  {
    
    
    path: '/',
    redirect: {
    
    
      name: 'home',
      query: {
    
    
        from: 'redirect',
      },
    },
  },
  // 真正的首页
  {
    
    
    path: '/home',
    name: 'home',
    component: () => import('@views/home.vue'),
  },
]

The final visited address is https://example.com/home?from=redirect, with parameters like this, you can check the traffic of the source on statistics sites such as "Baidu Statistics" or "CNZZ Statistics" .
Configured as function

It is the most intuitive to explain in combination with business scenarios. For example, the website has 3 user groups, one is an administrator, one is an ordinary user, and the other is a tourist (not logged in). Their website homepages are different:

insert image description here

The product needs to identify the user's identity and jump to a different homepage when accessing the main domain name of the website, so the routing redirection can be configured as follows:

const routes: Array<RouteRecordRaw> = [
  // 访问主域名时,根据用户的登录信息,重定向到不同的页面
  {
    
    
    path: '/',
    redirect: () => {
    
    
      // `loginInfo` 是当前用户的登录信息
      // 可以从 `localStorage` 或者 `Pinia` 读取
      const {
    
     groupId } = loginInfo

      // 根据组别 ID 进行跳转
      switch (groupId) {
    
    
        // 管理员跳去仪表盘
        case 1:
          return '/dashboard'

        // 普通用户跳去首页
        case 2:
          return '/home'

        // 其他都认为未登录,跳去登录页
        default:
          return '/login'
      }
    },
  },
]

Route alias configuration
According to the business needs, you can also specify an alias for the route, which is similar to the route redirection function above, but different:

Route redirection is configured, when a user visits /a, the URL will be replaced with /b, and then the actual route matched is /b.

The route alias is configured, the alias of /a is /b, when the user visits /b, the URL will remain as /b, but the route match is /a, just like the user visits /a.

configuration method

This is easily achieved by adding an alias field:

const routes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/home',
    alias: '/index',
    name: 'home',
    component: () => import('@views/home.vue'),
  },
]

With the above configuration, you can access the homepage through /home, or you can access the homepage through /index.

404 route page configuration
You can configure a 404 route to replace the 404 page in the site.

configuration method

const routes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/:pathMatch(.*)*',
    name: '404',
    component: () => import('@views/404.vue'),
  },
]

After this configuration, as long as a route that does not exist is accessed, it will be displayed as this 404 template.

WARNING

新版的路由不再支持直接配置通配符 *
而是必须使用带有自定义正则表达式的参数进行定义,
详见官网 删除了 *(星标或通配符)路由 的说明。

Navigation guards
are the same as routing used in Vue 2, Vue 3 also supports navigation guards, and the usage is basically the same.

The word navigation guard should be a bit confusing for developers who are new to it. In fact, it is just a few exclusive hook functions. First, let’s take a look at the usage scenarios and get a general understanding of the basic concepts and functions.

Application scenarios of hooks
For developers who are not familiar with navigation guards, they can strengthen their impressions from some actual usage scenarios, such as:

1. As mentioned above, configure the browser title when rendering. Since the Vue project has only one HTML file, there is only one title by default, but I want the title to be displayed as "Home" when visiting /home, and the title when visiting /about Displayed as "About".

2. Some pages require an administrator to access, and ordinary users are not allowed to enter this routing page.

3. For Vue single-page projects, traditional CNZZ/Baidu statistics and other website statistics codes will only count once when the page is loaded, but PV data needs to be reported once every time the route is switched.

There are many such scenarios. The navigation guard supports global use, and it can also be used separately in the .vue file. Next, let’s look at the specific usage.

Global hooks in routing

As the name implies, it is configured globally when creating a router, that is, as long as hooks are configured, all routes will trigger these hook functions when they are accessed.

insert image description here
The global configuration is very simple, in src/router/index.ts, after creating the route, before exporting:

import {
    
     createRouter } from 'vue-router'

// 创建路由
const router = createRouter({
    
     ... })

// 在这里调用导航守卫的钩子函数
router.beforeEach((to, from) => {
    
    
  // ...
})

// 导出去
export default router

beforeEach

Global pre-guard, this is the most used hook function in the navigation guard, usually called "routing interception".

The word interception, as the name implies, is to block XXX before its purpose is achieved, so the purpose of routing is to render the specified component, and routing interception is to do some interception operations before the component is rendered.

parameter
insert image description here

TIP

和 Vue 2 不同,Vue 2 的 beforeEach 是默认三个参数,
第三个参数是 next,用来操作路由接下来的跳转。

但在新版本路由里,已经通过 RFC 将其删除,
虽然目前还是作为可选参数使用,但以后不确定是否会移除,不建议继续使用,点击查看原因。

新版本路由可以通过 return 来代替 next。

Usage
For example, before entering the route, set the page title of the route according to the configuration of the Meta route meta information:

router.beforeEach((to, from) => {
    
    
  const {
    
     title } = to.meta
  document.title = title || '默认标题'
})

Or determine whether to log in:

router.beforeEach((to, from) => {
    
    
  const {
    
     isNoLogin } = to.meta
  if (!isNoLogin) return '/login'
})

Or intercept some routes that require ID parameters but the parameters are missing. For example, the article details pages of many websites have addresses in the format of https://example.com/article/123, which need to have the article ID as the URL Part of , if you only visit https://example.com/article, you need to intercept it.

Here is the configuration of article routing, which requires Params to carry ID parameters:

const routes: Array<RouteRecordRaw> = [
  // 这是一个配置了 `params` ,访问的时候必须带 `id` 的路由
  {
    
    
    path: '/article/:id',
    name: 'article',
    component: () => import('@views/article.vue'),
  },
  // ...
]

When the params of the route are lost, the route record matched is an empty array. In this case, an interception can be configured to return to the homepage when the parameters are lost:

router.beforeEach((to) => {
    
    
  if (to.name === 'article' && to.matched.length === 0) {
    
    
    return '/'
  }
})

The beforeResolve
global resolution guard, it will be triggered on each navigation, but after all component guards and asynchronous routing components are resolved, it will be called before confirming the navigation.

This hook is rarely used, because it is very similar to beforeEach, I believe most developers will use beforeEach to replace it.

So what is it good for?

It is usually used in the process of applying for permissions. For example, some H5 pages need to apply for system camera permissions, and some WeChat activities need to apply for WeChat login information authorization. Only after obtaining permissions are allowed to obtain interface data and give users more operations. Use beforeEach The timing is too early, and using afterEach is a bit late, so the timing of this hook is just right.

Parameter
insert image description here
Usage

Take the previous example of applying for camera permissions on the Vue Router official website:

// https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

router.beforeResolve(async (to) => {
    
    
  // 如果路由配置了必须调用相机权限
  if (to.meta.requiresCamera) {
    
    
    // 正常流程,咨询是否允许使用照相机
    try {
    
    
      await askForCameraPermission()
    } catch (error) {
    
    
      // 容错
      if (error instanceof NotAllowedError) {
    
    
        // ... 处理错误,然后取消导航
        return false
      } else {
    
    
        // 如果出现意外,则取消导航并抛出错误
        throw error
      }
    }
  }
})

afterEach
global post guard, which is also a hook function used more in navigation guards.
Parameter
insert image description here
Usage

There is an example in the application scenario of the hook just now, that is, the PV data is reported once every time the route is switched. Similar to this, each route must be executed once, but it does not need to be operated before rendering, and can be placed in the post-hook. to execute.

The author has written two plug-ins for data statistics before: CNZZ Statistics for Vue and Baidu Statistics for Vue. This post-hook is used to realize automatic data reporting.

router.afterEach((to, from) => {
    
    
  // 上报流量的操作
  // ...
})

Use global hooks in components
All the above are global hooks. Although they are generally used in routing files, they can also be operated in .vue files if necessary.

TIP

和路由的渲染不同,
渲染是父级路由组件必须带有 router-view标签才能渲染,
但是使用全局钩子不受此限制。

建议只在一些入口文件里使用,比如 App.vue ,
或者是一些全局的 Header.vue、Footer.vue 里使用,
方便后续维护。

In the setup, after defining a router variable to get the route, you can operate:

import {
    
     defineComponent } from 'vue'
import {
    
     useRouter } from 'vue-router'

export default defineComponent({
    
    
  setup() {
    
    
    // 定义路由
    const router = useRouter()

    // 调用全局钩子
    router.beforeEach((to, from) => {
    
    
      // ...
    })
  },
})

Exclusive Hooks in Routing
Introduces global hooks. If there are only individual routes to be processed, you can use route exclusive guards to customize some special functions for individual routes, which can reduce writing a bunch of judgments in global hooks.
insert image description here
Note: Route-only hooks must be configured in the JSON tree of routes and hung under the corresponding routes (at the same level as the fields of path, name, and meta).

beforeEnter
has the same function as the global hook beforeEach, both are triggered before entering the route, and the trigger timing is earlier than beforeResolve.

Order: beforeEach (global) > beforeEnter (exclusive) > beforeResolve (global).

parameter
insert image description here

TIP

和 beforeEach 一样,也是取消了 next,
可以通过 return 来代替。

usage

For example: the default title of the entire site is the title of the web page in the format of "column title" + "key title of the whole site", such as "Project Experience-Cheng Peiquan", but on the home page, I want to make some different customizations.

const routes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/home',
    name: 'home',
    component: () => import('@views/home.vue'),
    // 在这里添加单独的路由守卫
    beforeEnter: (to, from) => {
    
    
      document.title = '程沛权 - 养了三只猫'
    },
  },
]

You can use beforeEnter to realize the individual customization of some individual routes.

TIP

需要注意的是,只有从不同的路由切换进来,才会触发该钩子。

针对同一个路由,但是不同的 params 、 query 或者 hash ,都不会重复触发该钩子。

比如从 https://example.com/article/123 切换到 https://example.com/article/234 是不会触发的。

Other usages are the same as beforeEach.

Separately used
in components In addition to global hooks, component-specific routing hooks can also be used in components.
insert image description here

TIP

1、组件内钩子的入参,也都是取消了 next,可以通过 return 来代替。

2、和其他 Composition API 一样,需要先 import 再操作。

Unlike the old routing, the new Composition API removes the beforeRouteEnter hook.
onBeforeRouteUpdate
can change the current route, but when the component is reused, some functions inside are called again to update the rendering of the template data.

Parameter
insert image description here
Usage

For example, on a content website, there are usually relevant reading recommendations at the bottom of the article details page. At this time, there will be an operation scenario of jumping from article A to article B.

For example, cut https://example.com/article/222 from https://example.com/article/111, this situation belongs to the situation of "routing changes, but components are reused".

In this case, the function originally placed in onMounted to execute the data request will not be called, and this hook can be used to render new article content.

import {
    
     defineComponent, onMounted } from 'vue'
import {
    
     useRoute, onBeforeRouteUpdate } from 'vue-router'

export default defineComponent({
    
    
  setup() {
    
    
    // 其他代码略...

    // 查询文章详情
    async function queryArticleDetail(id: number) {
    
    
      // 请求接口数据
      const res = await axios({
    
    
        url: `/article/${
      
      id}`,
      })
      // ...
    }

    // 组件挂载完成后执行文章内容的请求
    // 注意这里是获取 `route` 的 `params`
    onMounted(async () => {
    
    
      const id = Number(route.params.id) || 0
      await queryArticleDetail(id)
    })

    // 组件被复用时重新请求新的文章内容
    onBeforeRouteUpdate(async (to, from) => {
    
    
      // ID 不变时减少重复请求
      if (to.params.id === from.params.id) return

      // 注意这里是获取 `to` 的 `params`
      const id = Number(to.params.id) || 0
      await queryArticleDetail(id)
    })
  },
})

onBeforeRouteLeave
can implement some judgment interception before leaving the current route.

Parameter
insert image description here
Usage

This leave guard is usually used to forbid the user to leave suddenly before saving the modification, and return false can be used to cancel the user's leaving the current route.

import {
    
     defineComponent } from 'vue'
import {
    
     onBeforeRouteLeave } from 'vue-router'

export default defineComponent({
    
    
  setup() {
    
    
    // 调用离开守卫
    onBeforeRouteLeave((to, from) => {
    
    
      // 弹出一个确认框
      const confirmText = '确认要离开吗?您的更改尚未保存!'
      const isConfirmLeave = window.confirm(confirmText)

      // 当用户点取消时,不离开路由
      if (!isConfirmLeave) {
    
    
        return false
      }
    })
  },
})

Routing listening
Routing listening can continue the previous watch method, or use the new watchEffect.

When watch
was in Vue 2, watch was the most used to listen to route changes, and the watch API of Vue 3 is easier to use.

Listen to the entire route
You can directly listen to the changes of the entire route as before:

import {
    
     defineComponent, watch } from 'vue'
import {
    
     useRoute } from 'vue-router'

export default defineComponent({
    
    
  setup() {
    
    
    const route = useRoute()

    // 侦听整个路由
    watch(route, (to, from) => {
    
    
      // 处理一些事情
      // ...
    })
  },
})

The first parameter is passed to the entire route; the second parameter is a Callback, which can get to and from to judge the change of the route.
Listening to a certain data of a route
If you only want to listen to a certain data change of a route, such as listening to a Query or a Param, you can use this method:

import {
    
     defineComponent, watch } from 'vue'
import {
    
     useRoute } from 'vue-router'

export default defineComponent({
    
    
  setup() {
    
    
    const route = useRoute()

    // 侦听路由参数的变化
    watch(
      () => route.query.id,
      () => {
    
    
        console.log('侦听到 ID 变化')
      }
    )
  },
})

The first parameter passes in a getter function, which returns the value to be listened to; the second parameter is a Callback, which can perform some operations on parameter changes.
watchEffect
This is a new listening function in Vue 3, which can simplify the behavior of watch.

For example, a function is defined to obtain the article ID through the routing parameters, and then request the article content:

import {
    
     defineComponent, watchEffect } from 'vue'
import {
    
     useRoute } from 'vue-router'

export default defineComponent({
    
    
  setup() {
    
    
    const route = useRoute()

    // 从接口查询文章详情
    async function queryArticleDetail() {
    
    
      const id = Number(route.params.id) || 0
      console.log('文章 ID 是:', id)

      const res = await axios({
    
    
        url: `/article/${
      
      id}`,
      })
      // ...
    }

    // 直接侦听包含路由参数的那个函数
    watchEffect(queryArticleDetail)
  },
})

Compared with the use of watch, watchEffect is simpler in operation, just pass in the function containing the data to be monitored as its input parameter.

Deployment problems and server-side configuration
Usually use the Hash mode of routing, and there are very few problems after deployment, but if you use History mode, you may encounter problems of one kind or another.

Common Deployment Problems
Here is a list of causes and solutions for some common deployment problems, which can be used as a reference.
When the page is refreshed
, after the 404 page is deployed to the server, it is normal to visit the home page; it is also normal to perform routing jumps through the links on the navigation; but it becomes 404 when the page is refreshed.
Cause of the problem
Generally, in this case, the router has enabled the History mode, but the server does not have configuration support.
Solution
Please communicate with your operation and maintenance colleague according to the instructions in the server configuration section, and ask him to help modify the configuration of the server.
Partial routing white screen
If in the project configuration file, the publicPath (using Vue CLI) or base (using Vite) is configured as a relative path ./, but the routing configuration is level two or above, then this problem will occur.

The cause of the problem
is that static resources such as JS and CSS after packaging are stored in the root directory of the project. The ./ of the first-level route is the root directory, so the access is normal; while the ./ of the second-level route is not the root directory, it is Loaded from the current directory, which caused the JS file to not be loaded correctly, resulting in a white screen.

Assuming the project domain name is https://example.com, then:

The first-level route is https://example.com/home
The second-level route is https://example.com/foo/bar
It is assumed that static assets such as packaged JS files are stored in https://example.com/assets/ files When accessing the first-level route under the folder
, the JS file accessed by ./ is https://example.com/assets/home.js, so the first-level route can be accessed normally.

When accessing the secondary route, the JS file accessed by ./ is https://example.com/foo/assets/bar.js, but the actual file is stored at https://example.com/assets/bar.js , the accessed URL resource does not exist, so the screen is blank.

Solution
If the project has History mode enabled and there are two or more routes configured, do not use relative paths like ./.

The correct way should be to modify the publicPath (using Vue CLI) or base (using Vite), if it is deployed in the root directory of the domain name, write /, if it is a subdirectory, follow the format of the subdirectory, start it with /, end with / The form configuration at the end (eg /hello-world/ )

Server Configuration Solution
If you are using the HTML5 History mode, then the server also needs to configure the corresponding support, otherwise there will be a situation where the routing jumps normally, but 404 occurs when the page is refreshed.

TIP

服务端配置后,就不再进入 404 了,需要在项目里手动配置 404 路由页面 的路由。

Nginx
is now used by most of the company's service programs. You can send the following code to the operation and maintenance engineer for reference to adjust the configuration of Nginx:

location / {
    
    
  try_files $uri $uri/ /index.html;
}

Express
If the front-end engineer uses Node.js as the server and uses the Express server-side framework, then the operation will become easier:

1. Only one middleware needs to be installed

npm install connect-history-api-fallback

2. Import and activate the middleware in the service startup entry file

const express = require('express')
const history = require('connect-history-api-fallback')

// 创建 Express 实例
const app = express()
app
  // 启用 History 中间件
  .use(history())
  // 这里是读取打包后的页面文件目录
  .use('/', express.static(resolve('../dist')))

Guess you like

Origin blog.csdn.net/m0_49515138/article/details/128870058