一对一传值
父子
子 -> 父 $emit
参数:
{string} eventName
[...args]

// child.vue
<script>
export default {
methods: {
emit() {
this.$emit('onChildChange', 'dataFromChild')
}
}
}
</script>
// parent.vue
<template>
<child @onChildChange="onChildChange" />
</template>
export default {
methods: {
onChildChange(e) {
console.log(e); // dataFromChild
}
}
}
子 -> 父 $parent
当前vue实例通过$parent指针可以获得父组件实例
同理,你可以通过parent一直向上获取data,直到根节点

// child.vue
<script>
export default {
methods: {
emit() {
console.log(this.$parent.parentData); // a
this.$parent.parentData = 'b';
console.log(this.$parent.parentData); // b
}
}
}
</script>
// parent.vue
<template>
<child />
</template>
<script>
export default {
data() {
return {
parentData: 'a',
}
}
}
</script>
父 -> 子 $ref
通过$ref可以很方便的拿到组件实例,例如组件的属性和方法等等。

// parent.vue
<template>
<child ref="child"/>
</template>
<script>
export default {
created() {
console.log(this.$refs.child) // -> child实例
console.log(this.$refs.child.a) // -> child的a属性值
}
}
</script>
父 -> 子 prop
prop是单向数据流

// parent.vue
<template>
<child :father-a="father.A" :father-b="father.B"></child>
// 等价于 <child v-bind="father"></child>
</template>
/*
father:{
A:'A',
B: { b: 'b' },
}
*/
/*
下行绑定,父组件的更新会下流到子组件,但反过来不行 */
<template>
{{fatherA}} // a
</template>
prop:{
fatherA:{
type:"String",
default:'default data',
},
fatherB:{
type:"Object",
default: function(){
return {};
},
}
}
-
type支持的属性
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
当你试图在子组件中修改父组件的数值时,就会发生错误,因为prop时单向的数据流,只允许父组件流向子组件。
当你的变量需要双向传值的时候,就需要用到.sync修饰符。
父子双向 .sync
.sync修饰符是v-bind:title="prop" v-on:update:title="prop = $event"的简写形式,当prop需要双向绑定时,可以用这种形式触发更新

js写法
// parent.vue
<template>
<child :parent-flag.sync="parentFlag" />
</template>
<script>
export default {
data() {
return {
parentFlag: false,
}
}
}
</script>
// child.vue
<template>
<div v-if="childFlag"></div>
</template>
<script>
export default {
prop: {
parentFlag: {
type: Boolean,
default: false,
},
},
methods: {
changeFlag() {
// this.parentFlag = true; // error
this.$emit('update:parentFlag', true);
}
}
}
</script>
ts版本
// Child.vue
<template>
<main v-if="flag">
<button @click="closeChild">点击关闭</button>
</main>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
@Component
export default class Child extends Vue {
@Prop() private flag = false;
closeChild() {
this.$emit("update:flag", false);
}
}
</script>
// Father.vue
<template>
<div>
<Child :flag.sync="flag"></Child>
<button @click="showChild">点击打开</button>
</div>
</template>
<script lang="ts">
import Child from "./Child.vue";
import { Component, Prop, Vue, Provide } from "vue-property-decorator";
@Component({
components: {
Child
}
})
export default class Father extends Vue {
@Provide() flag = false;
showChild() {
this.flag = true;
}
}
</script>
祖孙 (provide, inject)
通过provide向子组件注入依赖,子组件(无论嵌套多少层)通过inject属性都可以获取最外层的参数。

js写法
// parent.vue
<template>
<child />
</template>
<script>
export default {
provide() {
return {
msg: 'data from parent',
}
}
}
</script>
// child.vue
<template>
<div>{{ msg }}</div>
</template>
<script>
export default {
inject: ['msg']
}
</script>
ts写法
// parent.vue
<template>
<main>
<Child />
</main>
</template>
<script lang="ts">
import Child from "./Child.vue";
import { Component, Vue, Provide } from "vue-property-decorator";
@Component({
components: {
Child
}
})
export default class HelloWorld extends Vue {
// 注入依赖
@Provide() msg = "msg from father";
}
</script>
//child.vue
<template>
<main>
<grand-son />
</main>
</template>
<script lang="ts">
import GrandSon from "./GrandSon.vue";
import { Component, Vue } from "vue-property-decorator";
@Component({
components: {
GrandSon
}
})
</script>
// GrandSon.vue
<template>
<main>
{{ msg }}
</main>
</template>
<script lang="ts">
import Child from "./Child.vue";
import { Component, Vue, Inject } from "vue-property-decorator";
@Component({
components: {
Child
}
})
export default class GrandSon extends Vue {
@Inject() private msg!: string;
}
</script>
兄弟 (子 -> 父 -> 兄)
兄弟组件传值,除了下面要讲的一对多方法,简单的情况还可以使用子组件向父组件流动,再由父组件传递给兄弟组件。

// parent.vue
<template>
<main>
<com-a @onDataChange="onDataChange"/>
<com-b :prop="data"/>
</main>
</template>
<script>
import comA from './comA.js'
import comB from './comB.js'
export default {
data() {
return {
data: ''
}
},
methods: {
onDataChange(e) {
// 当数据变更时,向组件B传递
this.data = e;
}
}
}
</script>
// comA.vue
<script>
export default {
created() {
// 向父组件传递数据变更
this.$emit('onDataChange', 'data from component A')
}
}
</script>
// comB.vue
<template>
<main> {{ data }} </main>
</template>
<script>
export default {
prop: {
data: {
type: String,
default: ''
}
}
}
</script>
一对多传值
eventBus
原理:引入一个vue实例作为一个中间件,通过on进行事件的分发和监听。
emit, 区别在于一个调用当前vue实例,eventBus调用中间vue实例。

// bus.js
export default new Vue();
// A.vue
<script>
import bus from './bus.js'
export default {
methods: {
onChange() {
// onDataChange事件,值是'changed'字符串
// 值将作为$on中定义回调函数的形参
bus.$emit('onDataChange', 'changed');
}
}
}
</script>
// B.vue
<script>
import bus from './bus.js'
export default {
created() {
this.$on('onDataChange', (e) => {
// 每一次$emit分发’onDataChange',都会触发回调
console.log(e);
})
}
}
</script>
// C.vue
<script>
import bus from './bus.js'
export default {
created() {
this.$on('onDataChange', (e) => {
// 支持一对多传值,每一次$emit,将会触发所有订阅的回调
console.log(e);
})
}
}
</script>
vuex
相对于eventbus, vuex有一套更加完善的语法,state存储变量,getters作为计算属性,mutation中写所有对state操作的方法,actions支持异步操作,更加能够适应一些复杂的业务逻辑。

基本语法
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
const store = new Vuex.Store({
state: {
// 这里写要存储的数据
count: 1,
},
getters: {
// 类似于computed计算属性
doubleCount: state => {
return state.count * 2;
}
},
mutations: {
// 这里写操作state的所有方法
setCount(state, count) {
state.count = count
}
},
actions: {
// 这里写所有的异步方法
setCountAsync({ commit }, count) {
setTimeout(() => {
commit('setCount', count)
})
}
}
})
// hello.vue
import store from './store.js';
export default {
store,
created() {
console.log(store.getters.doubleCount) // -> 2
console.log(store.state.count) // -> 1
}
methods: {
submit() {
store.dispatch('setCount', 2);
}
}
}
vuex 模块化
vuex的modules属性还支持了模块化,可以对全局变量进行拆分 ,适应复杂的业务场景。
具体语法可以参照官方文档,在这里就不赘述。

对比
模块化 | 可封装 | 包体积 | 持久化 | 可响应 | |
---|---|---|---|---|---|
vuex | √ | √ | 大 | 配合localstorage | √ |
emitBus | × | × | 小 | × | √ |
- 总结
- vuex 由于可以注册模块和封装方法,因此适合复杂的业务传值情况,同时可以配合localstorage做持久化处理。
- eventbus 适用于简单的一对多传值情景,注重于值本身的传递。
router-view 路由传值
params
- 在浏览器里不显示参数
- 基于router,刷新就消失了(类似于post)
- 基于name设置,否则params会被忽视掉
this.$route.push({
name:'child',
params:{
options:'data'
}
})
// router.js
// :options 要严格保持一致,url类似于 /child/data
// 否则刷新后页面数据将会丢失
{
path:"child/:options",
name:'child',
component:child
}
query
- 在浏览器里显示参数
- 基于url,刷新还在(类似于get)
- 在编程式的导航中,如果使用了path,params会被忽略
// 父组件
this.$route.push({
path:'/child',
query:{
options:'data'
}
})
// 跳转后组件
this.$route.query.options;