vue-全局通讯层

今天分享的主题是复杂组件中的事件通讯,内容包括建立通讯层的出发点,建立过程中的方案确定,以及最终实现。

建立通讯层的出发点

我们来思考下复杂组件的业务场景,比如商品组件的商品卡片,组件拆分需要细致到商品图片,商品服务标签、氛围标签,商品价格,按钮等,很多触发事件都是指向跳转商祥页面,但是这些小组件的层级不是相同的而是存在嵌套关系,那就需要一个全局的通讯层,来接收共用的事件消息通知。

通讯层方案

装载/卸载全局消息处理器

专题实例创建时装载全局消息处理器,实例销毁时卸载全局消息处理器

 beforeMount() {
   // 装载全局消息处理器
   installHandler.call(this)
 }
deactivated() {
    // 卸载全局消息处理器
    unistallFunHandler.call(this)
 }

监听与发送触达

在vue1.0中,我们可以使用

$dispatch() // 派发事件,沿着父链冒泡
$broadcast() // 广播事件, 向下传递给所有的后代

这可以满足使用与全局广播的功能,但是在vue2.0中已经删除了这两个方法,我们需要换一种方式。
根据vue1.0 $dispatch的写法,Element 实现了dispatch,如下:

dispatch(componentName, eventName, params) {
  var parent = this.$parent || this.$root;
  var name = parent.$options.componentName;
  //寻找父级,如果父级不是符合的组件名,则循环向上查找
  while (parent && (!name || name !== componentName)) {
    parent = parent.$parent;
    if (parent) {
      name = parent.$options.componentName;
    }
  }
  //找到符合组件名称的父级后,触发其事件。整体流程类似jQuery的closest方法
  if (parent) {
    parent.$emit.apply(parent, [eventName].concat(params));
  }
 }

通过源码我们可以看到实现的方案是层层查找,对于此方案我们并不满意,在查找的过程中也发现了$root的使用,也就是我们现在所使用的层级传递解决方案:

/**
 * 装载全局消息处理器
 */
export function installHandler() {
  this.$root.$on('msgHandler', (messageName, param = {}) => {
    const { reportPoints } = param
    this.clickEventReport(reportPoints)
    methodHandle.call(this, messageName, param)
  })
}
/**
 * 卸载全局消息处理器
 */
export function unistallFunHandler() {
  this.$root.$off('msgHandler')
}     
/**
 * 发送消息示例
 */
this.$root.$emit('msgHandler', 'commonEvent', params)

消息处理

在此步骤中已经明确,需要将共用的方法抽取独立文件。
一开始我是在页面初始化阶段,将共用的方法通过setMethods设置在实例中,触发时调用。考虑后续拓展及更优的使用方式,最后使用mixin的方式将共用的方法插入实例中。下图的示例是setMethods的实现

/**
 *  设置公共方法在vue实例
 */
function setMethods () {
  function polyfillBind (fn, ctx) {
    function boundFn (a) {
      const l = arguments.length
      return l
        ? l > 1
          ? fn.apply(ctx, arguments)
          : fn.call(ctx, a)
        : fn.call(ctx)
    }
    boundFn._length = fn.length
    return boundFn
  }
  function nativeBind (fn, ctx) {
    return fn.bind(ctx)
  }
  const bind = Function.prototype.bind ? nativeBind : polyfillBind
  Object.keys(methods).forEach((key) => {
    this[key] = methods[key] == null ? noop : bind(methods[key], this)
  })
}

到此我们就只剩下最后的步骤,将接收到的消息转化为方法调用。

/**
 * 消息处理
 * @param messageName 消息名称
 * @param param       消息参数
 */
function methodHandle(messageName, param) {
  try {
    if (typeof (this[messageName]) !== 'function') return false
    this[messageName](param)
  } catch (e) {
    return false
  }
}

你可能感兴趣的:(vue-全局通讯层)