Vue笔记-组件通信

170 阅读4分钟

父子组件通信

props

prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。
任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。

clipboard.png

  1. 除了传递字符串外,传递其他类型必须 : 动态绑定
  2. 可传递一个函数,在子组件中执行父组件中的函数,并传递参数
  3. 给组件传递非 props 属性(组件内没有接收props)默认绑定到组件的根元素上
// 写法一:数组形式
props:['movies']

// 写法二:类型限制
props:{movies:Array}

// 写法三:提供默认值
props:{
  name:{type:String, required:true, default:'张三'},
  age:{type:Number, required:true, default:20}
}

注意事项:

  1. 对象或数组的 default 必须从一个函数获取,避免组件间引用数据类型共享。default(){ return []}
  2. 不要在子组件直接修改父组件传递来的 props 数据。应该由子组件 $emit 一个事件,让父组件监听到事件并修改数据。或者子组件接收到 props 赋值给子组件的一个 data 数据变量,使用该数据变量去做修改。
  3. prop 会在组件实例创建之前进行验证,所以实例的 property,如:data、computed等,在 default或 validator 函数中是不可用的。

缺陷

如果需要向非子后代传递数据必须多层、逐层传递;兄弟组件间也不能直接 props 通信,必须借助父组件。

自定义事件

子组件向父组件传递数据

  1. 子组件中绑定事件监听
// 触发事件
<button @click='btnClick'>删除父组件中嚣张的p</button>

// 执行事件处理函数向父组件派发事件
methods:{
  btnClick() {
    this.$emit('tellParent', {name:'lxx'})
  }
}
  1. 父组件中监听子组件派发的自定义事件
<Home @tellParent='deleteP'>
<p ref='refP'>谁能把我删除!</p>

methods:{
  deleteP(value) {
    this.$refs.refP.remove()
    console.log(value) // { name:'lxx' } 获取子组件传来的值
  }
}

Vue3 中的变化

emits 选项声明组件可以派发的事件

export default {
  emits:['deleteP'],
  setup(props, {emit}) {
    const value = { name:'lxx' }
    emit('deleteP', value)
  }
}

emits 对象方式,目的是进行参数的验证。验证不通过,参数仍然会被传递,但控制台会报警告:Invalid event arguments: event validation failed for event "addN".

emits:{
  add:null, // 不验证
  sub:null,
  addN:(n)=> {
    if(n > 5) {
      return true
    }
    return false
  }
}

缺陷

只适用于子组件向父组件发送数据,不适合隔代组件或兄弟组件间通信

非父子组件通信

Provide 和 Inject

用于非父子组件之间共享数据:深度嵌套的组件,子孙组件要获取父组件的部分内容。父组件不需要知道哪些子孙组件使用了它 provide 的 property,子孙组件也不需要知道 inject 的 property 来自哪里。

普通数据

// App.vue
provide:{
  sayHi:'hi,lxx!'
}
// 子孙组件
<h2>使用祖父或父组件提供的数据:{{sayHi}}</h2>
export default {
  name:'children',
  inject:['sayHi'] 
}

访问组件实例属性

如果尝试在 provide 中访问组件的实例属性则会报错。因为 provide 对象中的 this 指向 script 作用域中的 this,结果是 undefined(单文件组件开发方式,ES Module是严格模式,严格模式下的全局 thisundefined

// App.vue
provide:{
  colorsLength:this.colors.length // Error Cannot read property 'colors' of undefined
},
data() {
  return {
    colors:['red', 'blue', 'green']
  }
}

要访问组件实例属性,我们需要将 provide 转换为返回对象的函数。因为函数是有作用域的,这里函数的作用域中的 this 指向组件实例。

// App.vue
<button @click=handleAdd>增加一个name</button>
provide() {
  return {
    colorsLength:this.colors.length // ok this指向 vue组件实例
  }
},
data() {
 return {
   colors:['red', 'blue', 'green']
 }
},
methods:{
  handleAdd() {
    this.names.push('ddd')
  }
}

以上代码给 colors 数组 push 新的成员,不会让传递给子孙组件的 colorsLength 响应。

保持响应性

上面代码,如果更改了 colors ,这个变化不会反应在 inject 的 colorsLength 属性。这是因为默认情况下,provide/inject 绑定并不是响应式的。
使用组合式API computed 函数,来实现响应性。当 data 中的 colors 属性被改变,子孙组件 inject 的数据就会响应。

// App.vue
provide() {
  return {
    colorsLength:computed(()=> this.colors.length)
  }
},
data() {
  return {
    colors:['red', 'blue', 'green', 'purple'] // 新push一个purple
  }
}

// 子孙组件
<template>
  <h2>colors数量:{{colorsLength.value}}</h2>
</template>
<script>
  export default {
    inject:['colorsLength'] // colorsLength会变为4
  }
</script>

setup 书写方式

// App.vue
import {computed, provide, ref} from 'vue'  
const colors = ref(['red', 'green', 'blue'])  
provide('message', computed(()=> colors.value.length))

// 子孙组件
<p>Message: {{ message }}</p>  
<script setup>  
  import {inject} from 'vue'  
  const message = inject('message')  
</script>

全局事件总线库-mitt

  1. 封装一下mitt

    // utils/emitter.js
    import mitt from 'mitt'
    const emitter = mitt() // mitt函数返回一个对象
    
    export default emitter
    
  2. 派发一个事件

    import emitter from '@/utils/emitter'
    // 通过点击事件派发一个全局事件
    methods:{
      publishEvent() {
        emitter.emit('aaa', 'hello')
      }
    }
    
  3. 监听派发的事件,在 created 或 mounted 生命周期函数中。

    // 兄弟组件或子组件
    import emitter from '@/utils/emitter'
    export default {
      created() {
        emitter.on('aaa', (payload)=> {
          console.log(payload)
        })
      }
    }
    
  4. 取消事件监听。在 Vue2.x beforeDestory() 或 Vue3.x beforeUnmount() 生命周期钩子函数中取消。

    // 取消全部监听事件
    emitter.all.clear()
    
    // 使用处理程序引用取消
    function onFoo() {}
    emitter.on('foo', onFoo) // listen
    emitter.off('foo', onFoo) // unlisten
    

全局状态管理

Vuex

Vuex就是一个前端的公共仓库(状态管理工具),用于平行组件间的通信(传值,双向数据绑定)

请阅读 Vuex 专栏文章

Pinia

拥有组合式 API的 Vue 状态管理库,主要面向 Vue3.x,使用更加友好。

请阅读 Pinia 专栏文章