vue组件通讯的几种体式格局

写在前面

vue 的组件化应该是其最中心的思想了,不管是一个大的页面照样一个小的按钮,都能够被称之为组件。基于 Vue 的开辟,就是在写一个个组件,不管是基本组件照样营业组件,末了都是要拼装在一同。根据组件的层级关联,能够把组件之间的关联归结为:父子关联、隔代关联、兄弟关联,无关联关联。

$ref$parent$children

基于当前上下文的,能够经由历程 $ref$parent$children 接见组件实例,能够直接挪用组件的要领或接见数据。个中 $parent 能够接见当前组件的父组件,$children 能够接见当前组件的一切子组件。虽然 $parent$children 都能够猎取组件实例,然则它们没法在跨级或兄弟间通讯,这是它们的瑕玷。

provideinject

provide / inject 是 Vue 在 2.2.0 版本后新增的 API。

这对选项须要一同运用,以许可一个先人组件向其一切子孙后代注入一个依靠,不管组件条理有多深,并在起上下游关联建立的时间里一直见效。

也就是在父组件中供应一个值,并且在须要运用的子孙组件中注入改值,即:

// Parent.vue
export default {
    provide() {
        return {
            name: 'Stone'
        }
    }
}
// Child.vue
export default {
   inject: ['name'],
   mounted() {
       console.log(this.name)
   }
}

不仅仅是 Child.vue ,只假如 Parent.vue 的子组件,不管隔若干代,都能够经由历程这个的体式格局注入。 这类多级组件透传的体式格局能够保证单向数据流的清楚性,比方像用户信息如许的全局信息,就能够借助这两个 API 来完成,而没有引入第三方库 vuex

替换 Vuex

vuex 是把数据集合治理,然后那里须要就在那里猎取,根据这个思绪,我们能够在根组件 App.vue 中注入全局信息,并且在页面的任何地方运用。

// App.vue
<template>
  <div>
    <router-view></router-view>
  </div>
</template>
export default {
    provide() {
        return {
            userInfo: this.user
        }
    },
    data() {
        return {
            user: {}
        }
    },
    methods: {
      getUserInfo () {
        $.ajax('/user/info', (data) => {
          this.user = data
        })
      }
    }
}

把全部 App.vue 的实例 this 对外供应, 如许其他页面就能够经由历程 this.userInfo 来猎取用户信息。

<template>
  <div>
    {{ userInfo }}
  </div>
</template>
<script>
  export default {
    inject: ['userInfo']
  }
</script>

$attrs$listeners

这两个 API 是 Vue 2.4.0 新增的。
$attrs ,继续一切的父组件属性;
$listeners ,它是一个对象,内里包含了作用在这个组件上的一切监听器。

主要用途就是用在父组件通报数据给子组件或许孙组件。

<!-- Parent.vue -->
<template>
  <div id="app">
    <child1 :p-child1="child1"
            :p-child2="child2"
            :p-child3="child3"
            v-on:test1="onTest1"
            v-on:test2="onTest2">
    </child1>
  </div>
</template>
<script>
import Child1 from "./Child1.vue";
export default {
  data() {
    return {
        child1: 'child1',
        child2: 'child2',
        child3: 'child3'
    };
  },
  components: { Child1 },
  methods: {
    onTest1() {
      console.log("test1 running...");
    },
    onTest2() {
      console.log("test2 running");
    }
  }
};
</script>
<!-- Child1.vue -->
<template>
    <div class="child-1">
        <p>in child1:</p>
        <p>props: {{pChild1}}</p>
        <p>$attrs: {{$attrs}}</p>
        <hr>
        <!-- Child2 组件中能直接触发 test 的缘由在于 Child1 组件挪用 Child2 组件时 运用 v-on 绑定了 $listeners 属性 -->
        <!-- 经由历程 v-bind 绑定 $attrs 属性,Child2 组件能够直接猎取到 Parent 组件中通报下来的 props(除 了Child1 组件中 props 声明的) -->
        <child2 v-bind="$attrs" v-on="$listeners"></child2>
    </div>
</template>
<script>
import Child2 from './Child2.vue';
export default {
    props: ['pChild1'],
    data() {
        return {};
    },
    inheritAttrs: false,
    components: { Child2 },
    mounted() {
        this.$emit('test1');
    }
};
</script>
<!-- Child2.vue -->
<template>
    <div class="child-2">
        <p>in child2:</p>
        <p>props: {{pChild2}}</p>
        <p>$attrs: {{$attrs}}</p>
        <hr>
    </div>
</template>
<script>
export default {
    props: ['pChild2'],
    data() {
        return {};
    },
    inheritAttrs: false,
    mounted() {
        this.$emit('test2');
    }
};
</script>

dispatchbroadcast

这两个 API 是 Vue 1.0 版本的,$dispatch 用于向上级派发事宜,$broadcast 用于向下级播送事宜的,它们在 2.0 版本中已被烧毁了。

由于基于组件树构造的事宜流体式格局偶然让人难以明白,并且在组件构造扩大的历程中会变得愈来愈软弱。

不过我们能够自行完成 dispatch 和 broadcast 要领,用于组件的跨层级通讯。它们完成的关键在于怎样准确找到所要通讯的组件,也就是经由历程婚配组件的 name 选项向下或向上查找组件。

这两个要领都须要通报 3 个参数,第一个参数是要通讯组件的 name 选项值,第二个是自定义事宜称号,第三个是要给通讯组件通报的值。在 dispatch 里,经由历程 while 轮回不断向上查找父组件,直到查找到 componentName 与某个父级组件的 name 选项婚配时完毕,此时改父组件就是要通讯的组件。 broadcast 要领与 dispatch 相似,是经由历程 forEach 轮回向下查找子组件。 末了封装一个 mixins 来便于复用。

// emitter.js 
function broadcast(componentName, eventName, params) {
  this.$children.forEach(child => {
    const name = child.$options.name;
    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}
export default {
  methods: {
    dispatch(componentName, eventName, params) {
      let parent = this.$parent || this.$root;
      let name = parent.$options.name;
      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;
        if (parent) {
          name = parent.$options.name;
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};

经由历程 mixins 混入组件,完成组件间的通讯。

<!-- Parent.vue -->
<template>
  <button @click="handleClick">
    触发事宜
    <Child></Child>
  </button>
</template>
<script>
import Emitter from "../assets/js/emitter.js";
import Child from "./Child.vue";
export default {
  name: "Parent",
  mixins: [Emitter],
  created() {
    this.$on("on-message", message => {
      console.log(message);
    });
  },
  components: {
    Child
  },
  methods: {
    handleClick() {
      this.broadcast("Child", "on-message", "Hello, I am Parent Component");
    }
  }
};
</script>
<!-- Child.vue -->
<template>
  <div></div>
</template>
<script>
import Emitter from "../assets/js/emitter.js";
export default {
  name: "Child",
  mixins: [Emitter],
  mounted() {
    this.$on("on-message", message => {
      console.log(message);
      this.dispatch("Parent", "on-message", "Copy that, I am Child Component");
    });
  }
};
</script>

比拟 Vue 1.0 版本内置的两个 API,自行完成的要领有以下差别:

  • 须要分外传入组件的 name 作为第一个参数;
  • 婚配到组件就会住手轮回,不会冒泡;
  • 通报的值只能是一个参数,假如须要通报多个参数,只能经由历程对象或数组的情势;

其他要领

在 vue 中组件的通讯另有其他的手段,比方:

  1. props$emit
<!-- Parent.vue -->
<template>
  <Child :info="info"
         @update="onUpdateName"></Child>
</template>
<script>
import Child from "./Child.vue";
export default {
  data() {
    return {
      info: {
        name: "stone"
      }
    };
  },
  components: {
    Child
  },
  methods: {
    onUpdateName(name) {
      this.info.name = name;
    }
  }
};
</script>
<!-- Child.vue -->
<template>
  <div>
    <div>{{info.name}}</div>
    <button @click="handleUpdate">update</button>
  </div>
</template>
<script>
export default {
  props: {
    info: {
      type: Object,
      default: {}
    }
  },
  methods: {
    handleUpdate() {
      this.$emit("update", "new-name");
    }
  }
};
</script>
  1. 父组件将本身的要领通报给子组件,子组件挪用要领传数据给父组件
  2. 运用event bus事宜总线
  3. $attrs$listeners

Vue 2.4 新增的 API。
$attrs 包含了父作用域中不作为 prop 被辨认 (且猎取) 的特征绑定 (class 和 style 除外)。
$listeners 包含了父作用域中的 (不含 .native 润饰器的) v-on 事宜监听器。

  1. Vuex 集合式状况治理

写在末了

差别的通讯要领适用于差别的场景,既能够经由历程 Vue 内置的 API 来通讯,也能够经由历程自定义事宜的体式格局完成通讯要领,当应用充足庞杂情况下,就能够运用 Vuex 举行数据治理。

    原文作者:stone
    原文地址: https://segmentfault.com/a/1190000018563193
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞