写在前面
vue 的组件化应该是其最中心的思想了,不管是一个大的页面照样一个小的按钮,都能够被称之为组件。基于 Vue 的开辟,就是在写一个个组件,不管是基本组件照样营业组件,末了都是要拼装在一同。根据组件的层级关联,能够把组件之间的关联归结为:父子关联、隔代关联、兄弟关联,无关联关联。
$ref
、$parent
、$children
基于当前上下文的,能够经由历程 $ref
、$parent
、$children
接见组件实例,能够直接挪用组件的要领或接见数据。个中 $parent
能够接见当前组件的父组件,$children
能够接见当前组件的一切子组件。虽然 $parent
和 $children
都能够猎取组件实例,然则它们没法在跨级或兄弟间通讯,这是它们的瑕玷。
provide
、inject
provide / inject
是 Vue 在 2.2.0 版本后新增的 API。
这对选项须要一同运用,以许可一个先人组件向其一切子孙后代注入一个依靠,不管组件条理有多深,并在起上下游关联建立的时间里一直见效。
也就是在父组件中供应一个值,并且在须要运用的子孙组件中注入改值,即:
// Parent.vue
export default {
provide() {
return {
name: 'Stone'
}
}
}
// Child.vue
export default {
inject: ['name'],
mounted() {
console.log(this.name)
}
}
不仅仅是 Child.vue
,只假如 Parent.vue
的子组件,不管隔若干代,都能够经由历程这个的体式格局注入。 这类多级组件透传的体式格局能够保证单向数据流的清楚性,比方像用户信息如许的全局信息,就能够借助这两个 API 来完成,而没有引入第三方库 vuex
。
替换 Vuex
vuex
是把数据集合治理,然后那里须要就在那里猎取,根据这个思绪,我们能够在根组件 App.vue
中注入全局信息,并且在页面的任何地方运用。
// App.vue
<template>
<div>
<router-view></router-view>
</div>
</template>
export default {
provide() {
return {
userInfo: this.user
}
},
data() {
return {
user: {}
}
},
methods: {
getUserInfo () {
$.ajax('/user/info', (data) => {
this.user = data
})
}
}
}
把全部 App.vue 的实例 this
对外供应, 如许其他页面就能够经由历程 this.userInfo
来猎取用户信息。
<template>
<div>
{{ userInfo }}
</div>
</template>
<script>
export default {
inject: ['userInfo']
}
</script>
$attrs
、$listeners
这两个 API 是 Vue 2.4.0 新增的。
$attrs
,继续一切的父组件属性;
$listeners
,它是一个对象,内里包含了作用在这个组件上的一切监听器。
主要用途就是用在父组件通报数据给子组件或许孙组件。
<!-- Parent.vue -->
<template>
<div id="app">
<child1 :p-child1="child1"
:p-child2="child2"
:p-child3="child3"
v-on:test1="onTest1"
v-on:test2="onTest2">
</child1>
</div>
</template>
<script>
import Child1 from "./Child1.vue";
export default {
data() {
return {
child1: 'child1',
child2: 'child2',
child3: 'child3'
};
},
components: { Child1 },
methods: {
onTest1() {
console.log("test1 running...");
},
onTest2() {
console.log("test2 running");
}
}
};
</script>
<!-- Child1.vue -->
<template>
<div class="child-1">
<p>in child1:</p>
<p>props: {{pChild1}}</p>
<p>$attrs: {{$attrs}}</p>
<hr>
<!-- Child2 组件中能直接触发 test 的缘由在于 Child1 组件挪用 Child2 组件时 运用 v-on 绑定了 $listeners 属性 -->
<!-- 经由历程 v-bind 绑定 $attrs 属性,Child2 组件能够直接猎取到 Parent 组件中通报下来的 props(除 了Child1 组件中 props 声明的) -->
<child2 v-bind="$attrs" v-on="$listeners"></child2>
</div>
</template>
<script>
import Child2 from './Child2.vue';
export default {
props: ['pChild1'],
data() {
return {};
},
inheritAttrs: false,
components: { Child2 },
mounted() {
this.$emit('test1');
}
};
</script>
<!-- Child2.vue -->
<template>
<div class="child-2">
<p>in child2:</p>
<p>props: {{pChild2}}</p>
<p>$attrs: {{$attrs}}</p>
<hr>
</div>
</template>
<script>
export default {
props: ['pChild2'],
data() {
return {};
},
inheritAttrs: false,
mounted() {
this.$emit('test2');
}
};
</script>
dispatch
、 broadcast
这两个 API 是 Vue 1.0 版本的,$dispatch
用于向上级派发事宜,$broadcast
用于向下级播送事宜的,它们在 2.0 版本中已被烧毁了。
由于基于组件树构造的事宜流体式格局偶然让人难以明白,并且在组件构造扩大的历程中会变得愈来愈软弱。
不过我们能够自行完成 dispatch 和 broadcast 要领,用于组件的跨层级通讯。它们完成的关键在于怎样准确找到所要通讯的组件,也就是经由历程婚配组件的 name
选项向下或向上查找组件。
这两个要领都须要通报 3 个参数,第一个参数是要通讯组件的 name 选项值,第二个是自定义事宜称号,第三个是要给通讯组件通报的值。在 dispatch 里,经由历程 while 轮回不断向上查找父组件,直到查找到 componentName 与某个父级组件的 name 选项婚配时完毕,此时改父组件就是要通讯的组件。 broadcast 要领与 dispatch 相似,是经由历程 forEach 轮回向下查找子组件。 末了封装一个 mixins 来便于复用。
// emitter.js
function broadcast(componentName, eventName, params) {
this.$children.forEach(child => {
const name = child.$options.name;
if (name === componentName) {
child.$emit.apply(child, [eventName].concat(params));
} else {
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
export default {
methods: {
dispatch(componentName, eventName, params) {
let parent = this.$parent || this.$root;
let name = parent.$options.name;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
}
}
};
经由历程 mixins 混入组件,完成组件间的通讯。
<!-- Parent.vue -->
<template>
<button @click="handleClick">
触发事宜
<Child></Child>
</button>
</template>
<script>
import Emitter from "../assets/js/emitter.js";
import Child from "./Child.vue";
export default {
name: "Parent",
mixins: [Emitter],
created() {
this.$on("on-message", message => {
console.log(message);
});
},
components: {
Child
},
methods: {
handleClick() {
this.broadcast("Child", "on-message", "Hello, I am Parent Component");
}
}
};
</script>
<!-- Child.vue -->
<template>
<div></div>
</template>
<script>
import Emitter from "../assets/js/emitter.js";
export default {
name: "Child",
mixins: [Emitter],
mounted() {
this.$on("on-message", message => {
console.log(message);
this.dispatch("Parent", "on-message", "Copy that, I am Child Component");
});
}
};
</script>
比拟 Vue 1.0 版本内置的两个 API,自行完成的要领有以下差别:
- 须要分外传入组件的 name 作为第一个参数;
- 婚配到组件就会住手轮回,不会冒泡;
- 通报的值只能是一个参数,假如须要通报多个参数,只能经由历程对象或数组的情势;
其他要领
在 vue 中组件的通讯另有其他的手段,比方:
-
props
、$emit
<!-- Parent.vue -->
<template>
<Child :info="info"
@update="onUpdateName"></Child>
</template>
<script>
import Child from "./Child.vue";
export default {
data() {
return {
info: {
name: "stone"
}
};
},
components: {
Child
},
methods: {
onUpdateName(name) {
this.info.name = name;
}
}
};
</script>
<!-- Child.vue -->
<template>
<div>
<div>{{info.name}}</div>
<button @click="handleUpdate">update</button>
</div>
</template>
<script>
export default {
props: {
info: {
type: Object,
default: {}
}
},
methods: {
handleUpdate() {
this.$emit("update", "new-name");
}
}
};
</script>
- 父组件将本身的要领通报给子组件,子组件挪用要领传数据给父组件
- 运用event bus事宜总线
-
$attrs
、$listeners
Vue 2.4 新增的 API。
$attrs
包含了父作用域中不作为 prop 被辨认 (且猎取) 的特征绑定 (class 和 style 除外)。
$listeners
包含了父作用域中的 (不含 .native 润饰器的) v-on 事宜监听器。
- Vuex 集合式状况治理
写在末了
差别的通讯要领适用于差别的场景,既能够经由历程 Vue 内置的 API 来通讯,也能够经由历程自定义事宜的体式格局完成通讯要领,当应用充足庞杂情况下,就能够运用 Vuex 举行数据治理。