vue组件传值的13种方式
不写这篇文章,我自己都不知道我知道的vue组件传值的方式竟然有13种之多,其实静下来想想,常用的也有五六种,先上一张总结图:
1、父组件传给子组件
在子组件里定义一个props,即props:[‘msg’],msg可以是对象也可以是基本数据类型
如果你想定义一个默认值,即 props:{msg: {type: String, default: ‘hello world’}},
若默认值是对象类型:props: { msg: { type: Object, default: () => { return { name: ‘dan_seek’ } } }}
需要注意的是这种传值是单向的,你无法改变父组件的值(当然引用类型例外);而且如果直接修改props的值会报一个警告。
推荐的写法是在data()里重新定义一个变量(见Children.vue),并把props赋值给它,当然计算属性也行。
Children.vue
<template>
<section>
父组件传过来的消息是:{{myMsg}}
</section>
</template>
<script>
export default {
name: "Children",
components: {},
props:['msg'],
data() {
return {
myMsg:this.msg
}
},
methods: {}
}
</script>
Parent.vue
<template>
<div class="parent">
<Children :msg="message"></Children>
</div>
</template>
<script>
import Children from '../components/Children'
export default {
name: 'Parent',
components: {
Children
},
data() {
return {
message:'hello world'
}
},
}
</script>
2、子组件传给父组件
这里需要使用自定义事件,在子组件中使用this.$emit(‘myEvent’) 触发,然后在父组件中使用@myEvent监听
Children.vue
<template>
<section>
<br>
<div @click="clickme">click me</div>
</section>
</template>
<script>
export default {
name: "Children",
components: {},
data() {
return {
childNum:0
}
},
methods: {
clickme(){
// 通过自定义事件addNum把值传给父组件
this.$emit('addNum',this.childNum++)
}
}
}
</script>
Parent.vue
<template>
<div class="parent">
这里是计数:{{parentNum}}
<Children-Com @addNum="getNum"></Children-Com>
</div>
</template>
<script>
import ChildrenCom from '../components/Children'
export default {
name: 'Parent',
components: {
ChildrenCom
},
data() {
return {
parentNum: 0
}
},
methods:{
// childNum是由子组件传入的
getNum(childNum){
this.parentNum = childNum
}
}
}
</script>
3、兄弟组件间传值
运用自定义事件 e m i t 的触发和监听能力,定义一个公共的事件总线 e v e n t B u s ,通过它作为中间桥梁,我们就可以传值给任意组件了。而且通过 e v e n t B u s 的使用,可以加深 emit的触发和监听能力,定义一个公共的事件总线eventBus,通过它作为中间桥梁,我们就可以传值给任意组件了。而且通过eventBus的使用,可以加深 emit的触发和监听能力,定义一个公共的事件总线eventBus,通过它作为中间桥梁,我们就可以传值给任意组件了。而且通过eventBus的使用,可以加深emit的理解。
EventBus.js
import Vue from 'vue'
export default new Vue()
Children1.vue
<template>
<section>
<div @click="pushMsg">push message</div>
<br>
</section>
</template>
<script>
import eventBus from './EventBus'
export default {
name: "Children1",
components: {},
data() {
return {
childNum:0
}
},
methods: {
pushMsg(){
// 通过事件总线发送消息
eventBus.$emit('pushMsg',this.childNum++)
}
}
}
</script>
Children2.vue
<template>
<section>
children1传过来的消息:{{msg}}
</section>
</template>
<script>
import eventBus from './EventBus'
export default {
name: "Children2",
components: {},
data() {
return {
msg: ''
}
},
mounted() {
// 通过事件总线监听消息
eventBus.$on('pushMsg', (children1Msg) => {
this.msg = children1Msg
})
}
}
</script>
Parent.vue
<template>
<div class="parent">
<Children1></Children1>
<Children2></Children2>
</div>
</template>
<script>
import Children1 from '../components/Children1'
import Children2 from '../components/Children2'
export default {
name: 'Parent',
components: {
Children1,
Children2
},
data() {
return {
}
},
methods:{
}
}
</script>
github上还有一个开源vue-bus库,可以参考下: https://github.com/yangmingshan/vue-bus#readme
4、路由间传值
i.使用问号传值
A页面跳转B页面时使用 this.$router.push(‘/B?name=danseek’)
B页面可以使用 this.$route.query.name 来获取A页面传过来的值
上面要注意router和route的区别
ii.使用冒号传值
配置如下路由:
{
path: '/b/:name',
name: 'b',
component: () => import( '../views/B.vue')
},
在B页面可以通过 this.$route.params.name 来获取路由传入的name的值
iii.使用父子组件传值
由于router-view本身也是一个组件,所以我们也可以使用父子组件传值方式传值,然后在对应的子页面里加上props,因为type更新后没有刷新路由,所以不能直接在子页面的mounted钩子里直接获取最新type的值,而要使用watch。
<router-view :type="type"></router-view>
// 子页面
......
props: ['type']
......
watch: {
type(){
// console.log("在这个方法可以时刻获取最新的数据:type=",this.type)
},
},
5、使用$ref传值
通过$ref的能力,给子组件定义一个ID,父组件通过这个ID可以直接访问子组件里面的方法和属性
首先定义一个子组件Children.vue
<template>
<section>
传过来的消息:{{msg}}
</section>
</template>
<script>
export default {
name: "Children",
components: {},
data() {
return {
msg: '',
desc:'The use of ref'
}
},
methods:{
// 父组件可以调用这个方法传入msg
updateMsg(msg){
this.msg = msg
}
},
}
</script>
然后在父组件Parent.vue中引用Children.vue,并定义ref属性
<template>
<div class="parent">
<!-- 给子组件设置一个ID ref="children" -->
<Children ref="children"></Children>
<div @click="pushMsg">push message</div>
</div>
</template>
<script>
import Children from '../components/Children'
export default {
name: 'parent',
components: {
Children,
},
methods:{
pushMsg(){
// 通过这个ID可以访问子组件的方法
this.$refs.children.updateMsg('Have you received the clothes?')
// 也可以访问子组件的属性
console.log('children props:',this.$refs.children.desc)
}
},
}
</script>
6、使用依赖注入传给后代子孙曾孙
假设父组件有一个方法 getName(),需要把它提供给所有的后代
provide: function () {
return {
getName: this.getName()
}
}
provide 选项允许我们指定我们想要提供给后代组件的数据/方法
然后在任何后代组件里,我们都可以使用 inject
来给当前实例注入父组件的数据/方法:
inject: ['getName']
Parent.vue
<template>
<div class="parent">
<Children></Children>
</div>
</template>
<script>
import Children from '../components/Children'
export default {
name: 'Parent',
components: {
Children,
},
data() {
return {
name:'dan_seek'
}
},
provide: function () {
return {
getName: this.name
}
},
}
</script>
Children.vue
<template>
<section>
父组件传入的值:{{getName}}
</section>
</template>
<script>
export default {
name: "Children",
components: {},
data() {
return {
}
},
inject: ['getName'],
}
</script>
7、祖传孙 $attrs
正常情况下需要借助父亲的props作为中间过渡,但是这样在父亲组件就会多了一些跟父组件业务无关的属性,耦合度高,借助$attrs可以简化些,而且祖跟孙都无需做修改
GrandParent.vue
<template>
<section>
<parent name="grandParent" sex="男" age="88" hobby="code" @sayKnow="sayKnow"></parent>
</section>
</template>
<script>
import Parent from './Parent'
export default {
name: "GrandParent",
components: {
Parent
},
data() {
return {}
},
methods: {
sayKnow(val){
console.log(val)
}
},
mounted() {
}
}
</script>
Parent.vue
<template>
<section>
<p>父组件收到</p>
<p>祖父的名字:{{name}}</p>
<children v-bind="$attrs" v-on="$listeners"></children>
</section>
</template>
<script>
import Children from './Children'
export default {
name: "Parent",
components: {
Children
},
// 父组件接收了name,所以name值是不会传到子组件的
props:['name'],
data() {
return {}
},
methods: {},
mounted() {
}
}
</script>
Children.vue
<template>
<section>
<p>子组件收到</p>
<p>祖父的名字:{{name}}</p>
<p>祖父的性别:{{sex}}</p>
<p>祖父的年龄:{{age}}</p>
<p>祖父的爱好:{{hobby}}</p>
<button @click="sayKnow">我知道啦</button>
</section>
</template>
<script>
export default {
name: "Children",
components: {},
// 由于父组件已经接收了name属性,所以name不会传到子组件了
props:['sex','age','hobby','name'],
data() {
return {}
},
methods: {
sayKnow(){
this.$emit('sayKnow','我知道啦')
}
},
mounted() {
}
}
</script>
显示结果
父组件收到
祖父的名字:grandParent
子组件收到
祖父的名字:
祖父的性别:男
祖父的年龄:88
祖父的爱好:code
8、孙传祖
借助$listeners中间事件,孙可以方便的通知祖,代码示例见7
9、$parent
通过 p a r e n t 可以获父组件实例,然后通过这个实例就可以访问父组件的属性和方法,它还有一个兄弟 parent可以获父组件实例,然后通过这个实例就可以访问父组件的属性和方法,它还有一个兄弟 parent可以获父组件实例,然后通过这个实例就可以访问父组件的属性和方法,它还有一个兄弟root,可以获取根组件实例。
语法:
// 获父组件的数据
this.$parent.foo
// 写入父组件的数据
this.$parent.foo = 2
// 访问父组件的计算属性
this.$parent.bar
// 调用父组件的方法
this.$parent.baz()
于是,在子组件传给父组件例子中,可以使用this.$parent.getNum(100)传值给父组件。
10、使用observable或者reactive(仿vuex的Api)
i.定义全局变量。新建一个文件/store/index.js,这个文件可以直接复制使用
const data = {
indexNeedRefresh: false,
}
// 定义 mutations, 修改属性
export const mutations = {
async setiIndexNeedRefresh(flag=false) {
store.indexNeedRefresh = flag
},
}
// VUE2使用observable
import Vue from 'vue'
// 通过Vue.observable创建一个可响应的对象
export const store = Vue.observable(data)
如果是vue3,把最后两行代码换成下面两行
// VUE3使用reactive
import { reactive } from 'vue'
// 通过reactive创建一个可响应的对象
export const store = reactive(data)
ii.其他页面调用或组件调用
tabBar2.vue修改indexNeedRefresh
的值
<script>
//1、先引入
import {mutations} from '@/store/index.js'
.......
methods: {
async saveData(data) {
const db = uniCloud.database();
// 调用接口保存了一条数据
await db.collection('teacher-schedule').add(data)
uni.showToast({
title: '保存成功'
})
// 2、调用mutations修改
mutations.setiIndexNeedRefresh(true)
}
}
.......
</script>
tabBar1.vue,如果发现indexNeedRefresh
变成了true才调接口查询数据
<script>
import {
store,
mutations
} from '@/store/index.js'
.......
onShow() {
// 有更新才重新拉取数据
if (store.indexNeedRefresh) {
// 如果课表被编辑后需要重新获取课表
this.$refs.timeTable.getData()
// 调用mutations修改
mutations.setiIndexNeedRefresh(false)
}
},
.......
</script>
关注tabBar1.vue和tabBar2.vue里的store
和mutations
关键词即可
总结:使用store.属性
取值,使用mutations
里定义的方法修改值
11、sessionStorage传值
sessionStorage 是浏览器的全局对象,存在它里面的数据会在页面关闭时清除 。运用这个特性,我们可以在所有页面共享一份数据。
语法:
// 保存数据到 sessionStorage
sessionStorage.setItem('key', 'value');
// 从 sessionStorage 获取数据
let data = sessionStorage.getItem('key');
// 从 sessionStorage 删除保存的数据
sessionStorage.removeItem('key');
// 从 sessionStorage 删除所有保存的数据
sessionStorage.clear();
注意:里面存的是键值对,只能是字符串类型,如果要存对象的话,需要使用 let objStr = JSON.stringify(obj) 转成字符串然后再存储(使用的时候 let obj = JSON.parse(objStr) 解析为对象)。
这样存对象是不是很麻烦呢,推荐一个库 good-storage ,它封装了sessionStorage ,可以直接用它的API存对象
// localStorage
storage.set(key,val)
storage.get(key, def)
// sessionStorage
storage.session.set(key, val)
storage.session.get(key, val)
更多请移步:https://github.com/ustbhuangyi/storage#readme
12、vuex
这里我也不打算介绍这个大名鼎鼎的vuex怎么用,因为要把它写清楚篇幅太长了…
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您也许不需要使用 Vuex。
13、pinia
Pinia和Vuex的作用是一样的,它也充当的是一个存储数据的作用,存储在pinia的数据允许我们在各个组件中使用。一般被认为Pinia就是Vuex的升级版,与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。总之就是与vuex功能相似,使用起来更简单,新项目推荐使用。
更多推荐
所有评论(0)