Skip to content

Commit

Permalink
feat(runtime-mini): 修复微信转支付宝 observer和created触发时序问题 & 修复组件调用batchedUp…
Browse files Browse the repository at this point in the history
…dates报错问题
  • Loading branch information
hwaphon committed Jan 15, 2024
1 parent 998f4c9 commit 8dad870
Showing 1 changed file with 48 additions and 7 deletions.
55 changes: 48 additions & 7 deletions packages/runtime-mini/src/alipay/componentToAlipay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ function injectEventSupport(options: Record<string, any>) {
: {}

// 支付宝组件传递函数时 必须以 on 开头并且 on 后的第一个字母必须大写(微信必须全小写)
const callEventHandler = (eventName: string)=> {
const callEventHandler = (eventName: string) => {
const currentTarget = eventObj.currentTarget || {}
const target = eventObj.target || {}
const e = {
Expand Down Expand Up @@ -362,18 +362,24 @@ function injectPropertiesAndObserversSupport(options: Record<string, any>) {

// data变化触发双向绑定
this.props.onMorChildTWBProxy?.(this.data, this.props)

// created 生命周期触发之后, 会将 __createdEmitCallbacks__ 标记为null,只有首次进入时给当前组件实例初始化一个待执行队列
if (
isLifetimesSupported &&
typeof this.__createdEmitCallbacks__ === 'undefined'
)
this.__createdEmitCallbacks__ = {}
// 用于判断 nextProps 不为空对象
let hasProps = false
const updateProps: Record<string, any> = {}

// 遍历所有更新的 prop 并触发更新
for (const prop in nextProps) {
// 支付宝中 prop 为函数时, 通常代表事件, 此处直接跳过赋值
if (typeof nextProps[prop] === 'function') continue
// if (typeof nextProps[prop] === 'function') continue

// 哪些 prop 发生了改变
const isPropChanged = nextProps[prop] !== this.props[prop]

if (isPropChanged) {
updateProps[prop] = nextProps[prop]
}
Expand Down Expand Up @@ -404,11 +410,17 @@ function injectPropertiesAndObserversSupport(options: Record<string, any>) {
!(pureDataPattern && pureDataPattern.test(prop))
) {
try {
propertiesWithObserver[prop].call(
const updateFn = propertiesWithObserver[prop].bind(
this,
nextProps[prop],
originalValue
)
// created 执行之后 或者 lifetimes 不支持的场景,__createdEmitCallbacks__ 会为 null或者 undefined
if (this.__createdEmitCallbacks__) {
this.__createdEmitCallbacks__[prop] = updateFn // 推入待执行队列,等 created 生命周期触发后在执行,确保首次执行顺序
} else {
updateFn()
}
} catch (e) {
logger.error(
`组件 ${this.is} 属性监听器 properties.${prop}.observer 报错: `,
Expand All @@ -422,7 +434,7 @@ function injectPropertiesAndObserversSupport(options: Record<string, any>) {
this[MOR_FIRST_DERIVE_DATA_FROM_PROPS] = true
if (firstDeriveWithObserversSupported) return

// 触发一次更新
// // 触发一次更新
if (hasProps) {
this.setData(updateProps)
}
Expand Down Expand Up @@ -484,6 +496,29 @@ function hookComponentLifeCycle(options: Record<string, any>) {
delete options.export
}

const executeCreatedCallbacks = function () {
// created 生命周期触发时执行的回调(主要是为了解决首次 observer 和 created 生命周期的执行顺序问题)
// 在微信中,created 触发时机早于 observer,在支付宝中由于该生命周期使用 deriveDataFromProps 模拟,而 deriveDataFromProps
// 是先于 created 执行的,所以导致微信转支付宝场景下,observer 执行时机先于 created,这让某些依赖生命周期执行顺序的功能异常,所以在此做兼容适配
if (
!(
this.__createdEmitCallbacks__ &&
typeof this.__createdEmitCallbacks__ === 'object'
)
)
return
const keys = Object.keys(this.__createdEmitCallbacks__)
if (keys.length > 0) {
keys.forEach((key) => {
const value = this.__createdEmitCallbacks__[key]
value()
})

// 在这里将缓存队里置为 null,用于标记 created 已经执行过了,方便后续在 deriveDataFromProps 周期中进行判断(推入队列 or 直接执行)
this.__createdEmitCallbacks__ = null
}
}

/**
* 生命周期执行顺序:
* 1. 执行 hackSetData 劫持 setData,把需要变更的数据存入 MOR_PREV_DATA
Expand All @@ -502,6 +537,7 @@ function hookComponentLifeCycle(options: Record<string, any>) {
* 8. 执行 attached 生命周期
* …
*/

options.onInit = compose([
hackSetData,
injectCreateIntersectionObserverSupport(),
Expand All @@ -513,7 +549,8 @@ function hookComponentLifeCycle(options: Record<string, any>) {
if (isLifetimesSupported) {
options.lifetimes.created = compose([
initPropertiesAndData,
callOriginalFn('created')
callOriginalFn('created'),
executeCreatedCallbacks
])
}

Expand Down Expand Up @@ -542,7 +579,11 @@ function hookComponentLifeCycle(options: Record<string, any>) {
function injectComponentInstanceMethodSupport(options: Record<string, any>) {
// 批量更新数据 支持
options.groupSetData = function (cb: () => void) {
this.$batchedUpdates(cb)
if (typeof this.$batchedUpdates === 'function') {
this.$batchedUpdates(cb)
} else {
cb()
}
}

// 获取页面标识符
Expand Down

0 comments on commit 8dad870

Please sign in to comment.