An instance of using route redirection in nuxt

  • 2021-09-16 06:00:50
  • OfStack

As we all know, when writing SPA, we can redirect the route by configuring vue-router.

This 1 option is given in the definition of the official document (and ts type):


interface RouteConfig = {
 path: string,
 redirect?: string | Location | Function,
}

In other words, we can define such a route:


{
  path: "/foo",
  redirect: "/foo/bar",
}

This way, when we accessed the/foo, we were redirected to/foo/bar. These are platitudes.

However, in the environment of SSR, If you use nuxt, Because nuxt adopts the way that convention is greater than configuration, pages directory replaces routing, which simplifies configuration, but brings some troubles to manual configuration of some scenarios that need customization. Moreover, nuxt officially does not recommend manual configuration of router. If configuration is really needed, some middleware configuration can be carried out in nuxt. config. js, but this is suspected of killing chickens with a knife for such a particularly simple thing as redirection.

So, what I started thinking was to add an beforeCreate () hook to the routing component that needs to be redirected under the pages directory:


<template>
 <div></div>
</template>

<script>
export default {
 beforeCreate() {
  this.$router.replace('/path/to')
 }
}
</script>

This is equivalent to replacing 1 route before the component is created, and this component acts as a placeholder for the route. The reason why replace is used instead of push is that replace is closer to the effect of redirection, and we don't want users to be able to roll back (such as the browser's back button) to the page before redirection.

However, there is a problem with this scheme, that is, in the process of routing "redirection", the interface will flicker slightly, so the experience is very bad. Therefore, other solutions are definitely needed.

As for why the screen flashes, although the beforeCreate hook will theoretically be executed before the template compilation, this is in the SFC environment, and the template compilation will be executed in advance; If Vue is introduced with script tag, this problem will not occur:


<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
  <script src="https://unpkg.com/vue-router@3.1.6/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
  <router-view/>
</div>

<script>
  const Foo = {template: '<div>foo</div>'};
  const Bar = {template: '<div>bar</div>'};

  const routes = [
    {path: '/foo', component: Foo, redirect: '/bar'},
    {path: '/bar', component: Bar}
  ];

  const router = new VueRouter({routes});

  const app = new Vue({
    el: '#app',
    router
  })
</script>
</body>
</html>

If necessary, you can refer to the official life cycle diagram of Vue in one step.

After checking 1 document, fortunately, nuxt provides such a redirected API, which is hidden in context object. In fact, all of the following solutions are based on this context object:

属性字段 类型 可用 描述
redirect Function 客户端 & 服务端 用这个方法重定向用户请求到另1个路由。状态码在服务端被使用,默认 302 redirect([status,] path [, query])

At the same time, we also know:

The asyncData method is called before each load of the component (page component only). It can be called before the server or route update. When this method is called, the first parameter is set to the context object of the current page. You can use the asyncData method to get the data and return it to the current component.

So, we can write this:


<template>
 <div></div>
</template>

<script>
export default {
 asyncData({ redirect }) {
  redirect('/path/to')
 }
}
</script>

This will solve the problem. Maybe you will wonder why you don't use fetch for routing jump, because fetch is used to process the data of vuex store. Personally, I think it is not suitable for processing the task of routing jump semantically; Of course, it actually works, and it's all based on context objects anyway.

If you are unhappy with the empty div above and feel inelegant, you can also make it more extreme by using the rendering function to render the root node of the template:


<script>
export default {
 asyncData({ redirect }) {
  redirect('/path/to')
 },
 render(h) {
  return h('div')
 }
}
</script>

This may look a little simpler.

However, this writing is not suitable for the scenario of routing authentication. For example, I need to verify the user's identity before making a route jump. I can't write such a paragraph in every page. It's inconvenient to maintain. If the route is changed, every page has to be changed.

Therefore, at this time, we have to use the middleware mechanism provided by nuxt. Middleware can be configured at page level or globally. Refer to the official example:

pages/secret.vue


<template>
 <h1>Secret page</h1>
</template>

<script>
export default {
 middleware: 'authenticated'
}
</script>

middleware/authenticated.js


export default function ({ store, redirect }) {
 // If the user is not authenticated
 if (!store.state.authenticated) {
  return redirect('/login')
 }
}

This is also put into nuxt. config. js and becomes a global configuration:


module.exports = {
 router: {
  middleware: 'authenticated'
 }
}

But there is one point to note, and one that you need to pay attention to whenever you use routing: avoid circular redirects. This is the same reason as avoiding writing an infinite loop.

Under summary 1:

If it is a fixed redirection logic between a few pages, you can directly use asyncData (or fetch, although I personally think the semantics are not good) to redirect;

If there are a large number of pages to be redirected (middleware + table-driven can be considered), or there are some dynamically changing redirection logic (such as routing authentication), you can consider using middleware mechanism.

Supplementary knowledge: Use Nuxt. js and Vue-i18n to redirect to the same page but switch the language URL

Recently, the company put forward a requirement, that is, when users switch languages, they need to add the selected language to the route

For example, URL:

localhost/about

You should redirect to this method (by pressing a specific button):

localhost/bg/about

Recommended in the Nuxt documentation for localization using Vue-i18n https://nuxtjs.org/examples/i18n/

An example of internationalization is also given in Nuxt official website, but it does not meet the needs of the company. If you are interested, you can look at it

Example of internationalization of Nuxt official website

Create a new LangSelect. vue file under the components folder


<template>
 <el-dropdown trigger="click" class="international" @command="handleSetLanguage">
  <div>
   <i class="el-icon-share">{{$t('home.changelang')}}</i>
  </div>
  <el-dropdown-menu slot="dropdown">
   <el-dropdown-item :disabled="language==='zh'" command="zh"> Chinese </el-dropdown-item>
   <el-dropdown-item :disabled="language==='en'" command="en">English</el-dropdown-item>
  </el-dropdown-menu>
 </el-dropdown>
</template>

<script>
export default {
 computed: {
  language() {
   return this.$store.state.locale;
  }
 },
 methods: {
  handleSetLanguage(language) {
   this.$i18n.locale = language;
   console.log(this.$i18n.locale);
   this.$store.commit("SET_LANG", language);
  //  console.log(this.$store.state.locale, "locale");
   var beforePath = this.$nuxt.$router.history.current.path;
   // -- Removing the previous locale from the url
   var changePath = this.$store.state.locale
   var result = "";
   result = beforePath.replace("/en", "");
   result = result.replace("/zh", "");
  this.$nuxt.$router.replace({ path: "/" + changePath + result });
   this.$message({
    message: "Switch Language Success",
    type: "success"
   });
  }
 }

};
</script>

Create a new i18n. js in the middleware file


{
  path: "/foo",
  redirect: "/foo/bar",
}
0

Other configurations can be found in the official website link given above, so we will not repeat them here.


Related articles: