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组件中

  1. // 简单的传递数据给子组件
  2. <Manager :message="msg"></Manager>
  3. // 子组件更新了数据,要同步到父组件
  4. <Manager :message="msg" @update:message="updateEvent"></Manager>
  5. // 简写的语法糖
  6. <Manager :message.sync="msg"></Manager>

子Manager组件,在methods中进行事件派发

  1. manager(){
  2. this.$emit("update:message", "请假休闲休息");
  3. }

vue3中去掉.sync属性,改用v-model:xxx=”demoText”

  1. <Manager v-model:message="msg"></Manager>

ts中保证props的类型PropType

  1. interface ColumnProps{
  2. id: string;
  3. title: string;
  4. avatar: string;
  5. description: string;
  6. }
  7. export default ButtonComponent({
  8. name:'ColumnList',
  9. props:{
  10. list:{
  11. type: Array as PropType<ColumnProps[]>,
  12. //type: <PropType<ColumnProps>> Array,
  13. required:true
  14. }
  15. }
  16. })

为了类型推论,让我们在使用属性的时候获取更丰富的类型提示,比如在这里我们定义了一个属性 list,使用 vue 默认的 Array,只能确定它是一个数组类型,不能确定数组里面的每一项到底是什么样子的。你在 setup 中,看 props.list 就是一个any数组,但是如果使用PropType 这个时候,props.list 就变成一个 ColumnProps 的数组,你使用它的时候不论在 ts 中还是模版中都能获得类型的推断和自动补全等。

多层级传递

$parent / $children

Boss组件 -> Manager1组件 -> Worker1组件
-> Manager2组件 -> Worker2组件

$dispatch/$broadcast

使用场景:多层级组件间事件的通信。主要使$emit方法
在main.js中定义

  1. // 向上传递事件 $dispatch
  2. Vue.prototype.$dispatch = function(eventName, value){
  3. let parent = this.$parent;
  4. while(parent){
  5. parent.$emit(eventName, value);
  6. parent = parent.$parent;
  7. }
  8. }
  9. //element中的实现 https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js
  10. dispatch(componentName, eventName, params) {
  11. var parent = this.$parent || this.$root;
  12. var name = parent.$options.componentName;
  13. while (parent && (!name || name !== componentName)) {
  14. parent = parent.$parent;
  15. if (parent) {
  16. name = parent.$options.componentName;
  17. }
  18. }
  19. if (parent) {
  20. parent.$emit.apply(parent, [eventName].concat(params));
  21. }
  22. }
  23. // 向下通知事件 $broadcast
  24. Vue.prototype.$broadcast = function(eventName,value){
  25. let $broad = (children)=>{
  26. children.forEach(child => {
  27. child.$emit(eventName, value);
  28. if(child.$children){
  29. $broad(child.$children)
  30. }
  31. })
  32. }
  33. $broad(this.$children);
  34. }

$attrs/$listeners

使用场景:多层级间组件通信,可以把顶级的属性/事件,在中间层通过$attrs/$listeners进行传递,使用层直接接收。
$attrs:表示属性的集合
$listeners:表示方法的集合(vue3已废弃)
示例:boss组件中的属性和方法直接传递给worker
Boss组件

  1. //在boss组件中使用manager,并将属性传递给manger,但是manger不用解析,不使用
  2. <Manager :message="msg" :value="value" @work="change"></Manager>

Manger组件

  1. // Manager组件中使用Worker组件,可以使用$attrs传递所有属性,$listeners传递事件
  2. <Worker v-bind="$attrs" v-on="$listeners"></Worker>

注意:要给该组件设置一个属性,inheritAttrs: false。表示不把属性显示在Dom
Worker组件

  1. <template>
  2. <div>
  3. <p>{{ message }}</p>
  4. <button @click="handle">员工2号</button>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. name: "worker2",
  10. props: {
  11. message: {
  12. type: String,
  13. default: "",
  14. },
  15. },
  16. methods: {
  17. handle() {
  18. console.log("员工2号执行boss中的click事件",this.$listeners);
  19. this.$listeners.work("员工2号按时完成工作");
  20. },
  21. },
  22. };
  23. </script>

员工组件可以接收到Boss的所有属性和方法。

Provide/Inject

使用场景:多层级间组件通信,可以在顶层定义数据provide,中间层不用处理,直接在使用层用inject接收
注意:此方案多使用于组件开发,业务开发中不要使用。
boss组件传递数据给worker组件

  1. // boss
  2. export default {
  3. provide() {
  4. // 此操作会把boss组件的data传递给下层
  5. return { boss: this };
  6. },
  7. components: {
  8. Manager1,
  9. Manager2,
  10. },
  11. data() {
  12. return {
  13. msg: "老板说,经理都来加班!!!",
  14. value: "老板说,经理都来加班!!!",
  15. money: "1个亿",
  16. };
  17. },
  18. }

woker组件可以直接通过inject来使用

  1. <template>
  2. <div>
  3. <h3>{{this.boss.money}}</h3>
  4. </div>
  5. </template>
  6. // js
  7. export default {
  8. name: "worker1",
  9. inject:[
  10. "boss"
  11. ],
  12. }

Ref

使用场景:父组件可以拿到子组件中定义的方法,直接在父组件中使用。
ref可以用的dom元素上,获取到dom节点。
在boss组件中定义

  1. // 使用manager
  2. <Manager
  3. ref="manager"
  4. ></Manager>
  5. // js
  6. mounted() {
  7. this.$refs.manager1.bossUseManagerMethod();
  8. },

在Manger组件中定义方法

  1. methods:{
  2. bossUseManagerMethod(){
  3. console.log("bossUseManagerMethod");
  4. }
  5. }

使用ref时⚠️注意:

通过 :ref =某变量 添加ref(即加了:号),
如果想获取该ref时需要加 [0]即this.$refs[refsArrayItem] [0];
如果不是:ref =某变量的方式而是 ref =某字符串时则不需要加 即this.$refs[refsArrayItem]

EventBus

使用场景:多组件间以及跨组件间进行事件通信
vue2,直接在main.js中定义

  1. Vue.prototype.$Bus = new Vue()

共有四个组件boss/manger/worker1/worker2
woker1和worker2是manger的子组件,worker1中触发一个事件,boss和worker2组件都能接收到。
首先在worker1组件中定义事件

  1. mounted(){
  2. this.$Bus.$emit("useBus");
  3. }

在boss组件和worker2组件使用$on接收,$off是取消事件

  1. mounted() {
  2. // 因为boss组件先于worker1组件挂载,要使用$nextTick下次循环调用
  3. this.$nextTick(() => {
  4. this.$Bus.$on("useBus", () => {
  5. console.log("boss组件被触发");
  6. });
  7. });
  8. }

worker2组件接收$bus事件。

  1. mounted() {
  2. this.$nextTick(() => {
  3. this.$Bus.$on("useBus", () => {
  4. console.log("员工2号组件被触发");
  5. });
  6. });
  7. },

vue3中,不推荐使用

但是也可以单独定义一个Bus文件

  1. // 为保持和vue2版本中使用bus一致,emit,on,off前面都加了$
  2. class Bus{
  3. list: { [key: string]: Array<Function> };
  4. constructor() {
  5. // 收集订阅信息,调度中心
  6. this.list = {};
  7. }
  8. // 订阅
  9. $on(name: string, fn: Function) {
  10. this.list[name] = this.list[name] || [];
  11. this.list[name].push(fn);
  12. }
  13. // 发布
  14. $emit(name: string, data?: any) {
  15. if (this.list[name]) {
  16. this.list[name].forEach((fn: Function) => {
  17. fn(data);
  18. });
  19. }
  20. }
  21. // 取消订阅
  22. $off(name: string) {
  23. if (this.list[name]) {
  24. delete this.list[name];
  25. }
  26. }
  27. }
  28. export default new Bus() as Bus;

jsx语法render函数(IView框架常用语法)

作用域插槽v-slot(element框架常用语法)