一文搞定Vue2组件通信

  • 一文搞定Vue2组件通信已关闭评论
  • 134 次浏览
  • A+
所属分类:Web前端
摘要

父组件将自己的状态分享给子组件使用;方法:父组件通过子标签传递数据,子组件通过 props 接收


vue 组件通信方式

  1. 父组件将自己的状态分享给子组件使用;

    方法:父组件通过子标签传递数据,子组件通过 props 接收

  2. 子组件改变父组件的状态;

    方法:父组件在子标签上通过@abc 提供一个改变自身状态的方法,子组件通过$emit("abc", payload)触发这个函数

  3. 父组件直接改变子组件的状态;

    方法:父组件设法($ref,$children[0])拿到子组件实例对象,然后通过实例对象直接修改子组件状态

  4. 子组件直接改变父组件的状态

    方法:子组件通过$parent拿到父组件的改变自身状态的方法,然后直接调用($parent 可以拿到父组件状态,但是最好不要直接修改,而是通过父组件函数式修改,保持单向数据流)

  5. 父组件通过插槽向子组件传递数据

    方法:子组件定义具名插槽,父组件向插槽内传递自己的状态

  6. 父组件向后代组件传值

    方法:父组件正常在标签上向子组件传递数据,子组件不用 props 接收属性,通过$attrs获取属性,通过$listeners 获取方法。子组件再向下传递时,使用 v-bind="$attr"传递属性,使用v-on="$listeners"传递方法

  7. 父组件向后代组件传值

    方法:父组件js中定义provide函数提供数据源,后代组件通过inject去接收,inject可以是一个数组或对象。

    注意:父组件提供的数据源如果不是响应式的,那么后代修改数据,数据不会响应变化。如果父组件提供的数据源是响应式的,但是不是一个对象,那么它也不是响应式的,所以要用对象在外包一层对象(数组不行)。另外,如果子组件同时provide一个inject祖先组件相同名称的数据,那么子组件的后代会就近使用子组件的数据。

    官网tip:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。

    https://cn.vuejs.org/v2/api/#provide-inject

  8. 全局通信-事件总线

    方法:通过注册一个新的vue实例作为桥梁,使用$on和$emit来完成通信

  9. 全局通信-vuex

    略(看官方文档喽)

    https://vuex.vuejs.org/zh/

第一种: props传参

// 父组件向子组件传递msg <template>   <div>     <p>我是dad</p>     <Child :msg="msg" />   </div> </template>  <script> import Child from "./ChildItem.vue";  export default {   name: "App",   components: {     Child,   },   data() {     return {       msg: "父亲的忠告",     };   }, }; </script>  // 子组件props接收 <template>   <div>     <p>我是子组件</p>     <span>{{ msg }}</span>   </div> </template>  <script> export default {   props: {     msg: {       type: String,       default: "什么都没有",     },   }, }; </script>   

第二种:emit,on通信

// 父组件向子组件提供改变自己状态的函数 <template>   <div>     <p>我是dad</p>     <Child @changeMyMind="changeMyMind" />     <span>{{ mind }}</span>   </div> </template>  <script> import Child from "./ChildItem.vue";  export default {   name: "App",   components: {     Child,   },   data() {     return {       mind: "伟大万岁",     };   },   methods: {     changeMyMind(yourMind) {       this.mind = yourMind;     },   }, }; </script>   // 子组件不用接收,直接通过$emit触发并传参就行 <template>   <div>     <p>我是子组件</p>   </div> </template>  <script> export default {   mounted() {     this.$emit("changeMyMind", "躺平鸟");   }, }; </script>  

第三种:$ref,$children实例通信

// 父组件通过$ref或者$children拿到子组件实例,然后直接修改子组件状态 /**  * this.$children`数组中的元素不是响应式的,并且下标并不一定对用父组件引用的子组件的顺序,例如有异步加载的子组件,可能影响其在 children 数组中的顺序。所以使用时需要根据一定的条件例如子组件的name去找到相应的子组件。  **/ <template>   <div>     <p>我是dad</p>     <Child ref="childRef" />   </div> </template>  <script> import Child from "./ChildItem.vue";  export default {   components: {     Child,   },   mounted() {     // this.$children[0].childMsg = "不你不是";     this.$refs.childRef.childMsg = "不你不是";   }, }; </script>    // 子组件等着被改就行 <template>   <div>     <p>我是子组件</p>     <span>{{ childMsg }}</span>   </div> </template>  <script> export default {   data() {     return {       childMsg: "我是子组件数据",     };   }, }; </script>   

第四种:$parent通信

// 父组件 <template>   <div>     <p>我是dad</p>     <Child />     <span>{{ aa }}</span>   </div> </template>  <script> import Child from "./ChildItem.vue";  export default {   components: {     Child,   },   data() {     return {       aa: "",     };   },   methods: {     changeAa(data) {       this.aa = data;     },   }, }; </script>  // 子组件通过$parent拿到父组件实例,然后直接修改父组件状态 <template>   <div>     <p>我是子组件</p>     <span>{{ childMsg }}</span>   </div> </template>  <script> export default {   data() {     return {       childMsg: "我是子组件数据",     };   },   mounted() {     // this.$parent.aa = "我改了哈"; 不推荐     this.$parent.changeAa("我改了哦");   }, }; </script>  

第五种:插槽通信(一般不用)

// 父组件 <template>   <div>     <p>我是dad</p>     <Child>       <template v-slot:boring>         {{ aa }}       </template>     </Child>   </div> </template>  <script> import Child from "./ChildItem.vue";  export default {   components: {     Child,   },   data() {     return {       aa: "父组件的数据哦",     };   }, }; </script>   // 子组件定义插槽 <template>   <div>     <p>我是子组件</p>     <slot name="boring"></slot>   </div> </template>  <script> export default {}; </script>   

第六种:$attr,$listener深层双向通信

// 父组件 <template>   <div>     <p>我是dad</p>     <span>{{ dadData }}</span>     <Son :dadData="dadData" @changeDadData="changeDadData" @keyup="someKeyUp" />   </div> </template>  <script> import Son from "./SonItem.vue";  export default {   components: {     Son,   },   data() {     return {       dadData: "父组件的数据哦",     };   },   methods: {     changeDadData(newData) {       this.dadData = newData;     },     someKeyUp(e) {       console.log(e.target.value);     },   }, }; </script>    // 儿子组件 <template>   <div>     <p>我是儿子组件</p>     <span>{{ $attrs.dadData }}</span>     <input type="text" v-on="$listeners" />     <GrandSon v-bind="$attrs" v-on="$listeners" />   </div> </template>  <script> import GrandSon from "./GrandSon.vue";  export default {   components: {     GrandSon,   },   mounted() {     console.log(this.$listeners);   }, }; </script>  // 孙子组件 <template>   <div>     <p>我是孙子组件</p>     <input type="text" @input="grandsonInput" />   </div> </template>  <script> export default {   methods: {     grandsonInput(e) {       //   this.$emit("changeDadData", e.target.value); 也可以触发       this.$listeners.changeDadData(e.target.value);     },   }, }; </script>    

第七种:provide,inject依赖注入深层次单向通信

// 父组件 <template>   <div>     <p>我是dad</p>     <span>{{ dadMessage }}</span>     <Son />   </div> </template>  <script> import Son from "./SonItem.vue";  export default {   components: {     Son,   },   provide() {     return {       message: this.dadMessage,     };   },   data() {     return {       dadMessage: {         textContent: "我是个祖先数据",       },     };   }, }; </script>   // 儿子组件 <template>   <div>     <p>我是儿子组件</p>     <span>{{ theData }}</span>     <GrandSon />   </div> </template>  <script> import GrandSon from "./GrandSon.vue";  export default {   components: {     GrandSon,   },   inject: {     // 起个别名     theData: {       // 数据来源映射       from: "message",       // 默认值       default: () => ({ message: { textContent: "啥也不是" } }),     },   }, }; </script>   // 孙子组件 <template>   <div>     <p>我是孙子组件</p>     <input type="text" @input="grandsonInput" />     <span>{{ message.textContent }}</span>   </div> </template>  <script> export default {   methods: {     grandsonInput(e) {       this.message.textContent = e.target.value;     },   },   inject: ["message"], }; </script>    

第八种: $bus事件总线全局通信

// main.js中定义新的vue实例挂载到原型上 Vue.prototype.$bus = new Vue();  // 父组件通过this.$bus.$on监听事件 <template>   <div>     <p>我是dad</p>     <span>{{ dadData }}</span>     <Son />   </div> </template>  <script> import Son from "./SonItem.vue";  export default {   components: {     Son,   },   data() {     return {       dadData: "我是爹地",     };   },   mounted() {     this.$bus.$on("changeDadData", this.changeDadData);   },   methods: {     changeDadData(newData) {       this.dadData = newData;     },   },   // 记得清除监听   beforeDestroy() {     this.$bus.$off("changeDadData");   }, }; </script>   // 孙子组件通过this.$bus.$emit触发事件 <template>   <div>     <p>我是孙子组件</p>     <input type="text" @input="grandsonInput" />     <span></span>   </div> </template>  <script> export default {   methods: {     grandsonInput(e) {       this.$bus.$emit("changeDadData", e.target.value);     },   }, }; </script>