@mpxjs/core 2.10.6-beta.1 → 2.10.6-beta.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/core",
3
- "version": "2.10.6-beta.1",
3
+ "version": "2.10.6-beta.3",
4
4
  "description": "mpx runtime core",
5
5
  "keywords": [
6
6
  "miniprogram",
@@ -9,7 +9,8 @@ import { watch } from '../observer/watch'
9
9
  import { createElement, memo, useRef, useEffect } from 'react'
10
10
  import * as ReactNative from 'react-native'
11
11
  import { initAppProvides } from './export/inject'
12
- import { NavigationContainer, createNativeStackNavigator, SafeAreaProvider } from './env/navigationHelper'
12
+ import { NavigationContainer, createNativeStackNavigator, SafeAreaProvider, GestureHandlerRootView } from './env/navigationHelper'
13
+ import { innerNav } from './env/nav'
13
14
 
14
15
  const appHooksMap = makeMap(mergeLifecycle(LIFECYCLE).app)
15
16
 
@@ -55,23 +56,40 @@ export default function createApp (options) {
55
56
  const pages = currentInject.getPages() || {}
56
57
  const firstPage = currentInject.firstPage
57
58
  const Stack = createNativeStackNavigator()
59
+ const withHeader = (wrappedComponent, { pageConfig = {} }) => {
60
+ return ({ navigation, ...props }) => {
61
+ return createElement(GestureHandlerRootView,
62
+ {
63
+ style: {
64
+ flex: 1
65
+ }
66
+ },
67
+ createElement(innerNav, {
68
+ pageConfig: pageConfig,
69
+ navigation
70
+ }),
71
+ createElement(wrappedComponent, { navigation, ...props })
72
+ )
73
+ }
74
+ }
58
75
  const getPageScreens = (initialRouteName, initialParams) => {
59
76
  return Object.entries(pages).map(([key, item]) => {
60
77
  // const options = {
61
78
  // // __mpxPageStatusMap 为编译注入的全局变量
62
79
  // headerShown: !(Object.assign({}, global.__mpxPageConfig, global.__mpxPageConfigsMap[key]).navigationStyle === 'custom')
63
80
  // }
81
+ const pageConfig = Object.assign({}, global.__mpxPageConfig, global.__mpxPageConfigsMap[key])
64
82
  if (key === initialRouteName) {
65
83
  return createElement(Stack.Screen, {
66
84
  name: key,
67
- component: item,
85
+ component: withHeader(item, { pageConfig }),
68
86
  initialParams
69
87
  // options
70
88
  })
71
89
  }
72
90
  return createElement(Stack.Screen, {
73
91
  name: key,
74
- component: item
92
+ component: withHeader(item, { pageConfig })
75
93
  // options
76
94
  })
77
95
  })
@@ -10,13 +10,15 @@ export function init (Mpx) {
10
10
  show: [],
11
11
  hide: [],
12
12
  error: [],
13
- rejection: []
13
+ rejection: [],
14
+ lazyLoad: []
14
15
  }
15
16
  global.__navigationHelper = navigationHelper
16
17
  if (global.i18n) {
17
18
  Mpx.i18n = createI18n(global.i18n)
18
19
  }
19
20
  initGlobalErrorHandling()
21
+ initGlobalLazyLoadHandling()
20
22
  }
21
23
 
22
24
  function initGlobalErrorHandling () {
@@ -63,3 +65,13 @@ function initGlobalErrorHandling () {
63
65
  require('promise/setimmediate/rejection-tracking').enable(rejectionTrackingOptions)
64
66
  }
65
67
  }
68
+
69
+ function initGlobalLazyLoadHandling () {
70
+ global.onLazyLoadError = function (error) {
71
+ if (global.__mpxAppCbs?.lazyLoad?.length) {
72
+ global.__mpxAppCbs.lazyLoad.forEach((cb) => {
73
+ cb(error)
74
+ })
75
+ }
76
+ }
77
+ }
@@ -1,36 +1,40 @@
1
1
  import { createElement, useState, useMemo } from 'react'
2
2
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
3
- import * as ReactNative from 'react-native'
3
+ import { StatusBar, processColor, TouchableOpacity, Image, View, StyleSheet, Text } from 'react-native'
4
4
  import Mpx from '../../index'
5
5
 
6
+ function convertToHex (color) {
7
+ try {
8
+ const intColor = processColor(color)
9
+ if (intColor === null || intColor === undefined) {
10
+ return null
11
+ }
12
+ // 将32位整数颜色值转换为RGBA
13
+ const r = (intColor >> 16) & 255
14
+ const g = (intColor >> 8) & 255
15
+ const b = intColor & 255
16
+ // 转换为十六进制
17
+ const hexR = r.toString(16).padStart(2, '0')
18
+ const hexG = g.toString(16).padStart(2, '0')
19
+ const hexB = b.toString(16).padStart(2, '0')
20
+ return `#${hexR}${hexG}${hexB}`
21
+ } catch (error) {
22
+ return null
23
+ }
24
+ }
25
+
26
+ const titleHeight = 44
6
27
  export function useInnerHeaderHeight (pageconfig) {
7
28
  if (pageconfig.navigationStyle === 'custom') {
8
29
  return 0
9
30
  } else {
10
31
  const safeAreaTop = useSafeAreaInsets()?.top || 0
11
- const headerHeight = safeAreaTop + getTitleHeight()
32
+ const headerHeight = safeAreaTop + titleHeight
12
33
  return headerHeight
13
34
  }
14
35
  }
15
36
 
16
- // 固定写死高度
17
- function getTitleHeight () {
18
- return 44
19
- }
20
-
21
- // 计算颜色亮度
22
- const getColorBrightness = (color) => {
23
- const processedColor = ReactNative.processColor(color)
24
- if (typeof processedColor === 'number') {
25
- const r = (processedColor >> 16) & 255
26
- const g = (processedColor >> 8) & 255
27
- const b = processedColor & 255
28
- return (r * 299 + g * 587 + b * 114) / 1000
29
- }
30
- return 0
31
- }
32
-
33
- const styles = ReactNative.StyleSheet.create({
37
+ const styles = StyleSheet.create({
34
38
  header: {
35
39
  elevation: 3
36
40
  },
@@ -54,27 +58,41 @@ const styles = ReactNative.StyleSheet.create({
54
58
  },
55
59
  title: {
56
60
  fontSize: 17,
57
- fontWeight: 600
61
+ fontWeight: 600,
62
+ width: '60%',
63
+ textAlign: 'center'
58
64
  }
59
65
  })
60
-
61
- export function innerNav ({ props, navigation }) {
62
- const { pageConfig } = props
66
+ const NavColor = {
67
+ White: '#ffffff',
68
+ Black: '#000000'
69
+ }
70
+ // navigationBarTextStyle只支持黑白'white'/'black
71
+ const validBarTextStyle = (textStyle) => {
72
+ const textStyleColor = convertToHex(textStyle)
73
+ if (textStyle && [NavColor.White, NavColor.Black].includes(textStyleColor)) {
74
+ return textStyleColor
75
+ } else {
76
+ return NavColor.White
77
+ }
78
+ }
79
+ export function innerNav ({ pageConfig, navigation }) {
63
80
  const [innerPageConfig, setPageConfig] = useState(pageConfig || {})
64
81
  navigation.setPageConfig = (config) => {
65
82
  const newConfig = Object.assign({}, innerPageConfig, config)
66
83
  setPageConfig(newConfig)
67
84
  }
68
-
69
85
  const isCustom = innerPageConfig.navigationStyle === 'custom'
70
- if (isCustom) return null
71
- const safeAreaTop = useSafeAreaInsets()?.top || 0
72
-
73
- // 回退按钮的颜色,根据背景色的亮度来进行调节
74
- const backButtonColor = useMemo(() => {
75
- return getColorBrightness(innerPageConfig.navigationBarBackgroundColor) > 128 ? '#000000' : '#ffffff'
76
- }, [innerPageConfig.navigationBarBackgroundColor])
86
+ const navigationBarTextStyle = useMemo(() => validBarTextStyle(innerPageConfig.navigationBarTextStyle), [innerPageConfig.navigationBarTextStyle])
87
+ // 状态栏的颜色
88
+ const statusBarElement = createElement(StatusBar, {
89
+ translucent: true,
90
+ backgroundColor: 'transparent',
91
+ barStyle: (navigationBarTextStyle === NavColor.White) ? 'light-content' : 'dark-content' // 'default'/'light-content'/'dark-content'
92
+ })
77
93
 
94
+ if (isCustom) return statusBarElement
95
+ const safeAreaTop = useSafeAreaInsets()?.top || 0
78
96
  // 假设是栈导航,获取栈的长度
79
97
  const stackLength = navigation.getState()?.routes?.length
80
98
  // 用于外部注册打开RN容器之前的栈长度
@@ -82,27 +100,30 @@ export function innerNav ({ props, navigation }) {
82
100
 
83
101
  // 回退按钮与图标
84
102
  const backElement = stackLength + beforeStackLength > 1
85
- ? createElement(ReactNative.TouchableOpacity, {
103
+ ? createElement(TouchableOpacity, {
86
104
  style: [styles.backButton],
87
105
  onPress: () => { navigation.goBack() }
88
- }, createElement(ReactNative.Image, {
106
+ }, createElement(Image, {
89
107
  source: { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAABICAYAAACqT5alAAAA2UlEQVR4nO3bMQrCUBRE0Yla6AYEN2nnBrTL+izcitW3MRDkEUWSvPzJvfCqgMwhZbAppWhNbbIHzB1g9wATERFRVyvpkj1irlpJ5X326D7WHh1hbdFD2CLpLmmftm7kfsEe09aNHFiBrT+wAlt/YAW2/sAKbP2BFdj6Ayuwy+ufz6XPL893krZ//O6iu2n4LT8kndLWTRTo4EC7BDo40C6BDg60S6CDA+0S6OBAuwQ6uNWiD2nrJmoIfU7cNWkR2hbb1UfbY7uuWhGWiIg+a/iHuHmA3QPs3gu4JW9Gan+OJAAAAABJRU5ErkJggg==' },
90
- style: [styles.backButtonImage, { tintColor: backButtonColor }]
108
+ // 回退按钮的颜色与设置的title文案颜色一致
109
+ style: [styles.backButtonImage, { tintColor: navigationBarTextStyle }]
91
110
  }))
92
111
  : null
93
112
 
94
- return createElement(ReactNative.View, {
113
+ return createElement(View, {
95
114
  style: [styles.header, {
96
115
  paddingTop: safeAreaTop,
97
116
  backgroundColor: innerPageConfig.navigationBarBackgroundColor || '#000000'
98
117
  }]
99
118
  },
100
- createElement(ReactNative.View, {
119
+ statusBarElement,
120
+ createElement(View, {
101
121
  style: styles.headerContent,
102
- height: getTitleHeight()
122
+ height: titleHeight
103
123
  }, backElement,
104
- createElement(ReactNative.Text, {
105
- style: [styles.title, { color: innerPageConfig.navigationBarTextStyle || 'white' }]
124
+ createElement(Text, {
125
+ style: [styles.title, { color: navigationBarTextStyle }],
126
+ numberOfLines: 1
106
127
  }, innerPageConfig.navigationBarTitleText?.trim() || ''))
107
128
  )
108
129
  }
@@ -15,8 +15,8 @@ import {
15
15
  KeyboardAvoidContext,
16
16
  RouteContext
17
17
  } from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/context'
18
- import { PortalHost, useSafeAreaInsets, GestureHandlerRootView } from '../env/navigationHelper'
19
- import { innerNav, useInnerHeaderHeight } from '../env/nav'
18
+ import { PortalHost, useSafeAreaInsets } from '../env/navigationHelper'
19
+ import { useInnerHeaderHeight } from '../env/nav'
20
20
 
21
21
  const ProviderContext = createContext(null)
22
22
  function getSystemInfo () {
@@ -419,6 +419,22 @@ function usePageStatus (navigation, pageId) {
419
419
  }, [navigation])
420
420
  }
421
421
 
422
+ function usePagePreload (route) {
423
+ const name = route.name
424
+ useEffect(() => {
425
+ setTimeout(() => {
426
+ const preloadRule = global.__preloadRule || {}
427
+ const { packages } = preloadRule[name] || {}
428
+ if (packages?.length > 0) {
429
+ const downloadChunkAsync = mpxGlobal.__mpx.config?.rnConfig?.downloadChunkAsync
430
+ if (typeof downloadChunkAsync === 'function') {
431
+ callWithErrorHandling(() => downloadChunkAsync(packages))
432
+ }
433
+ }
434
+ }, 800)
435
+ }, [])
436
+ }
437
+
422
438
  const RelationsContext = createContext(null)
423
439
 
424
440
  const checkRelation = (options) => {
@@ -447,8 +463,6 @@ function getLayoutData (headerHeight) {
447
463
  const isLandscape = screenDimensions.height < screenDimensions.width
448
464
  const bottomVirtualHeight = isLandscape ? screenDimensions.height - windowDimensions.height : ((screenDimensions.height - windowDimensions.height - ReactNative.StatusBar.currentHeight) || 0)
449
465
  return {
450
- x: 0,
451
- y: headerHeight,
452
466
  left: 0,
453
467
  top: headerHeight,
454
468
  // 此处必须为windowDimensions.width,在横屏状态下windowDimensions.width才符合预期
@@ -461,8 +475,8 @@ function getLayoutData (headerHeight) {
461
475
  }
462
476
  }
463
477
 
464
- export function PageWrapperHOC (WrappedComponent) {
465
- return function PageWrapperCom ({ navigation, route, pageConfig = {}, ...props }) {
478
+ export function PageWrapperHOC (WrappedComponent, pageConfig = {}) {
479
+ return function PageWrapperCom ({ navigation, route, ...props }) {
466
480
  const rootRef = useRef(null)
467
481
  const keyboardAvoidRef = useRef(null)
468
482
  const intersectionObservers = useRef({})
@@ -479,11 +493,15 @@ export function PageWrapperHOC (WrappedComponent) {
479
493
  }
480
494
  const headerHeight = useInnerHeaderHeight(currentPageConfig)
481
495
  navigation.layout = getLayoutData(headerHeight)
482
- const onLayout = () => {
483
- // 当用户处于横屏或者竖屏状态的时候,需要进行layout修正
484
- navigation.layout = getLayoutData(headerHeight)
485
- }
486
496
 
497
+ useEffect(() => {
498
+ const dimensionListener = ReactNative.Dimensions.addEventListener('change', ({ screen }) => {
499
+ navigation.layout = getLayoutData(headerHeight)
500
+ })
501
+ return () => dimensionListener?.remove()
502
+ }, [])
503
+
504
+ usePagePreload(route)
487
505
  usePageStatus(navigation, currentPageId)
488
506
 
489
507
  const withKeyboardAvoidingView = (element) => {
@@ -506,49 +524,38 @@ export function PageWrapperHOC (WrappedComponent) {
506
524
  }
507
525
  // android存在第一次打开insets都返回为0情况,后续会触发第二次渲染后正确
508
526
  navigation.insets = useSafeAreaInsets()
509
- return createElement(GestureHandlerRootView,
510
- {
511
- style: {
512
- flex: 1
513
- }
514
- },
515
- createElement(innerNav, {
516
- props: { pageConfig: currentPageConfig },
517
- navigation
518
- }),
519
- withKeyboardAvoidingView(
520
- createElement(ReactNative.View,
527
+ return withKeyboardAvoidingView(
528
+ createElement(ReactNative.View,
529
+ {
530
+ style: {
531
+ flex: 1,
532
+ backgroundColor: currentPageConfig?.backgroundColor || '#fff',
533
+ // 解决页面内有元素定位relative left为负值的时候,回退的时候还能看到对应元素问题
534
+ overflow: 'hidden'
535
+ },
536
+ ref: rootRef
537
+ },
538
+ createElement(RouteContext.Provider,
521
539
  {
522
- style: {
523
- flex: 1,
524
- backgroundColor: currentPageConfig?.backgroundColor || '#fff',
525
- // 解决页面内有元素定位relative left为负值的时候,回退的时候还能看到对应元素问题
526
- overflow: 'hidden'
527
- },
528
- ref: rootRef,
529
- onLayout
540
+ value: routeContextValRef.current
530
541
  },
531
- createElement(RouteContext.Provider,
542
+ createElement(IntersectionObserverContext.Provider,
532
543
  {
533
- value: routeContextValRef.current
544
+ value: intersectionObservers.current
534
545
  },
535
- createElement(IntersectionObserverContext.Provider,
536
- {
537
- value: intersectionObservers.current
538
- },
539
- createElement(PortalHost,
540
- null,
541
- createElement(WrappedComponent, {
542
- ...props,
543
- navigation,
544
- route,
545
- id: currentPageId
546
- })
547
- )
546
+ createElement(PortalHost,
547
+ null,
548
+ createElement(WrappedComponent, {
549
+ ...props,
550
+ navigation,
551
+ route,
552
+ id: currentPageId
553
+ })
548
554
  )
549
555
  )
550
556
  )
551
- ))
557
+ )
558
+ )
552
559
  }
553
560
  }
554
561
  export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
@@ -618,7 +625,6 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
618
625
  })
619
626
 
620
627
  usePageEffect(proxy, pageId)
621
-
622
628
  useEffect(() => {
623
629
  proxy.mounted()
624
630
  return () => {
@@ -691,11 +697,7 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
691
697
  }
692
698
 
693
699
  if (type === 'page') {
694
- return (props) =>
695
- createElement(PageWrapperHOC(defaultOptions), {
696
- pageConfig: currentInject.pageConfig,
697
- ...props
698
- })
700
+ return PageWrapperHOC(defaultOptions, currentInject.pageConfig)
699
701
  }
700
702
  return defaultOptions
701
703
  }
@@ -1,17 +0,0 @@
1
- import { createNativeStackNavigator } from '@react-navigation/native-stack'
2
- import { NavigationContainer, StackActions } from '@react-navigation/native'
3
- import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host'
4
- import { useHeaderHeight } from '@react-navigation/elements'
5
- import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
6
- import { GestureHandlerRootView } from 'react-native-gesture-handler'
7
-
8
- export {
9
- createNativeStackNavigator,
10
- NavigationContainer,
11
- useHeaderHeight,
12
- StackActions,
13
- GestureHandlerRootView,
14
- PortalHost,
15
- SafeAreaProvider,
16
- useSafeAreaInsets
17
- }