@mpxjs/core 2.9.70-alpha.2 → 2.9.70-alpha.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.
@@ -1,9 +1,9 @@
1
- import { useEffect, useLayoutEffect, useSyncExternalStore, useRef, useMemo, useState, useCallback, createElement, memo, forwardRef, useImperativeHandle, useContext, Fragment, cloneElement } from 'react'
1
+ import { useEffect, useLayoutEffect, useSyncExternalStore, useRef, useMemo, useState, useCallback, 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'
5
5
  import { reactive, set, del } from '../../observer/reactive'
6
- import { hasOwn, isFunction, noop, isObject, isArray, getByPath, collectDataset, hump2dash, wrapMethodsWithErrorHandling } from '@mpxjs/utils'
6
+ import { hasOwn, isFunction, noop, isObject, isArray, getByPath, collectDataset, hump2dash, dash2hump, callWithErrorHandling, wrapMethodsWithErrorHandling } 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'
@@ -11,6 +11,8 @@ import { queueJob, hasPendingJob } from '../../observer/scheduler'
11
11
  import { createSelectorQuery, createIntersectionObserver } from '@mpxjs/api-proxy'
12
12
  import { IntersectionObserverContext, RouteContext, KeyboardAvoidContext } from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/context'
13
13
 
14
+ const ProviderContext = createContext(null)
15
+
14
16
  function getSystemInfo () {
15
17
  const window = ReactNative.Dimensions.get('window')
16
18
  const screen = ReactNative.Dimensions.get('screen')
@@ -42,7 +44,8 @@ function createEffect (proxy, components) {
42
44
  const getComponent = (tagName) => {
43
45
  if (!tagName) return null
44
46
  if (tagName === 'block') return Fragment
45
- return components[tagName] || getByPath(ReactNative, tagName)
47
+ const appComponents = global.__getAppComponents?.() || {}
48
+ return components[tagName] || appComponents[tagName] || getByPath(ReactNative, tagName)
46
49
  }
47
50
  const innerCreateElement = (type, ...rest) => {
48
51
  if (!type) return null
@@ -52,20 +55,18 @@ function createEffect (proxy, components) {
52
55
  proxy.effect = new ReactiveEffect(() => {
53
56
  // reset instance
54
57
  proxy.target.__resetInstance()
55
- return proxy.target.__injectedRender(innerCreateElement, getComponent)
58
+ return callWithErrorHandling(proxy.target.__injectedRender.bind(proxy.target), proxy, 'render function', [innerCreateElement, getComponent])
56
59
  }, () => queueJob(update), proxy.scope)
57
60
  // render effect允许自触发
58
61
  proxy.toggleRecurse(true)
59
62
  }
60
63
 
61
- function getRootProps (props) {
64
+ function getRootProps (props, validProps) {
62
65
  const rootProps = {}
63
66
  for (const key in props) {
64
- if (hasOwn(props, key)) {
65
- const match = /^(bind|catch|capture-bind|capture-catch|style|enable-var):?(.*?)(?:\.(.*))?$/.exec(key)
66
- if (match) {
67
- rootProps[key] = props[key]
68
- }
67
+ const altKey = dash2hump(key)
68
+ if (!hasOwn(validProps, key) && !hasOwn(validProps, altKey) && key !== 'children') {
69
+ rootProps[key] = props[key]
69
70
  }
70
71
  }
71
72
  return rootProps
@@ -195,7 +196,7 @@ const instanceProto = {
195
196
  }
196
197
  }
197
198
 
198
- function createInstance ({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, intersectionCtx }) {
199
+ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, intersectionCtx, relation, parentProvides }) {
199
200
  const instance = Object.create(instanceProto, {
200
201
  dataset: {
201
202
  get () {
@@ -246,9 +247,33 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
246
247
  return currentInject.getRefsData || noop
247
248
  },
248
249
  enumerable: false
250
+ },
251
+ __parentProvides: {
252
+ get () {
253
+ return parentProvides || null
254
+ },
255
+ enumerable: false
249
256
  }
250
257
  })
251
258
 
259
+ if (type === 'component') {
260
+ Object.defineProperty(instance, '__componentPath', {
261
+ get () {
262
+ return currentInject.componentPath || ''
263
+ },
264
+ enumerable: false
265
+ })
266
+ }
267
+
268
+ if (relation) {
269
+ Object.defineProperty(instance, '__relation', {
270
+ get () {
271
+ return relation
272
+ },
273
+ enumerable: false
274
+ })
275
+ }
276
+
252
277
  // bind this & assign methods
253
278
  if (rawOptions.methods) {
254
279
  Object.entries(rawOptions.methods).forEach(([key, method]) => {
@@ -367,34 +392,68 @@ function usePageStatus (navigation, pageId) {
367
392
  const blurSubscription = navigation.addListener('blur', () => {
368
393
  pageStatusMap[pageId] = 'hide'
369
394
  })
370
- const unWatchAppFocusedState = watch(global.__mpxAppFocusedState, (value) => {
371
- pageStatusMap[pageId] = value
372
- })
373
395
 
374
396
  return () => {
375
397
  focusSubscription()
376
398
  blurSubscription()
377
- unWatchAppFocusedState()
378
399
  del(pageStatusMap, pageId)
379
400
  }
380
401
  }, [navigation])
381
402
  }
382
403
 
404
+ const RelationsContext = createContext(null)
405
+
406
+ const checkRelation = (options) => {
407
+ const relations = options.relations || {}
408
+ let hasDescendantRelation = false
409
+ let hasAncestorRelation = false
410
+ Object.keys(relations).forEach((path) => {
411
+ const relation = relations[path]
412
+ const type = relation.type
413
+ if (['child', 'descendant'].includes(type)) {
414
+ hasDescendantRelation = true
415
+ } else if (['parent', 'ancestor'].includes(type)) {
416
+ hasAncestorRelation = true
417
+ }
418
+ })
419
+ return {
420
+ hasDescendantRelation,
421
+ hasAncestorRelation
422
+ }
423
+ }
424
+
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
+
383
436
  export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
384
437
  rawOptions = mergeOptions(rawOptions, type, false)
385
438
  const components = Object.assign({}, rawOptions.components, currentInject.getComponents())
386
439
  const validProps = Object.assign({}, rawOptions.props, rawOptions.properties)
440
+ const { hasDescendantRelation, hasAncestorRelation } = checkRelation(rawOptions)
387
441
  if (rawOptions.methods) rawOptions.methods = wrapMethodsWithErrorHandling(rawOptions.methods)
388
442
  const defaultOptions = memo(forwardRef((props, ref) => {
389
443
  const instanceRef = useRef(null)
390
444
  const propsRef = useRef(null)
391
445
  const intersectionCtx = useContext(IntersectionObserverContext)
392
446
  const pageId = useContext(RouteContext)
447
+ const parentProvides = useContext(ProviderContext)
448
+ let relation = null
449
+ if (hasDescendantRelation || hasAncestorRelation) {
450
+ relation = useContext(RelationsContext)
451
+ }
393
452
  propsRef.current = props
394
453
  let isFirst = false
395
454
  if (!instanceRef.current) {
396
455
  isFirst = true
397
- instanceRef.current = createInstance({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, intersectionCtx })
456
+ instanceRef.current = createInstance({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, intersectionCtx, relation, parentProvides })
398
457
  }
399
458
  const instance = instanceRef.current
400
459
  useImperativeHandle(ref, () => {
@@ -444,10 +503,17 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
444
503
 
445
504
  useEffect(() => {
446
505
  if (type === 'page') {
447
- if (!global.__mpxAppLaunched && global.__mpxAppOnLaunch) {
506
+ if (!global.__mpxAppHotLaunched && global.__mpxAppOnLaunch) {
448
507
  global.__mpxAppOnLaunch(props.navigation)
449
508
  }
450
- proxy.callHook(ONLOAD, [props.route.params || {}])
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])
451
517
  }
452
518
  proxy.mounted()
453
519
  return () => {
@@ -472,14 +538,28 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
472
538
  return proxy.finalMemoVersion
473
539
  }, [proxy.stateVersion, proxy.memoVersion])
474
540
 
475
- const root = useMemo(() => proxy.effect.run(), [finalMemoVersion])
541
+ let root = useMemo(() => proxy.effect.run(), [finalMemoVersion])
476
542
  if (root && root.props.ishost) {
477
- const rootProps = getRootProps(props)
543
+ // 对于组件未注册的属性继承到host节点上,如事件、样式和其他属性等
544
+ const rootProps = getRootProps(props, validProps)
478
545
  rootProps.style = Object.assign({}, root.props.style, rootProps.style)
479
546
  // update root props
480
- return cloneElement(root, rootProps)
547
+ root = cloneElement(root, rootProps)
481
548
  }
482
- return root
549
+
550
+ const provides = proxy.provides
551
+ if (provides) {
552
+ root = createElement(ProviderContext.Provider, { value: provides }, root)
553
+ }
554
+
555
+ return hasDescendantRelation
556
+ ? createElement(RelationsContext.Provider,
557
+ {
558
+ value: provideRelation(instance, relation)
559
+ },
560
+ root
561
+ )
562
+ : root
483
563
  }))
484
564
 
485
565
  if (rawOptions.options?.isCustomText) {
@@ -487,30 +567,25 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
487
567
  }
488
568
 
489
569
  if (type === 'page') {
490
- const { Provider, useSafeAreaInsets, GestureHandlerRootView } = global.__navigationHelper
570
+ const { Provider, useSafeAreaInsets, GestureHandlerRootView, useHeaderHeight } = global.__navigationHelper
491
571
  const pageConfig = Object.assign({}, global.__mpxPageConfig, currentInject.pageConfig)
492
572
  const Page = ({ navigation, route }) => {
493
- const [enabled, setEnabled] = useState(true)
573
+ const [enabled, setEnabled] = useState(false)
494
574
  const currentPageId = useMemo(() => ++pageId, [])
495
575
  const intersectionObservers = useRef({})
496
576
  usePageStatus(navigation, currentPageId)
497
577
 
498
578
  useLayoutEffect(() => {
499
579
  const isCustom = pageConfig.navigationStyle === 'custom'
500
- navigation.setOptions({
580
+ navigation.setOptions(Object.assign({
501
581
  headerShown: !isCustom,
502
582
  title: pageConfig.navigationBarTitleText || '',
503
583
  headerStyle: {
504
584
  backgroundColor: pageConfig.navigationBarBackgroundColor || '#000000'
505
585
  },
506
- headerTintColor: pageConfig.navigationBarTextStyle || 'white'
507
- })
508
- if (__mpx_mode__ === 'android') {
509
- ReactNative.StatusBar.setBarStyle(pageConfig.barStyle || 'dark-content')
510
- ReactNative.StatusBar.setTranslucent(isCustom) // 控制statusbar是否占位
511
- const color = isCustom ? 'transparent' : pageConfig.statusBarColor
512
- color && ReactNative.StatusBar.setBackgroundColor(color)
513
- }
586
+ headerTintColor: pageConfig.navigationBarTextStyle || 'white',
587
+ statusBarTranslucent: true
588
+ }, __mpx_mode__ === 'android' ? { statusBarStyle: pageConfig.statusBarStyle || 'light' } : {}))
514
589
  }, [])
515
590
 
516
591
  const rootRef = useRef(null)
@@ -548,7 +623,12 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
548
623
 
549
624
  return createElement(GestureHandlerRootView,
550
625
  {
551
- style: {
626
+ // https://github.com/software-mansion/react-native-reanimated/issues/6639 因存在此问题,iOS在页面上进行定宽来暂时规避
627
+ style: __mpx_mode__ === 'ios' && pageConfig.navigationStyle !== 'custom'
628
+ ? {
629
+ height: ReactNative.Dimensions.get('screen').height - useHeaderHeight()
630
+ }
631
+ : {
552
632
  flex: 1
553
633
  }
554
634
  },
@@ -560,10 +640,7 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
560
640
  backgroundColor: pageConfig.backgroundColor || '#ffffff'
561
641
  },
562
642
  ref: rootRef,
563
- onLayout,
564
- onTouchStart: () => {
565
- ReactNative.Keyboard.isVisible() && ReactNative.Keyboard.dismiss()
566
- }
643
+ onLayout
567
644
  },
568
645
  createElement(RouteContext.Provider,
569
646
  {
@@ -23,13 +23,11 @@ function transformProperties (properties) {
23
23
  } else {
24
24
  newFiled = Object.assign({}, rawFiled)
25
25
  }
26
- const rawObserver = rawFiled?.observer
27
26
  newFiled.observer = function (value, oldValue) {
28
27
  if (this.__mpxProxy) {
29
28
  this[key] = value
30
29
  this.__mpxProxy.propsUpdated()
31
30
  }
32
- rawObserver && rawObserver.call(this, value, oldValue)
33
31
  }
34
32
  newProps[key] = newFiled
35
33
  })
@@ -1 +0,0 @@
1
- export default {}
@@ -1,6 +0,0 @@
1
- import Vue from 'vue'
2
- import install from './vuePlugin'
3
-
4
- Vue.use(install)
5
-
6
- export default Vue
File without changes