• uniapp和vue组件之间的传值方法(父子传值,兄弟传值,跨级传值,vuex)



    前言

    在做vue项目或者uniapp开发微信小程序时,经常会用到组件之间传值,因此想总结记录下。

    一、父子传值(props / $emit 、ref / $refs)

    1)props / $emit

    父组件通过 props 向子组件传递数据,子组件通过 $emit 触发事件向父组件传递数据。

    // 父组件中:
    
    <template>
      <child-component :message="message" @update-message="updateMessage">child-component>
    template>
    <script>
    export default {
      data() {
        return {
          message: 'Hello World'
        }
      },
      methods: {
        updateMessage(msg) {
          this.message = msg
        }
      }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    // 子组件中:
    
    <template>
      <div>
        <p>{{ message }}p>
        <button @click="updateMessage">Update Messagebutton>
      div>
    template>
    <script>
    export default {
      props: ['message'],
      methods: {
        updateMessage() {
          this.$emit('update-message', 'New Message')
        }
      }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2)ref / $refs

    a)ref 用在组件可以调用组件的属性方法
    b)ref 用在标签可以对标签进行操作
    c) ref属性不能用在uniapp的内置组件上面,只能用在自定义组件上面;用在内置组件比如标签,this.$refs.xxx获取dom是undefind

    父组件想调用子组件的方法:通过 this.$refs(ref)
    ref:这个属性用在子组件上,他的引用就指向了子组件的实例。可以通过实例来访问组件的数据方法

    // 父组件
    <template>
      <view>
        <view class="index-goods-list-con">
          <WaterfallList
            ref="waterFallCon"
            :status="waterfall.status"
            :list="waterfall.list"
            :reset="waterfall.reset"
            @clickCard="onClickCard"
            @handleGood="handleGood"
            @done="onDone"
          ></WaterfallList>
        </view>
      </view>
    </template>
    
    <script>
    import WaterfallList from '@/components/waterfall/waterfall-list.vue'
    export default {
      data() {
        return {}
      },
      methods: {
        handleDailyBestGood(e, good) {
          // 用$refs赋值调用子组件的handleDailyBestNum方法
          this.$refs.waterFallCon.handleDailyBestNum(good) 
          // 调用子组件的属性
          this.$refs.waterFallCon.name // 铁锤妹妹
        },
      },
    }
    </script>
    
    <style></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    // 子组件
    <template></template>
    <script>
    export default {
      data() {
        return {
            renderBasic: '',
            name: '铁锤妹妹'
        }
      },
      methods: {
         //父组件调用的方法
         handleDailyBestNum(good) {  
           //good 是父组件弹窗传递过来的值,我们可以打印看看
              console.log(good)
              this.renderBasic = good
        },
      },
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    二、兄弟传值( $emit / $on )

    借助中间代理, $emit$on
    比如在uniapp项目中使用兄弟传值, uniapp页面通讯官方文档,vue使用同理。

    在Vue.js中,事件总线(Event Bus)是一种实现 发布-订阅模式 的机制。它允许不同组件之间进行解耦的通信。Vue的事件总线是一个空的 Vue 实例,作为中央事件处理器。
    事件总线的事件中心就是这个空的Vue 实例,它充当了消息的中介。通过在组件中引入并使用事件总线,可以在任何组件中发布事件和订阅事件。
    具体来说,事件总线使用$emit 方法发布事件,用 $on 方法订阅事件。通过调用 $emit 方法并指定事件名称,可以向事件总线发布一个事件;而通过调用 $on 方法并指定事件名称和回调函数,可以订阅该事件并在事件发生时执行相应的回调函数。

    a) 说明

    在这里插入图片描述

    在这里插入图片描述

    b) 使用场景

    在商品下单页点击选择优惠券,进入优惠券页面,选择一张适合的,再返回下单页,需要带着优惠券id返回,现在商品 提交订单页和优惠券页面是 跨页面 的。

    c) 使用方法

    //优惠券页面
     clickCoupon (item) {
          if (this.orderSource) {
          // 触发全局自定义事件,传参id
            uni.$emit('updateData', item.id)  
    		uni.navigateBack({
    		   delta: 1
    		})
          }
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
     // 从优惠券跳回下单页 
     // 监听全局自定义事件updateData,接收参数id
        uni.$on('updateData', async function (couponId) {
          that.couponId = couponId
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    d) 注意事项

    • uni.$emituni.$onuni.$onceuni.$off 触发的事件都是 App 全局级别的,跨任意组件,页面,nvue,vue
    • 使用时,注意及时销毁事件监听,比如,页面 onLoad 里边 uni.$on 注册监听,onUnload 里边uni.$off 移除,或者一次性的事件,直接使用 uni.$once 监听。
    • 注意 uni.$on 定义完成后才能接收到 uni.$emit 传递的数据

    三、祖孙或者更深嵌套的组件间传值

    1) provide / inject

    a) 类型

    provide:Object | () => Object
    inject:Array<string> | { [key: string]: string | Symbol | Object }
    
    • 1
    • 2

    b) 详细

    这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
    
    provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的 property。
    
    inject 选项应该是: 一个字符串数组,或一个对象
    
    提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    c) 使用场景

    如果要将祖先组件直接传递给孙子组件,我们要将props逐级传递下去:祖先组件 =》子组件 =》孙子组件,而通过provide/inject,可直接从祖先组件传给孙子组件,即使再嵌套多层也没关系。
    在这里插入图片描述
    d) 使用方法

    <template>
      <div>
        <button @click="changeMsg">祖组件触发</button>
        <h1>祖组件</h1>
        <parent></parent>
      </div>
    </template>
    
    <script>
    import parent from './parent.vue';
    export default {
      data(){
        return{
          obj:{
            name:'JavaScript',
          },
          developer:'布兰登·艾奇',
          year:1995,
          update:'2021年06月',
        }
      },
      provide(){
        return {
          obj: this.obj, // 方式1.传入一个可监听的对象
          developerFn:() => this.developer, // 方式2.通过 computed 来计算注入的值
          // developerFn: this.getDeveloper,  方式3.或者调用一个方法,也能实现响应式 
          year: this.year, // 方式4.直接传值
          app: this, // 方式5. 提供祖先组件的实例 缺点:实例上挂载很多没有必要的东西 比如:props,methods。
        }
      },
      components: {
        parent,
      },
      methods:{
        getShowCartValue() {
           return this.developer
        },
        changeMsg(){
          this.obj.name = 'Vue';
          this.developer = '尤雨溪';
          this.year = 2014;
          this.update = '2021年6月7日';
        },
      },
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    子组件

    <template>
      <div class="wrap">
        <h4>子组件(只做中转)</h4>
        <child></child>
      </div>
    </template>
    
    <script>
    import child from './child.vue';
    export default {
      components:{
        child,
      },
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    孙组件

    <template>
      <div>
        <h5>孙组件</h5>
        <span>名称:{{obj.name}}</span> |
        <span>作者:{{developer}}</span> |
        <span>诞生于:{{year}}</span> |
        <span>最后更新于:{{this.app.update}}</span>
      </div>
    </template>
     
    <script>
    export default {
      computed:{
        developer(){
          return this.developerFn()
        }
      },
      inject:['obj','developerFn','year','app'],
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    未点击按钮,原有状态
    在这里插入图片描述
    当点击按钮触发 changeMsg 方法后,效果如下:
    在这里插入图片描述
    对比一下前后差异:无论点击多少次,孙组件中的诞生于 year 字段永远都是1995 并不会发生变化,通过 方式1、方式2、方式3、方式5传值是可以响应的

    正是官网所提到的:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。

    注意:
    1)另外如果孙子组件更改祖先组件传来的值,会发现祖先组件的值也会跟着变,所以慎用 provide / inject;
    2)Vuex 和 provide/inject 最大的区别:Vuex 中的全局状态的每次修改是可以追踪回溯的,而 provide/inject 中变量的修改是无法控制的。换句话说,不知道是哪个组件修改了这个全局状态。

    所以对于业务庞大而复杂的,还是建议使用vuex~

    2) $attrs

    多层嵌套组件传递数据时,如果只是传递数据,而不做中间处理的话就可以用这个,比如父组件向孙子组件传递数据时使用。

    $attrs:包含父作用域里除 class 和 style 除外的非 props 属性集合。Vue2通过 this.$attrs 或者Vue3通过$attrs.props 获取父作用域中所有符合条件的属性集合,然后还要继续传给子组件内部的其他组件,就可以通过 v-bind="$attrs",进行多层组件的传值。

    //Parent.vue
    <template>
        <child :name="name" age="18" ></child>
    </template
    export default{
        data(){
            return {
                name:"oldCode"
            }
        }
    }
    
    //Child.vue
    <template>
        // 继续传给孙子组件
        <sun-child v-bind="$attrs"></sun-child>
    </template>
    export default{
        props:["name"], // 这里可以接收,也可以不接收
        mounted(){
            // 如果props接收了name 就是 { age:18 },否则就是{ name:"oldCode", age:18 }
            console.log(this.$attrs)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    四、更复杂的结构:vuex

    请移步看我之前写的关于vuex使用总结文章,学习笔记之Vuex总结(Vue状态管理)

    参考:
    provide、inject例子讲解参考这个

  • 相关阅读:
    安卓内部存储不需要申请权限,外部文件需要申请权限
    MyBatis-通用Mapper-tk.mybatis的使用
    Linux笔记 - - gcc/g++和make/Makefile的使用
    Golang的性能优化
    Python安装pycrypto出错处理方法
    【Docker从入门到入土 2】Docker数据管理、网络通信和网络模式 1.0
    常见排序算法
    van-calendar 实现移动端日历效果
    如何解决两个不同服务器,不同账户下的虚拟环境的克隆问题
    【java web】自己捣鼓的班级随机点名(html+servlet)
  • 原文地址:https://blog.csdn.net/weixin_45811256/article/details/127604610