diff --git a/packages/core/src/platform/builtInMixins/proxyEventMixin.js b/packages/core/src/platform/builtInMixins/proxyEventMixin.js index 3b1003bb5..b645dbc30 100644 --- a/packages/core/src/platform/builtInMixins/proxyEventMixin.js +++ b/packages/core/src/platform/builtInMixins/proxyEventMixin.js @@ -4,70 +4,87 @@ import contextMap from '../../dynamic/vnode/context' function logCallbackNotFound (context, callbackName) { const location = context.__mpxProxy && context.__mpxProxy.options.mpxFileResource - error(`Instance property [${callbackName}] is not function, please check.`, location) + error( + `Instance property [${callbackName}] is not function, please check.`, + location + ) } -export default function proxyEventMixin () { - const methods = { - __invoke ($event) { - if (typeof Mpx.config.proxyEventHandler === 'function') { - try { - Mpx.config.proxyEventHandler($event) - } catch (e) { - } - } - const location = this.__mpxProxy.options.mpxFileResource - const type = $event.type - // thanos 平台特殊事件标识 - const emitMode = $event.detail && $event.detail.mpxEmit - if (!type) { - error('Event object must have [type] property!', location) - return - } - let fallbackType = '' - if (type === 'begin' || type === 'end') { - // 地图的 regionchange 事件会派发 e.type 为 begin 和 end 的事件 - fallbackType = __mpx_mode__ === 'ali' ? 'regionChange' : 'regionchange' - } else if (/-([a-z])/.test(type)) { - fallbackType = dash2hump(type) - } else if (__mpx_mode__ === 'ali') { - fallbackType = type.replace(/^./, i => i.toLowerCase()) - } - const target = $event.currentTarget || $event.target - if (!target) { - error(`[${type}] event object must have [currentTarget/target] property!`, location) - return - } - const eventConfigs = target.dataset.eventconfigs || {} - const curEventConfig = eventConfigs[type] || eventConfigs[fallbackType] || [] - // 如果有 mpxuid 说明是运行时组件,那么需要设置对应的上下文 - const rootRuntimeContext = contextMap.get(target.dataset.mpxuid) - const context = rootRuntimeContext || this - let returnedValue - curEventConfig.forEach((item) => { - const callbackName = item[0] - if (emitMode) { - // thanos 平台特殊事件标识处理 - $event = $event.detail.data - } - if (callbackName) { - const params = item.length > 1 - ? item.slice(1).map(item => { +function handleEvent (context, $event, isCapture) { + if (typeof Mpx.config.proxyEventHandler === 'function') { + try { + Mpx.config.proxyEventHandler($event) + } catch (e) {} + } + const location = context.__mpxProxy.options.mpxFileResource + const type = $event.type + // thanos 平台特殊事件标识 + const emitMode = $event.detail && $event.detail.mpxEmit + if (!type) { + error('Event object must have [type] property!', location) + return + } + let fallbackType = '' + if (type === 'begin' || type === 'end') { + // 地图的 regionchange 事件会派发 e.type 为 begin 和 end 的事件 + fallbackType = __mpx_mode__ === 'ali' ? 'regionChange' : 'regionchange' + } else if (/-([a-z])/.test(type)) { + fallbackType = dash2hump(type) + } else if (__mpx_mode__ === 'ali') { + fallbackType = type.replace(/^./, (i) => i.toLowerCase()) + } + const target = $event.currentTarget || $event.target + if (!target) { + error( + `[${type}] event object must have [currentTarget/target] property!`, + location + ) + return + } + const mode = isCapture ? 'capture' : 'bubble' + const eventConfigs = target.dataset.eventconfigs?.[mode] || {} + const curEventConfig = eventConfigs[type] || eventConfigs[fallbackType] || [] + // 如果有 mpxuid 说明是运行时组件,那么需要设置对应的上下文 + const rootRuntimeContext = contextMap.get(target.dataset.mpxuid) + const runtimeContext = rootRuntimeContext || context + let returnedValue + curEventConfig.forEach((item) => { + const callbackName = item[0] + if (emitMode) { + // thanos 平台特殊事件标识处理 + $event = $event.detail.data + } + if (callbackName) { + const params = + item.length > 1 + ? item.slice(1).map((item) => { if (item === '__mpx_event__') { return $event } else { return item } }) - : [$event] - if (typeof context[callbackName] === 'function') { - returnedValue = context[callbackName].apply(context, params) - } else { - logCallbackNotFound(context, callbackName) - } - } - }) - return returnedValue + : [$event] + if (typeof runtimeContext[callbackName] === 'function') { + returnedValue = runtimeContext[callbackName].apply( + runtimeContext, + params + ) + } else { + logCallbackNotFound(runtimeContext, callbackName) + } + } + }) + return returnedValue +} + +export default function proxyEventMixin () { + const methods = { + __invoke ($event) { + return handleEvent(this, $event, false) + }, + __captureInvoke ($event) { + return handleEvent(this, $event, true) }, __model (expr, $event, valuePath = ['value'], filterMethod) { const innerFilter = { diff --git a/packages/webpack-plugin/lib/platform/template/wx/index.js b/packages/webpack-plugin/lib/platform/template/wx/index.js index 1da86356b..82d5db8c4 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/index.js +++ b/packages/webpack-plugin/lib/platform/template/wx/index.js @@ -301,9 +301,16 @@ module.exports = function getSpec ({ warn, error }) { const modifierStr = match[3] || '' const rPrefix = runRules(spec.event.prefix, prefix, { mode: 'ali' }) const rEventName = runRules(eventRules, eventName, { mode: 'ali' }) - return { - name: dash2hump(rPrefix + '-' + rEventName) + modifierStr, - value + if (rPrefix === 'capture-on' || rPrefix === 'capture-catch') { + return { + name: rPrefix + rEventName.replace(/^[a-z]/, l => l.toUpperCase()) + modifierStr, + value + } + } else { + return { + name: dash2hump(rPrefix + '-' + rEventName) + modifierStr, + value + } } }, swan ({ name, value }, { eventRules }) { @@ -444,7 +451,9 @@ module.exports = function getSpec ({ warn, error }) { ali (prefix) { const prefixMap = { bind: 'on', - catch: 'catch' + catch: 'catch', + 'capture-catch': 'capture-catch', + 'capture-bind': 'capture-on' } if (!prefixMap[prefix]) { error(`Ali environment does not support [${prefix}] event handling!`) diff --git a/packages/webpack-plugin/lib/template-compiler/compiler.js b/packages/webpack-plugin/lib/template-compiler/compiler.js index 2781eee4b..5a736ebac 100644 --- a/packages/webpack-plugin/lib/template-compiler/compiler.js +++ b/packages/webpack-plugin/lib/template-compiler/compiler.js @@ -1311,6 +1311,32 @@ function processEventReact (el) { // } } +function isNeedBind (configs, isProxy) { + if (isProxy) return true + if (configs.length > 1) return true + if (configs.length === 1) return configs[0].hasArgs + return false +} + +function processEventBinding (el, configs) { + let resultName + configs.forEach(({ name }) => { + if (name) { + // 清空原始事件绑定 + let has + do { + has = getAndRemoveAttr(el, name).has + } while (has) + + if (!resultName) { + // 清除修饰符 + resultName = name.replace(/\..*/, '') + } + } + }) + return { resultName } +} + function processEvent (el, options) { const eventConfigMap = {} el.attrsList.forEach(function ({ name, value }) { @@ -1324,12 +1350,15 @@ function processEvent (el, options) { const extraStr = runtimeCompile && prefix === 'catch' ? `, "__mpx_${prefix}"` : '' const parsedFunc = parseFuncStr(value, extraStr) if (parsedFunc) { + const isCapture = /^capture/.test(prefix) if (!eventConfigMap[type]) { eventConfigMap[type] = { - configs: [] + configs: [], + captureConfigs: [] } } - eventConfigMap[type].configs.push(Object.assign({ name }, parsedFunc)) + const targetConfigs = isCapture ? eventConfigMap[type].captureConfigs : eventConfigMap[type].configs + targetConfigs.push(Object.assign({ name }, parsedFunc)) if (modifiers.indexOf('proxy') > -1 || options.forceProxyEvent) { eventConfigMap[type].proxy = true } @@ -1371,48 +1400,49 @@ function processEvent (el, options) { } for (const type in eventConfigMap) { - let needBind = false - const { configs, proxy } = eventConfigMap[type] + const { configs = [], captureConfigs = [], proxy } = eventConfigMap[type] delete eventConfigMap[type] - if (proxy) { - needBind = true - } else if (configs.length > 1) { - needBind = true - } else if (configs.length === 1) { - needBind = !!configs[0].hasArgs - } + + let needBubblingBind = isNeedBind(configs, proxy) + let needCaptureBind = isNeedBind(captureConfigs, proxy) const escapedType = dash2hump(type) // 排除特殊情况 if (!isValidIdentifierStr(escapedType)) { warn$1(`EventName ${type} which need be framework proxy processed must be a valid identifier!`) - needBind = false + needBubblingBind = false + needCaptureBind = false } - if (needBind) { - let resultName - configs.forEach(({ name }) => { - if (name) { - // 清空原始事件绑定 - let has - do { - has = getAndRemoveAttr(el, name).has - } while (has) + if (needBubblingBind) { + const { resultName } = processEventBinding(el, configs) - if (!resultName) { - // 清除修饰符 - resultName = name.replace(/\..*/, '') - } + addAttrs(el, [ + { + name: resultName || config[mode].event.getEvent(type), + value: '__invoke' } + ]) + if (!eventConfigMap.bubble) { + eventConfigMap.bubble = {} + } + eventConfigMap.bubble[escapedType] = configs.map((item) => { + return item.expStr }) + } + if (needCaptureBind) { + const { resultName } = processEventBinding(el, captureConfigs) addAttrs(el, [ { name: resultName || config[mode].event.getEvent(type), - value: '__invoke' + value: '__captureInvoke' } ]) - eventConfigMap[escapedType] = configs.map((item) => { + if (!eventConfigMap.capture) { + eventConfigMap.capture = {} + } + eventConfigMap.capture[escapedType] = captureConfigs.map((item) => { return item.expStr }) }