Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/didi/mpx
Browse files Browse the repository at this point in the history
  • Loading branch information
hiyuki committed Jan 14, 2025
2 parents e2b5cdb + 26559f9 commit 3ce41dc
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 69 deletions.
3 changes: 2 additions & 1 deletion packages/core/src/core/mergeOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
Expand Down
20 changes: 11 additions & 9 deletions packages/core/src/core/proxy.js
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'))
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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的场景
Expand Down
58 changes: 53 additions & 5 deletions packages/core/src/platform/builtInMixins/proxyEventMixin.web.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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
}
}
}
Expand Down
91 changes: 44 additions & 47 deletions packages/core/src/platform/env/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -72,36 +75,30 @@ function MpxEvent (layer) {
changedTouches,
touches: changedTouches,
detail: {
// pc端点击事件可能没有changedTouches,所以直接从 event中取
x: changedTouches[0]?.pageX || event.pageX || 0,
y: changedTouches[0]?.pageY || event.pageY || 0
}
})
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)
}
}
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/platform/patch/getDefaultOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand Down
4 changes: 0 additions & 4 deletions packages/webpack-plugin/lib/platform/template/wx/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
44 changes: 44 additions & 0 deletions packages/webpack-plugin/lib/template-compiler/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) {
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion packages/webpack-plugin/test/platform/common/mode.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('template should transform correct', function () {
it('should work correct with web mode', function () {
const input = '<button @click@web="handleClick">获取用户信息</button>'
const output = compileTemplate(input, { mode: 'web' })
expect(output).toBe('<mpx-button @click="handleClick">获取用户信息</mpx-button>')
expect(output).toBe("<mpx-button @click='(e)=>__invoke(e, [[\"handleClick\"]])'>获取用户信息</mpx-button>")
})

it('should work normal if no attr in tag', function () {
Expand Down

0 comments on commit 3ce41dc

Please sign in to comment.