写在前面
- 知识就是力量。 ——安琪拉
- 知识就是力量,但更重要的是运用知识的技能。 —— 培根
- 本文主要介绍vue组件间常见的通信方式,边界情况及插槽的使用。
何为组件化?
vue组件系统提供了一种抽象,让我们可以使用独立可复用的组件来构建大型应用,任意类型的应用界 面都可以抽象为一个组件树。组件化能提高开发效率,方便重复使用,简化调试步骤,提升项目可维护 性,便于多人协同开发。
通常一个应用会以一棵嵌套的组件树的形式来组织:例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。如下图所示:
1.组件常用通信方式
- 父传子:props
- 子传父:自定义事件 event
- 跨层级通信:事件总线 Bus
- 跨层级通信:专为 Vue.js 应用程序开发的状态管理模式 vuex
1.1 props
// 父组件
// Child1组件
我是子组件1
{{msg}}
结果:
1.2 event
// 父组件
组件通信
Child2传过来的值是: {{child2Info}}
// Child2组件
我是子组件2
结果:
1.3 Bus
任意两个组件之间传值可以用事件总线的方式。事件总线其实利用的就是设计模式中观察者模式。其核心实现方式如下:
class Bus {
constructor () {
this.callbacks = {}
}
$on (name, fn) {
this.callbacks[name] = this.callbacks[name] || []
this.callbacks[name].push(fn)
}
$emit (name, args) {
if (this.callbacks[name]) {
this.callbacks[name].forEach(cb => cb(args))
}
}
}
export default Bus
使用方式:
1.先在main.js中注册$bus
import Vue from 'vue'
import App from './App'
import router from './router'
import Bus from './bus'
Vue.prototype.$bus = new Bus()
// 实际上Vue已经实现了事件总线,我们使用的时候不用自定义Bus,直接使用如下方式引入即可
// Vue.prototype.$bus = new Vue()
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
components: { App },
template: ' '
})
2.在需要接受值的地方注册监听事件,这里已 Child1 与 Child2 之间的传值为例:
Child2
我是子组件2
Child1
我是子组件1
结果:
点击Child1的button后,Child2打印:'Child1 点击了, 信息是: 子组件1通过bus传来的值'
1.4 vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。这里就不做举例啦~下期做个完成剖析给大家。
想学习的移步官网:vuex中文官方网站
2.边界情况
大多数情况下,并不推荐使用边界情况,而是使用更优的替代方案去代替边界情况。但是了解边界情况可以在理解源码封装组件化仓库中得到很大的便利。正如官网所说:在绝大多数情况下,我们最好不要触达另一个组件实例内部或手动操作 DOM 元素。不过也确实在一些情况下做这些事情是合适的。
2.1 $root——访问根实例
在每个 new Vue 实例的子组件中,其根实例可以通过 $root property 进行访问。
// Vue 根实例
new Vue({
data: {
foo: 1
},
computed: {
bar: function () { /* ... */ }
},
methods: {
baz: function () { /* ... */ }
}
})
// 所有的子组件都可以将这个实例作为一个全局 store 来访问或使用:
// 获取根组件的数据
this.$root.foo
// 写入根组件的数据
this.$root.foo = 2
// 访问根组件的计算属性
this.$root.bar
// 调用根组件的方法
this.$root.baz()
2.2 $parent——访问父级组件实例
兄弟组件之间通信可通过共同祖辈搭桥
// brother1
this.$parent.$on('foo', handle)
// brother2
this.$parent.$emit('foo')
2.3 $children——访问所有子组件实例或子元素
父组件可以通过children不能保证子元素顺序。
// parent
this.$children[0].xx = 'xxx'
2.4 $refs——访问子组件实例或子元素
获取子节点引用
// parent
mounted() {
this.$refs.hw.xx = 'xxx'
}
2.5 provide/inject——依赖注入
能够实现祖先和后代之间传值
// 祖先元素
provide() {
return {foo: 'foo'}
}
// 后代
{{foo}}
// 也可以在后代元素中设置别名,以防命名冲突
inject: {
bar: {
from: 'foo'
}
}
访问的时候: {{bar}}
3.非prop特性
用于隔代透传,只能传一代。
3.1 $attrs
包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。当一个组件没有 声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外),并且可以通过 v- bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
// 祖先组件
// Child2组件
// 孙子组件
孙子组件
父辈通过$attr透传过来的信息:{{msg}}
结果:
3.2 $listeners
透传事件
// 祖先组件
methods: {
getMsg (info) {
console.log(info)
}
}
// Child2组件
// 孙子组件
孙子组件
4.插槽
插槽语法是Vue实现的内容分发 API,用于复合组件开发。在通用组件库开发中有大量应用。
4.1 匿名插槽
// Layout
// parent
种一棵树最好是时间是十年前,而后是现在!
4.2 具名插槽
// Layout
// parent
欢迎来到捡代码的小女孩的vue世界
4.3 作用域插槽
// Layout
{{value.childValue}}
// parent
完整代码如下:
父组件
插槽
欢迎来到捡代码的小女孩的vue世界
种一棵树最好是时间是十年前,而后是现在!
{{value.childValue}}
Layout组件
效果如下:
这些就是组件化开发的必备知识啦 (〃'▽'〃)
get之后和我一起手写小组件吖~