zhaobinglong / myBlog

https://zhaobinglong.github.io/myBlog/
MIT License
7 stars 0 forks source link

vue之组件通信 #95

Open zhaobinglong opened 3 years ago

zhaobinglong commented 3 years ago

组件通信

props

// props的特点是它传递的是简单的字符串数组数据,也可以传递对象
// 但是最佳实践是不要传递对象,不好维护
<blog-post :title="xxxx"></blog-post>

Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})

props展开版

如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post:

post: {
  id: 1,
  title: 'My Journey with Vue'
}

//下面的模板:
<blog-post v-bind="post"></blog-post>

//等价于:
<blog-post
  v-bind:id="post.id"
  v-bind:title="post.title"
></blog-post>

子组件接受父组件通过props异步传递的引用类型的数据

子组件向父组件通信

// 子组件广播事件
this.$emit('eventname', params)

// 父页面中,写一个回调方法来接收
<blog-post  :eventname="getMessage" ></blog-post>

methods: {
  getMessage: function (params) {
      // xxxx
  }
}

父组件通过ref调用子组件

// 父组件

  <component-a ref="comA"></component-a>

<script>
  export default {
    mounted () {
      this.$refs.sayHello();  // 弹窗
    }
  }
</script>

parent/child

指定已创建的实例之父实例,在两者之间建立父子关系。子实例可以用 this.$parent 访问父实例,子实例被推入父实例的 $children 数组中。 image

zhaobinglong commented 3 years ago

全局组件通信

这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

// 新建bus.js
import Vue from 'vue';  
export default new Vue(); 

// 将event bus挂载到vue的原型链上
import Bus from '@/libs/bus.js';  
Vue.prototype.$EventBus = Bus; //全局的事件总线

// 在任意函数中广播事件
this.$EventBus.$emit(事件名,数据);

// 在create生命周期中开始监听广播的事件
this.$EventBus.$on(事件名,data => {});
zhaobinglong commented 3 years ago

跨级组件通信$attrs和$listeners

跨级组件通信主要是为了解决props的缺陷,如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想传递数据给组件C怎么办呢? 如果采用props,我们必须让组件A通过prop传递消息给组件B,组件B在通过prop传递消息给组件C;要是组件A和组件C之间有更多的组件,那采用这种方式就很复杂了。Vue 2.4开始提供了$attrs和$listeners来解决这个问题,能够让组件A之间传递消息给组件C。

场景

项目中有多层组件传参可以使用$attrs,可以使代码更加美观,更加简洁,维护代码的时候更方便。

对比

如果使用普通的父子组件传参prop和$emit,$on会很繁琐;如果使用vuex会大材小用,只是在这几个组件中使用,没必要使用vuex;使用事件总线eventBus,使用不恰当的话,有可能会出现事件多次执行。

一句话

简单点讲就是包含了所有父组件在子组件上设置的属性(除了prop传递的属性、class 和 style )。

demo

Vue.component('C',{
    template:`
      <div>
        <input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)"> </div>
    `,
    methods:{
      passCData(val){
        //触发父组件A中的事件
        this.$emit('getCData',val)
      }
    }
  })
  Vue.component('B',{
    data(){
      return {
        mymessage:this.message
      }
    },
    template:`
      <div>
        <input type="text" v-model="mymessage" @input="passData(mymessage)">
        <!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
        <!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
        <C v-bind="$attrs" v-on="$listeners"></C>
      </div>
    `,
    props:['message'],//得到父组件传递过来的数据
    methods:{
      passData(val){
        //触发父组件中的事件
        this.$emit('getChildData',val)
      }
    }
  })
  Vue.component('A',{
    template:`
      <div>
        <p>this is parent compoent!</p>
        <B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
      </div>
    `,
    data(){
      return {
        message:'hello',
        messagec:'hello c' //传递给c组件的数据
      }
    },
    methods:{
      getChildData(val){
        console.log('这是来自B组件的数据')
      },
      //执行C子组件触发的事件
      getCData(val){
        console.log("这是来自C组件的数据:"+val)
      }
    }
  })
zhaobinglong commented 3 years ago

vuex全局状态管理

在我看来 vuex 就是把需要共享的变量全部存储在一个对象里面,然后将这个对象放在顶层组件中供其他组件使用。这么说吧,将vue想作是一个js文件、组件是函数,那么vuex就是一个全局变量,只是这个“全局变量”包含了一些特定的规则而已

62

zhaobinglong commented 3 years ago

跨级组件通信provide/inject

简单的来说就是在父页面中通过provider来提供变量,然后在子组件中通过inject来注入变量。需要注意的是这里不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据。

// 父页面中提供provide
    provide() {
      return {
         // theme就是要在子组件中注入的对象
        theme: {
            color: this.color  // color就是真正要用的数据,这样的写法不会触发响应式
         }
         theme: this // 触发响应式
     }
      for: "demo"
    },
    components:{
      childOne
    }

// 子组件中注入
    inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    },