@mpxjs/core 2.10.1 → 2.10.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.
package/@types/index.d.ts CHANGED
@@ -119,7 +119,8 @@ interface Context {
119
119
  selectComponent: ReplaceWxComponentIns['selectComponent']
120
120
  selectAllComponents: ReplaceWxComponentIns['selectAllComponents']
121
121
  createSelectorQuery: WechatMiniprogram.Component.InstanceMethods<Record<string, any>>['createSelectorQuery']
122
- createIntersectionObserver: WechatMiniprogram.Component.InstanceMethods<Record<string, any>>['createIntersectionObserver']
122
+ createIntersectionObserver: WechatMiniprogram.Component.InstanceMethods<Record<string, any>>['createIntersectionObserver'],
123
+ getPageId: WechatMiniprogram.Component.InstanceMethods<Record<string, any>>['getPageId']
123
124
  }
124
125
 
125
126
  interface ComponentOpt<D extends Data, P extends Properties, C, M extends Methods, Mi extends Array<any>, S extends Record<any, any>> extends Partial<WechatMiniprogram.Component.Lifetimes & WechatMiniprogram.Component.OtherOption> {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/core",
3
- "version": "2.10.1",
3
+ "version": "2.10.2",
4
4
  "description": "mpx runtime core",
5
5
  "keywords": [
6
6
  "miniprogram",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "main": "src/index.js",
21
21
  "dependencies": {
22
- "@mpxjs/utils": "^2.10.0",
22
+ "@mpxjs/utils": "^2.10.2",
23
23
  "lodash": "^4.1.1",
24
24
  "miniprogram-api-typings": "^3.10.0"
25
25
  },
@@ -109,5 +109,5 @@
109
109
  "url": "https://github.com/didi/mpx/issues"
110
110
  },
111
111
  "sideEffects": false,
112
- "gitHead": "7e4f9ab5528f1ab0ae08fdfad1bfbbb445088848"
112
+ "gitHead": "4445de7c3ce29fb0166d3bb77a1a0460e41a32d3"
113
113
  }
@@ -2,6 +2,8 @@ import { getConvertRule } from '../convertor/convertor'
2
2
  import builtInKeysMap from '../platform/patch/builtInKeysMap'
3
3
  import { implemented } from './implement'
4
4
  import {
5
+ isArray,
6
+ isFunction,
5
7
  isObject,
6
8
  aliasReplace,
7
9
  makeMap,
@@ -221,6 +223,10 @@ function mergeMixins (parent, child) {
221
223
  mergeToArray(parent, child, key)
222
224
  } else if (/^behaviors|externalClasses$/.test(key)) {
223
225
  mergeArray(parent, child, key)
226
+ } else if (key === 'inject') {
227
+ mergeInject(parent, child, key)
228
+ } else if (key === 'provide') {
229
+ mergeProvide(parent, child, key)
224
230
  } else if (key !== 'mixins' && key !== 'mpxCustomKeysForBlend') {
225
231
  // 收集非函数的自定义属性,在Component创建的页面中挂载到this上,模拟Page创建页面的表现,swan当中component构造器也能自动挂载自定义数据,不需要框架模拟挂载
226
232
  if (curType === 'blend' && typeof child[key] !== 'function' && !builtInKeysMap[key] && __mpx_mode__ !== 'swan') {
@@ -277,6 +283,37 @@ function mergeDataFn (parent, child, key) {
277
283
  }
278
284
  }
279
285
 
286
+ function normalizeInject (options) {
287
+ const injectOpt = options.inject
288
+ if (isArray(injectOpt)) {
289
+ const normalized = (options.inject = {})
290
+ for (let i = 0; i < injectOpt.length; i++) {
291
+ normalized[injectOpt[i]] = injectOpt[i]
292
+ }
293
+ }
294
+ }
295
+
296
+ function mergeInject (parent, child, key) {
297
+ normalizeInject(child)
298
+ mergeShallowObj(parent, child, key)
299
+ }
300
+
301
+ function mergeProvide (parent, child, key) {
302
+ const parentVal = parent[key]
303
+ const childVal = child[key]
304
+ if (!parentVal) {
305
+ parent[key] = childVal
306
+ } else if (!childVal) {
307
+ parent[key] = parentVal
308
+ } else {
309
+ parent[key] = function mergedProvide () {
310
+ const to = isFunction(parentVal) ? parentVal.call(this) : parentVal
311
+ const from = isFunction(childVal) ? childVal.call(this) : childVal
312
+ return Object.assign(to, from)
313
+ }
314
+ }
315
+ }
316
+
280
317
  export function mergeArray (parent, child, key) {
281
318
  const childVal = child[key]
282
319
  if (!parent[key]) {
package/src/core/proxy.js CHANGED
@@ -101,7 +101,6 @@ function preProcessRenderData (renderData) {
101
101
  })
102
102
  return processedRenderData
103
103
  }
104
-
105
104
  export default class MpxProxy {
106
105
  constructor (options, target, reCreated) {
107
106
  this.target = target
@@ -317,7 +316,8 @@ export default class MpxProxy {
317
316
  selectComponent: this.target.selectComponent.bind(this.target),
318
317
  selectAllComponents: this.target.selectAllComponents.bind(this.target),
319
318
  createSelectorQuery: this.target.createSelectorQuery ? this.target.createSelectorQuery.bind(this.target) : envObj.createSelectorQuery.bind(envObj),
320
- createIntersectionObserver: this.target.createIntersectionObserver ? this.target.createIntersectionObserver.bind(this.target) : envObj.createIntersectionObserver.bind(envObj)
319
+ createIntersectionObserver: this.target.createIntersectionObserver ? this.target.createIntersectionObserver.bind(this.target) : envObj.createIntersectionObserver.bind(envObj),
320
+ getPageId: this.target.getPageId.bind(this.target)
321
321
  }
322
322
  ])
323
323
  if (!isObject(setupResult)) {
@@ -13,6 +13,7 @@ import pageRouteMixin from './pageRouteMixin'
13
13
  import { dynamicRefsMixin, dynamicRenderHelperMixin, dynamicSlotMixin } from '../../dynamic/dynamicRenderMixin.empty'
14
14
  import styleHelperMixin from './styleHelperMixin'
15
15
  import directiveHelperMixin from './directiveHelperMixin'
16
+ import pageIdMixin from './pageIdMixin'
16
17
 
17
18
  export default function getBuiltInMixins ({ type, rawOptions = {} }) {
18
19
  let bulitInMixins
@@ -36,7 +37,8 @@ export default function getBuiltInMixins ({ type, rawOptions = {} }) {
36
37
  getTabBarMixin(type),
37
38
  pageRouteMixin(type),
38
39
  // 由于relation可能是通过mixin注入的,不能通过当前的用户options中是否存在relations来简单判断是否注入该项mixin
39
- relationsMixin(type)
40
+ relationsMixin(type),
41
+ pageIdMixin(type)
40
42
  ]
41
43
  } else {
42
44
  // 此为差异抹平类mixins,原生模式下也需要注入也抹平平台差异
@@ -46,6 +48,11 @@ export default function getBuiltInMixins ({ type, rawOptions = {} }) {
46
48
  refsMixin(),
47
49
  relationsMixin(type)
48
50
  ]
51
+ if (__mpx_mode__ === 'ali') {
52
+ bulitInMixins = bulitInMixins.concat([
53
+ pageIdMixin(type)
54
+ ])
55
+ }
49
56
  // 此为纯增强类mixins,原生模式下不需要注入
50
57
  if (!rawOptions.__nativeRender__) {
51
58
  bulitInMixins = bulitInMixins.concat([
@@ -0,0 +1,13 @@
1
+ export default function pageIdMixin (mixinType) {
2
+ return {
3
+ methods: {
4
+ getPageId () {
5
+ if (mixinType === 'component') {
6
+ return this.$page.$id
7
+ } else {
8
+ return this.$id
9
+ }
10
+ }
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,25 @@
1
+ let pageId = 0
2
+
3
+ export default function pageIdMixin (mixinType) {
4
+ const mixin = {}
5
+
6
+ if (mixinType === 'page') {
7
+ Object.assign(mixin, {
8
+ beforeCreate () {
9
+ this.__pageId = ++pageId
10
+ },
11
+ provide () {
12
+ return {
13
+ __pageId: this.__pageId
14
+ }
15
+ }
16
+ })
17
+ }
18
+ if (mixinType === 'component') {
19
+ Object.assign(mixin, {
20
+ inject: ['__pageId']
21
+ })
22
+ }
23
+
24
+ return mixin
25
+ }
@@ -181,18 +181,29 @@ export default function createApp (options) {
181
181
  }, [])
182
182
 
183
183
  const { initialRouteName, initialParams } = initialRouteRef.current
184
- const headerBackImageSource = Mpx.config.rnConfig.headerBackImageSource || null
185
184
  const navScreenOpts = {
186
185
  // 7.x替换headerBackTitleVisible
187
186
  // headerBackButtonDisplayMode: 'minimal',
188
187
  headerBackTitleVisible: false,
189
- // 安卓上会出现初始化时闪现导航条的问题
190
- headerShown: false,
191
- // 隐藏导航下的那条线
192
188
  headerShadowVisible: false
193
189
  }
194
- if (headerBackImageSource) {
195
- navScreenOpts.headerBackImageSource = headerBackImageSource
190
+ if (__mpx_mode__ === 'ios') {
191
+ // ios使用native-stack
192
+ const headerBackImageSource = Mpx.config.rnConfig.headerBackImageSource || null
193
+ if (headerBackImageSource) {
194
+ navScreenOpts.headerBackImageSource = headerBackImageSource
195
+ }
196
+ } else {
197
+ // 安卓上会出现导航条闪现的问题所以默认加headerShown false(stack版本, native-stack版本可以干掉)
198
+ // iOS加上默认headerShown false的话会因为iOS根高度是screenHeight - useHeaderHeight()会导致出现渲染两次情况,因此iOS不加此默认值
199
+ navScreenOpts.headerShown = false
200
+ // 安卓和鸿蒙先用stack
201
+ const headerBackImageProps = Mpx.config.rnConfig.headerBackImageProps || null
202
+ if (headerBackImageProps) {
203
+ navScreenOpts.headerBackImage = () => {
204
+ return createElement(ReactNative.Image, headerBackImageProps)
205
+ }
206
+ }
196
207
  }
197
208
 
198
209
  return createElement(SafeAreaProvider,
@@ -126,5 +126,9 @@ export default function install (Vue) {
126
126
  Vue.prototype.createIntersectionObserver = function (options) {
127
127
  return createIntersectionObserver(this, options)
128
128
  }
129
+
130
+ Vue.prototype.getPageId = function () {
131
+ return this.__pageId
132
+ }
129
133
  hackEffectScope()
130
134
  }
@@ -1,4 +1,4 @@
1
- import { useEffect, useLayoutEffect, useSyncExternalStore, useRef, useMemo, useCallback, createElement, memo, forwardRef, useImperativeHandle, useContext, Fragment, cloneElement, createContext } from 'react'
1
+ import { useEffect, useLayoutEffect, useSyncExternalStore, useRef, useMemo, createElement, memo, forwardRef, useImperativeHandle, useContext, Fragment, cloneElement, createContext } from 'react'
2
2
  import * as ReactNative from 'react-native'
3
3
  import { ReactiveEffect } from '../../observer/effect'
4
4
  import { watch } from '../../observer/watch'
@@ -286,11 +286,27 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
286
286
  instance.route = props.route.name
287
287
  global.__mpxPagesMap = global.__mpxPagesMap || {}
288
288
  global.__mpxPagesMap[props.route.key] = [instance, props.navigation]
289
+ // App onLaunch 在 Page created 之前执行
290
+ if (!global.__mpxAppHotLaunched && global.__mpxAppOnLaunch) {
291
+ global.__mpxAppOnLaunch(props.navigation)
292
+ }
289
293
  }
290
294
 
291
295
  const proxy = instance.__mpxProxy = new MpxProxy(rawOptions, instance)
292
296
  proxy.created()
293
297
 
298
+ if (type === 'page') {
299
+ const loadParams = {}
300
+ const props = propsRef.current
301
+ // 此处拿到的props.route.params内属性的value被进行过了一次decode, 不符合预期,此处额外进行一次encode来与微信对齐
302
+ if (isObject(props.route.params)) {
303
+ for (const key in props.route.params) {
304
+ loadParams[key] = encodeURIComponent(props.route.params[key])
305
+ }
306
+ }
307
+ proxy.callHook(ONLOAD, [loadParams])
308
+ }
309
+
294
310
  Object.assign(proxy, {
295
311
  onStoreChange: null,
296
312
  stateVersion: Symbol(),
@@ -384,7 +400,9 @@ const pageStatusMap = global.__mpxPageStatusMap = reactive({})
384
400
 
385
401
  function usePageStatus (navigation, pageId) {
386
402
  navigation.pageId = pageId
387
- set(pageStatusMap, pageId, '')
403
+ if (!hasOwn(pageStatusMap, pageId)) {
404
+ set(pageStatusMap, pageId, '')
405
+ }
388
406
  useEffect(() => {
389
407
  const focusSubscription = navigation.addListener('focus', () => {
390
408
  pageStatusMap[pageId] = 'show'
@@ -422,17 +440,6 @@ const checkRelation = (options) => {
422
440
  }
423
441
  }
424
442
 
425
- const provideRelation = (instance, relation) => {
426
- const componentPath = instance.__componentPath
427
- if (relation) {
428
- return Object.assign({}, relation, { [componentPath]: instance })
429
- } else {
430
- return {
431
- [componentPath]: instance
432
- }
433
- }
434
- }
435
-
436
443
  export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
437
444
  rawOptions = mergeOptions(rawOptions, type, false)
438
445
  const components = Object.assign({}, rawOptions.components, currentInject.getComponents())
@@ -502,19 +509,6 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
502
509
  usePageEffect(proxy, pageId)
503
510
 
504
511
  useEffect(() => {
505
- if (type === 'page') {
506
- if (!global.__mpxAppHotLaunched && global.__mpxAppOnLaunch) {
507
- global.__mpxAppOnLaunch(props.navigation)
508
- }
509
- const loadParams = {}
510
- // 此处拿到的props.route.params内属性的value被进行过了一次decode, 不符合预期,此处额外进行一次encode来与微信对齐
511
- if (isObject(props.route.params)) {
512
- for (const key in props.route.params) {
513
- loadParams[key] = encodeURIComponent(props.route.params[key])
514
- }
515
- }
516
- proxy.callHook(ONLOAD, [loadParams])
517
- }
518
512
  proxy.mounted()
519
513
  return () => {
520
514
  proxy.unmounted()
@@ -552,14 +546,28 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
552
546
  root = createElement(ProviderContext.Provider, { value: provides }, root)
553
547
  }
554
548
 
555
- return hasDescendantRelation
556
- ? createElement(RelationsContext.Provider,
557
- {
558
- value: provideRelation(instance, relation)
559
- },
560
- root
561
- )
562
- : root
549
+ if (hasDescendantRelation) {
550
+ const relationProvide = useMemo(() => {
551
+ const componentPath = instance.__componentPath
552
+ if (relation) {
553
+ return Object.assign({}, relation, { [componentPath]: instance })
554
+ } else {
555
+ return {
556
+ [componentPath]: instance
557
+ }
558
+ }
559
+ }, [relation])
560
+
561
+ return createElement(
562
+ RelationsContext.Provider,
563
+ {
564
+ value: relationProvide
565
+ },
566
+ root
567
+ )
568
+ } else {
569
+ return root
570
+ }
563
571
  }))
564
572
 
565
573
  if (rawOptions.options?.isCustomText) {
@@ -572,28 +580,39 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
572
580
  const Page = ({ navigation, route }) => {
573
581
  const currentPageId = useMemo(() => ++pageId, [])
574
582
  const intersectionObservers = useRef({})
583
+ const routeContextValRef = useRef({
584
+ pageId: currentPageId,
585
+ navigation
586
+ })
575
587
  usePageStatus(navigation, currentPageId)
576
588
  useLayoutEffect(() => {
577
589
  const isCustom = pageConfig.navigationStyle === 'custom'
578
- navigation.setOptions(Object.assign({
590
+ navigation.setOptions({
579
591
  headerShown: !isCustom,
580
- title: pageConfig.navigationBarTitleText || '',
592
+ title: pageConfig.navigationBarTitleText?.trim() || '',
581
593
  headerStyle: {
582
594
  backgroundColor: pageConfig.navigationBarBackgroundColor || '#000000'
583
595
  },
584
- headerTintColor: pageConfig.navigationBarTextStyle || 'white',
585
- statusBarTranslucent: true
586
- }, __mpx_mode__ === 'android' ? { statusBarStyle: pageConfig.statusBarStyle || 'light' } : {}))
596
+ headerTintColor: pageConfig.navigationBarTextStyle || 'white'
597
+ })
598
+
599
+ if (__mpx_mode__ === 'android') {
600
+ ReactNative.StatusBar.setBarStyle(pageConfig.barStyle || 'dark-content')
601
+ ReactNative.StatusBar.setTranslucent(isCustom) // 控制statusbar是否占位
602
+ const color = isCustom ? 'transparent' : pageConfig.statusBarColor
603
+ color && ReactNative.StatusBar.setBackgroundColor(color)
604
+ }
587
605
  }, [])
588
606
 
589
607
  const rootRef = useRef(null)
590
- const keyboardAvoidRef = useRef({ cursorSpacing: 0, ref: null })
591
- const onLayout = useCallback(() => {
592
- rootRef.current?.measureInWindow((x, y, width, height) => {
593
- navigation.layout = { x, y, width, height }
594
- })
608
+ const keyboardAvoidRef = useRef(null)
609
+ useEffect(() => {
610
+ setTimeout(() => {
611
+ rootRef.current?.measureInWindow((x, y, width, height) => {
612
+ navigation.layout = { x, y, width, height }
613
+ })
614
+ }, 100)
595
615
  }, [])
596
-
597
616
  const withKeyboardAvoidingView = (element) => {
598
617
  return createElement(KeyboardAvoidContext.Provider,
599
618
  {
@@ -619,12 +638,12 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
619
638
  {
620
639
  // https://github.com/software-mansion/react-native-reanimated/issues/6639 因存在此问题,iOS在页面上进行定宽来暂时规避
621
640
  style: __mpx_mode__ === 'ios' && pageConfig.navigationStyle !== 'custom'
622
- ? {
623
- height: ReactNative.Dimensions.get('screen').height - useHeaderHeight()
624
- }
625
- : {
626
- flex: 1
627
- }
641
+ ? {
642
+ height: ReactNative.Dimensions.get('screen').height - useHeaderHeight()
643
+ }
644
+ : {
645
+ flex: 1
646
+ }
628
647
  },
629
648
  withKeyboardAvoidingView(
630
649
  createElement(ReactNative.View,
@@ -633,15 +652,11 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
633
652
  flex: 1,
634
653
  backgroundColor: pageConfig.backgroundColor || '#ffffff'
635
654
  },
636
- ref: rootRef,
637
- onLayout
655
+ ref: rootRef
638
656
  },
639
657
  createElement(RouteContext.Provider,
640
658
  {
641
- value: {
642
- pageId: currentPageId,
643
- navigation
644
- }
659
+ value: routeContextValRef.current
645
660
  },
646
661
  createElement(IntersectionObserverContext.Provider,
647
662
  {
@@ -60,7 +60,8 @@ export function getDefaultOptions ({ type, rawOptions = {} }) {
60
60
  selectComponent: instance.selectComponent.bind(instance),
61
61
  selectAllComponents: instance.selectAllComponents.bind(instance),
62
62
  createSelectorQuery: instance.createSelectorQuery.bind(instance),
63
- createIntersectionObserver: instance.createIntersectionObserver.bind(instance)
63
+ createIntersectionObserver: instance.createIntersectionObserver.bind(instance),
64
+ getPageId: instance.getPageId.bind(instance)
64
65
  }
65
66
  const setupRes = rawSetup(props, newContext)
66
67
  unsetCurrentInstance(instance.__mpxProxy)