Vue 3 和 Vue Router 4 仅对特定组件使用路由器视图时如何传递道具
Vue 3 and Vue Router 4 How to pass prop when using router-view only for specific component
我有基本的路由和道具工作,但我有它的方式,msg 道具将被发送到路由器加载的每个组件?有解决办法吗?
App.vue 是我的基础组件
<template>
<div>
<img alt="Vue logo" src="./assets/logo.png" />
<router-view msg="Hello Foo"></router-view>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
这是我的 HomePage.vue 组件
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HomePage',
props: {
msg: String,
},
};
</script>
这是我的 routes.js
import * as VueRouter from 'vue-router';
import HomePage from './components/HomePage';
const routes = [
{
name: 'HomePage',
path: '/',
component: HomePage,
},
];
const router = new VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes,
});
export default router;
我想知道有没有一种方法可以根据加载的 route/component 动态发送不同的道具?
您可以将 'key' 属性 添加到 router-view 这样每次路由更改都会强制更新 msg 属性,您必须根据路由为 msg 属性设置动态值。
但您可以做得更好:使用提供的 beforeEach 钩子 vue-router 并根据路由名称添加动态参数。
您可以为特定路由配置 route meta field:
// router.js
import { createRouter } from 'vue-router'
import HomePage from '@/views/HomePage.vue'
export default createRouter({
routes: [
{
name: 'HomePage',
path: '/',
component: HomePage,
meta: {
msg: 'Hello Foo',
},
},
⋮
],
})
并直接在模板中通过 $route.meta
在您的组件中使用它:
<!-- MyComponent.vue -->
<template>
<h2>Home</h2>
<h3>{{ $route.meta.msg }}</h3>
</template>
或通过useRoute().meta
:
<script setup>
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const msg = ref('')
onMounted(() => {
msg.value = route.meta.msg
})
</script>
这里的用例看起来有点傻,但它适用于任何需要作为 prop 或参数传递给组件的数据,无论是静态的还是动态设置的,所以我认为这是一个很好的信息.
一些选项:
1。 Route Meta Fields
@tony19 的回答根据文档还有其他目的,例如与 beforeEach Navigation Guard 一起用于身份验证检查,但它适用于简单地向特定路由添加 field/value可以从组件访问。
我也可以在路线中添加一个道具,只要我不需要对其进行任何动态处理即可。 Props 可以在每个路由中传递,但不能像 to.meta 和 to.params 那样在 beforeEach 钩子中访问。
2。 routes.js 文件
中的参数和动态路由
正如@Witold 所建议的,我们可以使用 beforeEach 挂钩并动态添加参数。这是我根据一些研究得出的结论。 setParams() 只是设置参数的一种真正简单的方法,可能会做得更好,但它适用于演示。
对于 App.vue 中的 <router-view>
我正在使用新的 v-slot 方式。文档使它看起来只是转换和 v-for 所必需的,即使没有 key 属性,旧方法也适用于我在这里所做的事情。事实上,我的 App.vue 中显示的所有 3 种方法都适用于此用例。但是从这个 和我在一些 github 问题中看到的一些小信息,我认为 v-slot 的新方法是首选方法,它涵盖了更复杂情况的所有基础。不过还不能 100% 确定。
在 routes.js 中,/
路由获取由我的 setParams 方法设置的 2 个参数。
props: true
将参数复制到道具。但是在 HomePage.vue 中我只声明了 msg2 和 msg3(在 App.vue 中设置)所以 msg1 必须作为参数访问,而 msg2 是一个道具。如果您尝试将它作为道具访问,msg1 将在控制台中显示警告,因为它未在组件的道具中声明:{} 属性 of the component.
/:foo?
路由是常规路由参数,因此我确保不要在我的 setParams 方法中覆盖它,因此它也可以在 HomePage.vue 中作为参数使用
3。 App.vue
中的动态设置道具
我不确定旧版本,但当前版本的 Vue Router 是异步工作的,因此生命周期挂钩将无法访问 $route.name。我在文档中找不到这个,但在已关闭的 github 问题中多次发现相同的重复情况。我们可以像选项 2 一样在路由文件中使用类似 beforeEach 的东西。或者我们可以创建一个观察者来观察 $route.name 中的变化,这就是我为 msg3.
所做的
msg3 作为数据存储在App.vue 中,当它改变时将触发消息的re-render。观察者调用 setMsg(),这是另一个适用于演示的简单 non-realistic js 方法。 changeMsg() 只是 HomePage.vue 中按钮的一种方法,可以在任何时候更改 msg3 时查看它是否有效。
您可以在主 Vue 实例中使用 router.isReady() 而不是观察者。
应用或main.js
//...imports and stuff
const app = Vue.createApp(App);
app.use(router);
router.isReady().then(() => app.mount('#app'));
//app.mount('#app');
我认为等待路由器准备就绪会导致白屏闪烁,尽管我在这个快速简单的演示中没有看到它。加载页面并观察 route.name 变化可以避免这种情况。
一些其他想法
我们可以在 window 或更好的情况下全局设置数组、对象或对象数组,作为所有组件都可以访问的 Vue 全局变量。
在app.js
app.config.globalProperties.$pageMessages = {};
然后在App.vue中访问它并设置动态消息。然后访问页面组件上的全局以检索和显示消息,而不将其作为 prop 或参数传递。
我们还可以在根应用程序或页面组件中使用 axios 或 fetch API 来检索消息,就像从数据库中检索任何其他数据一样。
routes.js
import * as VueRouter from 'vue-router';
import HomePage from './components/HomePage';
const routes = [
{
name: 'HomePage',
path: '/',
component: HomePage,
props: true,
},
{
name: 'HomePage',
path: '/:foo?',
component: HomePage,
},
];
const router = new VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes,
});
const setParams = name => {
// if matches name, set params
if (name === 'HomePage')
return {
msg1: 'Hello I am a param',
msg2: 'Hello I am a param/prop',
};
return {}; // empty object if no match
};
router.beforeEach(to => {
// copy original params if they exist so I don't overwrite
const params = { ...to.params, ...setParams(to.name) };
to.params = params;
});
export default router;
HomePage.vue
<template>
<div class="hello">
<!--msg1 accessible as param but not as prop-->
<p>{{ this.$route.params.msg1 }}</p>
<!--as a prop, msg1 warning in console because not set in props:{}-->
<p>{{ msg1 }}</p>
<!--msg2 prop copied from params-->
<p>{{ msg2 }}</p>
<!--msg3 prop dynamic set in App.vue-->
<p>{{ msg3 }}</p>
<!--foo is a regular route param-->
<p>{{ this.$route.params.foo }}</p>
</div>
</template>
<script>
export default {
name: 'HomePage',
props: {
msg2: String,
msg3: String,
},
};
</script>
App.vue
<template>
<div class="container">
<!-- <router-view :msg3="this.msg3"></router-view> -->
<!-- <router-view :key="$route.path" :msg3="this.msg3"></router-view> -->
<router-view v-slot="{ Component, route }">
<component :is="Component" :key="route.path" :msg3="this.msg3" />
</router-view>
<button @click="changeMsg">Change msg3</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg3: '',
};
},
watch: {
'$route.name'(name) {
this.msg3 = this.setMsg(name);
},
},
methods: {
setMsg(name) {
if (name === 'HomePage')
return 'Hello I am a dynamically set prop for the home page';
if (name === 'AboutPage')
return 'Hello I am a dynamically set prop for the about page';
return '';
},
changeMsg() {
this.msg3 = 'foobar';
},
},
};
</script>
我有基本的路由和道具工作,但我有它的方式,msg 道具将被发送到路由器加载的每个组件?有解决办法吗?
App.vue 是我的基础组件
<template>
<div>
<img alt="Vue logo" src="./assets/logo.png" />
<router-view msg="Hello Foo"></router-view>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
这是我的 HomePage.vue 组件
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HomePage',
props: {
msg: String,
},
};
</script>
这是我的 routes.js
import * as VueRouter from 'vue-router';
import HomePage from './components/HomePage';
const routes = [
{
name: 'HomePage',
path: '/',
component: HomePage,
},
];
const router = new VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes,
});
export default router;
我想知道有没有一种方法可以根据加载的 route/component 动态发送不同的道具?
您可以将 'key' 属性 添加到 router-view 这样每次路由更改都会强制更新 msg 属性,您必须根据路由为 msg 属性设置动态值。
但您可以做得更好:使用提供的 beforeEach 钩子 vue-router 并根据路由名称添加动态参数。
您可以为特定路由配置 route meta field:
// router.js
import { createRouter } from 'vue-router'
import HomePage from '@/views/HomePage.vue'
export default createRouter({
routes: [
{
name: 'HomePage',
path: '/',
component: HomePage,
meta: {
msg: 'Hello Foo',
},
},
⋮
],
})
并直接在模板中通过 $route.meta
在您的组件中使用它:
<!-- MyComponent.vue -->
<template>
<h2>Home</h2>
<h3>{{ $route.meta.msg }}</h3>
</template>
或通过useRoute().meta
:
<script setup>
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const msg = ref('')
onMounted(() => {
msg.value = route.meta.msg
})
</script>
这里的用例看起来有点傻,但它适用于任何需要作为 prop 或参数传递给组件的数据,无论是静态的还是动态设置的,所以我认为这是一个很好的信息.
一些选项:
1。 Route Meta Fields
@tony19 的回答根据文档还有其他目的,例如与 beforeEach Navigation Guard 一起用于身份验证检查,但它适用于简单地向特定路由添加 field/value可以从组件访问。
我也可以在路线中添加一个道具,只要我不需要对其进行任何动态处理即可。 Props 可以在每个路由中传递,但不能像 to.meta 和 to.params 那样在 beforeEach 钩子中访问。
2。 routes.js 文件
中的参数和动态路由正如@Witold 所建议的,我们可以使用 beforeEach 挂钩并动态添加参数。这是我根据一些研究得出的结论。 setParams() 只是设置参数的一种真正简单的方法,可能会做得更好,但它适用于演示。
对于 App.vue 中的 <router-view>
我正在使用新的 v-slot 方式。文档使它看起来只是转换和 v-for 所必需的,即使没有 key 属性,旧方法也适用于我在这里所做的事情。事实上,我的 App.vue 中显示的所有 3 种方法都适用于此用例。但是从这个
在 routes.js 中,/
路由获取由我的 setParams 方法设置的 2 个参数。
props: true
将参数复制到道具。但是在 HomePage.vue 中我只声明了 msg2 和 msg3(在 App.vue 中设置)所以 msg1 必须作为参数访问,而 msg2 是一个道具。如果您尝试将它作为道具访问,msg1 将在控制台中显示警告,因为它未在组件的道具中声明:{} 属性 of the component.
/:foo?
路由是常规路由参数,因此我确保不要在我的 setParams 方法中覆盖它,因此它也可以在 HomePage.vue 中作为参数使用
3。 App.vue
中的动态设置道具我不确定旧版本,但当前版本的 Vue Router 是异步工作的,因此生命周期挂钩将无法访问 $route.name。我在文档中找不到这个,但在已关闭的 github 问题中多次发现相同的重复情况。我们可以像选项 2 一样在路由文件中使用类似 beforeEach 的东西。或者我们可以创建一个观察者来观察 $route.name 中的变化,这就是我为 msg3.
所做的msg3 作为数据存储在App.vue 中,当它改变时将触发消息的re-render。观察者调用 setMsg(),这是另一个适用于演示的简单 non-realistic js 方法。 changeMsg() 只是 HomePage.vue 中按钮的一种方法,可以在任何时候更改 msg3 时查看它是否有效。
您可以在主 Vue 实例中使用 router.isReady() 而不是观察者。
应用或main.js
//...imports and stuff
const app = Vue.createApp(App);
app.use(router);
router.isReady().then(() => app.mount('#app'));
//app.mount('#app');
我认为等待路由器准备就绪会导致白屏闪烁,尽管我在这个快速简单的演示中没有看到它。加载页面并观察 route.name 变化可以避免这种情况。
一些其他想法
我们可以在 window 或更好的情况下全局设置数组、对象或对象数组,作为所有组件都可以访问的 Vue 全局变量。
在app.js
app.config.globalProperties.$pageMessages = {};
然后在App.vue中访问它并设置动态消息。然后访问页面组件上的全局以检索和显示消息,而不将其作为 prop 或参数传递。
我们还可以在根应用程序或页面组件中使用 axios 或 fetch API 来检索消息,就像从数据库中检索任何其他数据一样。
routes.js
import * as VueRouter from 'vue-router';
import HomePage from './components/HomePage';
const routes = [
{
name: 'HomePage',
path: '/',
component: HomePage,
props: true,
},
{
name: 'HomePage',
path: '/:foo?',
component: HomePage,
},
];
const router = new VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes,
});
const setParams = name => {
// if matches name, set params
if (name === 'HomePage')
return {
msg1: 'Hello I am a param',
msg2: 'Hello I am a param/prop',
};
return {}; // empty object if no match
};
router.beforeEach(to => {
// copy original params if they exist so I don't overwrite
const params = { ...to.params, ...setParams(to.name) };
to.params = params;
});
export default router;
HomePage.vue
<template>
<div class="hello">
<!--msg1 accessible as param but not as prop-->
<p>{{ this.$route.params.msg1 }}</p>
<!--as a prop, msg1 warning in console because not set in props:{}-->
<p>{{ msg1 }}</p>
<!--msg2 prop copied from params-->
<p>{{ msg2 }}</p>
<!--msg3 prop dynamic set in App.vue-->
<p>{{ msg3 }}</p>
<!--foo is a regular route param-->
<p>{{ this.$route.params.foo }}</p>
</div>
</template>
<script>
export default {
name: 'HomePage',
props: {
msg2: String,
msg3: String,
},
};
</script>
App.vue
<template>
<div class="container">
<!-- <router-view :msg3="this.msg3"></router-view> -->
<!-- <router-view :key="$route.path" :msg3="this.msg3"></router-view> -->
<router-view v-slot="{ Component, route }">
<component :is="Component" :key="route.path" :msg3="this.msg3" />
</router-view>
<button @click="changeMsg">Change msg3</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg3: '',
};
},
watch: {
'$route.name'(name) {
this.msg3 = this.setMsg(name);
},
},
methods: {
setMsg(name) {
if (name === 'HomePage')
return 'Hello I am a dynamically set prop for the home page';
if (name === 'AboutPage')
return 'Hello I am a dynamically set prop for the about page';
return '';
},
changeMsg() {
this.msg3 = 'foobar';
},
},
};
</script>