目录
1. provide / inject
2. props(父传子)
3. $emit(子传父)
4. eventBus(全局创建Vue实例)
5. vuex(状态管理)
6. $parent / $children / $refs (获取组件实例)
7. $attrs
8. $listeners
9. Vue.observable
10. mixin
11. 路由传值 /引用数据类型值传递实现父子间数据的共享
组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。
vue组件间的传值方式多种多样,并不局限于父子传值、事件传值这些。
这一组选项需要一起使用,允许一个祖先组件向其所有的后代组件注入一个依赖,不论组件层级有多深,并在其上下游关系成立的时间里始终生效.
// provide 选项应该是一个对象或返回一个对象的函数
// inject 选项应该是:一个字符串数组,或一个对象,对象的 key 是本地的绑定名
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo' (数组形式)
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
或 (对象形式)
var Child = {
inject: {
foo: {
from: 'bar', // 可选
default: 'self defined content' // 默认值
}
},
created () {
console.log(this.foo) // => "bar"
}
// ...
}
需要注意的是: Vue 2.2.1 或更高版本中,inject注入的值会在props和data初始化之前得到
// 使用一个注入的值作为数据(data)的入口 或者 属性(props)的默认值
const Child = {
inject: ['foo'],
data () {
return {
bar: this.foo // 输出bar的值与foo相同
}
}
}
const Child = {
inject: ['foo'],
props: {
bar: {
default () {
return this.foo
}
}
}
}
-----------------------------------------------------------------------------------
// 注入可以通过设置默认值使其变成可选项
const Child = {
inject: {
foo: { // 注意: 此处key值必须是父组件中provide的属性的key
from: 'bar', // 属性是在可用的注入内容中搜索用的 key (字符串或 Symbol), data或者props中的可搜索值
default: 'foo' // 属性是降级情况下使用的 value, 默认值为 ‘foo’
}
}
}
// 与 prop 的默认值类似
const Child = {
inject: {
foo: {
from: 'bar',
default: () => [1, 2, 3] // 默认值为引用类型时,需要使用一个工厂方法返回对象
}
}
}
// 创建组件
Vue.component('props-demo-advanced', {
props: {
age: {
type: Number,
default: 0
}
}
})
// 父组件中注册和使用组件,并传值
// 子组件Child, 负载payload可选
this.$emit('eventName', payload)
// 父组件 Parent
进行事件监听和数据传递。同时Vuex也是基于这个原理实现的
// 三步使用
// 1. 创建
window.$bus = new Vue()
// 2. 注册事件
window.$bus.$on('user_task_change', (payload) => {
console.log('事件触发')
})
// 3. 触发
window.$bus.$emit('user_task_change', payload)
目录结构如下
其中vuex相关的三个文件counter.js、 index.js、 operate.js,内容如下:
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import counter from './counter.js'
import operate from './operate.js'
Vue.use(Vuex)
const state = {
name: 'zhaoyh'
}
const getters = {
getName (state) {
return state.name
}
}
const mutations = {
changeName (state, payload) {
state.name = `${state.name} ${payload}`
}
}
const actions = {
changeNameAsync (context, payload) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('changeName', payload)
}, 1000)
})
}
}
const store = new Vuex.Store({
state,
getters,
mutations,
actions,
modules: {
counter,
operate
}
})
export default store
counter.js
// 模块内方法调用本模块内的方法和数据
const state = {
counterName: 'module > counter > zhaoyh'
}
const getters = {
// state, getters: 本模块内的state, getters
// rootState, rootGetters: 根模块/根节点内的state, getters
// rootState, rootGetters: 同时也包括各个模块中的state 和 getters
getCounterName (state, getters, rootState, rootGetters) {
// rootState.name
// rootState.counter.counterName
// rootState.operate.operateName
console.log(rootState)
// rootGetters.getName,
// rootGetters['counter/getCounterName']
// rootGetters['operate/getOperateName']
console.log(rootGetters)
return state.counterName
}
}
const mutations = {
changeCounterName (state, payload) {
state.counterName = `${state.counterName} ${payload}`
}
}
const actions = {
// context 与 store 实例具有相同方法和属性 ------ important!!!
// context 包括: dispatch, commit, state, getters, rootState, rootGetters
changeCounterNameAsync (context, payload) {
return new Promise((resolve, reject) => {
setTimeout(() => {context.commit('changeCounterName', payload)}, 1000)
})
}
}
export default {
// 注意此处是namespaced,而不是namespace,
// 写错的话程序不会报错,vuex静默无法正常执行
namespaced: true,
state,
getters,
mutations,
actions
}
operate.js
// 模块内方法调用和获取本模块之外的方法和数据
const state = {
operateName: 'module > operate > zhaoyh'
}
// 如果你希望使用全局 state 和 getter
// rootState 和 rootGetter 会作为第三和第四参数传入
// 也会通过 context 对象的属性传入 action
const getters = {
// state, getters: 本模块内的state, getters
// rootState, rootGetters: 根模块/根节点内的state, getters, 包括各个模块中的state 和 getters
getOperateName (state, getters, rootState, rootGetters) {
return state.operateName
}
}
const mutations = {
operateChangeCounterName (state, payload) {
state.counterName = `${state.counterName} ${payload}`
}
}
// 如果你希望使用全局 state 和 getter,
// 也会通过 context 对象的属性传入 action
const actions = {
// context 与 store 实例具有相同方法和属性---- !!important!!!
// context 包括:
// dispatch, commit, state, getters, rootState, rootGetters
operateChangeCounterNameAsync (context, payload) {
return new Promise((resolve, reject) => {
setTimeout(() => {
/*
* 若需要在全局命名空间内分发 action 或提交 mutation,
* 将 {root: true} 作为第三参数传给 dispatch 或 commit 即可
*/
context.commit('counter/changeCounterName', payload, { root: true })
// 或 context.dispatch('counter/changeCounterNameAsync', null, { root: true })
}, 1000)
})
}
}
export default {
// 注意此处是namespaced,而不是namespace,
// 写错的话程序不会报错,vuex静默无法正常执行
namespaced: true,
state,
getters,
mutations,
actions
}
vuex属性、方法的引用方式:
common-operate.vue
{{title}}
- name: {{name}}
- getName: {{getName}}
common-operate2.vue
{{title}}
- name: {{name}}
- getName: {{getName}}
module-operate.vue
{{title}}
- name: {{counterName}}
- getName: {{getCounterName}}
module-operate2.vue
{{title}}
- name: {{counterName}}
- getName: {{getCounterName}}
module-operate3.vue
{{title}}
- name: {{counterName}}
- getName: {{getCounterName}}
$refs 获取对应组件实例,如果是原生dom,那么直接获取的是该dom
$parent / $children 该属性只针对vue组件,获取父/子组件实例
注: 节制地使用 $parent 和 $children - 它们的主要目的是作为访问组件的应急方法。更推荐用 props 和 events 实现父子组件通信
1) 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)
2) 当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外)
可以通过v-bind="$attrs" 将所有父作用域的绑定 (class、style、 ref 除外) 传入内部组件
注: 在创建高级别的组件时非常有用
// 根组件HelloWorld.vue 中引入 ipc.vue
koa=“ipcRef”
name=“go”
id=“id”
ref=“ref”
style=“border: 1px solid red;”
class=“className”
// ipc.vue 中引入 ipcChild.vue
// ipcChild.vue中打印接收到的$attrs
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器
通过 v-on="$listeners" 将父作用域的时间监听器传入内部组件
A、B、C三个组件依次嵌套, B嵌套在A中,C嵌套在B中。 借助 B 组件的中转,从上到下props依次传递,从下至上,$emit事件的传递,达到跨级组件通信的效果。$attrs以及$listeners 的出现解决的的问题,B 组件在其中传递props以及事件的过程中,不必在写多余的代码,仅仅是将 $attrs以及$listeners 向上或者向下传递即可。
mixin (混入) (谨慎使用全局混入, 如有必要可以设计插件并引入使用)
参见官方文档