vue组件间通信11种方式

组件间多种通信方式

  1. 组件间通信1: props
  2. 组件间通信2: vue自定义事件
  3. 组件间通信3: 事件总线
  4. 组件间通信4: v-model
  5. 组件间通信5: .sync 属性修饰符
  6. 组件间通信6: a t t r s 与 attrs与 attrslisteners
  7. 组件间通信7: c h i l d r e n 与 children与 childrenparent
  8. 组件间通信8: provide与inject
  9. 组件间通信9: vuex
  10. 组件间通信10: 作用域插槽slot-scope
  11. 组件间通信11:PubSub 项目中不会使用

1. props(父子)

  • 用来实现父子之间相互通信的最基本方式, 也是用得最多的方式
    • 父 ==> 子, 传递的是非函数类型的属性
    • 子 ==> 父, 传递的是函数类型的属性
  • 问题: 其它关系的组件使用props就会比较麻烦

演示

<Child :count="count" :add="add" />
export default {
  name: "Child",
  // 不声明props接受属性,属性就在$attrs
  props: ["count"],
  props: {
    count: Number,
  },
  // props数据是只读不能修改
  props: {
    count: {
      type: Number,
      required: true,
      validator(value) {
        return value >= 0 && value <= 10;
      },
      // default: 0
    },
    add: {
      type: Function,
      required: true,
    },
  },
  inheritAttrs: false, // 是否显示在元素标签上
};

2. vue自定义事件(一般给组件绑定)

    <button @click="handleClick">按钮</button>
    <MyButton @click="handleClick($event)" @xxx="xxx($event)" />

1) 原生DOM事件

  • 绑定原生DOM事件监听的2种情况
    • 在html标签上绑定DOM事件名的监听
    • 在组件标签上绑定DOM事件名的监听, 事件绑定在组件的根标签上
  • 当用户操作对应的界面时, 浏览器就会自动创建并封闭包含相关数据的事件对象, 分发对应的事件, 从而触发事件监听回调函数调用
  • 事件对象event, 本质是 “事件数据对象”
  • event对象内的数据属性: target / offsetX / offsetY / keyCode等
  • $event就是浏览器创建的event对象, 默认传递给事件监听回调函数的就是它

2) vue自定义事件

  • 绑定vue自定义事件监听
    • 只能在组件标签上绑定
    • 事件名是任意的, 可以与原生DOM事件名相同
  • 只有当执行$emit(‘自定义事件名’, data)时分发自定义事件, 才会触发自定义事件监听函数调用
  • $event: 就是分发自定义事件时指定的data数据
  • $event可以是任意类型, 甚至可以没有
  • 用来实现子向父组件通信, 功能相当于函数类型的props

事件:

  • 原生DOM元素绑定事件
    事件名是原生DOM事件事件名,绑定就是原生DOM事件
    事件名不是原生DOM事件事件名,绑定就是自定义事件

  • 组件绑定事件
    所有事件都是自定义事件

    一般只会给组件绑定自定义事件,给普通元素绑定没有意义
    绑定自定义事件目的:
      给子组件传递一个函数,子组件触发这个函数从而修改父组件数据(子-->父)
    
    $event 代表事件触发时传递的第一个参数
    
    事件修饰符
      .prevent
      .stop
      .native  给组件绑定事件时使用的,作用:给组件的第一个元素绑定事件
    按键修饰符
      @keyup.enter
      @keyup.13
    
    触发自定义事件方式:
      1. this.$emit('eventName', xxx)
      2. this.$listeners.eventName(xxx)
    

    3) 应用场景:

    在父组件下面有两个子组件,需要切换显示,切换显示是在子组件完成的
      父组件定义切换显示的方法,通过自定义事件绑定给子组件,子组件通过触发自定义事件来切换显示
    表格修改功能时,跳转到了新页面进行修改,点击取消要回来展示另外一个组件
    
    
    
    使用某个el组件时,他没有click功能,但是我们需要
      @click.native
    
    <MyButton @click.native="handleClick" />
	
	// 子
	<div>
		<button>按钮</button>
	</div>

3. 事件总线(任意组件)

全局事件总线:
1. 使用场景:任意组件
2. 原理:
基于自定义事件
将能定义事件对象(Vue的实例)绑定在Vue的原型对象上
在main.js的new Vue beforeCreate生命周期绑定
在这里绑定所有组件都可以使用
这样所有组件实例对象都能通过原型方式继承到事件对象,从而去绑定或触发事件

  • 理解:

    • Vue原型对象上有3个事件处理的方法: $on() / $emit() / $off()
    • 组件对象的原型对象的原型对象是Vue的原型对象: 组件对象可以直接访问Vue原型对象上的方法
  • 实现任意组件间通信

  • 编码实现:

    • 将入口js中的vm作为全局事件总线对象:
   beforeCreate() {
   	Vue.prototype.$bus = this
   }
  • 分发事件/传递数据的组件: this. b u s . bus. bus.emit(‘eventName’, data)

  • 处理事件/接收数据的组件: this. b u s . bus. bus.on(‘eventName’, (data) => {})

4. v-model(表单项)

给组件绑定了一个value属性,和input事件

等于是给组件绑定了一个v-bind和自定义事件 props和自定义事件的组合

<template>
  <div>
    <!-- 给组件绑定了一个value属性,和input事件 -->
    <MyInput v-model="msg" />
  </div>
</template>
  • 子组件
<template>
  <input
    type="text"
    :value="value"
    @input="$emit('input', $event.target.value)"
  />

  // 一样效果,带解构  但是不会这么去写,这样子就不用接收props
  <input
    type="text"
    v-bind="$attrs"
    @input="$emit('input', $event.target.value)"
  />
</template>

<script>
/*
  应用场景:表单项
*/
export default {
  name: "MyInput",
  // props: ["value"],
  mounted() {
    console.log(this);
  },
};
</script>

1) 原生input上的本质:

动态的value属性与原生input事件监听

<input type="text" :value="name2" @input="name2=$event.target.value">

2) 组件标签上的本质:

动态的value属性与自定义input事件监听

// 父组件: 
<CustomInput :value="name4" @input="name4=$event"/>

// 子组件
props: ['value']
<input type="text" :value="value" @input="$emit('input', $event.target.value)">

3) 利用v-model能做什么?

  • v-model不仅能实现原生标签上的双向数据绑定, 也能实现父子组件间数据双向通信(同步)
  • 应用
    • 一般用于封装带表单项的复用性组件
    • elment-ui中: Input/CheckBox/Radio/Select等表单项组件都封装了v-model

5: sync 属性修饰符

1) 理解本质:

绑定一个自定义事件监听, 用来接收子组件分发事件携带的最新数据来更新父组件的数据

<child :money.sync="total"/>
<Child :money="total" @update:money="total=$event"/>

2) 利用sync能做什么呢?

  • 在原有父向子的基础上加上子向父通信
  • 应用
    • 常用于封装可复用组件(需要更新父组件数据)
      • v-model一般用于带表单项的组件
      • sync一般用于不带表单项标签的组件
    • element-ui中: Dialog就利用sync来实现组件的隐藏

3)示例

<template>
  <div>
    <h1><router-link to="/">回到首页</router-link></h1>
    <Child :count.sync="count"></Child>
  </div>
</template>

<script>
import Child from "./Child";

export default {
  name: "Sync",
  data() {
    return {
      count: 1,
    };
  },
  components: {
    Child,
  },
};
</script>
<template>
  <div>
    <p>{{ count }}</p>
    <button @click="$emit('update:count', count + 1)">点击数字加加</button>
  </div>
</template>

<script>
export default {
  name: "Child",
  props: ["count"],
  mounted() {
    console.log(this);
  },
};
</script>

vue组件间通信11种方式_第1张图片

6: $attrs$listeners

1) 理解:

  • $attrs: 排除props声明, class, style的所有组件标签属性组成的对象

  • $listeners: 级组件标签绑定的所有自定义事件监听的对象

  • v-bind: 的特别使用

    <div v-bind="{ id: someProp, 'other-attr': otherProp }">div>
    
  • v-on: 的特别使用:

    <button v-on="{ mousedown: doThis, mouseup: doThat }">button>
    

    一般: v-bind与$attrs配合使用, v-on与$listeners配合使用

2) 使用它们能做什么呢?

  • 在封装可复用组件时: HintButton
    • 从父组件中接收不定数量/名称的属性或事件监听
    • 在组件内部, 传递给它的子组件
  • element-ui中: Input就使用了v-bind与$attrs来接收不定的属性传递给input

3) 扩展双击监听:

  • @dblclick="add2"
    绑定是自定义事件监听, 而el-button内部并没处理(没有绑定对应的原生监听, 没有分发自定义事件)
    双击时, 不会有响应

  • @dblclick.native="add2"
    绑定的是原生的DOM事件监听, 最终是给组件的根标签a绑定的原生监听
    当双击a内部的button能响应, 因为事件有冒泡

4) 示例

  • 父组件的这三种写法效果都是相同的
<template>
  <!-- <Child
    v-bind="(person = person)"
    @click="handleClick1"
    @aaa="handleClick2"
    @bbb="handleClick3"
  ></Child> -->

  <!-- <Child
    v-bind="person"
    @click="handleClick1"
    @aaa="handleClick2"
    @bbb="handleClick3"
  ></Child> -->

  <Child
    :name="person.name"
    :age="person.age"
    :hobby="person.hobby"
    @click="handleClick1"
    @aaa="handleClick2"
    @bbb="handleClick3"
  ></Child>
</template>

<script>
import Child from "./Child";

export default {
  name: "AttrsAndListeners",
  data() {
    return {
      person: {
        name: "小明",
        age: 18,
        hobby: ["抽烟", "喝酒", "烫头"],
      },
    };
  },
  methods: {
    handleClick1() {
      console.log(123);
    },
    handleClick2() {
      console.log(123);
    },
    handleClick3() {
      console.log(123);
    },
  },
  components: {
    Child,
  },
};
</script>
<template>
  <div>
    child
    <C v-bind="$attrs" v-on="$listeners"></C>
  </div>
</template>

<script>
import C from "./C";
export default {
  name: "Child",
  components: {
    C,
  },
  mounted() {
    console.log(this);
  },
};
</script>

vue组件间通信11种方式_第2张图片

<template>
  <div>ccc</div>
</template>

<script>
export default {
  name: "C",
  mounted() {
    console.log(this);
  },
};
</script>

vue组件间通信11种方式_第3张图片

7: $children$parent

1) 理解:

  • $children: 所有直接子组件对象的数组, 利用它可以更新多个子组件的数据
  • $parent: 父组件对象, 从而可以更新父组件的数据
  • $refs: 包含所有有ref属性的标签对象或组件对象的容器对象

2) 利用它们能做什么?

  • 能方便的得到子组件/后代组件/父组件/祖辈组件对象, 从而更新其data或调用其方法
  • 官方建议不要大量使用, 优先使用props和event
  • 在一些UI组件库定义高复用组件时会使用 c h i l d r e n 和 children和 childrenparent, 如Carousel组件

3) 扩展: mixin

  • 多个组件有部分相同的js代码如何复用 ?
  • 答: 利用vue的mixin技术实现
  • 本质: 实现Vue组件的JS代码复用, 简化编码的一种技术

4) 练习mixin

  • mixin.js
<template>
  <div>
    mixin
    <p>{{ count }}</p>
    <button @click="handleClick">Mixin</button>
  </div>
</template>

<script>
import mixi from "./mixin";

export default {
  name: "Mixin",
  mixins: [mixi],
  mounted() {
    console.log(this);
  },
};
</script>
  • mixin.js
export default {
  data() {
    return {
      count: 0
    };
  },
  methods: {
    handleClick() {
      this.count++;
    }
  },
  created() {
    console.log("mixi created");
  },
  mounted() {
    console.log("mixi mounted");
  }
};

vue组件间通信11种方式_第4张图片
vue组件间通信11种方式_第5张图片

4)演示

<template>
  <div>
    <A></A>
    <B></B>
  </div>
</template>

<script>
import A from "./A";
import B from "./B";

export default {
  name: "ChildrenAndParent",
  data() {
    return {
      title: "Father",
    };
  },
  components: {
    A,
    B,
  },
  mounted() {
    console.log(this);
  },
};
</script>

vue组件间通信11种方式_第6张图片

<template>
  <div>aaa</div>
</template>

<script>
export default {
  name: "A",
  data() {
    return {
      title: "AAA",
    };
  },
  mounted() {
    console.log(this.$parent.title);
  },
};
</script>

vue组件间通信11种方式_第7张图片

8: provide与inject

1) 理解

用来实现祖孙组件直接通信
在祖组件中通过provide配置向后代组件提供数据
在后代组件中通过inject配置来读取数据

2) 注意:

不太建议在应用开发中使用, 一般用来封装vue插件
provide提供的数据本身不是响应式的 ==> 父组件更新了数据, 后代组件不会变化
provide提供的数据对象内部是响应式的 ==> 父组件更新了数据, 后代组件也会变化

3) 应用:

element-ui中的Form组件中使用了provide和inject

9: vuex

  1. 实现任意组件间通信

  2. Vuex 是一个专为 Vue 应用程序设计的管理多组件共享状态数据的 Vue 插件
    任意组件都可以读取到Vuex中store的state对象中的数据

    任意组件都可以通过dispatch()或commit()来触发store去更新state中的数据

    一旦state中的数据发生变化, 依赖于这些数据的组件就会自动更新

详情见vue分类之vuex

10.slot-scope

1) 什么情况下使用作用域插槽?

  • 父组件需要向子组件传递标签结构内容

  • 但决定父组件传递怎样标签结构的数据在子组件中

2) 编码

<!: 子组件: >
<slot :row="item" :$index="index">slot>

<!: 父组件: >
<template slot-scope="{row, $index}">
	<span>{{$index+1}}span>   
	<span :style="{color: $index%2===1 ? 'blue' : 'green'}" >{{row.text}}span>
template>

3) 应用

  • 对于封装列表之类的组件特别需要
  • element-ui中: Table组件中就用到了slot-scope

插槽(父给子传递带数据标签)

  • 使用场景:父给子传递带数据的标签

默认插槽

组件写成双标签,里面放入标签数据,那么这个标签数据就会以插槽的方式传递给子组件

// 父组件给子组件传递带数据的标签
<AChild>
  <p>hello vue000</p>
  <p>hello vue11</p>
  <p>{{msg}}</p>
</AChild>
// 子组件使用
// 使用父组件以插槽方式传递的标签数据
<slot></slot>

具名、命名插槽

具名、命名插槽: 给每一个插槽取一个名字

 // 具名、命名插槽: 给每一个插槽取一个名字
 <BChild>
   // 旧语法:slot="名称"
   <template slot="header">
     <header>头部...{{ title }}</header>
   </template>

   // 新语法:v-slot:名称 
   <template v-slot:main>
     <main>内容区...</main>
   </template>

   // 新语法可以简写:#名称 
   <template #footer>
     <footer>底部...</footer>
   </template>
 </BChild>
  • 子组件使用
<template>
  <div>
    // 显示头部 
    // 通过name属性来决定使用哪个具名插槽 
    <slot name="header"></slot>
    <p>---------</p>
    // 显示内容区 
    <slot name="main"></slot>

    <p>---------</p>
    // 显示底部 
    <slot name="footer"></slot>
  </div>
</template>

作用域插槽

  • 父组件
 <CChild>
   // 父组件插槽可以接受子组件通过slot传递的props数据
   <template #list="slotProps">
   <template v-slot:list="slotProps">

   // { person } -> 就是对数据进行解构赋值
   <template #list="{ person }">

   <template #list="{ person: { name, age } }">
     <ul>
       <li>姓名:{{ name }}</li>
       <li>年龄:{{ age }}</li>
     </ul>
   </template>
 </CChild>
  • 子组件
<template>
  <div>
    // 以标签属性(props)方式传递person数据 
    <slot name="list" :person="person"></slot>
  </div>
</template>

<script>
export default {
  name: "CChild",
  data() {
    return {
      person: {
        name: "jack",
        age: 18,
      },
    };
  },
};
</script>

11.PubSub

项目中很少使用,详情见react分类中

你可能感兴趣的:(Vue,vue.js,前端)