Vue基础2:传值方法(引申Vue3相关内容)

Description

传值就是为了联动,能够及时准确传值获取值才是王道。

Vue2当中我们常常通过this来使用我们相关的内容。那么Vue3因为走的是set up所以没有this,那么就需要我们去导入引入才能使用。

Valuation Methods 

props和$emit

props是父传子,$emit是子传父。

props的使用

父组件传出值
<tableList ref="table" :options="options" :header-data="Columns" :table-data="Data" />

 父组件的中的子组件标签上我们加入的options,headerData,tableData三个变量是准备要传给子组件的。

子组件收到值
props: {
    tableData: {
      type: Array,
      default: () => {
        return []
      }
    },

    headerData: {
      type: Array,
      default: () => {
        return []
      }
    },

    options: {
      type: Object,
      default: () => {
        return {
          multiSelect: true, // boolean 是否多选
          singleSelect: false, // boolean 是否单个checkbox
          isindex: false, // boolean 是否展示序列号
          stripe: true, // boolean 斑马纹
          border: true, // boolean 纵向边框
          size: 'medium', // String  medium / small / mini  table尺寸
          fit: true, // 自动撑开
          pagination: true, // 是否有分页
          numPage: false
        }
      }
    },

}
 延申
属性/方法作用
type传值的类型
default默认值;如果没有传,就使用
requied值为true/false,代表是否父组件是否必须传
validator(val){}可以判断父组件传过来的值是否有效

props:{
    name:{

        type:String,
        default:'默认为王三',
        required:true,
        validator(val){
            
            return val>0&&val<10
        }

    }

}
延申Vues 的 props 
子组件
<script setup lang='ts'>
let props = defineProps(['父组件属性名1','父组件属性名2'])

</scrpit> 

 $emit的使用

子组件传出值
that.$emit('usbKeyForm', that.usbKeyForm)
父组件收到值
<certificateDialog :ssuingName="ssuingName" @usbKeyForm="usbKeyForm"/>


methods: {
    usbKeyForm(val) {
      this.SerialNumber = val.SerialNumber
    },
}

延申Vue3 
子组件
<script setup lang='ts'>
let $emit = defineEmits(['事件名1','事件名2'])


const handle = () => {

    $emit('事件名1', '祖国强大')

}
</scrpit> 

然后父组件使用自定义事件就可以了,也会有返回值val,上边举例给的是‘祖国强大’ 

思考:为什么两种方法不一样呢?

首先要明白传值的前提,为什么要传值,因为作用域。其实在Vue.js中每个组件都有自己的作用域,而且每个组件定义了一个props属性可以去接收父组件传过来的值,而且定义了type类型就是为了确定父级传过来的值的类型就是我们需要的,当然也可以修饰数据。如果加入required属性,值为true的时候,要求父组件必须要传的。default是设置默认值的,如果父组件没有传我们就使用默认值。所以我们经常看到使用props时候经常使用type+default两个属性搭配使用。

$emits传值原理是通过事件系统进行传值的,因为作用域是上下关系,你要是像下上关系,就得需要闭包传值。

$attrs和$listeners

组件关系A组件---》B组件----》C组件;大家都知道props和$emit是最常用的传值方法,但是仅限A到B或者B到A,如果A到C或者C到A呢?就需要用到$attrs和$listeners了

$attrs的使用

$attrs可以继承父组件里边的属性数据,并且携带下去。

通俗地来说就是如果从父组件传过来的值,没有在子组件中被接收,那么这些值就会被存在$attrs对象中。

 爷组件传出值
<son title="aaaaaaaaaaa" name="李四"/>
子组件收到值(子组件作为中间组件继续传递  v-bind="$attrs"
<template>
  <div>
      儿子组件:{{$attrs.name}}
      <div>
         <sunzi v-bind="$attrs"/>
      </div>
  </div>
</template>
 
<script>
import sunzi from './sunzi.vue';
export default {
  inheritAttrs: true,
  components:{sunzi},
  mounted(){
      console.log(this.$attrs)  //{title:"aaaaaaaaaaa" name:"李四"}
  }
}
孙子组件收到值(孙子组件使用 inheritAttrs: false)
<template>
  <div>孙:{{name}}{{title}}</div>
</template>
 
<script>
export default {
 inheritAttrs: false,
  props: [
    "name",
    "title", //注意props里的参数名称不能改变,必须和父组件传递过来的是一样的
  ],
  data() {
    return {};
  },
  mounted(){
      console.log(this.$attrs) //可直接使用数据或者调用根组件的方法
  }
};
 
 
 

或者

 
<template>
  <div>
      或者:{{$attrs.name}},{{$attrs.title}}
  </div>
</template>
 
<script>
export default {
 inheritAttrs: false,
  data() {
    return {};
  },
  mounted(){
      console.log(this.$attrs)//可直接使用数据或者调用根组件的方法
  }
};
</script>

inheritAttrs: false 的含义是不希望本组件的根元素继承父组件的attribute,同时父组件传过来的属性(没有被子组件的props接收的属性),也不会显示在子组件的dom元素上,但是在组件里可以通过其$attrs可以获取到没有使用的注册属性。

inheritAttrs: false 是不会影响 style 和 class 的绑定

inheritAttrs: true

 inheritAttrs: false  不想继承所有父组件的内容,同时也不在组件根元素dom上显示属性

延申Vue3的useAttrs() 
<template>
<div>
    <el-button :='$attrs'></el-button>
</div>


</tempalte>
<script setup lang='ts'>
import {useAttrs} from 'vue'
let $attrs = useAttrs()

</scrpit> 

$listeners的使用

包含了父作用域中的(不包含.native修饰器的)v-on事件监听器。它可以通过v-on="$listeners"传入内部组件–在创建更高层次的组件时非常有用。

通俗地来说就是可以从孙子组件发送事件到父子组件中。

//Father.vue
<template>
  <div class="father">
    <span>父亲</span>
    <Son @event="event"></Son>
  </div>
</template>

<script>
import Son from "../components/Son.vue";
export default {
  components: {
    Son,
  },
  methods: {
    event() {
      console.log("从GrandSon组件发送过来的数据");
    },
  },
};
</script>

<style>
</style>


//Son.vue
<template>
  <div class="son">
    <span>儿子</span>
    <GrandSon v-on="$listeners"></GrandSon>
  </div>
</template>

<script>
import GrandSon from "../components/GrandSon.vue";
export default {
  components: {
    GrandSon,
  },
};
</script>

<style>
</style>


//GrandSon.vue
<template>
  <div class="grandson">
    <span>孙子</span>
    <button @click="click">发送事件</button>
  </div>
</template>

<script>
export default {
  methods: {
    click() {
      this.$emit("event");
    },
  },
};
</script>

<style>
</style>

普通的emit只能从子组件传到父组件,通过$listeners,我们可以实现从孙子组件把事件传到父子组件,不需要通过emit逐层传递。

$attrs和$listeners主要用来父孙组件之间传值,有的同学说也可以用provide和inject来实现父孙组件之间传值,但需要注意的是provide和inject只能从父组件向孙组件传值,不能从孙组件向父组件传值,这种情况就需要使用$attrs和$listeners来实现了。

中央事件总线 

上面两种方式处理的都是清楚父子组件之间的关系情况下传输数据,而如果不清楚两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式。新建一个Vue事件bus对象,然后通过bus.$emit触发事件,bus.$on监听触发的事件。

 使用方法:可以声明一个全局变量来使用事件中心,但如果在使用 webpack 之类的模块系统,这显然不合适。
每次使用都手动 import 进来也很不方便,所以本文使用 vue-bus 插件。

vue-bus npm地址

vue-bus github地址

安装及引入

npm install vue-bus --save

如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装 vue-bus:

// main.js
import Vue from 'vue';
import VueBus from 'vue-bus';
Vue.use(VueBus);

在组件中使用

假设有两个Vue组件需要通信 ,在 A 组件的按钮上面绑定了点击事件发送一则消息,想通知 B 组件。

// A 组件
<template>
  <div class="wrap">
    <button @click="sendMsg">触发</button>
  </div>
</template>

<script>
export default {
  data(){
    return {
      Amsg:'我是来自A组件的信息',
    }
  },
  methods:{
    sendMsg(){
      this.$bus.emit('changeMsg', this.Amsg );
      this.$bus.emit('doOnce','我只会触发一次');
    }
  },
}
</script>
// B 组件
<template>
  <div>
    <h3>{{Bmsg}}</h3>
  </div>
</template>

<script>
export default {
  data(){
    return {
      Bmsg:'我是B组件',
    }
  },
  methods:{
    getMsg(msg){
      this.Bmsg = msg;
      console.log(msg);
    }
  },
  created(){
    /*
    * 接收事件
    * 这种写法也体现了:A 组件调用 B 组件中的方法。如果只是传递数据,可参考如下简化写法:
    * this.$bus.on('changeMsg', (msg) => { this.Bmsg = msg });
	*/ 
    this.$bus.on('changeMsg', this.getMsg);
    // 此侦听器只会触发一次
    this.$bus.once('doOnce', (txt) => { console.log(txt) });
  },
  // 组件销毁时,移除EventBus事件监听
  beforeDestroy() {
    this.$bus.off('changeMsg', this.getMsg);
  },
}
</script>

在Vue3中的使用 

import mitt from ‘mitt’

const $bus = mitt()

export default $bus

在全局公用的js文件中我们暴漏这个就可以全局使用了

小总结

eventBus 适合小项目、数据被更少组件使用的项目,对于中大型项目数据在很多组件之间使用的情况 eventBus 就不太适用了。eventBus 其实就是一个发布订阅模式,利用 Vue 的自定义事件机制,在触发的地方通过 $emit 向外发布一个事件,在需要监听的页面,通过 $on 监听事件。

provide和inject 

简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。

需要注意的是这里不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据。

概念解析

成对出现:provide和inject是成对出现的

作用:用于父组件向子孙组件传递数据

使用方法:provide在父组件中返回要传给下级的数据,inject在需要使用这个数据的子辈组件或者孙辈等下级组件中注入数据。

使用场景:由于vue有$parent属性可以让子组件访问父组件。但孙组件想要访问祖先组件就比较困难。通过provide/inject可以轻松实现跨级访问父组件的数据

代码解释

写法一
//父组件
provide: {
   user: 'John Doe'
 }
 --------------------
//子组件
inject: ['user']
//使用
console.log(this.user)


缺点:如果我们尝试在此处提供一些组件实例 property,则这将不起作用,

 provide: {
    todoLength: this.todos.length // 将会导致错误 'Cannot read property 'length' of undefined`
  },

写法二
 provide() {
    return {
      todoLength: this.todos.length
    }
  },


缺点:数据不会响应变化

写法三(推荐)

在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 时来定义每个 property。

provide 函数允许你通过两个参数定义 property:

property 的 name ( 类型)
property 的 value

//父组件
import { provide } from 'vue'
export default {
  setup() {
    provide('location', 'North Pole')//location 键(标识符)-- North Pole //值 
    provide('geolocation', {
      longitude: 90,
      latitude: 135
    })
  }
}
// 子组件

延伸Vue3 中的provide与inject 

其实多一步  import {privide,inject} from ‘vue’

其他写法一致

v-model

简述

父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input',val)自动修改v-model绑定的值

代码 

Vue.component('child',{
    props:{
      value:String, //v-model会自动传递一个字段为value的prop属性    },
    data(){      return {
        mymessage:this.value
      }
    },
    methods:{
      changeValue(){        this.$emit('input',this.mymessage);//通过如此调用可以改变父组件上v-model绑定的值      }
    },
    template:`      <p>
        <input type="text" v-model="mymessage" @change="changeValue">
      </p>  })
  Vue.component('parent',{
    template:`      <p>
        <p>this is parent compoent!</p>
        <p>{{message}}</p>
        <child v-model="message"></child>
      </p>    `,
    data(){      return {
        message:'hello'
      }
    }
  })  var app=new Vue({
    el:'#app',
    template:`      <p>
        <parent></parent>
      </p>    `
  })

 

$parent和$children

简述

在组件内部可以直接通过子组件$parent对父组件进行操作,父组件通过$children对子组件进行操作.

代码 

Vue.component('child',{
    props:{
      value:String, //v-model会自动传递一个字段为value的prop属性    },
    data(){      return {
        mymessage:this.value
      }
    },
    methods:{
      changeValue(){        this.$parent.message = this.mymessage;//通过如此调用可以改变父组件的值      }
    },
    template:`      <p>
        <input type="text" v-model="mymessage" @change="changeValue">
      </p>  })
  Vue.component('parent',{
    template:`      <p>
        <p>this is parent compoent!</p>
        <button @click="changeChildValue">test</button >
        <child></child>
      </p>    `,
    methods:{
      changeChildValue(){        this.$children[0].mymessage = 'hello';
      }
    },
    data(){      return {
        message:'hello'
      }
    }
  })  var app=new Vue({
    el:'#app',
    template:`      <p>
        <parent></parent>
      </p>    `
  })

延申Vue3当中的使用 

子组件

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

        <button @click='handler($parent)'>点击父组件给的100元</button>
    </div>


</tempalte>

<script setup lang='ts'>
import {ref} from 'vue'

let money = ref('10')

const handler = ($parent)=> {
    子组件每点击一次数值加上100

    money.value += 100

        父组件每点击一次数值加上100

    $parent.value -= 100
}


</scrpit>

父组件 

<template>
<div>
    <son>
</div>

</template>

<script setup lang='ts'>
    import {ref} from 'vue'    
`   import son from './son.vue'    
let money = ref('10000')


// 暴漏相关的变量  供子组件使用
defineExport({

    money
})
</script>

 vuex处理组件之间的数据交互

如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。

localStorage / sessionStorage 

这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。

通过window.localStorage.getItem(key) 获取数据

通过window.localStorage.setItem(key,value) 存储数据

注意用JSON.parse() / JSON.stringify() 做数据格式转换

localStorage / sessionStorage可以结合vuex,实现数据的持久保存,同时使用vuex解决数据和状态混乱问题。

 大总结

传值方法特点及使用场景
props/$emit确定父子关系的父子组件
$attrs/$listeners确定关系的多层组件之间
中央事件总线不确定关系的组件,但是适合小项目,因为这个是借助于事件
provide/inject通过provide/inject可以轻松实现跨级访问父组件的数据,比较消耗性能
v-model父传子使用,但是有了props为什么我要用这个呢,所以用处不大
$parents/$children在组件内部可以直接通过子组件$parent对父组件进行操作,父组件通过$children对子组件进行操作.
vuex处理公共数据,更组件无关系,专门的数据状态管理。
浏览器缓存为了长时间留存数据使用的,用的也比较多。
$ref/$refs获取实Dom上的数据,从上至下

smallSkill:我们传值时候为了不影响的父值的变化,我们可以用变量接收的方法去解决这个问题(这个面试常常问到)

结束语:

本文就到此结束了,希望大家共同努力,早日拿下 Vue💪💪。

如果文中有不对的地方,或是大家有不同的见解,欢迎指出 🙏🙏。

如果大家觉得所有收获,欢迎一键三连💕💕。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值