标题描述的可能不太好,请看下面 demo
router.js
const routes = [
{
path: '/board/:boardId',
component: board
}
]
app.vue
<template>
<router-link to="/board/1">画板 1</router-link>
<router-link to="/board/2">画板 2</router-link>
<router-view />
</template>
board.vue
<template>
<div>画板 {{ $route.params.boardId }}</div>
<button @click="addP">添加一个段落</button>
<div class="container"></div>
</template>
<script setup>
function addP() {
let p = document.createElement('P')
p.innerText = '新增内容'
document.querySelector('.container').appendChild(p)
}
</script>
大概就是有两个画板,点击按钮就会切换画板,画板都是由 board 组件复用生成的,在画板中可以添加文本
我需要的功能是两个画板互不影响,在画板 1 中添加文字后,切换到画板 2 ,画板 2 上是没有画板 1 中文字
即下图中效果:
但实际的效果:
原因我也知道,复用组件生成的页面之间切换,代码不会重复执行
那怎样才能保持两个画板的独立呢?
之前请教的一个问题中,@vinsony 老哥提供了一种绑定 key 的思路
我改了下 demo 中 app.vue 实现了预定的效果
<template>
<router-link to="/board/1">画板 1</router-link>
<router-link to="/board/2">画板 2</router-link>
<router-view v-slot="{ Component, route }">
<keep-alive>
<component :is="Component" :key='route.fullPath'/>
</keep-alive>
</router-view>
</template>
但是 @Zzzz77 老哥说绑定 key 这种骚操作有点野路子,那不绑定 key 怎么解决这个问题呢?
1
signalas1 2022-10-01 17:54:21 +08:00
如果你不能把两个画板的所有 UI 状态都数据化,就只能用显隐来做。
|
2
renmu 2022-10-01 18:35:35 +08:00 via Android
加 key 解决成本最低,要么你就 watch route.path 然后重新处理初始化逻辑
|
3
bojackhorseman 2022-10-01 19:11:37 +08:00 via iPhone
绑定 key 并不是野路子,vue router 官方文档有提过
|
4
bojackhorseman 2022-10-01 19:21:25 +08:00 via iPhone
@bojackhorseman 抱歉我记错了,不过官方的建议是 watch 路由信息
|
5
tyx1703 2022-10-01 20:21:18 +08:00 2
首先你这个添加段落的方式就不对,不要直接操作 DOM ,而是用一个数组保存起来,在模板中渲染。要理解 UI 就是数据,数据就是 UI 。
根据你的需求,可以用一个 boardId 为键,段落列表 为值的对象保存,然后用计算属性获取当前路由下的段落列表再进行渲染。 |
6
WhateverYouLike 2022-10-01 20:31:41 +08:00 via Android 1
这是 vue3 的 feature 😄,名为 static hoisting 。
今天刚写了一篇文章: https://jaufey-blog.vercel.app/blog/static-hoisting/ |
7
gouflv 2022-10-01 22:36:46 +08:00 via iPhone
展开说说怎么个野路子、骚炒作? 这不就是基本操作吗
|
8
liyang5945 2022-10-01 23:13:04 +08:00 via Android
在 vue 里操作 dom ,你这才是骚操作野路子
|
9
arnosolo 2022-10-02 07:46:48 +08:00
可是为什么要放在两个路由下?
|
10
RabbitDR 2022-10-02 11:42:48 +08:00
可以把状态提升到父组件,或者其它地方,然后把状态传到子组件
也可以 keep-alive + key 组合,但挂载多个组件 还可以如 5 楼 所说,自己管理状态,根据路径渲染不同的列表 |
11
Zzzz77 2022-10-02 12:26:47 +08:00 1
1 、从上个问题和这个问题的示例就能看出来,OP 完完全全不理解 MV*,正确的做法 #5 已经说的非常非常非常清楚了。
2 、首先明确一个点:OP 绑定 key 的目的是让子组件重新渲染,以此到达重新执行生命周期的目的。且不说你的例子中是否真的有重新执行生命周期的需求,即使真的有,也不该使用这种手段,举个例子: 正常的做法: ``` // 父组件 <template> <ChildA :count="count" /> <button @click="count = count + 1">add</button> </template> <script lang="ts" setup> import { ref } from 'vue' import ChildA from './ChildA.vue' const count = ref(1) </script> ``` ``` // 子组件 <template> <div>{{count}}</div> </template> <script lang="ts" setup> import { watch } from 'vue' const props = defineProps({ count: { type: Number, required: true, }, }) const func = () => { console.log('render') } watch(() => props.count, func, { immediate: true }) </script> ``` OP 的做法: ``` // 父组件 <template> <ChildA :count="count" :key="count" /> <button @click="count = count + 1">add</button> </template> <script lang="ts" setup> import { ref } from 'vue' import ChildA from './ChildA.vue' const count = ref(1) </script> ``` ``` // 子组件 <template> <div>{{count}}</div> </template> <script lang="ts" setup> defineProps({ count: { type: Number, required: true, }, }) const func = () => { console.log('render') } func() </script> ``` 后者的问题: ①不易理解(特别是新手)。 ②性能问题。 ③最重要的一点,由于可控的粒度过大,很容易导致 BUG 。因此能 watch 解决的一般都不会选择这样去操作。 3 、继续强调刚才提到的一个点,OP 很多 [重新执行生命周期] 的需求根本就是伪需求,在之前的例子中完全没有必要,在本帖的例子中也没有必要(如#5 所讲的)。其实只是一个子组件接收父组件响应式数据的简单问题,就更没有必要绑定 key 强行让子组件重复渲染了。 |
12
Zzzz77 2022-10-02 12:46:05 +08:00
|
13
simple233 2022-10-02 17:17:47 +08:00 via iPhone
在画板组件里直接监听路由,路由改变清空数据就行了,我记得官方文档也是这么做的
|