props传递数据
简单的不跨层级传递
父->子:v-bind:value
子->父:this.$emit()
子组件中使用props进行属性接收,如果只是简单的给子组件传递数据,使用v-bind已经够用。更多情况子组件改变了props传递的数据后,需要立即同步到父组件中。
需要更新父组件中数据
第一种做法,给绑定message数据,使用@update:message=”updateEvent”进行事件绑定。
第二种做法使用语法糖,vue2中可以给属性绑定xxx.sync,在子组件中使用this.$emit(“update:xxx”, “this is text.”)能够同步更新父组件中xxx的值。
父Boss组件中
// 简单的传递数据给子组件
<Manager :message="msg"></Manager>
// 子组件更新了数据,要同步到父组件
<Manager :message="msg" @update:message="updateEvent"></Manager>
// 简写的语法糖
<Manager :message.sync="msg"></Manager>
子Manager组件,在methods中进行事件派发
manager(){
this.$emit("update:message", "请假休闲休息");
}
vue3中去掉.sync属性,改用v-model:xxx=”demoText”
<Manager v-model:message="msg"></Manager>
ts中保证props的类型PropType
interface ColumnProps{
id: string;
title: string;
avatar: string;
description: string;
}
export default ButtonComponent({
name:'ColumnList',
props:{
list:{
type: Array as PropType<ColumnProps[]>,
//type: <PropType<ColumnProps>> Array,
required:true
}
}
})
为了类型推论,让我们在使用属性的时候获取更丰富的类型提示,比如在这里我们定义了一个属性 list,使用 vue 默认的 Array,只能确定它是一个数组类型,不能确定数组里面的每一项到底是什么样子的。你在 setup 中,看 props.list 就是一个any数组,但是如果使用PropType
多层级传递
$parent / $children
Boss组件 -> Manager1组件 -> Worker1组件
-> Manager2组件 -> Worker2组件
$dispatch/$broadcast
使用场景:多层级组件间事件的通信。主要使$emit方法
在main.js中定义
// 向上传递事件 $dispatch
Vue.prototype.$dispatch = function(eventName, value){
let parent = this.$parent;
while(parent){
parent.$emit(eventName, value);
parent = parent.$parent;
}
}
//element中的实现 https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
}
// 向下通知事件 $broadcast
Vue.prototype.$broadcast = function(eventName,value){
let $broad = (children)=>{
children.forEach(child => {
child.$emit(eventName, value);
if(child.$children){
$broad(child.$children)
}
})
}
$broad(this.$children);
}
$attrs/$listeners
使用场景:多层级间组件通信,可以把顶级的属性/事件,在中间层通过$attrs/$listeners进行传递,使用层直接接收。
$attrs:表示属性的集合
$listeners:表示方法的集合(vue3已废弃)
示例:boss组件中的属性和方法直接传递给worker
Boss组件
//在boss组件中使用manager,并将属性传递给manger,但是manger不用解析,不使用
<Manager :message="msg" :value="value" @work="change"></Manager>
Manger组件
// Manager组件中使用Worker组件,可以使用$attrs传递所有属性,$listeners传递事件
<Worker v-bind="$attrs" v-on="$listeners"></Worker>
注意:要给该组件设置一个属性,inheritAttrs: false。表示不把属性显示在Dom
Worker组件
<template>
<div>
<p>{{ message }}</p>
<button @click="handle">员工2号</button>
</div>
</template>
<script>
export default {
name: "worker2",
props: {
message: {
type: String,
default: "",
},
},
methods: {
handle() {
console.log("员工2号执行boss中的click事件",this.$listeners);
this.$listeners.work("员工2号按时完成工作");
},
},
};
</script>
员工组件可以接收到Boss的所有属性和方法。
Provide/Inject
使用场景:多层级间组件通信,可以在顶层定义数据provide,中间层不用处理,直接在使用层用inject接收
注意:此方案多使用于组件开发,业务开发中不要使用。
boss组件传递数据给worker组件
// boss
export default {
provide() {
// 此操作会把boss组件的data传递给下层
return { boss: this };
},
components: {
Manager1,
Manager2,
},
data() {
return {
msg: "老板说,经理都来加班!!!",
value: "老板说,经理都来加班!!!",
money: "1个亿",
};
},
}
woker组件可以直接通过inject来使用
<template>
<div>
<h3>{{this.boss.money}}</h3>
</div>
</template>
// js
export default {
name: "worker1",
inject:[
"boss"
],
}
Ref
使用场景:父组件可以拿到子组件中定义的方法,直接在父组件中使用。
ref可以用的dom元素上,获取到dom节点。
在boss组件中定义
// 使用manager
<Manager
ref="manager"
></Manager>
// js
mounted() {
this.$refs.manager1.bossUseManagerMethod();
},
在Manger组件中定义方法
methods:{
bossUseManagerMethod(){
console.log("bossUseManagerMethod");
}
}
使用ref时⚠️注意:
通过 :ref =某变量 添加ref(即加了:号),
如果想获取该ref时需要加 [0]即this.$refs[refsArrayItem] [0];
如果不是:ref =某变量的方式而是 ref =某字符串时则不需要加 即this.$refs[refsArrayItem]
EventBus
使用场景:多组件间以及跨组件间进行事件通信
vue2,直接在main.js中定义
Vue.prototype.$Bus = new Vue()
共有四个组件boss/manger/worker1/worker2
woker1和worker2是manger的子组件,worker1中触发一个事件,boss和worker2组件都能接收到。
首先在worker1组件中定义事件
mounted(){
this.$Bus.$emit("useBus");
}
在boss组件和worker2组件使用$on接收,$off是取消事件
mounted() {
// 因为boss组件先于worker1组件挂载,要使用$nextTick下次循环调用
this.$nextTick(() => {
this.$Bus.$on("useBus", () => {
console.log("boss组件被触发");
});
});
}
worker2组件接收$bus事件。
mounted() {
this.$nextTick(() => {
this.$Bus.$on("useBus", () => {
console.log("员工2号组件被触发");
});
});
},
vue3中,不推荐使用
但是也可以单独定义一个Bus文件
// 为保持和vue2版本中使用bus一致,emit,on,off前面都加了$
class Bus{
list: { [key: string]: Array<Function> };
constructor() {
// 收集订阅信息,调度中心
this.list = {};
}
// 订阅
$on(name: string, fn: Function) {
this.list[name] = this.list[name] || [];
this.list[name].push(fn);
}
// 发布
$emit(name: string, data?: any) {
if (this.list[name]) {
this.list[name].forEach((fn: Function) => {
fn(data);
});
}
}
// 取消订阅
$off(name: string) {
if (this.list[name]) {
delete this.list[name];
}
}
}
export default new Bus() as Bus;
jsx语法render函数(IView框架常用语法)
作用域插槽v-slot(element框架常用语法)