简介
在开发中经常需要封装一些常用组件,像弹框、对话框等,无论是自己封装还是在第三方组件基础上进行二次封装(为了统一基本配置,精简代码以及后期维护),都会面对比较常见的父子组件通信,用的比较多的 props
(接收父组件传来的数据) 和 $emit
(将事件和数据发射出去),通过 v-model
和 .sync
语法糖可以简化当一个子组件需要改变了一个 prop 的值时,会通知其父组件进行同步的修改。
v-model
Vue2 中最开始比较常见的就是对表单元素默认绑定
- text,textarea 使用 value 属性 与 input 事件
- select 使用 value 与 change 事件
- radio checkbox 使用 checked 与 change 事件
如 input 的绑定
<input v-model="message" />
<!-- 是以下的简写: -->
<input :value="message" @input='($event) => message = $event.target.value' />
- message 赋值到 input 的 value 属性,更改了 input 的 value 值,是绑定中的单向绑定
- 通过监听 input 组件的 input 事件,更改 message 值,完成双向绑定
Vue2.2.0+
新增自定义组件的 v-model
,可以自定义属性(prop)和事件(event)名称,这样更加灵活也可以避免默认的 value 属性值有其它的用处
父组件
<template>
<children v-model="message"></children>
</template>
<script>
import children from "./children.vue";
export default {
components: {
children
},
data() {
return {
message: "父组件数据"
}
}
}
</script>
子组件
<template>
<h1>{{ message }}</h1>
<p @click='update'></p>
</template>
<script>
export default {
model: {
prop: "message", //父组件设置 v-model 时,将变量值传给子组件的 message
event: "updateMsg" //父组件监听事件
},
props: {
message: String //定义和model的prop相同的props来接收
},
update() {
//将message传到父组件v-model,实现双向控制,不需要父组件再请@updateMsg接收数据更新message
this.$emit("updateMsg", "子组件更新数据")
}
}
</script>
.sync
Vue2.3.0+
新增,默认以 update:prop 模式触发事件,注意.sync 绑定的 prop 不能是表达式(:title.sync="message + '!'")这种绑定,只能是属性名
<div :title.sync="message" ></div>
<!-- 是以下的简写: -->
<div :title="message" @update:title="message = $event" ></div>
Vue3 v-model 统一替代
一个组件中只能有一个 v-model,但是可以有多个 .sync,在 Vue3
移除了 .sync 修饰符和组件的 model 选项,统一使用 v-model,默认 prop 由 value 改为 modelValue,event 由 input 改为 update:modelValue,相当于结合了 v-model 和 .sync 的优点
<div v-model:title="message" ></div>
<!-- 是以下的简写: -->
<div :title="message" @update:title="message = $event" ></div>
对象数组处理
- 如果父组件传入的值是简单数据类型(string、number 和 boolean 等)时,子组件必须要使用 $emit 去通知父组件更新数据,如果需要根据值变化来进行其它操作,则父组件需要使用
watch
来监听值的变化 - 如果父组件传入的值是引用类型时,子组件修改了对象属性,不需要 $emit 去通知父组件更新数据,父组件的数据会直接变化,因为父子组件引用的是同一个对象,封装表单组件时为了便捷有使用这个特性,但不推荐子组件直接修改父组件中的参数,因为子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解,无法追溯,比如子组件和父组件同时在修改这个值,可以通过插槽
v-slot
来传递表单数据
elementui 对话框二次封装
结合业务统一封装对话框样式和基本功能,方便后期统一维护,以visible
控制对话框是否显隐为例
<!-- dialogPage.vue -->
<template>
<el-dialog :visible.sync="visible">
<div slot="title" class="dialog-title">标题</div>
<div class="dialog-content">
<slot></slot>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="onCancel">取消</el-button>
<el-button type="primary">确定</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
props: {
childVisible: {
type: Boolean,
default() {
return false
}
}
},
computed: {
// 计算属性默认只有 getter, 在修改变量时需要提供一个 setter 函数
// 读取一个变量的时候会触发该变量的 getter,修改该变量时候会触发他的 setter
// 通过计算属性来接收父组件传值,也可以在data中定义数据,使用watch监听可以达到相同效果
visible: {
// getter
get() {
return this.childVisible
},
// setter
set(val) {
// 当visible改变的时候,触发 update:childVisible 方法,把最新值传递给父组件,同步修改 childVisible 的值
this.$emit("update:childVisible", val);
}
}
},
methods: {
onCancel(){
this.visible = false // 计算属性赋值,触发 set 函数
}
}
}
</script>
组件使用时,只需要通过 .sync 绑定 childVisible 值即可,子组件变化会同步到 childVisible 值
<template>
<dialogPage childVisible.sync="childVisible"></dialogPage>
<!-- .sync 类似
<dialogPage
childVisible="childVisible"
@update:childVisible="(val) => childVisible = val">
</dialogPage>
简写 -->
</template>
<script>
import dialogPage from "./dialogPage.vue";
export default {
components: {
dialogPage
},
data() {
return {
childVisible: false
}
}
}
</script>