vue3的七种组件通信方式

452 阅读3分钟
  • 父传子 props方式
  • 子传父 emit方式
  • 子传父 v-model方式
  • 子传父 ref方式
  • provide/inject
  • 子组件传子组件 借助插件mitt
  • pinia 仓库

父传子 props方式

1.给子组件绑定属性
2.子组件用props接受并加入自己的数据源

父组件

<template>
  <div class="parent-wrap">
    <input type="text" v-model="value" class="form-control" placeholder="请输入">
    <div class="inpt-group-append">
        <button class="btn btn-primary" @click="handle">添加</button>
    </div>
  </div>

  <childVue :list="list"></childVue>                  //1.给子组件绑定属性list
</template>

<script>
import childVue from './child.vue'
import { reactive, toRefs } from '@vue/reactivity'
export default {
    components:{
        childVue
    },
setup(){
    const state=reactive({
        value:'',
        list:['javascrpit','html','css']
    })
  const handle=()=>{
    state.list.push(state.value)
    state.value=''
  }
    return {
        ...toRefs(state),
        handle
    }
}

}
</script>

子组件

<template>
  <ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item">
{{item}}
</li>
  </ul>
</template>

<script>
import { reactive, toRefs } from '@vue/reactivity'
export default {
    props:{                                //2.子组件用props接受并加入自己的数据源
list:Array
    },
setup(props,ctx){
  const  state=reactive({
    list:props.list,
   
  },

  )
  return{
    ...toRefs(state)
  }
}
}
</script>

效果

父组件输入你好点添加 子组件列表会出现 13.png

子传父 emit方式

14.png

子组件

  1. emits:['toList'], 声明toList事件
  2. ctx.emit('toList',state.list) 抛出事件toList
<template>
    <div class="parent-wrap">
      <input type="text" v-model="value" class="form-control" placeholder="请输入">
      <div class="inpt-group-append">
          <button class="btn btn-primary" @click="handle">添加</button>
      </div>
    </div>
  
 
  </template>
  
  <script>
  import { reactive, toRefs } from '@vue/reactivity'
  export default {
   emits:['toList'],                                                    //1
  setup(props,ctx){
      const state=reactive({
          value:'',
          list:['javascrpit','html','css']
      })
    const handle=()=>{
  ctx.emit('toList',state.list)                                     //2
    }
      return {
          ...toRefs(state),
          handle
      }
  }
  
  }
  </script>
  

父组件

  1. @toList="toList" 绑定事件
  2. const toList=(list)=>{ list就是传过来的值 state.list=list}
<template>
    <childVue @toList="toList"></childVue>                           //3
    <ul class="list-group">
  <li class="list-group-item" v-for="item in list" :key="item">
  {{item}}
  </li>
    </ul>
  </template>
  
  <script>
  import { reactive, toRefs } from '@vue/reactivity'
  import childVue from './child.vue'
  export default {
     components:{
        childVue
     },
  setup(){
    const  state=reactive({
      list:[]
    },
    )
    const toList=(list)=>{                                            //4
state.list=list}
    
    return{
      ...toRefs(state),
      toList
    }
  }
  }
  </script>
    

子传父 v-model方式

1.在父传子props基础上 给子组件绑定属性时加v-model: 2.子组件从props收到的数据修改,然后update抛出,这样父组件数据也改变了 3.切记不能直接拿着props.list修改 会变成双向数据流,改变vue单向数据流初衷,并且把握不住数据源流转

父组件

<template>
    <childVue v-model:list="list"></childVue>                   //111111111111111111111111111   
    <ul class="list-group">
  <li class="list-group-item" v-for="item in list" :key="item">
  {{item}}
  </li>
    </ul>
  </template>
  
  <script>
  import { reactive, toRefs } from '@vue/reactivity'
  import childVue from './child.vue'
  export default {
     components:{
        childVue
     },
  setup(){
    const  state=reactive({
      list:['javascrpit','html','css']
    },
    )

    
    return{
      ...toRefs(state),

    }
  }
  }
  </script>

子组件

<template>
    <div class="parent-wrap">
      <input type="text" v-model="value" class="form-control" placeholder="请输入">
      <div class="inpt-group-append">
          <button class="btn btn-primary" @click="handle">添加</button>
      </div>
    </div>
  
 
  </template>
  
  <script>
  import { reactive, toRefs } from '@vue/reactivity'
  export default {
    emits:['update:list'],                     //4.定义update:list  固定语法 list是父组件传过来的
props:[
    'list'
],
  setup(props,ctx){
      const state=reactive({
          value:'',
          
        
      })
    const handle=()=>{
const arr=props.list                                 //2.获取父组件数据
arr.push(state.value)                            //3.修改后的数据arr
ctx.emit('update:list',arr)                            //5.ctx.emit('update:list',arr)   
    }
      return {
          ...toRefs(state),
          handle
      }
  }
  
  }
  </script>

子传父 ref方式

父组件直接拿到子组件dom结构获取数据 1.ref="childref" 2. const childref=ref(null)
3. v-for="item in childref?.list" :key="item"

父组件

<template>
    <childVue  ref="childref"></childVue>                //1111111111111111111111111
    <ul class="list-group">
  <li class="list-group-item" v-for="item in childref?.list" :key="item">//33333333333333
  {{item}}
  </li>
    </ul>
  </template>
  
  <script>
  import { reactive, ref, toRefs } from '@vue/reactivity'
  import childVue from './child.vue'
  export default {
 
     components:{
        childVue
     },
  setup(){   
    const childref=ref(null)           //222222222222222222222222222222
    const  state=reactive({
      list:''
    },
    )

    
    return{
      ...toRefs(state),
      childref
    }
  }
  }
  </script>

子组件

子组件含有数据源list

<template>
    <div class="parent-wrap">
      <input type="text" v-model="value" class="form-control" placeholder="请输入">
      <div class="inpt-group-append">
          <button class="btn btn-primary" @click="handle">添加</button>
      </div>
    </div>
  
 
  </template>
  
  <script>
  import { reactive, toRefs } from '@vue/reactivity'
  export default {

  setup(props,ctx){
      const state=reactive({
          value:'',
          list:['javaScript','html','css']
      })
    const handle=()=>{
    }
      return {
          ...toRefs(state),
          handle
      }
  }

  }
  </script>

provide/inject api传值 只能外层向里传父传子,优点是不管嵌套多深都能传值

父组件

  1.  import { provide ,readonly} from 'vue'    //父组件引入
    
  2.  provide('list',readonly(state.list))     //父组件在setup里面传 randonly(只读,怕子组件对父组件修改)
    
  3.  import { inject } from 'vue'            //子组件引入api
    
  4.  state.list= inject('list')                 //子组件在setup接受
    
<template>
    <div class="parent-wrap">
      <input type="text" v-model="value" class="form-control" placeholder="请输入">
      <div class="inpt-group-append">
          <button class="btn btn-primary" @click="handle">添加</button>
      </div>
    </div>
  <childVue></childVue>
 
  </template>
  
  <script>
  import { reactive, toRefs } from '@vue/reactivity'
import { provide ,readonly} from 'vue'
import childVue from './child.vue'
  export default {
components:{
    childVue
},
  setup(props,ctx){
      const state=reactive({
          value:'',
          list:['javaScript','html','css']
      })
      provide('list',readonly(state.list))                //111111111111111
    const handle=()=>{
    }
      return {
          ...toRefs(state),
          handle
      }
  }

  }
  </script>

子组件

<template>
    <ul class="list-group">
  <li class="list-group-item" v-for="item in list" :key="item">
  {{item}}
  </li>
    </ul>
  </template>
  
  <script>
  import { reactive, ref, toRefs } from '@vue/reactivity'
  import childVue from './child.vue'
import { inject } from 'vue'
  export default {
 
     components:{
   
     },
  setup(){   
    const  state=reactive({
      list:[],
    },
    )
  state.list= inject('list')        //22222222222222222222222222222
    
    return{
      ...toRefs(state),
   
    
    }
  }
  }
  </script>

子组件传子组件 借助插件mitt 像vue2中的eventbus

mitt官方使用方法

1. npm install --save mitt

2. 创建bus.js文件

import mitt from 'mitt'
const emitter = mitt()
export default emitter

3. 子组件1引入并抛出值

add是名字,state.value是值

  import emitter from './bus'
  emitter.emit('add',state.value)  (在setup里面接收)

4. 子组件2引入并接收值

add是名字,e是接收的参数

import emitter from './bus'
 emitter.on('add',(e)=>{
state.list.push(e)
    })

pinia 仓库 相当于vue2中的vuex

  1. npm install pinia查看pinia文档
    2.在src 创建store文件夹和index.js文件

15.png index.js代码如下

import { createPinia } from 'pinia'
const store = createPinia()
export default store

main.js引入

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

createApp(App).use(store).mount('#app')

3.index同级再定义一个文件 index是仓库 新定义出来文件的像是仓库分类出来的一块区域

16.png user.js区域仓库代码如下

import { defineStore } from 'pinia'
export const useStore = defineStore('main', {    //userStore是区域名 任取
  id:'user',
  state(){
    return {
        //数据源
        name:'papapiu'
    }
  }
})

4.现在可以拿到页面去用(区域仓交互跟拿到页面去用一样,引入,调用拿到区域上下文对象,直接访问)

import {useStore} from '../store/user.js'//饮热
 const  userStore=useStore()//useStore调用结果就是取出来的厂库区域(上下文对象) 可以在页面userStore.name访问

5.组件通信时,可以将任一组件数据写一个区域仓库,任一组件都可以拿得到。