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

2022-07-22 13:46:42 +08:00
 lifesimple

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

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

2569 次点击
所在节点    Vue.js
35 条回复
wednesdayco
2022-07-22 14:05:00 +08:00
这么写的话耦合就比较严重
binhb
2022-07-22 14:06:53 +08:00
虽然开了口子, 但是不建议直接操作 dom, 而且这种情况明显不是一定要操作 dom 的
既然用了 vue, 应该要按着 vue 推荐的方式来写吧
yaphets666
2022-07-22 14:09:06 +08:00
为啥要传进 dialog 来控制 dialog 的显隐? 直接在 dialog 的模板上控制不就行了,当然,也可以传进去。ref 只是处理特殊情况的方法。
lin07hui
2022-07-22 14:17:04 +08:00
this.$refs.xxxDialog.visible = true 不推荐
this.$refs.xxxDialog.open() 推荐
调用组件方法是常见的,如:this.$refs.form.validate()
最好先看组件文档,如果有方法可调用,一般都会有说明的
lifesimple
2022-07-22 14:18:26 +08:00
@binhb 所以这种操作并不是 vue 的最佳实践吧 但是这种操作组件内部的 state 比 prop 要方便点
wu67
2022-07-22 14:33:40 +08:00
调子组件的方法可以. 直接改状态-->打死.
lifesimple
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
2022-07-22 14:46:57 +08:00
@lifesimple 你写这一段真的是看了头疼.
首先弹窗必定与当前页面有关, 你能在当前页面打开与当前页面无关的弹窗(除非全局的错误提示之类的), 那叫 bug. 不然就是全局的状态抽屉之类的...这种自然有其包装层的数据和逻辑控制.

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

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

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

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

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

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

vue2.x 的 option 定义的 methods 默认都可以直接访问到,到了 vue3.x 的 setup 模式下,只有被 expose 的内容才可以被外部访问,或许就并不能直接调用到所有资源了
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
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 嗯 有道理 谢谢
wunonglin
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
2022-07-22 15:09:46 +08:00
Vue 3 子组件需要显式 expose 一些属性后才可以被父组件读取,这种方式应该更合理一些吧。
sjhhjx0122
2022-07-22 15:12:24 +08:00
@wunonglin 可以啊,创建一个 promise 就 ok 了,我写过一个文章
https://juejin.cn/post/7101144285763862565 ,如果喜欢 ng ,用 rxjs 也可以实现
唯一的缺点是创建的弹窗是重新 new vue 没法使用项目里的上下文依赖
sjhhjx0122
2022-07-22 15:13:56 +08:00
@lifesimple 那不如通过 provide ,inject 啊
ipwx
2022-07-22 15:17:23 +08:00
this.$refs.XXX.open() 我觉得没啥毛病。

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

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


@sjhhjx0122 #17

provide ,inject 我记得会有严重问题。如果你是嵌套打开 dialog 的话,依赖注入就不能用了,嵌套的 dialog 不能获取它自己的 ref 。

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

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

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

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

© 2021 V2EX