@mpxjs/core 2.10.17 → 2.10.18-beta.2

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.
@@ -3,25 +3,25 @@ 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'
10
10
  import { queueJob, hasPendingJob } from '../../observer/scheduler'
11
11
  import { createSelectorQuery, createIntersectionObserver } from '@mpxjs/api-proxy'
12
- import MpxKeyboardAvoidingView from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view'
13
12
  import {
14
13
  IntersectionObserverContext,
15
14
  KeyboardAvoidContext,
16
15
  ProviderContext,
17
16
  RouteContext
18
17
  } from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/context'
19
- import { PortalHost, useSafeAreaInsets } from '../env/navigationHelper'
18
+ import { PortalHost, useSafeAreaInsets, initialWindowMetrics } from '../env/navigationHelper'
20
19
  import { useInnerHeaderHeight } from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-nav'
20
+ import Mpx from '../../index'
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: {
@@ -33,12 +33,13 @@ function getSystemInfo () {
33
33
  }
34
34
  }
35
35
 
36
- function createEffect (proxy, componentsMap) {
36
+ function createEffect (proxy, componentsMap, rawOptions) {
37
37
  const update = proxy.update = () => {
38
38
  // react update props in child render(async), do not need exec pre render
39
39
  // if (proxy.propsUpdatedFlag) {
40
40
  // proxy.updatePreRender()
41
41
  // }
42
+ // console.log(`[mpx] ${proxy.options?.mpxFileResource} 组件render响应时数据变更,开始修改stateVersion,驱动组件更新`)
42
43
  if (proxy.isMounted()) {
43
44
  proxy.callHook(BEFOREUPDATE)
44
45
  proxy.pendingUpdatedFlag = true
@@ -61,11 +62,18 @@ function createEffect (proxy, componentsMap) {
61
62
  return createElement(type, ...rest)
62
63
  }
63
64
 
65
+ const debugConfig = typeof global.__getMpxRenderEffectDebugRules === 'function'
66
+ ? global.__getMpxRenderEffectDebugRules(rawOptions.mpxFileResource)
67
+ : {
68
+ debug: 1,
69
+ name: `MpxRenderEffect-${rawOptions.mpxFileResource || 'unknown'}`
70
+ }
71
+
64
72
  proxy.effect = new ReactiveEffect(() => {
65
73
  // reset instance
66
74
  proxy.target.__resetInstance()
67
75
  return callWithErrorHandling(proxy.target.__injectedRender.bind(proxy.target), proxy, 'render function', [innerCreateElement, getComponent])
68
- }, () => queueJob(update), proxy.scope)
76
+ }, () => { return queueJob(update) }, proxy.scope, debugConfig.debug, debugConfig.name)
69
77
  // render effect允许自触发
70
78
  proxy.toggleRecurse(true)
71
79
  }
@@ -172,8 +180,8 @@ const instanceProto = {
172
180
  type: field
173
181
  }
174
182
  }
175
- // 处理props默认值
176
- propsData[key] = field.value
183
+ // 处理props默认值,没有显式设置value时根据type获取默认值,与微信小程序原生行为保持一致
184
+ propsData[key] = hasOwn(field, 'value') ? field.value : getDefaultValueByType(field.type)
177
185
  }
178
186
  }
179
187
  })
@@ -295,13 +303,18 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
295
303
  instance[key] = method.bind(instance)
296
304
  })
297
305
  }
298
-
306
+ const loadParams = {}
299
307
  if (type === 'page') {
300
308
  const props = propsRef.current
301
309
  instance.route = props.route.name
302
310
  global.__mpxPagesMap = global.__mpxPagesMap || {}
303
311
  global.__mpxPagesMap[props.route.key] = [instance, props.navigation]
304
312
  setFocusedNavigation(props.navigation)
313
+
314
+ if (!global.__mpxAppHotLaunched && global.__mpxInitialRunParams) {
315
+ Object.assign(loadParams, global.__mpxInitialRunParams)
316
+ }
317
+ set(global.__mpxPageSizeCountMap, pageId, global.__mpxSizeCount)
305
318
  // App onLaunch 在 Page created 之前执行
306
319
  if (!global.__mpxAppHotLaunched && global.__mpxAppOnLaunch) {
307
320
  global.__mpxAppOnLaunch(props.navigation)
@@ -310,17 +323,16 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
310
323
 
311
324
  const proxy = instance.__mpxProxy = new MpxProxy(rawOptions, instance)
312
325
  proxy.created()
313
-
314
326
  if (type === 'page') {
315
327
  const props = propsRef.current
316
- const loadParams = {}
317
- // 此处拿到的props.route.params内属性的value被进行过了一次decode, 不符合预期,此处额外进行一次encode来与微信对齐
318
- if (isObject(props.route.params)) {
319
- for (const key in props.route.params) {
320
- loadParams[key] = encodeURIComponent(props.route.params[key])
328
+ const decodedQuery = {}
329
+ const rawQuery = Object.assign({}, loadParams, props.route.params || {})
330
+ if (isObject(rawQuery)) {
331
+ for (const key in rawQuery) {
332
+ decodedQuery[key] = decodeURIComponent(rawQuery[key])
321
333
  }
322
334
  }
323
- proxy.callHook(ONLOAD, [loadParams])
335
+ proxy.callHook(ONLOAD, [rawQuery, decodedQuery])
324
336
  }
325
337
 
326
338
  Object.assign(proxy, {
@@ -328,7 +340,7 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
328
340
  stateVersion: Symbol(),
329
341
  subscribe: (onStoreChange) => {
330
342
  if (!proxy.effect) {
331
- createEffect(proxy, componentsMap)
343
+ createEffect(proxy, componentsMap, rawOptions)
332
344
  proxy.stateVersion = Symbol()
333
345
  }
334
346
  proxy.onStoreChange = onStoreChange
@@ -344,7 +356,7 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
344
356
  })
345
357
  // react数据响应组件更新管理器
346
358
  if (!proxy.effect) {
347
- createEffect(proxy, componentsMap)
359
+ createEffect(proxy, componentsMap, rawOptions)
348
360
  }
349
361
 
350
362
  return instance
@@ -375,9 +387,18 @@ const triggerPageStatusHook = (mpxProxy, event) => {
375
387
  }
376
388
  }
377
389
 
378
- const triggerResizeEvent = (mpxProxy) => {
379
- const type = mpxProxy.options.__type__
390
+ const triggerResizeEvent = (mpxProxy, sizeRef) => {
391
+ const oldSize = sizeRef.current.size
380
392
  const systemInfo = getSystemInfo()
393
+ const newSize = systemInfo.size
394
+
395
+ if (oldSize && oldSize.windowWidth === newSize.windowWidth && oldSize.windowHeight === newSize.windowHeight) {
396
+ return
397
+ }
398
+
399
+ Object.assign(sizeRef.current, systemInfo)
400
+
401
+ const type = mpxProxy.options.__type__
381
402
  const target = mpxProxy.target
382
403
  mpxProxy.callHook(ONRESIZE, [systemInfo])
383
404
  if (type === 'page') {
@@ -388,7 +409,9 @@ const triggerResizeEvent = (mpxProxy) => {
388
409
  }
389
410
  }
390
411
 
391
- function usePageEffect (mpxProxy, pageId) {
412
+ function usePageEffect (mpxProxy, pageId, type) {
413
+ const sizeRef = useRef(getSystemInfo())
414
+
392
415
  useEffect(() => {
393
416
  let unWatch
394
417
  const hasShowHook = hasPageHook(mpxProxy, [ONSHOW, 'show'])
@@ -399,21 +422,31 @@ function usePageEffect (mpxProxy, pageId) {
399
422
  unWatch = watch(() => pageStatusMap[pageId], (newVal) => {
400
423
  if (newVal === 'show' || newVal === 'hide') {
401
424
  triggerPageStatusHook(mpxProxy, newVal)
425
+ // 仅在尺寸确实变化时才触发resize事件
426
+ triggerResizeEvent(mpxProxy, sizeRef)
427
+
428
+ // 如果当前全局size与pagesize不一致,在show之后触发一次resize事件
429
+ if (type === 'page' && newVal === 'show' && global.__mpxPageSizeCountMap[pageId] !== global.__mpxSizeCount) {
430
+ // 刷新__mpxPageSizeCountMap, 每个页面仅会执行一次,直接驱动render刷新
431
+ global.__mpxPageSizeCountMap[pageId] = global.__mpxSizeCount
432
+ }
402
433
  } else if (/^resize/.test(newVal)) {
403
- triggerResizeEvent(mpxProxy)
434
+ triggerResizeEvent(mpxProxy, sizeRef)
404
435
  }
405
436
  }, { sync: true })
406
437
  }
407
438
  }
408
439
  return () => {
409
440
  unWatch && unWatch()
441
+ if (type === 'page') {
442
+ del(global.__mpxPageSizeCountMap, pageId)
443
+ }
410
444
  }
411
445
  }, [])
412
446
  }
413
447
 
414
448
  let pageId = 0
415
449
  const pageStatusMap = global.__mpxPageStatusMap = reactive({})
416
-
417
450
  function usePageStatus (navigation, pageId) {
418
451
  navigation.pageId = pageId
419
452
  if (!hasOwn(pageStatusMap, pageId)) {
@@ -486,7 +519,17 @@ function getLayoutData (headerHeight) {
486
519
  // 在横屏状态下 screen.height = window.height + bottomVirtualHeight
487
520
  // 在正常状态 screen.height = window.height + bottomVirtualHeight + statusBarHeight
488
521
  const isLandscape = screenDimensions.height < screenDimensions.width
489
- const bottomVirtualHeight = isLandscape ? screenDimensions.height - windowDimensions.height : ((screenDimensions.height - windowDimensions.height - ReactNative.StatusBar.currentHeight) || 0)
522
+ let bottomVirtualHeight = 0
523
+ if (ReactNative.Platform.OS === 'android') {
524
+ if (isLandscape) {
525
+ bottomVirtualHeight = screenDimensions.height - windowDimensions.height
526
+ } else {
527
+ bottomVirtualHeight = initialWindowMetrics?.insets?.bottom || 0
528
+ if (typeof mpxGlobal.__mpx.config?.rnConfig?.getBottomVirtualHeight === 'function') {
529
+ bottomVirtualHeight = mpxGlobal.__mpx.config?.rnConfig?.getBottomVirtualHeight() || 0
530
+ }
531
+ }
532
+ }
490
533
  return {
491
534
  left: 0,
492
535
  top: headerHeight,
@@ -519,7 +562,7 @@ export function PageWrapperHOC (WrappedComponent, pageConfig = {}) {
519
562
  navigation.layout = getLayoutData(headerHeight)
520
563
 
521
564
  useEffect(() => {
522
- const dimensionListener = ReactNative.Dimensions.addEventListener('change', ({ screen }) => {
565
+ const dimensionListener = ReactNative.Dimensions.addEventListener('change', ({ window, screen }) => {
523
566
  navigation.layout = getLayoutData(headerHeight)
524
567
  })
525
568
  return () => dimensionListener?.remove()
@@ -529,6 +572,8 @@ export function PageWrapperHOC (WrappedComponent, pageConfig = {}) {
529
572
  usePageStatus(navigation, currentPageId)
530
573
 
531
574
  const withKeyboardAvoidingView = (element) => {
575
+ if (currentPageConfig.disableKeyboardAvoiding) return element
576
+ const MpxKeyboardAvoidingView = require('@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view').default
532
577
  return createElement(KeyboardAvoidContext.Provider,
533
578
  {
534
579
  value: keyboardAvoidRef
@@ -549,39 +594,53 @@ export function PageWrapperHOC (WrappedComponent, pageConfig = {}) {
549
594
  // android存在第一次打开insets都返回为0情况,后续会触发第二次渲染后正确
550
595
  navigation.insets = useSafeAreaInsets()
551
596
  return withKeyboardAvoidingView(
552
- createElement(ReactNative.View,
597
+ createElement(ReactNative.View,
598
+ {
599
+ style: {
600
+ flex: 1,
601
+ // 页面容器背景色
602
+ backgroundColor: currentPageConfig?.backgroundColorContent || '#fff',
603
+ // 解决页面内有元素定位relative left为负值的时候,回退的时候还能看到对应元素问题
604
+ overflow: 'hidden'
605
+ }
606
+ },
607
+ createElement(RouteContext.Provider,
608
+ {
609
+ value: routeContextValRef.current
610
+ },
611
+ createElement(IntersectionObserverContext.Provider,
553
612
  {
554
- style: {
555
- flex: 1,
556
- // 页面容器背景色
557
- backgroundColor: currentPageConfig?.backgroundColorContent || '#fff',
558
- // 解决页面内有元素定位relative left为负值的时候,回退的时候还能看到对应元素问题
559
- overflow: 'hidden'
560
- }
613
+ value: intersectionObservers.current
561
614
  },
562
- createElement(RouteContext.Provider,
563
- {
564
- value: routeContextValRef.current
565
- },
566
- createElement(IntersectionObserverContext.Provider,
567
- {
568
- value: intersectionObservers.current
569
- },
570
- createElement(PortalHost,
571
- null,
572
- createElement(WrappedComponent, {
573
- ...props,
574
- navigation,
575
- route,
576
- id: currentPageId
577
- })
578
- )
579
- )
615
+ createElement(PortalHost,
616
+ null,
617
+ createElement(WrappedComponent, {
618
+ ...props,
619
+ navigation,
620
+ route,
621
+ id: currentPageId
622
+ })
580
623
  )
624
+ )
581
625
  )
626
+ )
582
627
  )
583
628
  }
584
629
  }
630
+
631
+ function updateProps (instance, props, validProps) {
632
+ Object.keys(validProps).forEach((key) => {
633
+ if (hasOwn(props, key)) {
634
+ instance[key] = props[key]
635
+ } else {
636
+ const altKey = hump2dash(key)
637
+ if (hasOwn(props, altKey)) {
638
+ instance[key] = props[altKey]
639
+ }
640
+ }
641
+ })
642
+ }
643
+
585
644
  export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
586
645
  rawOptions = mergeOptions(rawOptions, type, false)
587
646
  const componentsMap = currentInject.componentsMap
@@ -634,16 +693,14 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
634
693
 
635
694
  if (!isFirst) {
636
695
  // 处理props更新
637
- Object.keys(validProps).forEach((key) => {
638
- if (hasOwn(props, key)) {
639
- instance[key] = props[key]
640
- } else {
641
- const altKey = hump2dash(key)
642
- if (hasOwn(props, altKey)) {
643
- instance[key] = props[altKey]
644
- }
645
- }
646
- })
696
+ if (Mpx.config.forceFlushSync) {
697
+ // 避免开启forceFlushSync时react报错:Cannot update a component while rendering a different component
698
+ Promise.resolve().then(() => {
699
+ updateProps(instance, props, validProps)
700
+ })
701
+ } else {
702
+ updateProps(instance, props, validProps)
703
+ }
647
704
  }
648
705
 
649
706
  useEffect(() => {
@@ -653,7 +710,7 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
653
710
  }
654
711
  })
655
712
 
656
- usePageEffect(proxy, pageId)
713
+ usePageEffect(proxy, pageId, type)
657
714
  useEffect(() => {
658
715
  proxy.mounted()
659
716
  return () => {
@@ -676,6 +733,15 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
676
733
  proxy.memoVersion = Symbol()
677
734
  }
678
735
 
736
+ // if (rawOptions.mpxFileResource.includes('nav.mpx')) {
737
+ // const stateVersionChange = proxy.stateVersion === global.__mpxNavStateVersion
738
+ // const memoVersionChange = proxy.memoVersion === global.__mpxNavMemoVersion
739
+ // const propsChange = props === global.__mpxNavProps
740
+ // console.log(`[mpx] nav.mpx render, stateVersionChange: ${stateVersionChange}, memoVersionChange: ${memoVersionChange}, propsChange: ${propsChange}`)
741
+ // global.__mpxNavStateVersion = proxy.stateVersion
742
+ // global.__mpxNavMemoVersion = proxy.memoVersion
743
+ // global.__mpxNavProps = props
744
+ // }
679
745
  const finalMemoVersion = useMemo(() => {
680
746
  if (!hasPendingJob(proxy.update)) {
681
747
  proxy.finalMemoVersion = Symbol()