@mpxjs/core 2.9.69-beta.2 → 2.9.69-beta.5

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.9.69-beta.2",
3
+ "version": "2.9.69-beta.5",
4
4
  "description": "mpx runtime core",
5
5
  "keywords": [
6
6
  "miniprogram",
@@ -1,13 +1,12 @@
1
1
  import transferOptions from '../core/transferOptions'
2
2
  import builtInKeysMap from './patch/builtInKeysMap'
3
- import { makeMap, spreadProp, getFocusedNavigation, hasOwn, extend } from '@mpxjs/utils'
3
+ import { makeMap, spreadProp, getFocusedNavigation, hasOwn } 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'
7
7
  import { createElement, memo, useRef, useEffect } from 'react'
8
8
  import * as ReactNative from 'react-native'
9
9
  import { Image } from 'react-native'
10
- import { ref } from '../observer/ref'
11
10
 
12
11
  const appHooksMap = makeMap(mergeLifecycle(LIFECYCLE).app)
13
12
 
@@ -30,22 +29,24 @@ function filterOptions (options, appData) {
30
29
  return newOptions
31
30
  }
32
31
 
33
- function createAppInstance (appData) {
34
- return extend({}, Mpx.prototype, appData)
35
- }
36
-
37
- export default function createApp (option, config = {}) {
32
+ export default function createApp (options) {
38
33
  const appData = {}
39
34
 
40
35
  const { NavigationContainer, createStackNavigator, SafeAreaProvider } = global.__navigationHelper
41
36
  // app选项目前不需要进行转换
42
- const { rawOptions, currentInject } = transferOptions(option, 'app', false)
37
+ const { rawOptions, currentInject } = transferOptions(options, 'app', false)
43
38
  const defaultOptions = filterOptions(spreadProp(rawOptions, 'methods'), appData)
44
- defaultOptions.onAppInit && defaultOptions.onAppInit()
45
39
  // 在页面script执行前填充getApp()
46
40
  global.getApp = function () {
47
41
  return appData
48
42
  }
43
+
44
+ defaultOptions.onShow && global.__mpxAppCbs.show.push(defaultOptions.onShow.bind(appData))
45
+ defaultOptions.onHide && global.__mpxAppCbs.hide.push(defaultOptions.onHide.bind(appData))
46
+ defaultOptions.onError && global.__mpxAppCbs.error.push(defaultOptions.onError.bind(appData))
47
+ defaultOptions.onUnhandledRejection && global.__mpxAppCbs.rejection.push(defaultOptions.onUnhandledRejection.bind(appData))
48
+ defaultOptions.onAppInit && defaultOptions.onAppInit()
49
+
49
50
  const pages = currentInject.getPages() || {}
50
51
  const firstPage = currentInject.firstPage
51
52
  const Stack = createStackNavigator()
@@ -82,55 +83,50 @@ export default function createApp (option, config = {}) {
82
83
  }
83
84
 
84
85
  global.__mpxAppLaunched = false
85
-
86
- global.__mpxAppFocusedState = ref('show')
87
86
  global.__mpxOptionsMap[currentInject.moduleId] = memo((props) => {
88
- const instanceRef = useRef(null)
89
- if (!instanceRef.current) {
90
- instanceRef.current = createAppInstance(appData)
91
- }
92
- const instance = instanceRef.current
87
+ const firstRef = useRef(true)
93
88
  const initialRouteRef = useRef({
94
89
  initialRouteName: firstPage,
95
90
  initialParams: {}
96
91
  })
97
-
98
- if (!global.__mpxAppLaunched) {
92
+ if (firstRef.current) {
93
+ // 热启动情况下,app会被销毁重建,将__mpxAppHotLaunched重置保障路由等初始化逻辑正确执行
94
+ global.__mpxAppHotLaunched = false
95
+ // 热启动情况下重置__mpxPagesMap避免页面销毁函数未及时执行时错误地引用到之前的navigation
96
+ global.__mpxPagesMap = {}
97
+ firstRef.current = false
98
+ }
99
+ if (!global.__mpxAppHotLaunched) {
99
100
  const { initialRouteName, initialParams } = Mpx.config.rnConfig.parseAppProps?.(props) || {}
100
101
  initialRouteRef.current.initialRouteName = initialRouteName || initialRouteRef.current.initialRouteName
101
102
  initialRouteRef.current.initialParams = initialParams || initialRouteRef.current.initialParams
102
103
 
103
104
  global.__mpxAppOnLaunch = (navigation) => {
104
- global.__mpxAppLaunched = true
105
105
  const state = navigation.getState()
106
106
  Mpx.config.rnConfig.onStateChange?.(state)
107
107
  const current = state.routes[state.index]
108
- global.__mpxEnterOptions = {
108
+ const options = {
109
109
  path: current.name,
110
110
  query: current.params,
111
111
  scene: 0,
112
112
  shareTicket: '',
113
- referrerInfo: {}
113
+ referrerInfo: {},
114
+ isLaunch: true
115
+ }
116
+ global.__mpxEnterOptions = options
117
+ if (!global.__mpxAppLaunched) {
118
+ global.__mpxLaunchOptions = options
119
+ defaultOptions.onLaunch && defaultOptions.onLaunch.call(appData, options)
114
120
  }
115
- defaultOptions.onLaunch && defaultOptions.onLaunch.call(instance, global.__mpxEnterOptions)
116
- defaultOptions.onShow && defaultOptions.onShow.call(instance, global.__mpxEnterOptions)
121
+ global.__mpxAppCbs.show.forEach((cb) => {
122
+ cb(options)
123
+ })
124
+ global.__mpxAppLaunched = true
125
+ global.__mpxAppHotLaunched = true
117
126
  }
118
127
  }
119
128
 
120
129
  useEffect(() => {
121
- if (defaultOptions.onShow) {
122
- global.__mpxAppCbs.show.push(defaultOptions.onShow.bind(instance))
123
- }
124
- if (defaultOptions.onHide) {
125
- global.__mpxAppCbs.hide.push(defaultOptions.onHide.bind(instance))
126
- }
127
- if (defaultOptions.onError) {
128
- global.__mpxAppCbs.error.push(defaultOptions.onError.bind(instance))
129
- }
130
- if (defaultOptions.onUnhandledRejection) {
131
- global.__mpxAppCbs.rejection.push(defaultOptions.onUnhandledRejection.bind(instance))
132
- }
133
-
134
130
  const changeSubscription = ReactNative.AppState.addEventListener('change', (currentState) => {
135
131
  if (currentState === 'active') {
136
132
  let options = global.__mpxEnterOptions
@@ -183,7 +179,6 @@ export default function createApp (option, config = {}) {
183
179
  const { initialRouteName, initialParams } = initialRouteRef.current
184
180
  const headerBackImageProps = Mpx.config.rnConfig.headerBackImageProps || null
185
181
  const navScreenOpts = {
186
- gestureEnabled: true,
187
182
  // 7.x替换headerBackTitleVisible
188
183
  // headerBackButtonDisplayMode: 'minimal',
189
184
  headerBackTitleVisible: false,
@@ -24,19 +24,19 @@ function filterOptions (options, appData) {
24
24
  return newOptions
25
25
  }
26
26
 
27
- export default function createApp (option, config = {}) {
28
- // 在App中挂载mpx对象供周边工具访问,如e2e测试
27
+ export default function createApp (options, config = {}) {
28
+ const appData = {}
29
+ // app选项目前不需要进行转换
30
+ const { rawOptions, currentInject } = transferOptions(options, 'app', false)
29
31
  const builtInMixins = [{
32
+ // 在App中挂载mpx对象供周边工具访问,如e2e测试
30
33
  getMpx () {
31
34
  return Mpx
32
35
  }
33
36
  }]
34
- const appData = {}
35
37
  if (__mpx_mode__ === 'web') {
36
38
  builtInMixins.push({
37
39
  created () {
38
- Object.assign(this, Mpx.prototype)
39
- Object.assign(this, appData)
40
40
  const current = this.$root.$options?.router?.currentRoute || {}
41
41
  const options = {
42
42
  path: current.path && current.path.replace(/^\//, ''),
@@ -45,48 +45,41 @@ export default function createApp (option, config = {}) {
45
45
  shareTicket: '',
46
46
  referrerInfo: {}
47
47
  }
48
+ // web不分冷启动和热启动
48
49
  global.__mpxEnterOptions = options
49
- this.$options.onLaunch && this.$options.onLaunch.call(this, options)
50
- if (isBrowser) {
51
- if (this.$options.onShow) {
52
- this.$options.onShow.call(this, options)
53
- global.__mpxAppCbs.show.push(this.$options.onShow.bind(this))
54
- }
55
- if (this.$options.onHide) {
56
- global.__mpxAppCbs.hide.push(this.$options.onHide.bind(this))
57
- }
58
- if (this.$options.onError) {
59
- global.__mpxAppCbs.error.push(this.$options.onError.bind(this))
60
- }
61
- if (this.$options.onUnhandledRejection) {
62
- global.__mpxAppCbs.rejection.push(this.$options.onUnhandledRejection.bind(this))
63
- }
64
- }
50
+ global.__mpxLaunchOptions = options
51
+ rawOptions.onLaunch && rawOptions.onLaunch.call(appData, options)
52
+ global.__mpxAppCbs.show.forEach((cb) => {
53
+ cb(options)
54
+ })
65
55
  }
66
56
  })
67
57
  } else {
68
58
  builtInMixins.push({
69
59
  onLaunch () {
70
- Object.assign(this, Mpx.prototype)
60
+ initAppProvides(rawOptions.provide, this)
71
61
  }
72
62
  })
73
63
  }
74
- // app选项目前不需要进行转换
75
- const { rawOptions, currentInject } = transferOptions(option, 'app', false)
76
64
  rawOptions.mixins = builtInMixins
77
65
  const defaultOptions = filterOptions(spreadProp(mergeOptions(rawOptions, 'app', false), 'methods'), appData)
78
66
 
79
67
  if (__mpx_mode__ === 'web') {
80
- global.__mpxOptionsMap = global.__mpxOptionsMap || {}
81
- global.__mpxOptionsMap[currentInject.moduleId] = defaultOptions
82
68
  global.getApp = function () {
83
69
  if (!isBrowser) {
84
70
  console.error('[Mpx runtime error]: Dangerous API! global.getApp method is running in non browser environments')
85
71
  }
86
72
  return appData
87
73
  }
74
+ if (isBrowser) {
75
+ defaultOptions.onShow && global.__mpxAppCbs.show.push(defaultOptions.onShow.bind(appData))
76
+ defaultOptions.onHide && global.__mpxAppCbs.hide.push(defaultOptions.onHide.bind(appData))
77
+ defaultOptions.onError && global.__mpxAppCbs.error.push(defaultOptions.onError.bind(appData))
78
+ defaultOptions.onUnhandledRejection && global.__mpxAppCbs.rejection.push(defaultOptions.onUnhandledRejection.bind(appData))
79
+ }
80
+ global.__mpxOptionsMap = global.__mpxOptionsMap || {}
81
+ global.__mpxOptionsMap[currentInject.moduleId] = defaultOptions
88
82
  } else {
89
- initAppProvides(rawOptions)
90
83
  defaultOptions.onAppInit && defaultOptions.onAppInit()
91
84
  const ctor = config.customCtor || global.currentCtor || App
92
85
  ctor(defaultOptions)
@@ -1,3 +1,4 @@
1
+ import { isFunction, isNumber, isString } from '@mpxjs/utils'
1
2
  import { createI18n } from '../builtInMixins/i18nMixin'
2
3
 
3
4
  export function init (Mpx) {
@@ -30,21 +31,30 @@ function initGlobalErrorHandling () {
30
31
  })
31
32
  }
32
33
 
34
+ function onUnhandledRejection (event) {
35
+ if (global.__mpxAppCbs && global.__mpxAppCbs.rejection && global.__mpxAppCbs.rejection.length) {
36
+ global.__mpxAppCbs.rejection.forEach((cb) => {
37
+ cb(event)
38
+ })
39
+ } else {
40
+ console.warn(`UNHANDLED PROMISE REJECTION ${(isNumber(event.id) || isString(event.id)) ? '(id:' + event.id + ')' : ''}: ${event.reason}\n`)
41
+ }
42
+ }
33
43
  const rejectionTrackingOptions = {
34
44
  allRejections: true,
35
45
  onUnhandled (id, error) {
36
- if (global.__mpxAppCbs && global.__mpxAppCbs.rejection && global.__mpxAppCbs.rejection.length) {
37
- global.__mpxAppCbs.rejection.forEach((cb) => {
38
- cb(error, id)
39
- })
40
- } else {
41
- console.warn(`UNHANDLED PROMISE REJECTION (id: ${id}): ${error}\n`)
42
- }
46
+ onUnhandledRejection({ id, reason: error, promise: null })
43
47
  }
44
48
  }
45
49
 
46
- if (global?.HermesInternal?.hasPromise?.()) {
47
- global.HermesInternal?.enablePromiseRejectionTracker?.(rejectionTrackingOptions)
50
+ // 支持 core-js promise polyfill
51
+ const oldOnUnhandledRejection = global.onunhandledrejection
52
+ global.onunhandledrejection = function onunhandledrejection (event) {
53
+ onUnhandledRejection(event)
54
+ isFunction(oldOnUnhandledRejection) && oldOnUnhandledRejection.call(this, event)
55
+ }
56
+ if (global.HermesInternal?.hasPromise?.()) {
57
+ global.HermesInternal.enablePromiseRejectionTracker?.(rejectionTrackingOptions)
48
58
  } else {
49
59
  require('promise/setimmediate/rejection-tracking').enable(rejectionTrackingOptions)
50
60
  }
@@ -38,7 +38,7 @@ function initGlobalErrorHandling () {
38
38
  window.addEventListener('unhandledrejection', (event) => {
39
39
  if (global.__mpxAppCbs && global.__mpxAppCbs.rejection && global.__mpxAppCbs.rejection.length) {
40
40
  global.__mpxAppCbs.rejection.forEach((cb) => {
41
- cb(event.reason, event.promise)
41
+ cb(event)
42
42
  })
43
43
  } else {
44
44
  console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}\n`)
@@ -11,11 +11,10 @@ const providesMap = {
11
11
  global.__mpxProvidesMap = providesMap
12
12
 
13
13
  /** @internal createApp() 初始化应用层 scope provide */
14
- export function initAppProvides (appOptions) {
15
- const provideOpt = appOptions.provide
14
+ export function initAppProvides (provideOpt, instance) {
16
15
  if (provideOpt) {
17
16
  const provided = isFunction(provideOpt)
18
- ? callWithErrorHandling(provideOpt.bind(appOptions), appOptions, 'createApp provide function')
17
+ ? callWithErrorHandling(provideOpt.bind(instance), instance, 'createApp provide function')
19
18
  : provideOpt
20
19
  if (isObject(provided)) {
21
20
  providesMap.__app = provided
@@ -367,14 +367,10 @@ function usePageStatus (navigation, pageId) {
367
367
  const blurSubscription = navigation.addListener('blur', () => {
368
368
  pageStatusMap[pageId] = 'hide'
369
369
  })
370
- const unWatchAppFocusedState = watch(global.__mpxAppFocusedState, (value) => {
371
- pageStatusMap[pageId] = value
372
- })
373
370
 
374
371
  return () => {
375
372
  focusSubscription()
376
373
  blurSubscription()
377
- unWatchAppFocusedState()
378
374
  del(pageStatusMap, pageId)
379
375
  }
380
376
  }, [navigation])
@@ -445,10 +441,17 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
445
441
 
446
442
  useEffect(() => {
447
443
  if (type === 'page') {
448
- if (!global.__mpxAppLaunched && global.__mpxAppOnLaunch) {
444
+ if (!global.__mpxAppHotLaunched && global.__mpxAppOnLaunch) {
449
445
  global.__mpxAppOnLaunch(props.navigation)
450
446
  }
451
- proxy.callHook(ONLOAD, [props.route.params || {}])
447
+ const loadParams = {}
448
+ // 此处拿到的props.route.params内属性的value被进行过了一次decode, 不符合预期,此处额外进行一次encode来与微信对齐
449
+ if (isObject(props.route.params)) {
450
+ for (const key in props.route.params) {
451
+ loadParams[key] = encodeURIComponent(props.route.params[key])
452
+ }
453
+ }
454
+ proxy.callHook(ONLOAD, [loadParams])
452
455
  }
453
456
  proxy.mounted()
454
457
  return () => {
@@ -530,9 +533,11 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
530
533
 
531
534
  useEffect(() => {
532
535
  const unsubscribe = navigation.addListener('transitionEnd', (e) => {
533
- rootRef.current?.measureInWindow((x, y, width, height) => {
534
- navigation.layout = { x, y, width, height }
535
- })
536
+ setTimeout(() => {
537
+ rootRef.current?.measureInWindow((x, y, width, height) => {
538
+ navigation.layout = { x, y, width, height }
539
+ })
540
+ }, 200)
536
541
  });
537
542
  return unsubscribe;
538
543
  }, [navigation]);