Vuejs 里面在父组件通过$refs.childComp.xxx 改变子组件 state 或者调用方法 很常用么? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lifesimple
V2EX    Vue.js

Vuejs 里面在父组件通过$refs.childComp.xxx 改变子组件 state 或者调用方法 很常用么?

  •  
  •   lifesimple 2022-07-22 13:46:42 +08:00 3257 次点击
    这是一个创建于 1176 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如 dialog 弹框 之前 react 最常见的写法就是传个 visible prop 来空值显示隐藏,vue 也是这么写,后面看别人的代码里经常就 this.$refs.xxxDialog.visible = true 或者 this.$refs.xxxDialog.open() 直接处理子组件的状态,而不是通过传 prop 的方式,我感觉这样写确实更方便。

    这种处理方式是不是也比较常见?有没有什么弊端这么写的话

    35 条回复    2022-07-23 16:55:50 +08:00
    wednesdayco
        1
    wednesdayco  
       2022-07-22 14:05:00 +08:00
    这么写的话耦合就比较严重
    binhb
        2
    binhb  
       2022-07-22 14:06:53 +08:00
    虽然开了口子, 但是不建议直接操作 dom, 而且这种情况明显不是一定要操作 dom 的
    既然用了 vue, 应该要按着 vue 推荐的方式来写吧
    yaphets666
        3
    yaphets666  
       2022-07-22 14:09:06 +08:00
    为啥要传进 dialog 来控制 dialog 的显隐? 直接在 dialog 的模板上控制不就行了,当然,也可以传进去。ref 只是处理特殊情况的方法。
    lin07hui
        4
    lin07hui  
       2022-07-22 14:17:04 +08:00
    this.$refs.xxxDialog.visible = true 不推荐
    this.$refs.xxxDialog.open() 推荐
    调用组件方法是常见的,如:this.$refs.form.validate()
    最好先看组件文档,如果有方法可调用,一般都会有说明的
    lifesimple
        5
    lifesimple  
    OP
       2022-07-22 14:18:26 +08:00
    @binhb 所以这种操作并不是 vue 的最佳实践吧 但是这种操作组件内部的 state 比 prop 要方便点
    wu67
        6
    wu67  
       2022-07-22 14:33:40 +08:00   1
    调子组件的方法可以. 直接改状态-->打死.
    lifesimple
        7
    lifesimple  
    OP
       2022-07-22 14:38:49 +08:00
    @wu67 就比如页面中打开个弹框 A (没写在当前页面的)要么就是当前页面的 visible state 传入弹框组件中展示;还有就是直接在弹框 A 中写个 visible state 来空值,点击打开的时候通过$refs.dialog.visible 直接改变,或者含蓄点就是在弹框 A 中写个 openDialog() { this.visible = true } 然后$refs.dialog.openDialog() 但实际上没啥区别吧,你一般这种会采用哪种方式写呢
    wu67
        8
    wu67  
       2022-07-22 14:46:57 +08:00
    @lifesimple 你写这一段真的是看了头疼.
    首先弹窗必定与当前页面有关, 你能在当前页面打开与当前页面无关的弹窗(除非全局的错误提示之类的), 那叫 bug. 不然就是全局的状态抽屉之类的...这种自然有其包装层的数据和逻辑控制.

    其次, 打开弹窗, 你可以直接调里面的 openDialog(). 如果不想调, 就绑状态变量 A 到弹窗组件, 然后组件里面 wath 这个 prop A, 合适的时候更改变量, 真正的 dialog 例如 el-dialog 的显示由变量 B 控制, 而不是直接控制弹窗组件里面的变量.
    linkopeneyes
        9
    linkopeneyes  
       2022-07-22 14:49:33 +08:00
    既然觉得弹窗通过 true false 奇怪,其实不如封装个服务来调用弹窗,把 show 变量隐藏起来
    cyrbuzz
        10
    cyrbuzz  
       2022-07-22 14:50:37 +08:00
    这种方法我个人感觉是最后山穷水尽的方法,不应该首选。

    非 props 的东西除非明确在文档里注明,否则应该一律视为私有。

    私有内容的改动就不会对外部的调用负责,比如哪一天我去组件里把 visible 的名字改了就改了,不需要让调用者知道,调用者本来也不需要关心。

    Vue3 和 React 也是这个思路,通过`defineExpose`和`useImperativeHandle`只暴露出公开方法。
    Terry05
        11
    Terry05  
       2022-07-22 14:52:03 +08:00
    严重不推荐这么用,通常他们这么弄,基本原因只是不想在界面上去多定义一个 visiable 变量

    有这样使用习惯的,感觉是还停留在 jquery 的使用习惯上,还没切换到数据驱动的模式上来

    通过 ref 去访问组件内部的 method 不到一些特殊需求也是不推荐使用,绝大数情况下通过 props 和 emit 就可以很好的完成功能实现了,就像官方文档里经常提及的,如果这种经典的输入输出不能解决问题,通常情况不是设计出了问题,就是真的需求超级复杂,比如富文本之类

    vue2.x 的 option 定义的 methods 默认都可以直接访问到,到了 vue3.x 的 setup 模式下,只有被 expose 的内容才可以被外部访问,或许就并不能直接调用到所有资源了
    Mark85
        12
    Mark85  
       2022-07-22 14:54:14 +08:00
    弹窗与否本来不就是应该交给外部控制的,并同步显示、隐藏状态的吗?还把 visible 隐藏在组件内让人通过 refs 来改显示隐藏,怎么想的,看到`$refs.dialog.openDialog()`这种代码我就想骂

    ```
    // 弹窗组件
    props: {
    visible: {
    type: Boolean,
    default: false,
    },
    },
    computed: {
    _visible {
    get() {
    return this.visible
    },
    set(val) {
    this.$emit('update:visible', val)
    },
    },
    },
    // 内部操作_visible
    ```

    外部使用

    ```
    <dialog :visible.sync="xxxVisible" />
    ```
    lifesimple
        13
    lifesimple  
    OP
       2022-07-22 14:56:57 +08:00
    @sjhhjx0122 emm 不是这个觉得奇怪,只是讨论对于一个子组件父子组件通信一般都是 props 方式把值传递给子组件,子组件做了什么操作通过$emit 在父组件中改变 prop 值。如果通过$refs.childComp 的方式,可以把这些 props 值全部设置成子组件的 state 值,操作时候可以调用子组件的一个比如 init() 方法 参数就是原本的 props 值,init(a,b,c,d) { this.a = a;this.b = b...} 把值传过去控制。

    @cyrbuzz 嗯 有道理 谢谢
    shakaraka
        14
    shakaraka  
    PRO
       2022-07-22 15:02:58 +08:00
    vue 的设计模式决定了只能通过 this.$ref.xxx.open()或者 this.dialog = true 这样打开弹窗。


    @sjhhjx0122 #9

    我曾经在 vue 里实现了一个和 angular dialog 一样的东西,但是有个很严重的问题,无法将值传递给组件还有弹窗的返回值无法回到调用者的页面。


    https://stackblitz.com/edit/vue-ic6mz2?file=src/components/dialog.vue

    这个示例的设计逻辑参考 https://material.angular.io/components/dialog/api#MatDialog
    Moeyua
        15
    Moeyua  
       2022-07-22 15:09:46 +08:00 via iPhone
    Vue 3 子组件需要显式 expose 一些属性后才可以被父组件读取,这种方式应该更合理一些吧。
    linkopeneyes
        16
    linkopeneyes  
       2022-07-22 15:12:24 +08:00
    @wunonglin 可以啊,创建一个 promise 就 ok 了,我写过一个文章
    https://juejin.cn/post/7101144285763862565 ,如果喜欢 ng ,用 rxjs 也可以实现
    唯一的缺点是创建的弹窗是重新 new vue 没法使用项目里的上下文依赖
    linkopeneyes
        17
    linkopeneyes  
       2022-07-22 15:13:56 +08:00
    @lifesimple 那不如通过 provide ,inject 啊
    ipwx
        18
    ipwx  
       2022-07-22 15:17:23 +08:00
    this.$refs.XXX.open() 我觉得没啥毛病。

    this.$refs.XXX.visible = true 我觉得有大猫饼
    jrtzxh020
        19
    jrtzxh020  
       2022-07-22 15:20:40 +08:00
    当写了无数个弹窗后,发现用 this.$refs.xxxDialog.open() 真香 哈哈
    shakaraka
        20
    shakaraka  
    PRO
       2022-07-22 15:31:14 +08:00
    @sjhhjx0122 #16

    vue3 还没看,弃坑了。专心 ng 了,我这里以 vue2 为例。


    @sjhhjx0122 #17

    provide ,inject 我记得会有严重问题。如果你是嵌套打开 dialog 的话,依赖注入就不能用了,嵌套的 dialog 不能获取它自己的 ref 。
    lifesimple
        21
    lifesimple  
    OP
       2022-07-22 15:31:35 +08:00
    @jrtzxh020 如果只是打开的话 直接 @click='$refs.xxxDialog.visible = true' 感觉这么直接控制其他组件的 state 写起来确实要比传 prop 顺手点
    linkopeneyes
        22
    linkopeneyes  
       2022-07-22 15:41:08 +08:00
    @wunonglin vue2 也可以的,用 Vue.extend 创建就好了,我也是主用 ng ,依赖注入我还没遇到你说的这个问题
    wangtian2020
        23
    wangtian2020  
       2022-07-22 15:56:54 +08:00
    我都是这么写的
    this.$refs['childDialogComponent'].showAddDialog(params)
    调用子组件方法,子组件执行自己的方法修改自身状态
    shakaraka
        24
    shakaraka  
    PRO
       2022-07-22 16:03:54 +08:00
    @sjhhjx0122 #22

    看到了。简单看下 vue2 的文档,好像可以用 render 创建组件,然后在 context 把 ref 和 data 放进去,有时间我再试试。
    好难受。
    sechi
        25
    sechi  
       2022-07-22 16:47:37 +08:00
    大部分时候都是用 this.$refs.xxxDialog.open(),反正大部分时候都是写 crud 业务,真的没有那么多条条框框,怎么舒服怎么来
    AlphaTr
        26
    AlphaTr  
       2022-07-22 18:03:22 +08:00
    简单一个调用 result = await this.$refs.dialog.open(params) 直接将给 Dialog 传参数并打开、获取弹窗的返回值都做了;比通过给组件传 props ,然后监听组件的事件这样子分布在各处的方式个人感觉更好些
    snarkprayer
        27
    snarkprayer  
       2022-07-22 18:36:50 +08:00
    ref 引用读数据调方法可以,写数据应该禁止
    $refs.ddialog.open()这种的可以加一层封装 点击 trigger slot 显示
    ```
    <Extend-Modal>
    <content-compoennt />
    <button slot="trigger">按钮</button>
    </Extend-Modal>
    ```
    ymcz852
        28
    ymcz852  
       2022-07-22 19:46:57 +08:00 via iPhone
    看那么多人支持 $refs 去调用子组件方法有点惊讶,虽然方便,但个人认为除了用 $refs 去调用第三方组件的方法的场景外,这种方法带来的可维护性肯定不佳的,子组件根本不知道有哪些父组件调用了它的方法,一旦是改动了这些方法就难搞了
    jdi
        29
    jdi  
       2022-07-22 20:46:34 +08:00
    官方是不推荐的,原来开了个口子,现在把口子缩小了点:
    > 使用了 <script setup> 的组件是默认私有的:一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西
    humbass
        30
    humbass  
       2022-07-22 21:11:02 +08:00   1
    如果是 Alert 、Loading... 这样的框,最好是用 extend 封装一层,然后用函数调用,这样就完全解耦了。

    vue2 的写法 : https://joyran.github.io/yi-blog/blog/alert.html (随便搜的)
    thtznet
        31
    thtznet  
       2022-07-22 21:48:27 +08:00   1
    两种不通的思想,面向对象的写法和数据驱动,Vue 嘛,既然得了 2 者的优势就会有带着 2 者的影子,所以感觉有点不伦不类。
    dengshen
        32
    dengshen  
       2022-07-23 10:07:07 +08:00 via iPhone
    sync 不好用吗?
    xsldebugger0030
        33
    xsldebugger0030  
       2022-07-23 16:13:57 +08:00
    @wunonglin vue 的设计模式决定了你有 n 种方式打开弹窗。不需要调内部方法或修改内部属性。直接把 dialog 的 visible 绑定到 prop 里,由父组件控制就好了。
    shakaraka
        34
    shakaraka  
    PRO
       2022-07-23 16:17:57 +08:00
    @xsldebugger0030 #33 笑了
    xsldebugger0030
        35
    xsldebugger0030  
       2022-07-23 16:55:50 +08:00
    @wunonglin help/assertive
    我们希望能够在 V2EX 建立和倡导一种好好说话的氛围。

    请尽量描述事实,而非观点。
    如果你要反驳什么,请反驳那个主要的要点,而不是一些旁枝末节。
    如果你要说的话是为了伤害别人,那么请不要说。如果你要说的话,你有预感在将来你会想要删掉它,那你最好现在就不要说。
    在一个公共空间的公共讨论中,我们应该关注的,是自己能够在这些讨论中提供什么样的建设性增益,而不是那些纯粹个人的感受。比如当大家在讨论一件你不了解的东西时,你没有必要去回复一条“不明觉厉”。
    回忆一下你看过的电影里的那些正面角色的说话方式把一件事情好好陈述出来,没有冷笑,没有嘲讽,没有反问,就只是好好说话而已。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3885 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 05:13 PVG 13:13 LAX 22:13 JFK 01:13
    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