@mpxjs/core 2.10.17-beta.1 → 2.10.17-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  // declaration for mpx mode
2
- declare let __mpx_mode__: 'wx' | 'ali' | 'swan' | 'qq' | 'tt' | 'web' | 'dd' | 'qa' | 'jd' | 'android' | 'ios' | 'harmony'
2
+ declare let __mpx_mode__: 'wx' | 'ali' | 'swan' | 'qq' | 'tt' | 'web' | 'dd' | 'qa' | 'jd' | 'android' | 'ios' | 'harmony' | 'ks'
3
3
 
4
4
  // declaration for mpx env
5
5
  declare let __mpx_env__: string
package/@types/index.d.ts CHANGED
@@ -412,7 +412,7 @@ interface MpxConfig {
412
412
  rnConfig: RnConfig,
413
413
  }
414
414
 
415
- type SupportedMode = 'wx' | 'ali' | 'qq' | 'swan' | 'tt' | 'web' | 'qa'
415
+ type SupportedMode = 'wx' | 'ali' | 'qq' | 'swan' | 'tt' | 'web' | 'qa'| 'ks' | 'jd' | 'dd'
416
416
 
417
417
  interface ImplementOptions {
418
418
  modes?: Array<SupportedMode>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/core",
3
- "version": "2.10.17-beta.1",
3
+ "version": "2.10.17-beta.4",
4
4
  "description": "mpx runtime core",
5
5
  "keywords": [
6
6
  "miniprogram",
@@ -9,6 +9,7 @@ import wxToTtRule from './wxToTt'
9
9
  import wxToDdRule from './wxToDd'
10
10
  import wxToJdRule from './wxToJd'
11
11
  import wxToReactRule from './wxToReact'
12
+ import wxToKsRule from './wxToKs'
12
13
 
13
14
  /**
14
15
  * 转换规则包含四点
@@ -38,7 +39,8 @@ const rulesMap = {
38
39
  wxToJd: extend({}, defaultConvertRule, wxToJdRule),
39
40
  wxToIos: extend({}, defaultConvertRule, wxToReactRule),
40
41
  wxToAndroid: extend({}, defaultConvertRule, wxToReactRule),
41
- wxToHarmony: extend({}, defaultConvertRule, wxToReactRule)
42
+ wxToHarmony: extend({}, defaultConvertRule, wxToReactRule),
43
+ wxToKs: extend({}, defaultConvertRule, wxToKsRule)
42
44
  }
43
45
 
44
46
  export function getConvertRule (convertMode) {
@@ -8,7 +8,8 @@ const convertModes = {
8
8
  'wx-dd': 'wxToDd',
9
9
  'wx-ios': 'wxToIos',
10
10
  'wx-android': 'wxToAndroid',
11
- 'wx-harmony': 'wxToHarmony'
11
+ 'wx-harmony': 'wxToHarmony',
12
+ 'wx-ks': 'wxToKs'
12
13
  }
13
14
 
14
15
  export function getConvertMode (srcMode) {
@@ -0,0 +1,21 @@
1
+ import { error } from '@mpxjs/utils'
2
+
3
+ const BEHAVIORS_MAP = [
4
+ 'wx://form-field',
5
+ 'wx://form-field-group',
6
+ 'wx://form-field-button',
7
+ 'wx://component-export'
8
+ ]
9
+
10
+ export default {
11
+ convert (options) {
12
+ if (options.behaviors) {
13
+ options.behaviors.forEach((behavior, idx) => {
14
+ if (BEHAVIORS_MAP.includes(behavior)) {
15
+ error(`Built-in behavior "${behavior}" is not supported in ks environment!`, global.currentResource || global.currentModuleId)
16
+ options.behaviors.splice(idx, 1)
17
+ }
18
+ })
19
+ }
20
+ }
21
+ }
@@ -6,7 +6,8 @@ import {
6
6
  diffAndCloneA,
7
7
  error,
8
8
  hasOwn,
9
- isDev
9
+ isDev,
10
+ getDefaultValueByType
10
11
  } from '@mpxjs/utils'
11
12
  import { implemented } from '../core/implement'
12
13
 
@@ -56,6 +57,12 @@ export default {
56
57
  return diffAndCloneA(prop.value).clone
57
58
  }
58
59
  : prop.value
60
+ } else {
61
+ // 没有显式设置value时,根据type自动添加默认值,与微信小程序原生行为保持一致
62
+ const defaultValue = getDefaultValueByType(prop.type, 'web')
63
+ if (defaultValue !== undefined) {
64
+ newProp.default = defaultValue
65
+ }
59
66
  }
60
67
  props[key] = newProp
61
68
  } else {
package/src/core/proxy.js CHANGED
@@ -31,8 +31,10 @@ import {
31
31
  wrapMethodsWithErrorHandling,
32
32
  warn,
33
33
  error,
34
- getEnvObj
34
+ getEnvObj,
35
+ def
35
36
  } from '@mpxjs/utils'
37
+ import { renderHelperDefs } from '../platform/builtInMixins/renderHelperMixin'
36
38
  import {
37
39
  BEFORECREATE,
38
40
  CREATED,
@@ -190,6 +192,7 @@ export default class MpxProxy {
190
192
  this.callHook(CREATED)
191
193
 
192
194
  if (!isWeb && !isReact) {
195
+ this.initRenderHelpers()
193
196
  this.initRender()
194
197
  }
195
198
 
@@ -335,7 +338,7 @@ export default class MpxProxy {
335
338
  createSelectorQuery: this.target.createSelectorQuery ? this.target.createSelectorQuery.bind(this.target) : envObj.createSelectorQuery.bind(envObj),
336
339
  createIntersectionObserver: this.target.createIntersectionObserver ? this.target.createIntersectionObserver.bind(this.target) : envObj.createIntersectionObserver.bind(envObj),
337
340
  getPageId: this.target.getPageId.bind(this.target),
338
- getOpenerEventChannel: this.target.getOpenerEventChannel.bind(this.target)
341
+ getOpenerEventChannel: this.target.getOpenerEventChannel ? this.target.getOpenerEventChannel.bind(this.target) : noop
339
342
  }
340
343
  ])
341
344
  if (!isObject(setupResult)) {
@@ -728,6 +731,15 @@ export default class MpxProxy {
728
731
  this.toggleRecurse(true)
729
732
  }
730
733
 
734
+ initRenderHelpers () {
735
+ if (this.options.__nativeRender__ || __mpx_mode__ !== 'ks') return
736
+ Object.keys(renderHelperDefs).forEach((key) => {
737
+ if (!hasOwn(this.target, key)) {
738
+ def(this.target, key, renderHelperDefs[key])
739
+ }
740
+ })
741
+ }
742
+
731
743
  initRender () {
732
744
  if (this.options.__nativeRender__) return this.doRender()
733
745
 
@@ -57,8 +57,8 @@ export default class MpxScroll {
57
57
  const isIntersecting = change.isIntersecting
58
58
  this.isIntersecting = isIntersecting
59
59
  if (!isIntersecting) {
60
- // 非 inter section 状态下及时清除 transtorm,以免影响正常滚动时元素的 fixed 定位
61
- this.el.style.cssText = ''
60
+ // 非 inter section 状态下及时清除 transform,以免影响正常滚动时元素的 fixed 定位
61
+ this.el.style.transform = ''
62
62
  this.pullDownEventRegister && this.pullDownEventRegister.destroy()
63
63
  } else {
64
64
  this.pullDownEventRegister = new EventRegister(this.el, [
@@ -103,7 +103,12 @@ export default class MpxScroll {
103
103
 
104
104
  transformPage (distance) {
105
105
  this.translateY = distance
106
- this.el.style.cssText = `transform: translateY(${distance}px)`
106
+ if (distance === 0) {
107
+ // 距离为 0 时移除 transform,避免影响页面 fixed 定位
108
+ this.el.style.transform = ''
109
+ } else {
110
+ this.el.style.transform = `translateY(${distance}px)`
111
+ }
107
112
  }
108
113
 
109
114
  onTouchEnd (e) {
@@ -219,8 +224,15 @@ export default class MpxScroll {
219
224
  }
220
225
 
221
226
  onReachBottom (onReachBottomDistance, callback) {
222
- const { bottom } = this.el.getBoundingClientRect()
223
- const mark = bottom - window.innerHeight <= onReachBottomDistance
227
+ const scrollTop = getScrollTop()
228
+ const scrollHeight = document.documentElement.scrollHeight
229
+ const clientHeight = window.innerHeight
230
+
231
+ // 使用 scrollHeight 判断实际内容高度是否超过视口,只有可滚动时才计算触底
232
+ const scrollable = scrollHeight > clientHeight
233
+ // 距离底部的距离 = 内容总高度 - (当前滚动位置 + 视口高度)
234
+ const distanceToBottom = scrollHeight - (scrollTop + clientHeight)
235
+ const mark = scrollable && (distanceToBottom <= onReachBottomDistance)
224
236
 
225
237
  if (!this.bottomReached && mark) {
226
238
  this.bottomReached = true
@@ -57,14 +57,18 @@ export default function getBuiltInMixins ({ type, rawOptions = {} }) {
57
57
  }
58
58
  // 此为纯增强类mixins,原生模式下不需要注入
59
59
  if (!rawOptions.__nativeRender__) {
60
- bulitInMixins = bulitInMixins.concat([
61
- renderHelperMixin(),
60
+ const enhancedMixins = [
62
61
  showMixin(type),
63
62
  i18nMixin(),
64
63
  dynamicRenderHelperMixin(),
65
64
  dynamicSlotMixin(),
66
65
  dynamicRefsMixin()
67
- ])
66
+ ]
67
+ if (__mpx_mode__ !== 'ks') {
68
+ // ks methods 不支持 _ 或者 $ 开头的方法名,所以 ks 不走 methods mixin
69
+ enhancedMixins.unshift(renderHelperMixin())
70
+ }
71
+ bulitInMixins = bulitInMixins.concat(enhancedMixins)
68
72
  }
69
73
  }
70
74
  return bulitInMixins.filter(item => item)
@@ -1,4 +1,5 @@
1
1
  import { CREATED, ONLOAD, ONSHOW, ONHIDE, ONRESIZE } from '../../core/innerLifecycle'
2
+ import { isObject } from '@mpxjs/utils'
2
3
 
3
4
  export default function pageStatusMixin (mixinType) {
4
5
  if (mixinType === 'page') {
@@ -12,8 +13,19 @@ export default function pageStatusMixin (mixinType) {
12
13
  onResize (e) {
13
14
  this.__mpxProxy.callHook(ONRESIZE, [e])
14
15
  },
15
- onLoad (query) {
16
- this.__mpxProxy.callHook(ONLOAD, [query])
16
+ onLoad (rawQuery) {
17
+ if (__mpx_mode__ === 'wx' || __mpx_mode__ === 'qq' || __mpx_mode__ === 'tt') {
18
+ const decodedQuery = {}
19
+ // 处理以上平台直接透传encode的结果,给到onload第二个参数供开发者使用
20
+ if (isObject(rawQuery)) {
21
+ for (const key in rawQuery) {
22
+ decodedQuery[key] = decodeURIComponent(rawQuery[key])
23
+ }
24
+ }
25
+ this.__mpxProxy.callHook(ONLOAD, [rawQuery, decodedQuery])
26
+ } else {
27
+ this.__mpxProxy.callHook(ONLOAD, [rawQuery, rawQuery])
28
+ }
17
29
  }
18
30
  }
19
31
  if (__mpx_mode__ === 'ali') {
@@ -15,6 +15,52 @@ const setRef = function (target, ref, isAsync) {
15
15
  }
16
16
  })
17
17
  }
18
+ const proxyMethods = ['boundingClientRect', 'scrollOffset']
19
+ function rewriteAliCreateSelectorQuery (target) {
20
+ const rawCreateSelectorQuery = target.createSelectorQuery
21
+ target.createSelectorQuery = function (...args) {
22
+ let selectorQuery = rawCreateSelectorQuery.apply(target, args)
23
+ const cbs = []
24
+
25
+ if (typeof selectorQuery === 'undefined') {
26
+ // 兜底 selectorQuery 在ali为 undefined 情况
27
+ // 调用 createSelectorQuery时,组件实例已经被销毁,ali this._originCreateSelectorQuery 返回 undefined。导致后续 selectorQuery[name] 报错
28
+ // 方案:对齐微信,微信实例销毁时,其他调用正常,仅 createSelectorQuery.exec 不执行回调
29
+ // 复现:setTimeout 中调用,倒计时未回调时切换页面
30
+ selectorQuery = {}
31
+ // ['boundingClientRect', 'context', 'exec', 'fields', 'in', 'node', 'scrollOffset', 'select', 'selectAll', 'selectViewport', 'toImage']
32
+ const backupMethodKeys = Object.keys(envObj.createSelectorQuery())
33
+ const backupFn = function () {
34
+ return selectorQuery
35
+ }
36
+ backupMethodKeys.forEach(key => {
37
+ selectorQuery[key] = backupFn
38
+ })
39
+ return selectorQuery
40
+ }
41
+
42
+ proxyMethods.forEach((name) => {
43
+ const originalMethod = selectorQuery[name]
44
+ selectorQuery[name] = function (cb = noop) {
45
+ cbs.push(cb)
46
+ return originalMethod.call(this)
47
+ }
48
+ })
49
+
50
+ const originalExec = selectorQuery.exec
51
+ selectorQuery.exec = function (originalCb = noop) {
52
+ const cb = function (results) {
53
+ results.forEach((item, index) => {
54
+ cbs[index] && cbs[index](item)
55
+ })
56
+ originalCb(results)
57
+ }
58
+ return originalExec.call(this, cb)
59
+ }
60
+
61
+ return selectorQuery
62
+ }
63
+ }
18
64
 
19
65
  export default function getRefsMixin () {
20
66
  const refsMixin = {
@@ -24,8 +70,7 @@ export default function getRefsMixin () {
24
70
  this.__getRefs()
25
71
 
26
72
  if (__mpx_mode__ === 'ali') {
27
- this._originCreateSelectorQuery = this.createSelectorQuery
28
- this.createSelectorQuery = this._createSelectorQuery
73
+ rewriteAliCreateSelectorQuery(this)
29
74
  }
30
75
  },
31
76
  methods: {
@@ -68,51 +113,7 @@ export default function getRefsMixin () {
68
113
  }
69
114
  })
70
115
 
71
- const proxyMethods = ['boundingClientRect', 'scrollOffset']
72
-
73
116
  Object.assign(refsMixin.methods, {
74
- _createSelectorQuery (...args) {
75
- let selectorQuery = this._originCreateSelectorQuery(...args)
76
- const cbs = []
77
-
78
- if (typeof selectorQuery === 'undefined') {
79
- // 兜底 selectorQuery 在ali为 undefined 情况
80
- // 调用 createSelectorQuery时,组件实例已经被销毁,ali this._originCreateSelectorQuery 返回 undefined。导致后续 selectorQuery[name] 报错
81
- // 方案:对齐微信,微信实例销毁时,其他调用正常,仅 createSelectorQuery.exec 不执行回调
82
- // 复现:setTimeout 中调用,倒计时未回调时切换页面
83
- selectorQuery = {}
84
- // ['boundingClientRect', 'context', 'exec', 'fields', 'in', 'node', 'scrollOffset', 'select', 'selectAll', 'selectViewport', 'toImage']
85
- const backupMethodKeys = Object.keys(envObj.createSelectorQuery())
86
- const backupFn = function () {
87
- return selectorQuery
88
- }
89
- backupMethodKeys.forEach(key => {
90
- selectorQuery[key] = backupFn
91
- })
92
- return selectorQuery
93
- }
94
-
95
- proxyMethods.forEach((name) => {
96
- const originalMethod = selectorQuery[name]
97
- selectorQuery[name] = function (cb = noop) {
98
- cbs.push(cb)
99
- return originalMethod.call(this)
100
- }
101
- })
102
-
103
- const originalExec = selectorQuery.exec
104
- selectorQuery.exec = function (originalCb = noop) {
105
- const cb = function (results) {
106
- results.forEach((item, index) => {
107
- cbs[index] && cbs[index](item)
108
- })
109
- originalCb(results)
110
- }
111
- return originalExec.call(this, cb)
112
- }
113
-
114
- return selectorQuery
115
- },
116
117
  selectComponent (selector) {
117
118
  return this.$selectComponent(selector)
118
119
  },
@@ -1,44 +1,46 @@
1
1
  import { getByPath, hasOwn, isObject } from '@mpxjs/utils'
2
2
 
3
- export default function renderHelperMixin () {
4
- return {
5
- methods: {
6
- _i (val, handler) {
7
- let i, l, keys, key
8
- if (Array.isArray(val) || typeof val === 'string') {
9
- for (i = 0, l = val.length; i < l; i++) {
10
- handler.call(this, val[i], i)
11
- }
12
- } else if (typeof val === 'number') {
13
- for (i = 0; i < val; i++) {
14
- handler.call(this, i + 1, i)
15
- }
16
- } else if (isObject(val)) {
17
- keys = Object.keys(val)
18
- for (i = 0, l = keys.length; i < l; i++) {
19
- key = keys[i]
20
- handler.call(this, val[key], key, i)
21
- }
22
- }
23
- },
24
- // collect
25
- _c (key, value) {
26
- if (hasOwn(this.__mpxProxy.renderData, key)) {
27
- return this.__mpxProxy.renderData[key]
28
- }
29
- if (value === undefined) {
30
- value = getByPath(this, key)
31
- }
32
- this.__mpxProxy.renderData[key] = value
33
- return value
34
- },
35
- // simple collect
36
- _sc (key) {
37
- return (this.__mpxProxy.renderData[key] = this[key])
38
- },
39
- _r (skipPre, vnode) {
40
- this.__mpxProxy.renderWithData(skipPre, vnode)
3
+ export const renderHelperDefs = {
4
+ _i (val, handler) {
5
+ let i, l, keys, key
6
+ if (Array.isArray(val) || typeof val === 'string') {
7
+ for (i = 0, l = val.length; i < l; i++) {
8
+ handler.call(this, val[i], i)
9
+ }
10
+ } else if (typeof val === 'number') {
11
+ for (i = 0; i < val; i++) {
12
+ handler.call(this, i + 1, i)
41
13
  }
14
+ } else if (isObject(val)) {
15
+ keys = Object.keys(val)
16
+ for (i = 0, l = keys.length; i < l; i++) {
17
+ key = keys[i]
18
+ handler.call(this, val[key], key, i)
19
+ }
20
+ }
21
+ },
22
+ // collect
23
+ _c (key, value) {
24
+ if (hasOwn(this.__mpxProxy.renderData, key)) {
25
+ return this.__mpxProxy.renderData[key]
26
+ }
27
+ if (value === undefined) {
28
+ value = getByPath(this, key)
42
29
  }
30
+ this.__mpxProxy.renderData[key] = value
31
+ return value
32
+ },
33
+ // simple collect
34
+ _sc (key) {
35
+ return (this.__mpxProxy.renderData[key] = this[key])
36
+ },
37
+ _r (skipPre, vnode) {
38
+ this.__mpxProxy.renderWithData(skipPre, vnode)
39
+ }
40
+ }
41
+
42
+ export default function renderHelperMixin () {
43
+ return {
44
+ methods: renderHelperDefs
43
45
  }
44
46
  }
@@ -1,33 +1,74 @@
1
- import { isObject, isArray, dash2hump, cached, isEmptyObject, hasOwn } from '@mpxjs/utils'
2
- import { Dimensions, StyleSheet } from 'react-native'
1
+ import { isObject, isArray, dash2hump, cached, isEmptyObject, hasOwn, getFocusedNavigation } from '@mpxjs/utils'
2
+ import { StyleSheet, Dimensions } from 'react-native'
3
+ import { reactive } from '../../observer/reactive'
3
4
  import Mpx from '../../index'
4
5
 
5
- const rawDimensions = {
6
- screen: Dimensions.get('screen'),
7
- window: Dimensions.get('window')
6
+ global.__mpxAppDimensionsInfo = {
7
+ window: Dimensions.get('window'),
8
+ screen: Dimensions.get('screen')
8
9
  }
9
- let width, height
10
+ global.__mpxSizeCount = 0
11
+ global.__mpxPageSizeCountMap = reactive({})
10
12
 
11
- function customDimensions (dimensions) {
13
+ global.__GCC = function (className, classMap, classMapValueCache) {
14
+ if (!classMapValueCache.has(className)) {
15
+ const styleObj = classMap[className]?.(global.__formatValue)
16
+ styleObj && classMapValueCache.set(className, styleObj)
17
+ }
18
+ return classMapValueCache.get(className)
19
+ }
20
+
21
+ let dimensionsInfoInitialized = false
22
+ function useDimensionsInfo (dimensions) {
23
+ dimensionsInfoInitialized = true
12
24
  if (typeof Mpx.config.rnConfig?.customDimensions === 'function') {
13
25
  dimensions = Mpx.config.rnConfig.customDimensions(dimensions) || dimensions
14
26
  }
15
- width = dimensions.screen.width
16
- height = dimensions.screen.height
27
+ global.__mpxAppDimensionsInfo.window = dimensions.window
28
+ global.__mpxAppDimensionsInfo.screen = dimensions.screen
17
29
  }
18
30
 
19
- Dimensions.addEventListener('change', customDimensions)
31
+ function getPageSize (window = global.__mpxAppDimensionsInfo.screen) {
32
+ return window.width + 'x' + window.height
33
+ }
34
+
35
+ Dimensions.addEventListener('change', ({ window, screen }) => {
36
+ const oldScreen = getPageSize(global.__mpxAppDimensionsInfo.screen)
37
+ useDimensionsInfo({ window, screen })
38
+
39
+ // 对比 screen 高宽是否存在变化
40
+ if (getPageSize(screen) === oldScreen) return
20
41
 
42
+ global.__classCaches?.forEach(cache => cache?.clear())
43
+
44
+ // 更新全局和栈顶页面的标记,其他后台页面的标记在show之后更新
45
+ global.__mpxSizeCount++
46
+
47
+ const navigation = getFocusedNavigation()
48
+
49
+ if (navigation) {
50
+ global.__mpxPageSizeCountMap[navigation.pageId] = global.__mpxSizeCount
51
+ if (hasOwn(global.__mpxPageStatusMap, navigation.pageId)) {
52
+ global.__mpxPageStatusMap[navigation.pageId] = `resize${global.__mpxSizeCount}`
53
+ }
54
+ }
55
+ })
56
+
57
+ // TODO: 1 目前测试鸿蒙下折叠屏screen固定为展开状态下屏幕尺寸,仅window会变化,且window包含状态栏高度
58
+ // TODO: 2 存在部分安卓折叠屏机型在折叠/展开切换时,Dimensions监听到的width/height尺寸错误,并触发多次问题
21
59
  function rpx (value) {
60
+ const screenInfo = global.__mpxAppDimensionsInfo.screen
22
61
  // rn 单位 dp = 1(css)px = 1 物理像素 * pixelRatio(像素比)
23
62
  // px = rpx * (750 / 屏幕宽度)
24
- return value * width / 750
63
+ return value * screenInfo.width / 750
25
64
  }
26
65
  function vw (value) {
27
- return value * width / 100
66
+ const screenInfo = global.__mpxAppDimensionsInfo.screen
67
+ return value * screenInfo.width / 100
28
68
  }
29
69
  function vh (value) {
30
- return value * height / 100
70
+ const screenInfo = global.__mpxAppDimensionsInfo.screen
71
+ return value * screenInfo.height / 100
31
72
  }
32
73
 
33
74
  const unit = {
@@ -38,8 +79,14 @@ const unit = {
38
79
 
39
80
  const empty = {}
40
81
 
41
- function formatValue (value) {
42
- if (width === undefined) customDimensions(rawDimensions)
82
+ function formatValue (value, unitType) {
83
+ if (!dimensionsInfoInitialized) useDimensionsInfo(global.__mpxAppDimensionsInfo)
84
+ if (unitType === 'hairlineWidth') {
85
+ return StyleSheet.hairlineWidth
86
+ }
87
+ if (unitType && typeof unit[unitType] === 'function') {
88
+ return unit[unitType](+value)
89
+ }
43
90
  const matched = unitRegExp.exec(value)
44
91
  if (matched) {
45
92
  if (!matched[2] || matched[2] === 'px') {
@@ -180,29 +227,58 @@ function isNativeStyle (style) {
180
227
  )
181
228
  }
182
229
 
230
+ function getMediaStyle (media) {
231
+ if (!media || !media.length) return {}
232
+ const { width } = global.__mpxAppDimensionsInfo.screen
233
+ return media.reduce((styleObj, item) => {
234
+ const { options = {}, value = {} } = item
235
+ const { minWidth, maxWidth } = options
236
+ if (!isNaN(minWidth) && !isNaN(maxWidth) && width >= minWidth && width <= maxWidth) {
237
+ Object.assign(styleObj, value)
238
+ } else if (!isNaN(minWidth) && width >= minWidth) {
239
+ Object.assign(styleObj, value)
240
+ } else if (!isNaN(maxWidth) && width <= maxWidth) {
241
+ Object.assign(styleObj, value)
242
+ }
243
+ return styleObj
244
+ }, {})
245
+ }
246
+
183
247
  export default function styleHelperMixin () {
184
248
  return {
185
249
  methods: {
250
+ __getSizeCount () {
251
+ return global.__mpxPageSizeCountMap[this.__pageId]
252
+ },
186
253
  __getClass (staticClass, dynamicClass) {
187
254
  return concat(staticClass, stringifyDynamicClass(dynamicClass))
188
255
  },
189
256
  __getStyle (staticClass, dynamicClass, staticStyle, dynamicStyle, hide) {
190
257
  const isNativeStaticStyle = staticStyle && isNativeStyle(staticStyle)
191
258
  let result = isNativeStaticStyle ? [] : {}
192
- const mergeResult = isNativeStaticStyle ? (o) => result.push(o) : (o) => Object.assign(result, o)
193
-
194
- const classMap = this.__getClassMap?.() || {}
195
- const appClassMap = global.__getAppClassMap?.() || {}
259
+ const mergeResult = isNativeStaticStyle ? (...args) => result.push(...args) : (...args) => Object.assign(result, ...args)
260
+ // 使用一下 __getSizeCount 触发其 get
261
+ this.__getSizeCount()
196
262
 
197
263
  if (staticClass || dynamicClass) {
198
264
  // todo 当前为了复用小程序unocss产物,暂时进行mpEscape,等后续正式支持unocss后可不进行mpEscape
199
265
  const classString = mpEscape(concat(staticClass, stringifyDynamicClass(dynamicClass)))
266
+
200
267
  classString.split(/\s+/).forEach((className) => {
201
- if (classMap[className]) {
202
- mergeResult(classMap[className])
203
- } else if (appClassMap[className]) {
204
- // todo 全局样式在每个页面和组件中生效,以支持全局原子类,后续支持样式模块复用后可考虑移除
205
- mergeResult(appClassMap[className])
268
+ let localStyle, appStyle
269
+ const getAppClassStyle = global.__getAppClassStyle || noop
270
+ if (localStyle = this.__getClassStyle?.(className)) {
271
+ if (localStyle._media?.length) {
272
+ mergeResult(localStyle._default, getMediaStyle(localStyle._media))
273
+ } else {
274
+ mergeResult(localStyle)
275
+ }
276
+ } else if (appStyle = global.__getAppClassStyle?.(className)) {
277
+ if (appStyle._media?.length) {
278
+ mergeResult(appStyle._default, getMediaStyle(appStyle._media))
279
+ } else {
280
+ mergeResult(appStyle)
281
+ }
206
282
  } else if (isObject(this.__props[className])) {
207
283
  // externalClasses必定以对象形式传递下来
208
284
  mergeResult(this.__props[className])
@@ -1,6 +1,6 @@
1
1
  import transferOptions from '../core/transferOptions'
2
2
  import builtInKeysMap from './patch/builtInKeysMap'
3
- import { makeMap, spreadProp, getFocusedNavigation, hasOwn } from '@mpxjs/utils'
3
+ import { makeMap, spreadProp, getFocusedNavigation, hasOwn, callWithErrorHandling } from '@mpxjs/utils'
4
4
  import { mergeLifecycle } from '../convertor/mergeLifecycle'
5
5
  import { LIFECYCLE } from '../platform/patch/lifecycle/index'
6
6
  import Mpx from '../index'
@@ -14,10 +14,6 @@ import MpxNav from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-
14
14
 
15
15
  const appHooksMap = makeMap(mergeLifecycle(LIFECYCLE).app)
16
16
 
17
- function getPageSize (window = ReactNative.Dimensions.get('window')) {
18
- return window.width + 'x' + window.height
19
- }
20
-
21
17
  function filterOptions (options, appData) {
22
18
  const newOptions = {}
23
19
  Object.keys(options).forEach(key => {
@@ -74,7 +70,7 @@ export default function createApp (options) {
74
70
  )
75
71
  }
76
72
  const getComponent = () => {
77
- return item.displayName ? item : item()
73
+ return item.displayName ? item : callWithErrorHandling(item, null, 'require page script')
78
74
  }
79
75
  if (key === initialRouteName) {
80
76
  return createElement(Stack.Screen, {
@@ -207,22 +203,9 @@ export default function createApp (options) {
207
203
  if (Mpx.config.rnConfig.disableAppStateListener) return
208
204
  onAppStateChange(state)
209
205
  })
210
-
211
- let count = 0
212
- let lastPageSize = getPageSize()
213
- const resizeSubScription = ReactNative.Dimensions.addEventListener('change', ({ window }) => {
214
- const pageSize = getPageSize(window)
215
- if (pageSize === lastPageSize) return
216
- lastPageSize = pageSize
217
- const navigation = getFocusedNavigation()
218
- if (navigation && hasOwn(global.__mpxPageStatusMap, navigation.pageId)) {
219
- global.__mpxPageStatusMap[navigation.pageId] = `resize${count++}`
220
- }
221
- })
222
206
  return () => {
223
207
  appState.state = 'exit'
224
208
  changeSubscription && changeSubscription.remove()
225
- resizeSubScription && resizeSubScription.remove()
226
209
  }
227
210
  }, [])
228
211
 
@@ -3,7 +3,7 @@ import * as ReactNative from 'react-native'
3
3
  import { ReactiveEffect } from '../../observer/effect'
4
4
  import { watch } from '../../observer/watch'
5
5
  import { del, reactive, set } from '../../observer/reactive'
6
- import { hasOwn, isFunction, noop, isObject, isArray, getByPath, collectDataset, hump2dash, dash2hump, callWithErrorHandling, wrapMethodsWithErrorHandling, error, setFocusedNavigation } from '@mpxjs/utils'
6
+ import { hasOwn, isFunction, noop, isObject, isArray, getByPath, collectDataset, hump2dash, dash2hump, callWithErrorHandling, wrapMethodsWithErrorHandling, error, setFocusedNavigation, getDefaultValueByType } from '@mpxjs/utils'
7
7
  import MpxProxy from '../../core/proxy'
8
8
  import { BEFOREUPDATE, ONLOAD, UPDATED, ONSHOW, ONHIDE, ONRESIZE, REACTHOOKSEXEC } from '../../core/innerLifecycle'
9
9
  import mergeOptions from '../../core/mergeOptions'
@@ -20,8 +20,8 @@ import { PortalHost, useSafeAreaInsets } from '../env/navigationHelper'
20
20
  import { useInnerHeaderHeight } from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-nav'
21
21
 
22
22
  function getSystemInfo () {
23
- const windowDimensions = ReactNative.Dimensions.get('window')
24
- const screenDimensions = ReactNative.Dimensions.get('screen')
23
+ const windowDimensions = global.__mpxAppDimensionsInfo.window
24
+ const screenDimensions = global.__mpxAppDimensionsInfo.screen
25
25
  return {
26
26
  deviceOrientation: windowDimensions.width > windowDimensions.height ? 'landscape' : 'portrait',
27
27
  size: {
@@ -172,8 +172,8 @@ const instanceProto = {
172
172
  type: field
173
173
  }
174
174
  }
175
- // 处理props默认值
176
- propsData[key] = field.value
175
+ // 处理props默认值,没有显式设置value时根据type获取默认值,与微信小程序原生行为保持一致
176
+ propsData[key] = hasOwn(field, 'value') ? field.value : getDefaultValueByType(field.type)
177
177
  }
178
178
  }
179
179
  })
@@ -295,17 +295,14 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
295
295
  instance[key] = method.bind(instance)
296
296
  })
297
297
  }
298
- const loadParams = {}
298
+
299
299
  if (type === 'page') {
300
300
  const props = propsRef.current
301
301
  instance.route = props.route.name
302
302
  global.__mpxPagesMap = global.__mpxPagesMap || {}
303
303
  global.__mpxPagesMap[props.route.key] = [instance, props.navigation]
304
304
  setFocusedNavigation(props.navigation)
305
-
306
- if (!global.__mpxAppHotLaunched && global.__mpxInitialRunParams) {
307
- Object.assign(loadParams, global.__mpxInitialRunParams)
308
- }
305
+ set(global.__mpxPageSizeCountMap, pageId, global.__mpxSizeCount)
309
306
  // App onLaunch 在 Page created 之前执行
310
307
  if (!global.__mpxAppHotLaunched && global.__mpxAppOnLaunch) {
311
308
  global.__mpxAppOnLaunch(props.navigation)
@@ -317,13 +314,14 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
317
314
 
318
315
  if (type === 'page') {
319
316
  const props = propsRef.current
320
- // 此处拿到的props.route.params内属性的value被进行过了一次decode, 不符合预期,此处额外进行一次encode来与微信对齐
321
- if (isObject(props.route.params)) {
322
- for (const key in props.route.params) {
323
- loadParams[key] = encodeURIComponent(props.route.params[key])
317
+ const decodedQuery = {}
318
+ const rawQuery = props.route.params || {}
319
+ if (isObject(rawQuery)) {
320
+ for (const key in rawQuery) {
321
+ decodedQuery[key] = decodeURIComponent(rawQuery[key])
324
322
  }
325
323
  }
326
- proxy.callHook(ONLOAD, [loadParams])
324
+ proxy.callHook(ONLOAD, [rawQuery, decodedQuery])
327
325
  }
328
326
 
329
327
  Object.assign(proxy, {
@@ -378,9 +376,18 @@ const triggerPageStatusHook = (mpxProxy, event) => {
378
376
  }
379
377
  }
380
378
 
381
- const triggerResizeEvent = (mpxProxy) => {
382
- const type = mpxProxy.options.__type__
379
+ const triggerResizeEvent = (mpxProxy, sizeRef) => {
380
+ const oldSize = sizeRef.current.size
383
381
  const systemInfo = getSystemInfo()
382
+ const newSize = systemInfo.size
383
+
384
+ if (oldSize && oldSize.windowWidth === newSize.windowWidth && oldSize.windowHeight === newSize.windowHeight) {
385
+ return
386
+ }
387
+
388
+ Object.assign(sizeRef.current, systemInfo)
389
+
390
+ const type = mpxProxy.options.__type__
384
391
  const target = mpxProxy.target
385
392
  mpxProxy.callHook(ONRESIZE, [systemInfo])
386
393
  if (type === 'page') {
@@ -392,6 +399,8 @@ const triggerResizeEvent = (mpxProxy) => {
392
399
  }
393
400
 
394
401
  function usePageEffect (mpxProxy, pageId) {
402
+ const sizeRef = useRef(getSystemInfo())
403
+
395
404
  useEffect(() => {
396
405
  let unWatch
397
406
  const hasShowHook = hasPageHook(mpxProxy, [ONSHOW, 'show'])
@@ -402,21 +411,29 @@ function usePageEffect (mpxProxy, pageId) {
402
411
  unWatch = watch(() => pageStatusMap[pageId], (newVal) => {
403
412
  if (newVal === 'show' || newVal === 'hide') {
404
413
  triggerPageStatusHook(mpxProxy, newVal)
414
+ // 仅在尺寸确实变化时才触发resize事件
415
+ triggerResizeEvent(mpxProxy, sizeRef)
416
+
417
+ // 如果当前全局size与pagesize不一致,在show之后触发一次resize事件
418
+ if (newVal === 'show' && global.__mpxPageSizeCountMap[pageId] !== global.__mpxSizeCount) {
419
+ // 刷新__mpxPageSizeCountMap, 每个页面仅会执行一次,直接驱动render刷新
420
+ global.__mpxPageSizeCountMap[pageId] = global.__mpxSizeCount
421
+ }
405
422
  } else if (/^resize/.test(newVal)) {
406
- triggerResizeEvent(mpxProxy)
423
+ triggerResizeEvent(mpxProxy, sizeRef)
407
424
  }
408
425
  }, { sync: true })
409
426
  }
410
427
  }
411
428
  return () => {
412
429
  unWatch && unWatch()
430
+ del(global.__mpxPageSizeCountMap, pageId)
413
431
  }
414
432
  }, [])
415
433
  }
416
434
 
417
435
  let pageId = 0
418
436
  const pageStatusMap = global.__mpxPageStatusMap = reactive({})
419
-
420
437
  function usePageStatus (navigation, pageId) {
421
438
  navigation.pageId = pageId
422
439
  if (!hasOwn(pageStatusMap, pageId)) {
@@ -522,7 +539,7 @@ export function PageWrapperHOC (WrappedComponent, pageConfig = {}) {
522
539
  navigation.layout = getLayoutData(headerHeight)
523
540
 
524
541
  useEffect(() => {
525
- const dimensionListener = ReactNative.Dimensions.addEventListener('change', ({ screen }) => {
542
+ const dimensionListener = ReactNative.Dimensions.addEventListener('change', ({ window, screen }) => {
526
543
  navigation.layout = getLayoutData(headerHeight)
527
544
  })
528
545
  return () => dimensionListener?.remove()