Vue组件通信方式 - 青蓝鱼的博客-没有bug的代码是不完美的
侧边栏壁纸
  • 累计撰写 27 篇文章
  • 累计收到 16 条评论
Vue

Vue组件通信方式

admin
2022-07-28 / 0 评论 / 69 阅读 / 正在检测是否收录...

1.props
2.$emit/$on
3.$parent/$children
4.ref
5.$attrs / $listeners
6.$root
7.eventBus
8.vuex

props

父子组件通信
1 props 父传子
1.父组件以属性的方式传值给子组件
2.子组件通过props方式接收数据
父组件核心代码
在父组件中引入子组件并绑定parentData自定义属性

<Child:parentData="parentData"></Child >

<script>
import Child  from '@/components/child'
  export default{
    name:'Parent',
    components:{Child},
    data(){
      return{
        parentData:'我是父组件向子组件传递的值-props方式'
      }
    }
  }
</script>

子组件核心代码

1.在子组件中使用 props 接收父组件传递的数据,
2.props 里的名字跟父组件定义的属性名一致

<template>
  <div>我是父组件的数据:{{parentData}}</div>
  <div>我是父组件传递修改后的数据:{{mydata}}</div>
</template>
<script>
  export default{
    name:'Child',
    props:{
      parentData:{
        type:String,
        default:''
      }
    }
    data(){
      mydata:'俺的小破站 '+ this.parentData
    },
    watch:{
      parentData(newVal){
        this.mydata='俺的小破站 '+ newVal
      }
    },
  }
</script>

Vue的单向数据流机制,子组件不能够直接去修改父组件传递的值修改的,否则能改的话那父组件的值就被污染了。

但是子组件内想要修改父组件传过来的值却不“污染”父组件的话,
可以在子组件内定义一个变量mydata去接收parentData数据,并使用 watch 监听parentData数据的变更

$emit/$on 子传父

1.子组件绑定自定义事件
2.使用 $emit() 触发更改数据

子组件核心代码

<el-button @click="handleEmit">告诉父组件我要更改数据啦</el-button>

<script>
 export default{
   name:'Child',
   methods:{
     handleEmit(){
       this.$emit('triggerEmit','我是来自子组件的数据')
     }
   }
 }
</script>

父组件核心代码

1.父组件定义并绑定子组件传递的triggerEmit事件
2.triggerEmit事件名需跟子组件 $emit() 的事件名

<Child  @triggerEmit="changeData"></Child>

<script>
 import Child from '@/components/child'
 export default{
   name:'Parent',
   components:{Child},
   methods:{
     changeData(name){
       console.log(name) // => 我是来自子组件的数据
     }
   }
 }
</script>

$parent/$children

1.子组件通过 $parent 获得父组件实例
2.父组件通过 $children 获得子组件实例数组
子组件

<template>
  <div>我是子组件</div>
</template>

<script>
export default{
  name:"Child",
  data(){
    return{
      childTitle: '我是子组件的数据'
    }
  },
  methods:{
    childHandle(){
      console.log('我是子组件的方法')
    }
  },
  created(){
    console.log(this.$parent)
    console.log(this.$parent.parentTitle) // => 我是父组件的数据
    this.$parent.parentHandle() // => 我是父组件的方法
  }
}
</script>

this.$parent可以获取到父组件的方法、data的数据等,并可以直接使用和执行。

父组件

<template>
  <div>
    <Child>我是父组件</Child>
  </div>
</template>

<script>
import Child from './child.vue'

export default{
  name: 'parent',
  components:{
    Child
  },
  data(){
    return{
      parentTitle: '我是父组件的数据'
    }
  },
  methods:{
    parentHandle(){
      console.log('我是父组件的方法')
    }
  },
  mounted(){
    console.log(this.$children)
    console.log(this.$children[0].childTitle) // => 我是子组件的数据
    this.$children[0].childHandle() // => 我是子组件的方法
  }
}
</script>

注意钩子的使用
父组件是在 mounted()生命周期中获取子组件实例的,并且获取的实例是一个数组形式

层级发生变化的时候咋办呢 ???

1.源码其实有更高层次的封装,在引用parent到时候抽象一个更高级的方法类似 dispatch,
2.直接指定比较重要的父组件的类型或者名称,在循环查找的时候避免程序的脆弱性

ref

父组件使用 $refs 获得组件实例

<template>
  <div>
    <Child ref="child"></Child >
  </div>
</template>

<script>
import Child from './child.vue'

export default{
  name: 'parent',
  components:{
    Child 
  },
  mounted(){
    console.log(this.$refs.child ) /*组件实例*/
  }
}
</script>

1.注意 钩子 mounted

父组件就可以直接使用this.$refs.xx获取子组件的实例

$attrs/$listeners

  1. 兄弟组件
    核心就是找共同点, 搭建桥梁,中间人,话事人的感觉

$parent
既然是兄弟往上找 总能找到共同的祖先
不常用,可以参考文章上面 的 写法

$root

其实 根也是共享的

eventBus

也是都可以访问的
创建一个Vue实例
作为调度中心 eventBus

import Vue from "vue"
export default new Vue()

需要进行通信的组件中 引入

<template>
  <div>
    <div>我是通信组件A</div>
    <el-button @click="changeName">修改</el-button>
  </div>
</template

<script>
import { EventBus } from "../bus.js"
export default{
  data(){
    return{}
  },
  methods:{
    changeName(){
      EventBus.$emit("editName", '俺的小破站')
    }
  }
}
</script>

监听事件

<template>
  <div>我是通信组件B</div>
</template

<script>
import { EventBus } from "../bus.js"
export default{
  data(){
    return{}
  },
  mounted:{
    EventBus.$on('editName',(name)=>{
      console.log(name) // 俺的小破站!
    })
  }
}
</script>

vuex

1.Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
2.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测 的方式发生变化
3.Vuex 也集成到 Vue 的官方调试工具
npm 安装
npm install vue --S
需要 简单的配置

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

组件 A 模板 重点
< button @click="add()" >传给兄弟组件 -- {{num}}</button>组件 A 传值
重要 的是创建了一个 中间 Vue 实例对象 用于周转
这个就是 事件中心 event bus

 创建一个空的vue实例对象,目的是实现兄弟组件之间的交互
var bus = new Vue({});
    add(){
        this.num++;
    // /当组件需要给其他组件传值时,可以调用总线bus的$emit发射一个事件,需要接收这个传值的组件监听这个事件即可。
        bus.$emit("changeData",this.num);
    },

组件B
核心代码 是监听 组件A 的事件

        created(){
                // 此时this指代自定义的f_view对象,记录该对象
                var _this = this;
                console.log(this);
                //  接收传值的组件需要监听总线bus上的传值事件
            // 通过bus.$on添加总线事件监听,第一个参数是监听的事件类型,第二个参数是事件绑定的函数,事件函数的参数就是发射事件时所传递的参数。
                bus.$on("changeData",function(a){
                    // 此时的this指代vue实例tempVe对象
                    // a接收事件发射传递过来的num值 --- h_view中的数据源 
                    console.log(a);
                    _this.count = a;
                });
            }

并不是所有的项目都适合 用vuex,适合自己的才是最好的
还有哪些可以变向管理状态或者说 页面共享数据
dataset
cookie
storage
极端情况 window 挂载属性

provide/inject
父组件使用 provide 注入数据
子组件使用 inject 使用数据

父组件

export default{
 provide: {
   return{
     provideName: '俺的小破站'
   }
 }
}

分析
provideName这个变量可以提供给它其下的所有子组件,包括曾孙、孙子组件等,只需要使用 inject 就能获取数据

子组件

export default{
  inject: ['provideName'],
  created () {
    console.log(this.provideName) // 俺的小破站
  }
}

优缺点
1.父组件不需要知道哪个组件使用它提供出去的数据
2.子组件不需要知道这个数据从哪里来
3.更加的原生,不会对第三方库产生依赖

缺点:单向的;只能祖辈向子辈传值
vue3 通信方式
props和emit
setup函数可以接受两个参数, prop 和 context ,其中context可以解构出emit slots attrs
利用 emit实例来传参

子组件 代码

<template>
  <el-button @click="handle">子组件  ---点击</el-button>
  <div>我是父组件传过来的数据:{{name}}</div>
</template>

<script>
export default {
  name:"Child",
  props: {
    name: {
      type: String,
      default: ''
    }
  },
  setup(props,{ emit }) {
    console.log(props.name) //  yzs
    function handle() {
      emit('handleClick', 'Vue3  学起来')
    }
    return {
      handle
    }
  }
}
</script>

简要分析
Vue3中没有this的概念了,所以就不会有this.$emit存在,
所以可以从setup传入的context解构出emit实例,从而派发事件给父组件
父组件

<template>
  <Test name="yzs" @handleClick="myClick">父组件---点呀</Test>
</template>

<script>
import Test from './index.vue'
export default {
  name:"parent",
  components: { Test },
  setup() {
    function myClick(name) {
      console.log(name)// Vue3  学起来
    }
    return {
      myClick
    }
  }
}
</script>

ref
Vue3我们可以从Vue中导出 ref 方法,得到子组件的实例

分析
1.通过,在子组件声明ref属性,
2.属性值必须和const btnRef = ref(null)这里声明的变量名一致,否则会报错
3.拿到子组件实例后就可以直接调用组件的sendParent方法了
父组件代码

<template>
  <Test ref="btnRef">
    <el-button @click="click">点我呀</el-button>
  </Test>
</template>
<script>
import { ref } from "vue"
import Test from './index.vue'
export default {
  components: {Test},
  setup() {
    const btnRef = ref(null)
    function click() {
      btnRef.value?.sendParent() //  我是给父组件调用的方法
    }
    return {
      btnRef,
      click
    }
  }
}
</script>

这里使用的btnRef.value?.是可选链操作符语法,
代表?前面的值为true才继续执行后面的语句

使用

<template>
  <slot></slot>
</template>
<script>
export default {
  setup() {
    function sendParent() {
      console.log("我是给父组件调用的方法")
    }
    return {
      sendParent
    }
  }
}
</script>

provide/inject
和vue2差不离儿

父组件

<template>
  <Test></Test>
</template>
<script>
import { provide } from "vue"
import Test from './index.vue'
export default {
  components: {Test},
  setup() {
    //我已经把数据注入到fromParent里面去了
    provide('fromParent', '俺的小破站')
    return {}
  }
}
</script>

子组件

<template>
  <slot></slot>
  <div>我是父组件注入的数据:{{parentData }}</div>
</template>
<script>
import { inject } from "vue"
export default {
  setup() {
    //我来去父组件注入的数据
    let parentData = inject('fromParent')
    return {
      parentData 
    }
  }
}
</script>

子孙组件使用inject获取到父组件注入的数

区别
其实主要就是 Vue3采用了 这个模块化
所以inject 和 provide 需要单独导入

nextTick 是什么

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

官方案例

// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
  // DOM 更新了
})

// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick()
  .then(function () {
    // DOM 更新了
  })

为什么 需要nextTick
由于vue的异步更新策略导致我们对数据的修改不会立刻体现在dom变化上,此时如果想要立即获取更新后的dom状态,就需要使用这个方法

vue在更新DOM时是异步执行的.只要侦听到数据变化,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更.如果同一个watcher被多次触发,只会会被推入到队列中1次.
这种在缓冲时取出重复数据对于避免不必要的计算和DOM操作是非常重要的.
nextTick方法会在队列中加入一个回调函数,确保该函数在前面的DOM 操作完成后才调用

所以当我们想在数据修改后立即看到都DOM执行结果就需要用到nextTick方法

本文共 1897 个字数,平均阅读时长 ≈ 5分钟
0

打赏

评论 (0)

取消