最近开始深入学习vue以帮助自己跨入高阶,将学习内容记录一下以免日后忘记。
基础篇(vue的API)
- props 父传子
- $emit 子传父
- provide & inject
提升篇
一.子孙向祖先通讯
1.话不多说,先开始实现子孙组件向祖先组件单向传递数据 ( 需要祖先组件内添加$on事件监听 )
function dispatch(componentName, eventName, ...params) {
let parant = this.$parent || this.$root;
let parentName = parant.$options.name;
while (parant && (!parentName && parentName !== componentName)) {
parent = parent.$parent;
if (parent) {
parentName = parent.$options.name;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
}
注意:this是指向当前vue组件实力的,如果不是通过混入的方式实现的话,需要调用时手动改变this的指向。
2.当然了,上面的方法只是通过父组件向自身发送消息,如果想要获取第一个符合条件的父组件,那么就要稍微进行处理一下了
function findComponentUpward(context, componentName) {
let parent = context.$parent;
let parentName = parent.$options.name;
while (parent && (!parentName || parentName !== componentName)) {
parent = parent.$parent;
if (parent) {
parentName = parent.$options.name;
}
}
return parent;
}
3.那么再进一步,获取符合条件的所有父组件呢
function findComponentsUpward(context, componentName) {
const parents = [];
let parent = context.$parent;
if (parent) {
if (parent.$options.name == componentName) {
parents.push(parent);
}
const nextParent = findComponentsUpward(parent, componentName);
parents.concat(nextParent);
}
return parents;
}
二.祖先向子孙通讯
1.跟子孙向祖先通讯实现类似,借鉴广播的方式实现 ( 需要祖先组件内添加$on事件监听 )
function broadcast(componentName, eventName, ...params) {
this.$children.forEach(child => {
const childName = child.$options.name;
if (childName === componentName) {
child.$emit.apply(child, [eventName].concat(params));
} else {
broadcast.call(child, componentName, eventName, ...params);
}
});
}
2.稍加改进,就可实现获取符合条件的子组件了,当然不一定是直接子组件
function findComponentDownward(context, componentName) {
const children = context.$children;
let childComponent;
if (children.length) {
for (let child of children) {
const childName = child.$options.name;
if (childName === componentName) {
childComponent = child;
break;
} else {
childComponent = findComponentDownward(child, componentName);
if (childComponent) {
break;
}
}
}
}
return childComponent;
}
注意: $children是不保证顺序的,无法确保同一条件多次执行的结果是一致的。
3.进一步拓展,实现获取所有符合条件的子组件
function findComponentsDownward(context, componentName) {
return context.$children.reduce((childComponents, child) => {
if (child.$options.name === componentName) {
childComponents.push(child);
}
const nextChildComponents = findComponentsDownward(child, componentName);
return childComponents.concat(nextChildComponents);
}, []);
}
三.没有关联关系的组件通讯(eventBus)
我的实现借鉴了vue源码event篇中的一些精华部分进行实现的(果然,还是站在巨人的肩膀上才能看的更远)
class EventBus {
constructor() {
this.eventQueues = {}
}
on(event, fn) {
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
this.on(event[i], fn);
}
} else {
(this.eventQueues[event] || (this.eventQueues[event] = [])).push(fn);
}
}
emit(event, ...params) {
const events = this.eventQueues[event];
if (events) {
for (let i = 0, l = events.length; i < l; i++) {
events[i].apply(this, params);
}
}
}
off(event, fn) {
if (!arguments.length) {
this.eventQueues = Object.create(null);
return;
}
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
this.off(event[i], fn);
}
return;
}
if (!fn) {
this.eventQueues[event] = null;
}
const cbs = vm._events[event]
let cb;
let i = cbs.length;
while (i--) {
cb = cbs[i];
if (cb === fn) {
cbs.splice(i, 1);
break;
}
}
}
}