首次提交
This commit is contained in:
@@ -0,0 +1,369 @@
|
||||
import _get from 'lodash/get'
|
||||
import _set from 'lodash/set'
|
||||
import _isEmpty from 'lodash/isEmpty'
|
||||
import _isEqual from 'lodash/isEqual'
|
||||
import _forEach from 'lodash/forEach'
|
||||
import _isUndefined from 'lodash/isUndefined'
|
||||
import _flattenDeep from 'lodash/flattenDeep'
|
||||
import _toString from 'lodash/toString'
|
||||
import _has from 'lodash/has'
|
||||
import _includes from 'lodash/includes'
|
||||
import _keys from 'lodash/keys'
|
||||
import _isFunction from 'lodash/isFunction'
|
||||
import _uniq from 'lodash/uniq'
|
||||
import _flatMap from 'lodash/flatMap'
|
||||
import _union from 'lodash/union'
|
||||
/**
|
||||
* @desc
|
||||
* Vue 跨组件通信插件
|
||||
*/
|
||||
let unicomIdName = ''
|
||||
let unicomGroupName = ''
|
||||
// vm容器、分组、事件、命名 唯一、推迟触发的事件
|
||||
const [vmMap, groupForVm, events, idForVm, sendDefer] = [new Map(), {}, {}, {}, []]
|
||||
/**
|
||||
* @desc 触发执行事件
|
||||
* @param {string} method - 指令的名称
|
||||
* @param {string} toKey - 目标组件的 unicomId 或者 unicomGroup
|
||||
* @param {string} aim - 标识 (#)
|
||||
* @param {Array} args - 参数
|
||||
*/
|
||||
const emitEvent = function (method, toKey, aim, args) {
|
||||
const evs = _get(events, method, [])
|
||||
let evLen = 0
|
||||
const len = evs.length
|
||||
// 循环已经注册的指令
|
||||
for (evLen; evLen < len; evLen++) {
|
||||
// 存储的数据
|
||||
const { fn, scope } = evs[evLen]
|
||||
if (_isEqual(aim, '#')) {
|
||||
// id
|
||||
if (scope[unicomIdName] !== toKey) {
|
||||
// 目标不存在
|
||||
continue
|
||||
}
|
||||
} else if (_isEqual(aim, '@')) {
|
||||
// 分组
|
||||
const group = _get(vmMap.get(scope), 'group', [])
|
||||
if (_isEmpty(group) || _isEqual(_includes(group, toKey), false)) {
|
||||
// 目标不存在
|
||||
continue
|
||||
}
|
||||
}
|
||||
_isEqual(_isUndefined(scope), false) && fn.apply(scope, args)
|
||||
}
|
||||
// 返回被触发的指令
|
||||
return evLen
|
||||
}
|
||||
/**
|
||||
* @desc 发送容器 或者 获得 目标容器
|
||||
* @param {string} query - 指令名称 (~instruct1#id1)
|
||||
* @param {...any} args - 可变参数
|
||||
*/
|
||||
const unicomQuery = function (query, ...args) {
|
||||
let [toKey, aim, defer, eventIndex] = ['', '', false, -1]
|
||||
// query=instruct1#id1
|
||||
const method = query
|
||||
.replace(/^([`~])/, function (s0, s1) {
|
||||
// query=~instruct1#id1
|
||||
if (_isEqual(s1, '~')) {
|
||||
defer = true
|
||||
}
|
||||
return ''
|
||||
})
|
||||
.replace(/([@#])([^@#]*)$/, function (s0, s1, s2) {
|
||||
// query=instruct1@child-a
|
||||
// s0=@child-a s1=@ s2=child-a
|
||||
toKey = s2
|
||||
aim = s1
|
||||
return ''
|
||||
})
|
||||
// method=instruct1
|
||||
if (defer) {
|
||||
sendDefer.push([method, toKey, aim, args, this])
|
||||
return this
|
||||
}
|
||||
if (_isEqual(_isEqual(method, ''), false)) {
|
||||
args.unshift(this)
|
||||
eventIndex = emitEvent(method, toKey, aim, args)
|
||||
}
|
||||
// 获取目标 vm
|
||||
switch (aim) {
|
||||
case '#':
|
||||
return _get(idForVm, toKey, null)
|
||||
case '@':
|
||||
return _get(groupForVm, toKey, [])
|
||||
}
|
||||
return eventIndex
|
||||
}
|
||||
/**
|
||||
* @desc 更新分组
|
||||
* @param {Object} scope - 组件的实例
|
||||
* @param {string[]} nv - 指令 unicom-group 传入的组数据 (新)
|
||||
* @param {string[]} [ov] - 指令 unicom-group 传入的组数据 (旧)
|
||||
*/
|
||||
const updateName = function (scope, nv, ov) {
|
||||
// 实例上设置分组
|
||||
const vmData = vmMap.get(scope) || {}
|
||||
const group = _get(vmData, 'group', [])
|
||||
// 删除旧的 vm
|
||||
if (_isEqual(_isUndefined(ov), false)) {
|
||||
_uniq(_flattenDeep(_flatMap(ov))).forEach(function (key) {
|
||||
if (_includes(group, key)) {
|
||||
const vms = _get(groupForVm, key, [])
|
||||
_isEqual(_isEmpty(vms), false) && vms.splice(vms.indexOf(scope), 1)
|
||||
if (_isEmpty(vms)) {
|
||||
group.splice(group.indexOf(key), 1)
|
||||
Reflect.deleteProperty(groupForVm, key)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// 增加新的
|
||||
if (_isEqual(_isUndefined(nv), false)) {
|
||||
_uniq(_flattenDeep(_flatMap(nv))).forEach(function (key) {
|
||||
const vms = _get(groupForVm, key, [])
|
||||
if (_isEmpty(vms)) {
|
||||
_set(groupForVm, key, vms)
|
||||
}
|
||||
// 新添加 组件 到组里面
|
||||
if (_isEqual(_includes(vms, scope), false)) {
|
||||
vms.push(scope)
|
||||
}
|
||||
if (_isEqual(_includes(group, key), false)) {
|
||||
group.push(key)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @desc 更新 unicomId
|
||||
* @param {Object} scope - 组件的实例
|
||||
* @param {string} newValue - 指令 unicom-id 传入的id数据 (新)
|
||||
* @param {string} [oldValue] - 指令 unicom-id 传入的id数据 (旧)
|
||||
*/
|
||||
const updateId = function (scope, newValue, oldValue) {
|
||||
if (_isEqual(newValue, oldValue)) {
|
||||
return
|
||||
}
|
||||
if (_isEqual(_isUndefined(oldValue), false) && _isEqual(_get(idForVm, oldValue), scope)) {
|
||||
// watch 监测值修改需要删除,组件销毁时需要删除
|
||||
Reflect.deleteProperty(idForVm, oldValue)
|
||||
}
|
||||
if (_isEqual(_isUndefined(newValue), false) && _isEqual(_has(idForVm, newValue), false)) {
|
||||
_set(idForVm, newValue, scope)
|
||||
} else if (_isEqual(_isUndefined(newValue), false) && _has(idForVm, newValue)) {
|
||||
console.warn(`${unicomIdName}='${newValue}'的组件已经定义并存在。`)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @desc 添加事件
|
||||
* @param {string} method - 指令的名称
|
||||
* @param {function} fn - 指令名称对应的函数
|
||||
* @param {Object} scope - 指令名称对应函数所运行的作用域
|
||||
*/
|
||||
const appendEvent = function (method, fn, scope) {
|
||||
if (_isEqual(_has(events, method), false)) {
|
||||
events[method] = []
|
||||
}
|
||||
if (_isFunction(fn)) {
|
||||
events[method].push({ fn, scope, method })
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @desc 移除事件
|
||||
* @param {string} method - 指令的名称
|
||||
* @param {Object} scope - 指令名称对应函数所运行的作用域
|
||||
*/
|
||||
const removeEvent = function (method, scope) {
|
||||
const evs = _get(events, method, [])
|
||||
if (_isEqual(_isEmpty(evs), false)) {
|
||||
for (let i = 0; i < evs.length; i++) {
|
||||
if (_isEqual(scope, evs[i].scope)) {
|
||||
evs.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export default {
|
||||
install (Vue, { name = 'unicom', idName = `${name}Id`, groupName = `${name}Group` } = {}) {
|
||||
// 添加原型方法 (unicomQuery 函数放在 install 外部不然无法获取调用函数的 this 对象)
|
||||
Vue.prototype['$' + name] = unicomQuery
|
||||
// unicom-id
|
||||
unicomIdName = idName
|
||||
// 分组 unicom-group
|
||||
unicomGroupName = groupName
|
||||
// 全局混入
|
||||
Vue.mixin({
|
||||
props: {
|
||||
// 命名 unicom-id="id1"
|
||||
[idName]: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 分组(纯字符串类数组不能是真实的数组) unicom-group="['child-a', 'child-b']"
|
||||
[groupName]: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
[idName] (nv, ov) {
|
||||
updateId(this, nv, ov)
|
||||
},
|
||||
[groupName]: {
|
||||
deep: true,
|
||||
handler (nv, ov) {
|
||||
updateName(this, nv, ov);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 创建的时候加入事件机制
|
||||
beforeCreate () {
|
||||
const opt = this.$options
|
||||
const vmData = {}
|
||||
const [us, uni, group] = [
|
||||
_get(opt, name, {}),
|
||||
(vmData.uni = {}),
|
||||
(vmData.group = [])
|
||||
]
|
||||
if (_isEqual(_isEmpty(us), false)) {
|
||||
_forEach(us, opt => {
|
||||
if (_isEmpty(opt)) {
|
||||
return true
|
||||
}
|
||||
_forEach(opt, (handler, key) => {
|
||||
if (_isEqual(_isUndefined(handler), false)) {
|
||||
_isUndefined(_get(uni, key)) && _set(uni, key, [])
|
||||
uni[key].push(handler)
|
||||
// 添加事件
|
||||
appendEvent(key, handler, this)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
// 命名分组
|
||||
const groupNameOpt = _get(opt, groupName, [])
|
||||
if (_isEqual(_isEmpty(groupNameOpt), false)) {
|
||||
_forEach(_uniq(_flattenDeep(_flatMap(groupNameOpt))), item => {
|
||||
const key = _toString(item)
|
||||
_isUndefined(_get(groupForVm, key)) && (groupForVm[key] = [])
|
||||
group.push(key)
|
||||
groupForVm[key].push(this)
|
||||
})
|
||||
}
|
||||
if (_isEqual(_isEmpty(group), false) || _isEqual(_isEmpty(uni), false)) {
|
||||
vmMap.set(this, vmData)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
// 实例命名 唯一
|
||||
const uId = this[idName]
|
||||
const uGroupName = this[groupName]
|
||||
_isEqual(_isEqual(uId, ''), false) && updateId(this, uId)
|
||||
_isEqual(_isEmpty(uGroupName), false) && updateName(this, uGroupName)
|
||||
// 实例上设置分组
|
||||
const vmData = vmMap.get(this) || {}
|
||||
for (let i = 0; i < sendDefer.length; i++) {
|
||||
const [method, toKey, aim, args, scope] = sendDefer[i]
|
||||
let flag = false
|
||||
if (_isEqual(aim, '#')) {
|
||||
if (_isEqual(toKey, uId)) {
|
||||
flag = true
|
||||
}
|
||||
} else if (_isEqual(aim, '@')) {
|
||||
if (
|
||||
_isEqual(_isUndefined(_get(vmData, 'group')), false) &&
|
||||
_includes(_get(vmData, 'group'), toKey)
|
||||
) {
|
||||
flag = true
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
_isEqual(method, '') ||
|
||||
_includes(_keys(_get(vmData, 'uni', {})), method)
|
||||
) {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
// 延后,并且方法为空
|
||||
if (_isEqual(method, '')) {
|
||||
/**
|
||||
* @desc 监听组件事件
|
||||
* @example
|
||||
* this.$unicom('~#id1', function (childScope) {// unicomId=id1的组件创建完成})
|
||||
*/
|
||||
if (_isFunction(args[0])) {
|
||||
args[0](this)
|
||||
}
|
||||
} else {
|
||||
sendDefer.splice(i--, 1)
|
||||
args.unshift(scope)
|
||||
emitEvent(method, toKey, aim, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 全局混合,销毁实例的时候销毁事件
|
||||
destroyed () {
|
||||
// 移除唯一ID
|
||||
const uId = this[idName]
|
||||
if (_isEqual(_isEqual(uId, ''), false)) {
|
||||
updateId(this, undefined, uId)
|
||||
}
|
||||
// 移除 命名分组、实例命名
|
||||
const uGroupName = _get(this, groupName, [])
|
||||
const optGroupName = _get(this.$options, groupName, [])
|
||||
if (_isEqual(_isEmpty(uGroupName), false) || _isEqual(_isEmpty(optGroupName), false)) {
|
||||
updateName(this, undefined, _union(uGroupName, optGroupName))
|
||||
}
|
||||
|
||||
const vmData = vmMap.get(this)
|
||||
if (_isUndefined(vmData)) {
|
||||
return
|
||||
}
|
||||
vmMap.delete(this)
|
||||
|
||||
const uni = _get(vmData, 'uni', {})
|
||||
// 移除事件
|
||||
for (const key in uni) {
|
||||
removeEvent(key, this)
|
||||
}
|
||||
// 分组,一对多, 单个vm可以多个分组名称 组件命名
|
||||
const group = _get(vmData, 'group', [])
|
||||
_forEach(group, function (name) {
|
||||
const gs = groupForVm[name]
|
||||
if (_isEqual(_isUndefined(gs), false)) {
|
||||
const index = gs.indexOf(this)
|
||||
if (index > -1) {
|
||||
gs.splice(index, 1)
|
||||
}
|
||||
if (gs.length === 0) {
|
||||
delete groupForVm[name]
|
||||
}
|
||||
}
|
||||
})
|
||||
// 监控销毁 method为空
|
||||
for (let i = 0; i < sendDefer.length;) {
|
||||
const pms = sendDefer[i]
|
||||
if (_isEqual(pms[0], '') && _isEqual(pms[4], this)) {
|
||||
sendDefer.splice(i, 1)
|
||||
} else {
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// 自定义属性合并策略
|
||||
// 混入(mixins)时不是简单的覆盖而是追加
|
||||
const merge = _get(Vue, 'config.optionMergeStrategies', {})
|
||||
merge[name] = merge[unicomGroupName] = function (parentVal, childVal) {
|
||||
const p = parentVal || []
|
||||
if (_isEqual(_isUndefined(childVal), false)) {
|
||||
p.push(childVal)
|
||||
}
|
||||
return p
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user