打破约定俗成:其实Vue3的子组件也是可以“直接”改props的

简介: 为了避免混淆,先介绍一下后端语言用的类。一般类可以包含内部成员、属性、方法、事件等。内部成员一般都是私有的(其实也可以设置为公有),调用者不可以直接访问内部成员,而是要通过属性来访问内部成员。

后端编程语言的类

为了避免混淆,先介绍一下后端语言用的类。一般类可以包含内部成员、属性、方法、事件等。内部成员一般都是私有的(其实也可以设置为公有),调用者不可以直接访问内部成员,而是要通过属性来访问内部成员。

59.png

类的结构和调用

属性是内部成员的安全通道,可以限制访问方式,比如只读;也可以设置关卡,比如年龄 > 18 且 年龄 < 60的才可以通过。

类设置属性,就是想限制调用者操作内部成员的方式,其数据“根源”在类的内部成员。请注意这一点,下面要用。

vue 子组件的 props

vue 的组件,也可以设置 data、props、computed、methods等,看起来和类的设置很像,但是却有着本质的区别。

60.png


vue的组件的props和调用

  • 首先
    组件的 data 和 props 是相互独立的,默认情况下没有任何关系,如果想要发生关联,需要手动写代码实现,比如用 watch、computed 等方式。
  • 其次
    props 是 父组件的 data 的“容器”,其数据“根源”在父组件,而不是子组件的 data,这一点和类是有本质区别的。

所以请不要把类的理解和使用方式,生硬的套在 vue 的父子组件上面,要注意区分。

为啥不可以改 props,为啥又可以改props?

现在来讨论一下,props 到底可不可以改的问题。

按照官网的说法,子组件是不可以修改 props 的,原因云云,于是好多人也跟着说不能改,改了就云云。

那么本质原因是啥呢?知其然还要知其所以然!


61.png

props不同的类型

这个要从js的数据类型说起,js的类型比较乱,有很多种划分方式,从传递的角度来看,可以分为传值类型和引用类型。

  • 传值类型:拷贝副本传递,改副本不影响原值!
  • 引用类型:传递地址,可以通过地址修改属性!

对于传值类型,传递副本之后,副本和“本尊”已经没有任何联系了,副本随便改,都不会影响“本尊”。

引用类型,传递的是自己的地址(指针),所以可以通过地址修改“本尊”的属性,这样改副本就可以影响到“本尊”。

vue组件的 props 能改与不能改,就是这两种传递方式导致的。

props 的本质形态

我们经常用到组件的 props,那么 props 到底是什么样子的呢?

这里以 Vue3 为例来分析一下,我们设置一个简单的父子组件,设置几种常见的类型:

  • 子组件
exportdefault defineComponent({
name: 'test2',
props: {
modelValue: String,
name: String,
user: Object,
info: Object
},
emits: ['update:modelValue'],
setup (props, context) {
console.log('props-text', props)
console.log('props-ctx', context)
// 使用 emit 修改
const submit = () => {
context.emit('update:modelValue', newDate())
}
// 使用 proxy 修改
const user = props.user // 可以直接获取,不需要使用 toRef
const direct = () => {
user.name = newDate()
}
return {
submit,
direct
}
}
})

子组件定义一个 props,有基础类型,和引用类型几个成员。基础类型需要使用 emit 来修改,引用类型(reactive),可以直接通过 proxy 的拦截原理来方向修改。

另外 props 的引用类型,是可以直接解构的,不需要使用 toRefs。

  • 父组件

模板:

<test
v-model="model"
:name="refName"
:user="retUser"
:attrs1="retUser"
>test>

js:

const model = ref('aa')
const refName = ref(0)
const retUser = reactive({
name: 'jyk'
})


父组件定义几个类型的data传递给子组件。基础类型用 ref,引用类型使用 reactive。因为这样可以有响应性。

  • 看看结果

我们先来看看 props 的打印结果,发现是一个套娃 proxy:

62.png

props的套娃结构

在 vue3 里面,reactive、shallowReactive、readonly、shallowReadonly 都用了proxy,那么到底是哪一种呢?

简单测试一下就会发现是 shallowReadonly(浅层只读),那么问题来了,既然不让改,为啥不用 readonly?是遗漏了吗?

我猜测这是一个平衡各种需求后的折中处理方案。

然后可能官方为了避免心智负担,于是干脆一刀切,就说不让改props,这样就省事了。

而对于懂得原理的,那就可以传递引用类型,实现更简洁的操作方式。

所以想要用好一个框架,还是需要了解一些原理的。分清楚什么情况可以改,什么情况不可以改,可以让代码更简洁。

vue 的双向绑定 VS 单向数据流

  • 误区一:看到双向绑定,就会认为是双向数据流,但是其实不是的,vue为了更好的实现响应性,所有的数据流向都是 单向 的。
  • 误区二:在子组件里,只能通过emit来修改props,否则违背了单向数据流的规定。其实,在子组件里无论采用 emit 修改 props,还是通过 proxy 修改,其本质都是单向数据流。

63.png


单向数据流的原理

上图比较清晰的表达了数据的流向。

  • 开始:父组件设置data,传递给子组件,子组件渲染。
  • 变化:子组件通过 emit 修改基础类型,或者通过 proxy 来修改引用类型,都会先去修改父组件的data,然后再通知子组件,最后才是子组件的渲染。

实例

一个常见的例子就是,“弹窗显示表单”。以element-plus 为例:

父组件:

// 弹窗信息
const dialogInfo = reactive({
isShow: false,
width: '50%'
})
// 弹窗
dialogInfo.isShow = true
//关闭
dialogInfo.isShow = false


子组件

// 属性:模块ID
const props = defineProps({
moduleId: [Number, String],
buttonMeta: Object,
dialogInfo: Object
})
// 直接解构,不需要使用 toRefs
const _dialogInfo = props.dialogInfo
// 弹窗
_dialogInfo.isShow = true
//关闭
_dialogInfo.isShow = false

模板

<el-dialog
:title="这是一个演示"
v-model="_dialogInfo.isShow"
:modal="true"
:width="_dialogInfo.width"
>
el-dialiog>

这样父组件和子组件都可以轻松的控制 el-dialog 了。

本文作者:自然框架

个人网址:jyk.cnblogs.com

声明:本文为 脚本之家专栏作者 投稿,未经允许请勿转载。

相关文章
|
10月前
|
JavaScript 前端开发 安全
Vue 3
Vue 3以组合式API、Proxy响应式系统和全面TypeScript支持,重构前端开发范式。性能优化与生态协同并进,兼顾易用性与工程化,引领Web开发迈向高效、可维护的新纪元。(238字)
1211 139
|
10月前
|
缓存 JavaScript 算法
Vue 3性能优化
Vue 3 通过 Proxy 和编译优化提升性能,但仍需遵循最佳实践。合理使用 v-if、key、computed,避免深度监听,利用懒加载与虚拟列表,结合打包优化,方可充分发挥其性能优势。(239字)
626 1
|
11月前
|
开发工具 iOS开发 MacOS
基于Vite7.1+Vue3+Pinia3+ArcoDesign网页版webos后台模板
最新版研发vite7+vue3.5+pinia3+arco-design仿macos/windows风格网页版OS系统Vite-Vue3-WebOS。
1100 11
|
10月前
|
JavaScript 安全
vue3使用ts传参教程
Vue 3结合TypeScript实现组件传参,提升类型安全与开发效率。涵盖Props、Emits、v-model双向绑定及useAttrs透传属性,建议明确声明类型,保障代码质量。
695 0
|
缓存 前端开发 大数据
虚拟列表在Vue3中的具体应用场景有哪些?
虚拟列表在 Vue3 中通过仅渲染可视区域内容,显著提升大数据列表性能,适用于 ERP 表格、聊天界面、社交媒体、阅读器、日历及树形结构等场景,结合 `vue-virtual-scroller` 等工具可实现高效滚动与交互体验。
1136 1
|
缓存 JavaScript UED
除了循环引用,Vue3还有哪些常见的性能优化技巧?
除了循环引用,Vue3还有哪些常见的性能优化技巧?
570 0
|
JavaScript
vue3循环引用自已实现
当渲染大量数据列表时,使用虚拟列表只渲染可视区域的内容,显著减少 DOM 节点数量。
277 0
|
JavaScript
Vue中父组件与子组件之间的通信
Vue中父组件与子组件之间的通信
371 0
|
11月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
808 2
|
10月前
|
缓存 JavaScript
vue中的keep-alive问题(2)
vue中的keep-alive问题(2)
680 137