基于 Vue-Router 语言的路由前缀

Vue-Router language based route prefix

我正在使用 prerender-spa-plugin 来预呈现某些页面,以便从我的 Vue 应用程序中获得更好的 SEO。

我的目标是改变我目前使用 Vue-i18n 的方式,因此我可以将其基于 url 参数 /lang。示例:/en/home/nl/home。有了这个,我就可以根据语言进行预渲染。

我创建了一个前缀函数,它向每个父路由添加了可选参数 /:lang?。这是:

const withPrefix = (prefix: string, routes: RouteConfig[]): RouteConfig[] => routes.map((route): RouteConfig => {
  // Avoiding mutations
  const clonedRoute = { ...route };
  // Every route except for '/'
  if (clonedRoute.path !== '/') {
    clonedRoute.path = prefix + clonedRoute.path;
  }
  return clonedRoute;
});

在 Vue 模板中,我使用:

<router-link :to="`/account`">

所以我试图根据 lang 参数将 redirect 操纵到 next 页面。

第一种方法

最符合逻辑的是(在 Router 的 beforeEach 内):

const { lang } = to.params;
const redirectTo = lang ? to.fullPath : `${fullToLang}${to.fullPath}`;
if (from.fullPath !== redirectTo) {
  next({ path: redirectTo });
} else {
  next();
}

但是它进入了无限循环,因为 from 总是相同的。

第二种方法

使用 Routerbase 属性.

import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import HelloWorld from "./components/HelloWorld";
import Test from "./components/Test";

Vue.config.productionTip = false;

Vue.use(VueRouter);

const router = new VueRouter({
  mode: "history",
  base: "/en",
  routes: [
    {
      path: ":lang?/",
      component: HelloWorld,
      beforeEnter: (to, from, next) => {
        console.log(1);
        next();
      }
    },
    {
      path: "/:lang?/nope",
      component: Test,
      beforeEnter: (to, from, next) => {
        console.log(2);
        next();
      }
    },
    {
      path: "/:lang?/*",
      beforeEnter: (to, from, next) => {
        console.log(to);
        next("/nope");
      }
    }
  ]
});

new Vue({
  render: h => h(App),
  router
}).$mount("#app");

或者更好的是,直播: https://codesandbox.io/embed/vue-template-0bwr9

但是,我不明白为什么只有在路由上找不到 url 时才会 重定向 /en/nope(最后一种情况)。而且,每次我想更改 base 时,我是否必须创建一个新的 Router 实例?

第三种方法

router-link 的包装组件注入 :to 基于 this.$route.params.lang.

这会在应用程序加载后进行导航,但不会在第一次加载时进行导航 refresh/initialization。

那么,我该如何解决呢?

~解决方案~

所以是的,第一种方法是正确的方法,但我误解了 Router 在 nextredirects 下的行为方式。条件应该是检查 to 而不是 from.

const redirectTo = lang ? to.fullPath : `${fullToLang}${to.fullPath}`;
if (to.fullPath !== redirectTo) {
  // Change language at i18n
  loadLanguageAsync(toLang as Language);

  next({ path: redirectTo });

  return;
}

我不太确定你在问什么。但我假设你想在你的导航前面加上当前语言参数 (../en/..) 如果他们还没有?

您可以使用 beforeEach() 挂钩解决此问题,并且仅在不存在 lang 参数时才重定向。

const { lang } = to.params
if(!lang) {
  next({ path: redirectTo })
}
next()

如果这不是你想要的,请澄清,我会编辑我的答案

是这样的吗?假设新路径从 /[lang]/...

开始

请注意 - 路由时仍然存在错误,例如/:lang/bar -> /foo/bar

Vue.lang = 'en'
function beforeEnter(to, from, next){

  if ((new RegExp(`^/${Vue.lang}$`))
  .test(to.path) 
  || 
  (new RegExp(`^/${Vue.lang}/`))
  .test(to.path))
  {
    next();
  } else {
    
    next({path: `/${Vue.lang}${to.path}`})
  }
};
Vue.mixin({
  beforeRouteEnter: beforeEnter
})
const Foo = { template: '<div>foo - {{$route.path}}</div>' }
const Bar = { template: '<div>bar - {{$route.path}}</div>' }
const Root = { template: '<div>Root - {{$route.path}}</div>' }
const Invalid = { template: '<div>404</div>' }

const routes = [
  
  { path: '/:lang/foo', component: Foo },
  { path: '/:lang/bar', component: Bar },
  { path: '/:lang/*', component: Invalid },
  { path: '/:lang', name: 'Home', component: Root },
  // some weird issue that prevents beforeRouteEnter ? so redirect, but else next is needed
  { path: '/', redirect: to => `/${Vue.lang}`}
]

const router = new VueRouter({
  routes
})

new Vue({
  data(){
    return {
      pLang: Vue.lang,
    }
  },
  computed: {
    lang: {
      get(){
        return this.pLang
      },
      set(val){
        Vue.lang = val
        this.pLang = val
      }
    }
  },
  router,
}).$mount('#app');
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
  <h1>Hello App!</h1>
  <p>
     {{lang}}
    <select v-model="lang">
      <option value="en">en</option>
      <option value="cn">cn</option>
    </select>
    <!-- use router-link component for navigation. -->
    <!-- specify the link by passing the `to` prop. -->
    <!-- `<router-link>` will be rendered as an `<a>` tag by default -->
    
    <router-link to="/">Root</router-link>
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
    <router-link to="/foo/bar">Go to Foo/Bar - not defined</router-link>
  </p>
  <!-- route outlet -->
  <!-- component matched by the route will render here -->
  <router-view></router-view>
</div>