diff --git a/packages/core/src/core/mergeOptions.js b/packages/core/src/core/mergeOptions.js
index 517c7c41cb..8b7eaebb3a 100644
--- a/packages/core/src/core/mergeOptions.js
+++ b/packages/core/src/core/mergeOptions.js
@@ -122,9 +122,10 @@ function extractObservers (options) {
Object.keys(props).forEach(key => {
const prop = props[key]
if (prop && prop.observer) {
+ let callback = prop.observer
+ delete prop.observer
mergeWatch(key, {
handler (...rest) {
- let callback = prop.observer
if (typeof callback === 'string') {
callback = this[callback]
}
diff --git a/packages/core/src/core/proxy.js b/packages/core/src/core/proxy.js
index 54d4603f55..15ff6719c6 100644
--- a/packages/core/src/core/proxy.js
+++ b/packages/core/src/core/proxy.js
@@ -1,4 +1,4 @@
-import { reactive } from '../observer/reactive'
+import { reactive, defineReactive } from '../observer/reactive'
import { ReactiveEffect, pauseTracking, resetTracking } from '../observer/effect'
import { effectScope } from '../platform/export/index'
import { watch } from '../observer/watch'
@@ -111,7 +111,7 @@ export default class MpxProxy {
this.uid = uid++
this.name = options.name || ''
this.options = options
- this.ignoreReactivePattern = this.options.options?.ignoreReactivePattern
+ this.shallowReactivePattern = this.options.options?.shallowReactivePattern
// beforeCreate -> created -> mounted -> unmounted
this.state = BEFORECREATE
this.ignoreProxyMap = makeMap(Mpx.config.ignoreProxyWhiteList)
@@ -145,10 +145,12 @@ export default class MpxProxy {
this.initApi()
}
- processIgnoreReactive (obj) {
- if (this.ignoreReactivePattern && isObject(obj)) {
+ processShallowReactive (obj) {
+ if (this.shallowReactivePattern && isObject(obj)) {
Object.keys(obj).forEach((key) => {
- if (this.ignoreReactivePattern.test(key)) {
+ if (this.shallowReactivePattern.test(key)) {
+ // 命中shallowReactivePattern的属性将其设置为 shallowReactive
+ defineReactive(obj, key, obj[key], true)
Object.defineProperty(obj, key, {
enumerable: true,
// set configurable to false to skip defineReactive
@@ -290,10 +292,10 @@ export default class MpxProxy {
if (isReact) {
// react模式下props内部对象透传无需深clone,依赖对象深层的数据响应触发子组件更新
this.props = this.target.__getProps()
- reactive(this.processIgnoreReactive(this.props))
+ reactive(this.processShallowReactive(this.props))
} else {
this.props = diffAndCloneA(this.target.__getProps(this.options)).clone
- reactive(this.processIgnoreReactive(this.props))
+ reactive(this.processShallowReactive(this.props))
}
proxy(this.target, this.props, undefined, false, this.createProxyConflictHandler('props'))
}
@@ -333,7 +335,7 @@ export default class MpxProxy {
if (isFunction(dataFn)) {
Object.assign(this.data, callWithErrorHandling(dataFn.bind(this.target), this, 'data function'))
}
- reactive(this.processIgnoreReactive(this.data))
+ reactive(this.processShallowReactive(this.data))
proxy(this.target, this.data, undefined, false, this.createProxyConflictHandler('data'))
this.collectLocalKeys(this.data)
}
@@ -514,7 +516,7 @@ export default class MpxProxy {
if (hasOwn(renderData, key)) {
const data = renderData[key]
const firstKey = getFirstKey(key)
- if (!this.localKeysMap[firstKey] || (this.ignoreReactivePattern && this.ignoreReactivePattern.test(firstKey))) {
+ if (!this.localKeysMap[firstKey]) {
continue
}
// 外部clone,用于只需要clone的场景
diff --git a/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js b/packages/core/src/platform/builtInMixins/proxyEventMixin.web.js
index 019c74b158..1938d8e349 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 } from '@mpxjs/utils'
+import { setByPath, error, parseDataset } from '@mpxjs/utils'
+import Mpx from '../../index'
export default function proxyEventMixin () {
return {
@@ -19,11 +20,58 @@ export default function proxyEventMixin () {
const value = filterMethod ? (innerFilter[filterMethod] ? innerFilter[filterMethod](originValue) : typeof this[filterMethod] === 'function' && this[filterMethod]) : originValue
setByPath(this, expr, value)
},
- __invokeHandler (eventName, $event) {
- const handler = this[eventName]
- if (handler && typeof handler === 'function') {
- handler.call(this, $event)
+ __invoke (rawEvent, eventConfig = []) {
+ if (typeof Mpx.config.proxyEventHandler === 'function') {
+ try {
+ Mpx.config.proxyEventHandler(rawEvent)
+ } catch (e) {}
}
+ const location = this.__mpxProxy.options.mpxFileResource
+
+ if (rawEvent.target && !rawEvent.target._datasetProcessed) {
+ const originalDataset = rawEvent.target.dataset
+ Object.defineProperty(rawEvent.target, 'dataset', {
+ get: () => parseDataset(originalDataset),
+ configurable: true,
+ enumerable: true
+ })
+ rawEvent.target._datasetProcessed = true
+ }
+ if (rawEvent.currentTarget && !rawEvent.currentTarget._datasetProcessed) {
+ const originalDataset = rawEvent.currentTarget.dataset
+ Object.defineProperty(rawEvent.currentTarget, 'dataset', {
+ get: () => parseDataset(originalDataset),
+ configurable: true,
+ enumerable: true
+ })
+ rawEvent.currentTarget._datasetProcessed = true
+ }
+
+ 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/core/src/platform/env/event.js b/packages/core/src/platform/env/event.js
index 82cb7bf77a..d6fdb09b66 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) => {
+ const 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)
+ 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/core/src/platform/patch/getDefaultOptions.js b/packages/core/src/platform/patch/getDefaultOptions.js
index 5701775f15..38b39e99d4 100644
--- a/packages/core/src/platform/patch/getDefaultOptions.js
+++ b/packages/core/src/platform/patch/getDefaultOptions.js
@@ -23,13 +23,11 @@ function transformProperties (properties) {
} else {
newFiled = Object.assign({}, rawFiled)
}
- const rawObserver = rawFiled?.observer
newFiled.observer = function (value, oldValue) {
if (this.__mpxProxy) {
this[key] = value
this.__mpxProxy.propsUpdated()
}
- rawObserver && rawObserver.call(this, value, oldValue)
}
newProps[key] = newFiled
})
diff --git a/packages/webpack-plugin/lib/platform/template/wx/index.js b/packages/webpack-plugin/lib/platform/template/wx/index.js
index cc6335c642..1da86356b1 100644
--- a/packages/webpack-plugin/lib/platform/template/wx/index.js
+++ b/packages/webpack-plugin/lib/platform/template/wx/index.js
@@ -384,10 +384,6 @@ 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 9445aad991..2781eee4be 100644
--- a/packages/webpack-plugin/lib/template-compiler/compiler.js
+++ b/packages/webpack-plugin/lib/template-compiler/compiler.js
@@ -1157,6 +1157,49 @@ function getModelConfig (el, match) {
}
}
+function processEventWeb (el) {
+ const eventConfigMap = {}
+ el.attrsList.forEach(function ({ name, value }) {
+ if (/^@[a-zA-Z]+$/.test(name)) {
+ const parsedFunc = parseFuncStr(value)
+ if (parsedFunc) {
+ if (!eventConfigMap[name]) {
+ eventConfigMap[name] = {
+ configs: []
+ }
+ }
+ eventConfigMap[name].configs.push(
+ Object.assign({ name, value }, parsedFunc)
+ )
+ }
+ }
+ })
+
+ // let wrapper
+ for (const name in eventConfigMap) {
+ const { configs } = eventConfigMap[name]
+ if (!configs.length) continue
+ configs.forEach(({ name }) => {
+ if (name) {
+ // 清空原始事件绑定
+ let has
+ do {
+ has = getAndRemoveAttr(el, name).has
+ } while (has)
+ }
+ })
+ const value = `(e)=>__invoke(e, [${configs.map(
+ (item) => item.expStr
+ )}])`
+ addAttrs(el, [
+ {
+ name,
+ value
+ }
+ ])
+ }
+}
+
function processEventReact (el) {
const eventConfigMap = {}
el.attrsList.forEach(function ({ name, value }) {
@@ -2642,6 +2685,7 @@ function processElement (el, root, options, meta) {
// 预处理代码维度条件编译
processIfWeb(el)
processScoped(el)
+ processEventWeb(el)
// processWebExternalClassesHack(el, options)
processExternalClasses(el, options)
processComponentGenericsWeb(el, options, meta)
diff --git a/packages/webpack-plugin/test/platform/common/mode.spec.js b/packages/webpack-plugin/test/platform/common/mode.spec.js
index 7aa4e81e5b..7df73a9d2b 100644
--- a/packages/webpack-plugin/test/platform/common/mode.spec.js
+++ b/packages/webpack-plugin/test/platform/common/mode.spec.js
@@ -76,7 +76,7 @@ describe('template should transform correct', function () {
it('should work correct with web mode', function () {
const input = ''
const output = compileTemplate(input, { mode: 'web' })
- expect(output).toBe('获取用户信息')
+ expect(output).toBe("__invoke(e, [[\"handleClick\"]])'>获取用户信息")
})
it('should work normal if no attr in tag', function () {