今天分享的主题是复杂组件中的事件通讯,内容包括建立通讯层的出发点,建立过程中的方案确定,以及最终实现。
建立通讯层的出发点
我们来思考下复杂组件的业务场景,比如商品组件的商品卡片,组件拆分需要细致到商品图片,商品服务标签、氛围标签,商品价格,按钮等,很多触发事件都是指向跳转商祥页面,但是这些小组件的层级不是相同的而是存在嵌套关系,那就需要一个全局的通讯层,来接收共用的事件消息通知。
通讯层方案
装载/卸载全局消息处理器
专题实例创建时装载全局消息处理器,实例销毁时卸载全局消息处理器
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
}
}
网友评论