props/$emit

最为基础的使用方式,vue官网基础组件中的示例

演示地址

  1. <body>
  2. <div id="blog-posts-events-demo">
  3. <div :style="{ fontSize: postFontSize + 'em' }">
  4. <blog-post v-on:enlarge-text="postFontSize += 0.1" v-for="post in posts" v-bind:key="post.id" v-bind:post="post">
  5. </blog-post>
  6. </div>
  7. </div>
  8. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  9. <script>
  10. Vue.component('blog-post', {
  11. props: ['post'],
  12. template: `
  13. <div class="blog-post">
  14. <h3>{{ post.title }}</h3>
  15. <button v-on:click="$emit('enlarge-text')">
  16. Enlarge text
  17. </button>
  18. <div v-html="post.content"></div>
  19. </div>
  20. `
  21. })
  22. new Vue({
  23. el: '#blog-posts-events-demo',
  24. data: {
  25. posts: [{ title: 'lisan' }, { title: 'wuzhao' }, { title: 'selirea' }],
  26. postFontSize: 1
  27. }
  28. })
  29. </script>
  30. </body>

通过Vue.component全局注册了一个组件blog-post,在根组件上也就是blog-post的父组件上通过v-bindblog-post传递了数据,子组件blog-post通过props收到数据进行渲染,并且可以通过$emit 发出事件携带数据。


$emit/$on

这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。


$attrs/$listeners

多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法–$attrs/$listeners

$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。

下边例子中并没有使用props接收数据,则使用this.$attrs可以把父组件绑定的值传进来,

演示地址

<body>
  <div id="blog-posts-events-demo">
    <div>
      <blog-post v-for="post in posts" v-bind:attr="attr" v-bind:key="post.id" v-bind:post="post">
      </blog-post>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('blog-post', {
      props: ['post'],
      inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
      template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <button @click ="getAttrValue">getAttrValue</button>
      <div></div>
    </div>
  `,
      methods: {
        getAttrValue() {
          console.log(this)
        }
      },
    })
    // 定义一个名为 button-counter 的新组件
    new Vue({
      el: '#blog-posts-events-demo',
      data: {
        posts: [{ title: 'lisan' }, { title: 'wuzhao' }, { title: 'selirea' }],
        attr: 'hello im child component whitout prop',
        postFontSize: 1
      }
    })
  </script>
</body>

如果props接收了某几个父组件绑定的值,则this.$attrs不包含这几个值。
通常配合 inheritAttrs 选项一起使用,可以关闭自动挂载到组件根元素上的没有在props声明的属性
image.png

简单来说:$attrs$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件,比如子组件通过$emit发射上来的。


provide/inject

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

父组件通过provide 提供数据,子组件声明inject会被注入

<body>
  <div id="blog-posts-events-demo">
    <div>
      <blog-post v-for="post in posts" v-bind:attr="attr" v-bind:key="post.id" v-bind:post="post">
      </blog-post>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('blog-post', {
      props: ['post'],
      inject: ['foo'],
      template: `
    <div class="blog-post">
      <button @click ="getAttrValue" >getAttrValue</button>
    </div>
  `,
      created() {
        console.log(this.foo) // => "bar"
      },
      methods: {
        getAttrValue() {
          console.log(this.foo)
        }
      },
    })
    // 定义一个名为 button-counter 的新组件
    new Vue({
      el: '#blog-posts-events-demo',
      provide: {
        foo: 'bar'
      },
      data: {
        posts: [{ title: 'lisan' }, { title: 'wuzhao' }, { title: 'selirea' }],
        attr: 'hello im child component whitout prop',
        postFontSize: 1
      }
    })
  </script>
</body>

image.png


$parent/$children

<body>
  <div id="blog-posts-events-demo">
    <div>
      <blog-post v-for="post in posts" v-bind:attr="attr" v-bind:key="post.id" v-bind:post="post">
      </blog-post>
    </div>
    <button @click="getChildConponent">getChildConponent</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('blog-post', {
      props: ['post'],
      inject: ['foo'],
      template: `
    <div class="blog-post">
      <button @click ="getParentConponent" >getParentConponent</button>
    </div>
  `,
      created() {
        console.log(this.$parent, '----子组件拿到了父组件----')
      },
      methods: {
        getParentConponent() {
          console.log(this.$parent)
        }
      },
    })
    new Vue({
      el: '#blog-posts-events-demo',
      provide: {
        foo: 'bar'
      },
      data: {
        posts: [{ title: 'lisan' }, { title: 'wuzhao' }, { title: 'selirea' }],
        attr: 'hello im child component whitout prop',
        postFontSize: 1
      },
      methods: {
        getChildConponent() {
          console.log(this.$children, '---父组件拿到了直接子组件---')
        }
      }
    })
  </script>
</body>

image.png
直接拿到父组件的实例,可以做想做的一切


vuex


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


当然还有依赖全局变量、本地缓存等这类傻逼不太推荐的用法

总结

无疑的,vuex是最强大的跨组件通信解决方案;
$emit/$on是类似全局变量的方案,不推荐;
$attr/$listeners若乱用只会让代码更乱;
$provide/inject用在业务代码官方不太推荐;
$parent/$children有点取巧了;

常用的就是props/$emit的组合,和vuex