123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- 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
- }
- }
- }
|