请教一个 vue 中组件复用的生成的页面间切换的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
yezheyu
V2EX    程序员

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

  •  
  •   yezheyu 2022-10-01 17:50:02 +08:00 2199 次点击
    这是一个创建于 1109 天前的主题,其中的信息可能已经有所发展或是发生改变。

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


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

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

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

    新手让大家见笑了

    现在基本明白了

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

    然后 UI 数据化,根据多个画板的数据存在一起,使用路由配合 watch 或 computed 选择性渲染
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     983 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 22:35 PVG 06:35 LAX 15:35 JFK 18:35
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86