请教一个 vue 中组件复用的生成的页面间切换的问题

2022-10-01 17:50:02 +08:00
 yezheyu

标题描述的可能不太好,请看下面 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 怎么解决这个问题呢?


1865 次点击
所在节点    程序员
14 条回复
signalas1
2022-10-01 17:54:21 +08:00
如果你不能把两个画板的所有 UI 状态都数据化,就只能用显隐来做。
renmu
2022-10-01 18:35:35 +08:00
加 key 解决成本最低,要么你就 watch route.path 然后重新处理初始化逻辑
bojackhorseman
2022-10-01 19:11:37 +08:00
绑定 key 并不是野路子,vue router 官方文档有提过
bojackhorseman
2022-10-01 19:21:25 +08:00
@bojackhorseman 抱歉我记错了,不过官方的建议是 watch 路由信息
tyx1703
2022-10-01 20:21:18 +08:00
首先你这个添加段落的方式就不对,不要直接操作 DOM ,而是用一个数组保存起来,在模板中渲染。要理解 UI 就是数据,数据就是 UI 。

根据你的需求,可以用一个 boardId 为键,段落列表 为值的对象保存,然后用计算属性获取当前路由下的段落列表再进行渲染。
WhateverYouLike
2022-10-01 20:31:41 +08:00
这是 vue3 的 feature 😄,名为 static hoisting 。
今天刚写了一篇文章:
https://jaufey-blog.vercel.app/blog/static-hoisting/
gouflv
2022-10-01 22:36:46 +08:00
展开说说怎么个野路子、骚炒作? 这不就是基本操作吗
liyang5945
2022-10-01 23:13:04 +08:00
在 vue 里操作 dom ,你这才是骚操作野路子
arnosolo
2022-10-02 07:46:48 +08:00
可是为什么要放在两个路由下?
RabbitDR
2022-10-02 11:42:48 +08:00
可以把状态提升到父组件,或者其它地方,然后把状态传到子组件
也可以 keep-alive + key 组合,但挂载多个组件
还可以如 5 楼 所说,自己管理状态,根据路径渲染不同的列表
Zzzz77
2022-10-02 12:26:47 +08:00
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 强行让子组件重复渲染了。
Zzzz77
2022-10-02 12:46:05 +08:00
simple233
2022-10-02 17:17:47 +08:00
在画板组件里直接监听路由,路由改变清空数据就行了,我记得官方文档也是这么做的
yezheyu
2022-10-04 11:57:00 +08:00
@Zzzz77
@tyx1703

还有上面的其它老哥
谢谢大家

新手让大家见笑了

现在基本明白了

首先 vue 中还是尽量少操作 dom

然后 UI 数据化,根据多个画板的数据存在一起,使用路由配合 watch 或 computed 选择性渲染

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/884233

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX