@mpxjs/core 2.9.71 → 2.9.72

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.71",
3
+ "version": "2.9.72",
4
4
  "description": "mpx runtime core",
5
5
  "keywords": [
6
6
  "miniprogram",
@@ -24,17 +24,16 @@
24
24
  "miniprogram-api-typings": "^3.10.0"
25
25
  },
26
26
  "peerDependencies": {
27
- "@ant-design/react-native": "^5.1.3",
28
27
  "@d11/react-native-fast-image": "^8.6.12",
29
28
  "@mpxjs/api-proxy": "^2.9.0",
30
29
  "@mpxjs/store": "^2.9.0",
31
30
  "@react-navigation/native": "^7.0.3",
32
31
  "@react-navigation/stack": "^7.0.4",
33
- "promise": "^8.3.0",
34
32
  "react": "*",
35
33
  "react-native": "*",
36
34
  "react-native-gesture-handler": "^2.19.0",
37
35
  "react-native-linear-gradient": "^2.8.3",
36
+ "react-native-reanimated": "^3.15.2",
38
37
  "react-native-safe-area-context": "^4.14.0",
39
38
  "react-native-screens": "^4.1.0",
40
39
  "react-native-webview": "^13.10.5",
@@ -62,7 +61,7 @@
62
61
  "react-native": {
63
62
  "optional": true
64
63
  },
65
- "promise": {
64
+ "react-native-reanimated": {
66
65
  "optional": true
67
66
  },
68
67
  "@react-navigation/native": {
@@ -71,9 +70,6 @@
71
70
  "@react-navigation/stack": {
72
71
  "optional": true
73
72
  },
74
- "@ant-design/react-native": {
75
- "optional": true
76
- },
77
73
  "react-native-safe-area-context": {
78
74
  "optional": true
79
75
  },
@@ -109,5 +105,5 @@
109
105
  "url": "https://github.com/didi/mpx/issues"
110
106
  },
111
107
  "sideEffects": false,
112
- "gitHead": "38446f301a17e78dbe3ca7dacd771c79eb7a8ede"
108
+ "gitHead": "5ceca0430a79a41456260d87a81760f2cd31fad1"
113
109
  }
@@ -5,7 +5,7 @@ import {
5
5
  import { implemented } from '../core/implement'
6
6
 
7
7
  // 暂不支持的wx选项,后期需要各种花式支持
8
- const unsupported = ['relations', 'moved', 'definitionFilter']
8
+ const unsupported = ['moved', 'definitionFilter']
9
9
 
10
10
  function convertErrorDesc (key) {
11
11
  error(`Options.${key} is not supported in runtime conversion from wx to react native.`, global.currentResource)
package/src/core/proxy.js CHANGED
@@ -171,6 +171,8 @@ export default class MpxProxy {
171
171
  // web中BEFORECREATE钩子通过vue的beforeCreate钩子单独驱动
172
172
  this.callHook(BEFORECREATE)
173
173
  setCurrentInstance(this)
174
+ this.parent = this.resolveParent()
175
+ this.provides = this.parent ? this.parent.provides : Object.create(null)
174
176
  // 在 props/data 初始化之前初始化 inject
175
177
  this.initInject()
176
178
  this.initProps()
@@ -195,6 +197,18 @@ export default class MpxProxy {
195
197
  }
196
198
  }
197
199
 
200
+ resolveParent () {
201
+ if (isReact) {
202
+ return {
203
+ provides: this.target.__parentProvides
204
+ }
205
+ }
206
+ if (isFunction(this.target.selectOwnerComponent)) {
207
+ const parent = this.target.selectOwnerComponent()
208
+ return parent ? parent.__mpxProxy : null
209
+ }
210
+ }
211
+
198
212
  createRenderTask (isEmptyRender) {
199
213
  if ((!this.isMounted() && this.currentRenderTask) || (this.isMounted() && isEmptyRender)) {
200
214
  return
@@ -234,16 +248,6 @@ export default class MpxProxy {
234
248
  // 页面/组件销毁清除上下文的缓存
235
249
  contextMap.remove(this.uid)
236
250
  }
237
- if (!isWeb && this.options.__type__ === 'page') {
238
- // 小程序页面销毁时移除对应的 provide
239
- if (isFunction(this.target.getPageId)) {
240
- const pageId = this.target.getPageId()
241
- const providesMap = global.__mpxProvidesMap
242
- if (providesMap.__pages[pageId]) {
243
- delete providesMap.__pages[pageId]
244
- }
245
- }
246
- }
247
251
  this.callHook(BEFOREUNMOUNT)
248
252
  this.scope?.stop()
249
253
  if (this.update) this.update.active = false
@@ -22,7 +22,8 @@ export default function getBuiltInMixins ({ type, rawOptions = {} }) {
22
22
  directiveHelperMixin(),
23
23
  styleHelperMixin(),
24
24
  refsMixin(),
25
- i18nMixin()
25
+ i18nMixin(),
26
+ relationsMixin(type)
26
27
  ]
27
28
  } else if (__mpx_mode__ === 'web') {
28
29
  bulitInMixins = [
@@ -0,0 +1,67 @@
1
+ import { BEFORECREATE, MOUNTED, BEFOREUNMOUNT } from '../../core/innerLifecycle'
2
+
3
+ const relationTypeMap = {
4
+ parent: 'child',
5
+ ancestor: 'descendant'
6
+ }
7
+
8
+ export default function relationsMixin (mixinType) {
9
+ if (mixinType === 'component') {
10
+ return {
11
+ [BEFORECREATE] () {
12
+ this.__relationNodesMap = {}
13
+ },
14
+ [MOUNTED] () {
15
+ this.__mpxExecRelations('linked')
16
+ },
17
+ [BEFOREUNMOUNT] () {
18
+ this.__mpxExecRelations('unlinked')
19
+ this.__relationNodesMap = {}
20
+ },
21
+ methods: {
22
+ getRelationNodes (path) {
23
+ return this.__relationNodesMap[path] || null
24
+ },
25
+ __mpxExecRelations (type) {
26
+ const relations = this.__mpxProxy.options.relations
27
+ const relationContext = this.__relation
28
+ const currentPath = this.__componentPath
29
+ if (relations && relationContext) {
30
+ Object.keys(relations).forEach((path) => {
31
+ const relation = relations[path]
32
+ const relationType = relation.type
33
+ if ((relationType === 'parent' || relationType === 'ancestor') && relationContext[path]) {
34
+ const target = relationContext[path]
35
+ const targetRelation = target.__mpxProxy.options.relations?.[currentPath]
36
+ if (targetRelation && targetRelation.type === relationTypeMap[relationType] && target.__componentPath === path) {
37
+ if (type === 'linked') {
38
+ this.__mpxLinkRelationNodes(target, currentPath)
39
+ } else if (type === 'unlinked') {
40
+ this.__mpxRemoveRelationNodes(target, currentPath)
41
+ }
42
+ if (typeof targetRelation[type] === 'function') {
43
+ targetRelation[type].call(target, this)
44
+ }
45
+ if (typeof relation[type] === 'function') {
46
+ relation[type].call(this, target)
47
+ }
48
+ this.__relationNodesMap[path] = [target]
49
+ }
50
+ }
51
+ })
52
+ }
53
+ },
54
+ __mpxLinkRelationNodes (target, path) {
55
+ target.__relationNodesMap[path] = target.__relationNodesMap[path] || [] // 父级绑定子级
56
+ target.__relationNodesMap[path].push(this)
57
+ },
58
+ __mpxRemoveRelationNodes (target, path) {
59
+ const relationNodesMap = target.__relationNodesMap
60
+ const arr = relationNodesMap[path] || []
61
+ const index = arr.indexOf(this)
62
+ if (index !== -1) arr.splice(index, 1)
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
@@ -7,6 +7,7 @@ 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 { initAppProvides } from './export/inject'
10
11
 
11
12
  const appHooksMap = makeMap(mergeLifecycle(LIFECYCLE).app)
12
13
 
@@ -35,6 +36,7 @@ export default function createApp (options) {
35
36
  const { NavigationContainer, createStackNavigator, SafeAreaProvider } = global.__navigationHelper
36
37
  // app选项目前不需要进行转换
37
38
  const { rawOptions, currentInject } = transferOptions(options, 'app', false)
39
+ initAppProvides(rawOptions.provide, rawOptions)
38
40
  const defaultOptions = filterOptions(spreadProp(rawOptions, 'methods'), appData)
39
41
  // 在页面script执行前填充getApp()
40
42
  global.getApp = function () {
@@ -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`)
@@ -1,14 +1,8 @@
1
1
  import { callWithErrorHandling, isFunction, isObject, warn } from '@mpxjs/utils'
2
2
  import { currentInstance } from '../../core/proxy'
3
3
 
4
- const providesMap = {
5
- /** 全局 scope */
6
- __app: Object.create(null),
7
- /** 页面 scope */
8
- __pages: Object.create(null)
9
- }
10
-
11
- global.__mpxProvidesMap = providesMap
4
+ /** 全局 scope */
5
+ let appProvides = Object.create(null)
12
6
 
13
7
  /** @internal createApp() 初始化应用层 scope provide */
14
8
  export function initAppProvides (provideOpt, instance) {
@@ -17,22 +11,20 @@ export function initAppProvides (provideOpt, instance) {
17
11
  ? callWithErrorHandling(provideOpt.bind(instance), instance, 'createApp provide function')
18
12
  : provideOpt
19
13
  if (isObject(provided)) {
20
- providesMap.__app = provided
14
+ appProvides = provided
21
15
  } else {
22
16
  warn('App provides must be an object or a function that returns an object.')
23
17
  }
24
18
  }
25
19
  }
26
20
 
27
- function resolvePageId (context) {
28
- if (context && isFunction(context.getPageId)) {
29
- return context.getPageId()
21
+ function resolveProvides (vm) {
22
+ const provides = vm.provides
23
+ const parentProvides = vm.parent && vm.parent.provides
24
+ if (parentProvides === provides) {
25
+ return (vm.provides = Object.create(parentProvides))
30
26
  }
31
- }
32
-
33
- function resolvePageProvides (context) {
34
- const pageId = resolvePageId(context)
35
- return providesMap.__pages[pageId] || (providesMap.__pages[pageId] = Object.create(null))
27
+ return provides
36
28
  }
37
29
 
38
30
  export function provide (key, value) {
@@ -41,8 +33,7 @@ export function provide (key, value) {
41
33
  warn('provide() can only be used inside setup().')
42
34
  return
43
35
  }
44
- // 小程序无法实现组件父级引用,所以 provide scope 设置为组件所在页面
45
- const provides = resolvePageProvides(instance.target)
36
+ const provides = resolveProvides(instance)
46
37
  provides[key] = value
47
38
  }
48
39
 
@@ -52,11 +43,11 @@ export function inject (key, defaultValue, treatDefaultAsFactory = false) {
52
43
  warn('inject() can only be used inside setup()')
53
44
  return
54
45
  }
55
- const provides = resolvePageProvides(instance.target)
56
- if (key in provides) {
46
+ const provides = instance.parent && instance.parent.provides
47
+ if (provides && key in provides) {
57
48
  return provides[key]
58
- } else if (key in providesMap.__app) {
59
- return providesMap.__app[key]
49
+ } else if (key in appProvides) {
50
+ return appProvides[key]
60
51
  } else if (arguments.length > 1) {
61
52
  return treatDefaultAsFactory && isFunction(defaultValue)
62
53
  ? defaultValue.call(instance && instance.target)
@@ -1,4 +1,4 @@
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'
@@ -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')
@@ -193,7 +195,7 @@ const instanceProto = {
193
195
  }
194
196
  }
195
197
 
196
- function createInstance ({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, intersectionCtx }) {
198
+ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, intersectionCtx, relation, parentProvides }) {
197
199
  const instance = Object.create(instanceProto, {
198
200
  dataset: {
199
201
  get () {
@@ -244,9 +246,33 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
244
246
  return currentInject.getRefsData || noop
245
247
  },
246
248
  enumerable: false
249
+ },
250
+ __parentProvides: {
251
+ get () {
252
+ return parentProvides || null
253
+ },
254
+ enumerable: false
247
255
  }
248
256
  })
249
257
 
258
+ if (type === 'component') {
259
+ Object.defineProperty(instance, '__componentPath', {
260
+ get () {
261
+ return currentInject.componentPath || ''
262
+ },
263
+ enumerable: false
264
+ })
265
+ }
266
+
267
+ if (relation) {
268
+ Object.defineProperty(instance, '__relation', {
269
+ get () {
270
+ return relation
271
+ },
272
+ enumerable: false
273
+ })
274
+ }
275
+
250
276
  // bind this & assign methods
251
277
  if (rawOptions.methods) {
252
278
  Object.entries(rawOptions.methods).forEach(([key, method]) => {
@@ -374,21 +400,59 @@ function usePageStatus (navigation, pageId) {
374
400
  }, [navigation])
375
401
  }
376
402
 
403
+ const RelationsContext = createContext(null)
404
+
405
+ const checkRelation = (options) => {
406
+ const relations = options.relations || {}
407
+ let hasDescendantRelation = false
408
+ let hasAncestorRelation = false
409
+ Object.keys(relations).forEach((path) => {
410
+ const relation = relations[path]
411
+ const type = relation.type
412
+ if (['child', 'descendant'].includes(type)) {
413
+ hasDescendantRelation = true
414
+ } else if (['parent', 'ancestor'].includes(type)) {
415
+ hasAncestorRelation = true
416
+ }
417
+ })
418
+ return {
419
+ hasDescendantRelation,
420
+ hasAncestorRelation
421
+ }
422
+ }
423
+
424
+ const provideRelation = (instance, relation) => {
425
+ const componentPath = instance.__componentPath
426
+ if (relation) {
427
+ return Object.assign({}, relation, { [componentPath]: instance })
428
+ } else {
429
+ return {
430
+ [componentPath]: instance
431
+ }
432
+ }
433
+ }
434
+
377
435
  export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
378
436
  rawOptions = mergeOptions(rawOptions, type, false)
379
437
  const components = Object.assign({}, rawOptions.components, currentInject.getComponents())
380
438
  const validProps = Object.assign({}, rawOptions.props, rawOptions.properties)
439
+ const { hasDescendantRelation, hasAncestorRelation } = checkRelation(rawOptions)
381
440
  if (rawOptions.methods) rawOptions.methods = wrapMethodsWithErrorHandling(rawOptions.methods)
382
441
  const defaultOptions = memo(forwardRef((props, ref) => {
383
442
  const instanceRef = useRef(null)
384
443
  const propsRef = useRef(null)
385
444
  const intersectionCtx = useContext(IntersectionObserverContext)
386
445
  const pageId = useContext(RouteContext)
446
+ const parentProvides = useContext(ProviderContext)
447
+ let relation = null
448
+ if (hasDescendantRelation || hasAncestorRelation) {
449
+ relation = useContext(RelationsContext)
450
+ }
387
451
  propsRef.current = props
388
452
  let isFirst = false
389
453
  if (!instanceRef.current) {
390
454
  isFirst = true
391
- instanceRef.current = createInstance({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, intersectionCtx })
455
+ instanceRef.current = createInstance({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, intersectionCtx, relation, parentProvides })
392
456
  }
393
457
  const instance = instanceRef.current
394
458
  useImperativeHandle(ref, () => {
@@ -473,15 +537,28 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
473
537
  return proxy.finalMemoVersion
474
538
  }, [proxy.stateVersion, proxy.memoVersion])
475
539
 
476
- const root = useMemo(() => proxy.effect.run(), [finalMemoVersion])
540
+ let root = useMemo(() => proxy.effect.run(), [finalMemoVersion])
477
541
  if (root && root.props.ishost) {
478
542
  // 对于组件未注册的属性继承到host节点上,如事件、样式和其他属性等
479
543
  const rootProps = getRootProps(props, validProps)
480
544
  rootProps.style = Object.assign({}, root.props.style, rootProps.style)
481
545
  // update root props
482
- return cloneElement(root, rootProps)
546
+ root = cloneElement(root, rootProps)
547
+ }
548
+
549
+ const provides = proxy.provides
550
+ if (provides) {
551
+ root = createElement(ProviderContext.Provider, { value: provides }, root)
483
552
  }
484
- return root
553
+
554
+ return hasDescendantRelation
555
+ ? createElement(RelationsContext.Provider,
556
+ {
557
+ value: provideRelation(instance, relation)
558
+ },
559
+ root
560
+ )
561
+ : root
485
562
  }))
486
563
 
487
564
  if (rawOptions.options?.isCustomText) {