vue组件间的通讯(随笔)

343 阅读2分钟
最近开始深入学习vue以帮助自己跨入高阶,将学习内容记录一下以免日后忘记。

基础篇(vue的API)

  1. props 父传子
  2. $emit 子传父
  3. 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;
      }
    }
  }
}
好了,这次纪录到这里了。以上就是我能想到的vue中组件间的所有通讯方式。如果有什么不足或者错误的地方,请大家指点;另外,我也蛮期望有小伙伴跟我在学习道路上中一起前进,共同进步。当然啦,找我打王者上分,我也乐意之至。