基于 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 总是相同的。
第二种方法
使用 Router
的 base
属性.
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 在 next
和 redirects
下的行为方式。条件应该是检查 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>
我正在使用 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 总是相同的。
第二种方法
使用 Router
的 base
属性.
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 在 next
和 redirects
下的行为方式。条件应该是检查 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>