From a8646b09015e5e7a08f44525f01190a736ad2893 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Fri, 20 Dec 2024 14:32:01 +0800 Subject: [PATCH 01/18] fix: dataset in web --- .../builtInMixins/proxyEventMixin.web.js | 25 ++++- packages/core/src/platform/env/event.js | 101 +++++++++--------- .../lib/platform/template/wx/index.js | 4 +- 3 files changed, 70 insertions(+), 60 deletions(-) diff --git a/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js b/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js index 019c74b158..03ad219f8e 100644 --- a/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js +++ b/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js @@ -1,4 +1,4 @@ -import { setByPath } from '@mpxjs/utils' +import { setByPath, extend, parseDataset } from '@mpxjs/utils' export default function proxyEventMixin () { return { @@ -13,16 +13,31 @@ export default function proxyEventMixin () { methods: { __model (expr, $event, valuePath = ['value'], filterMethod) { const innerFilter = { - trim: val => typeof val === 'string' && val.trim() + trim: (val) => typeof val === 'string' && val.trim() } - const originValue = valuePath.reduce((acc, cur) => acc[cur], $event.detail) - const value = filterMethod ? (innerFilter[filterMethod] ? innerFilter[filterMethod](originValue) : typeof this[filterMethod] === 'function' && this[filterMethod]) : originValue + const originValue = valuePath.reduce( + (acc, cur) => acc[cur], + $event.detail + ) + const value = filterMethod + ? innerFilter[filterMethod] + ? innerFilter[filterMethod](originValue) + : typeof this[filterMethod] === 'function' && this[filterMethod] + : originValue setByPath(this, expr, value) }, __invokeHandler (eventName, $event) { + const newEvent = extend({}, $event, { + target: extend({}, $event.target, { + dataset: parseDataset($event.target.dataset) + }), + currentTarget: extend({}, $event.currentTarget, { + dataset: parseDataset($event.currentTarget.dataset) + }) + }) const handler = this[eventName] if (handler && typeof handler === 'function') { - handler.call(this, $event) + handler.call(this, newEvent) } } } diff --git a/packages/core/src/platform/env/event.js b/packages/core/src/platform/env/event.js index 82cb7bf77a..04af5deab8 100644 --- a/packages/core/src/platform/env/event.js +++ b/packages/core/src/platform/env/event.js @@ -11,57 +11,60 @@ function extendEvent (e, extendObj = {}) { }) } -function MpxEvent (layer) { - this.targetElement = null - this.touches = [] - this.touchStartX = 0 - this.touchStartY = 0 - this.startTimer = null - this.needTap = true - this.isTouchDevice = document && ('ontouchstart' in document.documentElement) +function createMpxEvent (layer) { + let startTimer = null + let needTap = true + let touchStartX = 0 + let touchStartY = 0 + let targetElement = null + const isTouchDevice = document && 'ontouchstart' in document.documentElement - this.onTouchStart = (event) => { - if (event.targetTouches?.length > 1) { - return true - } - this.touches = event.targetTouches - this.targetElement = event.target - this.needTap = true - this.startTimer = null - this.touchStartX = this.touches[0].pageX - this.touchStartY = this.touches[0].pageY - this.startTimer = setTimeout(() => { - this.needTap = false - this.sendEvent(this.targetElement, 'longpress', event) - this.sendEvent(this.targetElement, 'longtap', event) - }, 350) - } + const onTouchStart = (event) => { + if (event.targetTouches?.length > 1) { + return true + } + const touches = event.targetTouches + targetElement = event.target + needTap = true + startTimer = null + touchStartX = touches[0].pageX + touchStartY = touches[0].pageY + startTimer = setTimeout(() => { + needTap = false + sendEvent(targetElement, 'longpress', event) + sendEvent(targetElement, 'longtap', event) + }, 350) + } - this.onTouchMove = (event) => { + const onTouchMove = (event) => { const touch = event.changedTouches[0] - if (Math.abs(touch.pageX - this.touchStartX) > 1 || Math.abs(touch.pageY - this.touchStartY) > 1) { - this.needTap = false - this.startTimer && clearTimeout(this.startTimer) - this.startTimer = null + if ( + Math.abs(touch.pageX - touchStartX) > 1 || + Math.abs(touch.pageY - touchStartY) > 1 + ) { + needTap = false + startTimer && clearTimeout(startTimer) + startTimer = null } } - this.onTouchEnd = (event) => { + const onTouchEnd = (event) => { if (event.targetTouches?.length > 1) { return true } - this.startTimer && clearTimeout(this.startTimer) - this.startTimer = null - if (this.needTap) { - this.sendEvent(this.targetElement, 'tap', event) + startTimer && clearTimeout(startTimer) + startTimer = null + if (needTap) { + sendEvent(targetElement, 'tap', event) } } - this.onClick = (event) => { - this.targetElement = event.target - this.sendEvent(this.targetElement, 'tap', event) + const onClick = (event) => { + targetElement = event.target + sendEvent(targetElement, 'tap', event) } - this.sendEvent = (targetElement, type, event) => { + + const sendEvent = (targetElement, type, event) => { const touchEvent = new CustomEvent(type, { bubbles: true, cancelable: true @@ -72,7 +75,6 @@ function MpxEvent (layer) { changedTouches, touches: changedTouches, detail: { - // pc端点击事件可能没有changedTouches,所以直接从 event中取 x: changedTouches[0]?.pageX || event.pageX || 0, y: changedTouches[0]?.pageY || event.pageY || 0 } @@ -80,28 +82,23 @@ function MpxEvent (layer) { targetElement && targetElement.dispatchEvent(touchEvent) } - this.addListener = () => { - if (this.isTouchDevice) { - layer.addEventListener('touchstart', this.onTouchStart, true) - layer.addEventListener('touchmove', this.onTouchMove, true) - layer.addEventListener('touchend', this.onTouchEnd, true) - } else { - layer.addEventListener('click', this.onClick, true) - } + if (isTouchDevice) { + layer.addEventListener('touchstart', onTouchStart, true) + layer.addEventListener('touchmove', onTouchMove, true) + layer.addEventListener('touchend', onTouchEnd, true) + } else { + layer.addEventListener('click', onClick, true) } - this.addListener() } export function initEvent () { if (isBrowser && !global.__mpxCreatedEvent) { global.__mpxCreatedEvent = true if (document.readyState === 'complete' || document.readyState === 'interactive') { - // eslint-disable-next-line no-new - new MpxEvent(document.body) + createMpxEvent(document.body) } else { document.addEventListener('DOMContentLoaded', function () { - // eslint-disable-next-line no-new - new MpxEvent(document.body) + createMpxEvent(document.body) }, false) } } diff --git a/packages/webpack-plugin/lib/platform/template/wx/index.js b/packages/webpack-plugin/lib/platform/template/wx/index.js index cc6335c642..f32e635b9a 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/index.js +++ b/packages/webpack-plugin/lib/platform/template/wx/index.js @@ -385,9 +385,7 @@ module.exports = function getSpec ({ warn, error }) { }, web ({ name, value }, { eventRules, el, usingComponents }) { const parsed = parseMustacheWithContext(value) - if (parsed.hasBinding) { - value = '__invokeHandler(' + parsed.result + ', $event)' - } + value = '__invokeHandler(' + parsed.result + ', $event)' const match = this.test.exec(name) const prefix = match[1] const eventName = match[2] From 2a2b612ef49e915f3c1bb9e8a724f6b6d9498ee5 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Thu, 2 Jan 2025 14:34:24 +0800 Subject: [PATCH 02/18] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0web=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builtInMixins/proxyEventMixin.web.js | 62 ++++++++++++++----- .../lib/platform/template/wx/index.js | 2 - .../lib/template-compiler/compiler.js | 47 ++++++++++++++ 3 files changed, 94 insertions(+), 17 deletions(-) diff --git a/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js b/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js index 03ad219f8e..ee992d6a53 100644 --- a/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js +++ b/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js @@ -1,4 +1,5 @@ -import { setByPath, extend, parseDataset } from '@mpxjs/utils' +import { setByPath, error, extend, parseDataset } from '@mpxjs/utils' +import Mpx from '../../index' export default function proxyEventMixin () { return { @@ -26,20 +27,51 @@ export default function proxyEventMixin () { : originValue setByPath(this, expr, value) }, - __invokeHandler (eventName, $event) { - const newEvent = extend({}, $event, { - target: extend({}, $event.target, { - dataset: parseDataset($event.target.dataset) - }), - currentTarget: extend({}, $event.currentTarget, { - dataset: parseDataset($event.currentTarget.dataset) - }) - }) - const handler = this[eventName] - if (handler && typeof handler === 'function') { - handler.call(this, newEvent) - } - } + // __invokeHandler (eventName, $event) { + // const newEvent = extend({}, $event, { + // target: extend({}, $event.target, { + // dataset: parseDataset($event.target.dataset) + // }), + // currentTarget: extend({}, $event.currentTarget, { + // dataset: parseDataset($event.currentTarget.dataset) + // }) + // }) + // const handler = this[eventName] + // if (handler && typeof handler === 'function') { + // handler.call(this, newEvent) + // } + // } + __invoke (rawEvent, eventConfig = []) { + if (typeof Mpx.config.proxyEventHandler === 'function') { + try { + Mpx.config.proxyEventHandler(rawEvent) + } catch (e) { + } + } + const location = this.__mpxProxy.options.mpxFileResource + + let returnedValue + eventConfig.forEach((item) => { + const callbackName = item[0] + if (callbackName) { + const params = item.length > 1 + ? item.slice(1).map(item => { + if (item === '__mpx_event__') { + return rawEvent + } else { + return item + } + }) + : [rawEvent] + if (typeof this[callbackName] === 'function') { + returnedValue = this[callbackName].apply(this, params) + } else { + error(`Instance property [${callbackName}] is not function, please check.`, location) + } + } + }) + return returnedValue + } } } } diff --git a/packages/webpack-plugin/lib/platform/template/wx/index.js b/packages/webpack-plugin/lib/platform/template/wx/index.js index f32e635b9a..1da86356b1 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/index.js +++ b/packages/webpack-plugin/lib/platform/template/wx/index.js @@ -384,8 +384,6 @@ module.exports = function getSpec ({ warn, error }) { } }, web ({ name, value }, { eventRules, el, usingComponents }) { - const parsed = parseMustacheWithContext(value) - value = '__invokeHandler(' + parsed.result + ', $event)' const match = this.test.exec(name) const prefix = match[1] const eventName = match[2] diff --git a/packages/webpack-plugin/lib/template-compiler/compiler.js b/packages/webpack-plugin/lib/template-compiler/compiler.js index dbe300bf09..a836eb4251 100644 --- a/packages/webpack-plugin/lib/template-compiler/compiler.js +++ b/packages/webpack-plugin/lib/template-compiler/compiler.js @@ -1151,6 +1151,52 @@ function getModelConfig (el, match) { } } +function processEventWeb (el) { + const eventConfigMap = {} + el.attrsList.forEach(function ({ name, value }) { + const parsedEvent = config[mode].event.parseEvent(name) + if (parsedEvent) { + const type = config[mode].event.getEvent( + parsedEvent.eventName, + parsedEvent.prefix + ) + const parsedFunc = parseFuncStr(value); + if (parsedFunc) { + if (!eventConfigMap[type]) { + eventConfigMap[type] = { + configs: [] + } + } + eventConfigMap[type].configs.push( + Object.assign({ name, value }, parsedFunc) + ) + } + } + }) + + // let wrapper + for (const type in eventConfigMap) { + const { configs } = eventConfigMap[type]; + if (!configs.length) continue; + configs.forEach(({ name }) => { + if (name) { + // 清空原始事件绑定 + let has; + do { + has = getAndRemoveAttr(el, name).has; + } while (has); + } + }); + const value = `{{(e)=>this.__invokeHandler(e, [${configs.map((item) => item.expStr)}])}}`; + addAttrs(el, [ + { + name: type, + value + } + ]) + } +} + function processEventReact (el) { const eventConfigMap = {} el.attrsList.forEach(function ({ name, value }) { @@ -2633,6 +2679,7 @@ function processElement (el, root, options, meta) { // 预处理代码维度条件编译 processIfWeb(el) processScoped(el) + processEventWeb(el) // processWebExternalClassesHack(el, options) processExternalClasses(el, options) processComponentGenericsWeb(el, options, meta) From 189ccafe8245a9e884e134ee23fcd12e8587ce80 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Fri, 3 Jan 2025 19:40:07 +0800 Subject: [PATCH 03/18] feat: add proxy event --- .../builtInMixins/proxyEventMixin.web.js | 73 +- .../lib/platform/template/wx/index.js | 4 + .../lib/template-compiler/compiler.js | 843 ++++++++++++------ 3 files changed, 612 insertions(+), 308 deletions(-) diff --git a/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js b/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js index ee992d6a53..431d80d90f 100644 --- a/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js +++ b/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js @@ -27,51 +27,48 @@ export default function proxyEventMixin () { : originValue setByPath(this, expr, value) }, - // __invokeHandler (eventName, $event) { - // const newEvent = extend({}, $event, { - // target: extend({}, $event.target, { - // dataset: parseDataset($event.target.dataset) - // }), - // currentTarget: extend({}, $event.currentTarget, { - // dataset: parseDataset($event.currentTarget.dataset) - // }) - // }) - // const handler = this[eventName] - // if (handler && typeof handler === 'function') { - // handler.call(this, newEvent) - // } - // } - __invoke (rawEvent, eventConfig = []) { - if (typeof Mpx.config.proxyEventHandler === 'function') { - try { - Mpx.config.proxyEventHandler(rawEvent) - } catch (e) { - } - } - const location = this.__mpxProxy.options.mpxFileResource + __invokeHandler (rawEvent, eventConfig = []) { + if (typeof Mpx.config.proxyEventHandler === 'function') { + try { + Mpx.config.proxyEventHandler(rawEvent) + } catch (e) {} + } + const location = this.__mpxProxy.options.mpxFileResource + const newEvent = extend({}, rawEvent, { + target: extend({}, rawEvent.target, { + dataset: parseDataset(rawEvent.target.dataset) + }), + currentTarget: extend({}, rawEvent.currentTarget, { + dataset: parseDataset(rawEvent.currentTarget.dataset) + }) + }) - let returnedValue - eventConfig.forEach((item) => { - const callbackName = item[0] - if (callbackName) { - const params = item.length > 1 - ? item.slice(1).map(item => { + let returnedValue + eventConfig.forEach((item) => { + const callbackName = item[0] + if (callbackName) { + const params = + item.length > 1 + ? item.slice(1).map((item) => { if (item === '__mpx_event__') { - return rawEvent + return newEvent } else { return item } }) - : [rawEvent] - if (typeof this[callbackName] === 'function') { - returnedValue = this[callbackName].apply(this, params) - } else { - error(`Instance property [${callbackName}] is not function, please check.`, location) - } - } - }) - return returnedValue + : [newEvent] + if (typeof this[callbackName] === 'function') { + returnedValue = this[callbackName].apply(this, params) + } else { + error( + `Instance property [${callbackName}] is not function, please check.`, + location + ) + } } + }) + return returnedValue + } } } } diff --git a/packages/webpack-plugin/lib/platform/template/wx/index.js b/packages/webpack-plugin/lib/platform/template/wx/index.js index 1da86356b1..f6673f089b 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/index.js +++ b/packages/webpack-plugin/lib/platform/template/wx/index.js @@ -384,6 +384,10 @@ module.exports = function getSpec ({ warn, error }) { } }, web ({ name, value }, { eventRules, el, usingComponents }) { + // const parsed = parseMustacheWithContext(value); + // if (parsed.hasBinding) { + // value = "__invokeHandler(" + parsed.result + ", $event)"; + // } const match = this.test.exec(name) const prefix = match[1] const eventName = match[2] diff --git a/packages/webpack-plugin/lib/template-compiler/compiler.js b/packages/webpack-plugin/lib/template-compiler/compiler.js index 47a80d6738..7889825705 100644 --- a/packages/webpack-plugin/lib/template-compiler/compiler.js +++ b/packages/webpack-plugin/lib/template-compiler/compiler.js @@ -1,7 +1,11 @@ const JSON5 = require('json5') const he = require('he') const config = require('../config') -const { MPX_ROOT_VIEW, MPX_APP_MODULE_ID, PARENT_MODULE_ID } = require('../utils/const') +const { + MPX_ROOT_VIEW, + MPX_APP_MODULE_ID, + PARENT_MODULE_ID +} = require('../utils/const') const normalize = require('../utils/normalize') const { normalizeCondition } = require('../utils/match-condition') const isValidIdentifierStr = require('../utils/is-valid-identifier-str') @@ -29,12 +33,13 @@ const no = function () { */ // Regular Expressions for parsing tags and attributes -const attribute = /^\s*([^\s"'<>/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/ +const attribute = + /^\s*([^\s"'<>/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/ const ncname = '[a-zA-Z_][\\w\\-\\.]*' const qnameCapture = '((?:' + ncname + '\\:)?' + ncname + ')' -const startTagOpen = new RegExp(('^<' + qnameCapture)) +const startTagOpen = new RegExp('^<' + qnameCapture) const startTagClose = /^\s*(\/?)>/ -const endTag = new RegExp(('^<\\/' + qnameCapture + '[^>]*>')) +const endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>') const doctype = /^]+>/i const comment = /^