不写这篇文章,我自己都不知道我知道的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里的storemutations关键词即可
总结:使用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功能相似,使用起来更简单,新项目推荐使用。

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐