@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.
@@ -5,7 +5,7 @@ import { makeMap, spreadProp, isBrowser } from '@mpxjs/utils'
5
5
  import { mergeLifecycle } from '../convertor/mergeLifecycle'
6
6
  import { LIFECYCLE } from '../platform/patch/lifecycle/index'
7
7
  import Mpx from '../index'
8
- import { initAppProvides } from './export/apiInject'
8
+ import { initAppProvides } from './export/inject'
9
9
 
10
10
  const appHooksMap = makeMap(mergeLifecycle(LIFECYCLE).app)
11
11
 
@@ -24,19 +24,29 @@ 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({
39
+ beforeCreate () {
40
+ // for vue provide vm access
41
+ Object.assign(this, appData, Mpx.prototype)
42
+ if (isBrowser) {
43
+ rawOptions.onShow && global.__mpxAppCbs.show.push(rawOptions.onShow.bind(this))
44
+ rawOptions.onHide && global.__mpxAppCbs.hide.push(rawOptions.onHide.bind(this))
45
+ rawOptions.onError && global.__mpxAppCbs.error.push(rawOptions.onError.bind(this))
46
+ rawOptions.onUnhandledRejection && global.__mpxAppCbs.rejection.push(rawOptions.onUnhandledRejection.bind(this))
47
+ }
48
+ },
37
49
  created () {
38
- Object.assign(this, Mpx.prototype)
39
- Object.assign(this, appData)
40
50
  const current = this.$root.$options?.router?.currentRoute || {}
41
51
  const options = {
42
52
  path: current.path && current.path.replace(/^\//, ''),
@@ -45,25 +55,13 @@ export default function createApp (option, config = {}) {
45
55
  shareTicket: '',
46
56
  referrerInfo: {}
47
57
  }
58
+ // web不分冷启动和热启动
48
59
  global.__mpxEnterOptions = options
49
- this.$options.onLaunch && this.$options.onLaunch.call(this, options)
50
- global.__mpxAppCbs = global.__mpxAppCbs || {
51
- show: [],
52
- hide: [],
53
- error: []
54
- }
55
- if (isBrowser) {
56
- if (this.$options.onShow) {
57
- this.$options.onShow.call(this, options)
58
- global.__mpxAppCbs.show.push(this.$options.onShow.bind(this))
59
- }
60
- if (this.$options.onHide) {
61
- global.__mpxAppCbs.hide.push(this.$options.onHide.bind(this))
62
- }
63
- if (this.$options.onError) {
64
- global.__mpxAppCbs.error.push(this.$options.onError.bind(this))
65
- }
66
- }
60
+ global.__mpxLaunchOptions = options
61
+ rawOptions.onLaunch && rawOptions.onLaunch.call(this, options)
62
+ global.__mpxAppCbs.show.forEach((cb) => {
63
+ cb(options)
64
+ })
67
65
  }
68
66
  })
69
67
  } else if (__mpx_mode__ === 'tenon') {
@@ -77,25 +75,23 @@ export default function createApp (option, config = {}) {
77
75
  builtInMixins.push({
78
76
  onLaunch () {
79
77
  Object.assign(this, Mpx.prototype)
78
+ initAppProvides(rawOptions.provide, this)
80
79
  }
81
80
  })
82
81
  }
83
- // app选项目前不需要进行转换
84
- const { rawOptions, currentInject } = transferOptions(option, 'app', false)
85
82
  rawOptions.mixins = builtInMixins
86
83
  const defaultOptions = filterOptions(spreadProp(mergeOptions(rawOptions, 'app', false), 'methods'), appData)
87
84
 
88
85
  if (__mpx_mode__ === 'web' || __mpx_mode__ === 'tenon') {
89
- global.__mpxOptionsMap = global.__mpxOptionsMap || {}
90
- global.__mpxOptionsMap[currentInject.moduleId] = defaultOptions
91
86
  global.getApp = function () {
92
87
  if (!isBrowser) {
93
88
  console.error('[Mpx runtime error]: Dangerous API! global.getApp method is running in non browser environments')
94
89
  }
95
90
  return appData
96
91
  }
92
+ global.__mpxOptionsMap = global.__mpxOptionsMap || {}
93
+ global.__mpxOptionsMap[currentInject.moduleId] = defaultOptions
97
94
  } else {
98
- initAppProvides(rawOptions)
99
95
  defaultOptions.onAppInit && defaultOptions.onAppInit()
100
96
  const ctor = config.customCtor || global.currentCtor || App
101
97
  ctor(defaultOptions)
@@ -0,0 +1,105 @@
1
+ import { isBrowser } from '@mpxjs/utils'
2
+
3
+ function extendEvent (e, extendObj = {}) {
4
+ Object.keys(extendObj).forEach((key) => {
5
+ Object.defineProperty(e, key, {
6
+ value: extendObj[key],
7
+ enumerable: true,
8
+ configurable: true,
9
+ writable: true
10
+ })
11
+ })
12
+ }
13
+
14
+ function createMpxEvent (layer) {
15
+ let startTimer = null
16
+ let needTap = true
17
+ let touchStartX = 0
18
+ let touchStartY = 0
19
+ let targetElement = null
20
+ const isTouchDevice = document && 'ontouchstart' in document.documentElement
21
+
22
+ const onTouchStart = (event) => {
23
+ if (event.targetTouches?.length > 1) {
24
+ return true
25
+ }
26
+ const touches = event.targetTouches
27
+ targetElement = event.target
28
+ needTap = true
29
+ startTimer = null
30
+ touchStartX = touches[0].pageX
31
+ touchStartY = touches[0].pageY
32
+ startTimer = setTimeout(() => {
33
+ needTap = false
34
+ sendEvent(targetElement, 'longpress', event)
35
+ sendEvent(targetElement, 'longtap', event)
36
+ }, 350)
37
+ }
38
+
39
+ const onTouchMove = (event) => {
40
+ const touch = event.changedTouches[0]
41
+ if (
42
+ Math.abs(touch.pageX - touchStartX) > 1 ||
43
+ Math.abs(touch.pageY - touchStartY) > 1
44
+ ) {
45
+ needTap = false
46
+ startTimer && clearTimeout(startTimer)
47
+ startTimer = null
48
+ }
49
+ }
50
+
51
+ const onTouchEnd = (event) => {
52
+ if (event.targetTouches?.length > 1) {
53
+ return true
54
+ }
55
+ startTimer && clearTimeout(startTimer)
56
+ startTimer = null
57
+ if (needTap) {
58
+ sendEvent(targetElement, 'tap', event)
59
+ }
60
+ }
61
+
62
+ const onClick = (event) => {
63
+ targetElement = event.target
64
+ sendEvent(targetElement, 'tap', event)
65
+ }
66
+
67
+ const sendEvent = (targetElement, type, event) => {
68
+ const touchEvent = new CustomEvent(type, {
69
+ bubbles: true,
70
+ cancelable: true
71
+ })
72
+ const changedTouches = event.changedTouches || []
73
+ extendEvent(touchEvent, {
74
+ timeStamp: event.timeStamp,
75
+ changedTouches,
76
+ touches: changedTouches,
77
+ detail: {
78
+ x: changedTouches[0]?.pageX || event.pageX || 0,
79
+ y: changedTouches[0]?.pageY || event.pageY || 0
80
+ }
81
+ })
82
+ targetElement && targetElement.dispatchEvent(touchEvent)
83
+ }
84
+
85
+ if (isTouchDevice) {
86
+ layer.addEventListener('touchstart', onTouchStart, true)
87
+ layer.addEventListener('touchmove', onTouchMove, true)
88
+ layer.addEventListener('touchend', onTouchEnd, true)
89
+ } else {
90
+ layer.addEventListener('click', onClick, true)
91
+ }
92
+ }
93
+
94
+ export function initEvent () {
95
+ if (isBrowser && !global.__mpxCreatedEvent) {
96
+ global.__mpxCreatedEvent = true
97
+ if (document.readyState === 'complete' || document.readyState === 'interactive') {
98
+ createMpxEvent(document.body)
99
+ } else {
100
+ document.addEventListener('DOMContentLoaded', function () {
101
+ createMpxEvent(document.body)
102
+ }, false)
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,61 @@
1
+ import { isFunction, isNumber, isString } from '@mpxjs/utils'
2
+ import { createI18n } from '../builtInMixins/i18nMixin'
3
+
4
+ export function init (Mpx) {
5
+ global.__mpx = Mpx
6
+ global.__mpxAppCbs = global.__mpxAppCbs || {
7
+ show: [],
8
+ hide: [],
9
+ error: [],
10
+ rejection: []
11
+ }
12
+ if (global.i18n) {
13
+ Mpx.i18n = createI18n(global.i18n)
14
+ }
15
+ initGlobalErrorHandling()
16
+ }
17
+
18
+ function initGlobalErrorHandling () {
19
+ if (global.ErrorUtils) {
20
+ const defaultHandler = global.ErrorUtils.getGlobalHandler()
21
+ global.ErrorUtils.setGlobalHandler((error, isFatal) => {
22
+ if (global.__mpxAppCbs && global.__mpxAppCbs.error && global.__mpxAppCbs.error.length) {
23
+ global.__mpxAppCbs.error.forEach((cb) => {
24
+ cb(error)
25
+ })
26
+ } else if (defaultHandler) {
27
+ defaultHandler(error, isFatal)
28
+ } else {
29
+ console.error(`${error.name}: ${error.message}\n`)
30
+ }
31
+ })
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
+ }
43
+ const rejectionTrackingOptions = {
44
+ allRejections: true,
45
+ onUnhandled (id, error) {
46
+ onUnhandledRejection({ id, reason: error, promise: null })
47
+ }
48
+ }
49
+
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)
58
+ } else {
59
+ require('promise/setimmediate/rejection-tracking').enable(rejectionTrackingOptions)
60
+ }
61
+ }
@@ -0,0 +1,8 @@
1
+ import { createI18n } from '../builtInMixins/i18nMixin'
2
+
3
+ export function init (Mpx) {
4
+ global.__mpx = Mpx
5
+ if (global.i18n) {
6
+ Mpx.i18n = createI18n(global.i18n)
7
+ }
8
+ }
@@ -0,0 +1,48 @@
1
+ import Vue from 'vue'
2
+ import install from './vuePlugin'
3
+ import { isBrowser, error, warn } from '@mpxjs/utils'
4
+ import { initEvent } from './event'
5
+
6
+ export function init (Mpx) {
7
+ global.__mpx = Mpx
8
+ global.__mpxAppCbs = global.__mpxAppCbs || {
9
+ show: [],
10
+ hide: [],
11
+ error: [],
12
+ rejection: []
13
+ }
14
+ Mpx.__vue = Vue
15
+ Vue.use(install)
16
+ initEvent()
17
+ initGlobalErrorHandling()
18
+ }
19
+
20
+ function initGlobalErrorHandling () {
21
+ Vue.config.errorHandler = (e, vm, info) => {
22
+ error(`Unhandled error occurs${info ? ` during execution of [${info}]` : ''}!`, vm?.__mpxProxy?.options.mpxFileResource, e)
23
+ }
24
+ Vue.config.warnHandler = (msg, vm, trace) => {
25
+ warn(msg, vm?.__mpxProxy?.options.mpxFileResource, trace)
26
+ }
27
+
28
+ if (isBrowser) {
29
+ window.addEventListener('error', (event) => {
30
+ if (global.__mpxAppCbs && global.__mpxAppCbs.error && global.__mpxAppCbs.error.length) {
31
+ global.__mpxAppCbs.error.forEach((cb) => {
32
+ cb(event.error)
33
+ })
34
+ } else {
35
+ console.error(`${event.type}: ${event.message}\n`)
36
+ }
37
+ })
38
+ window.addEventListener('unhandledrejection', (event) => {
39
+ if (global.__mpxAppCbs && global.__mpxAppCbs.rejection && global.__mpxAppCbs.rejection.length) {
40
+ global.__mpxAppCbs.rejection.forEach((cb) => {
41
+ cb(event)
42
+ })
43
+ } else {
44
+ console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}\n`)
45
+ }
46
+ })
47
+ }
48
+ }
@@ -1,7 +1,7 @@
1
1
  import { walkChildren, parseSelector, error, hasOwn, collectDataset } from '@mpxjs/utils'
2
2
  import { createSelectorQuery, createIntersectionObserver } from '@mpxjs/api-proxy'
3
3
  import { EffectScope } from 'vue'
4
- import { PausedState } from '../helper/const'
4
+ import { PausedState } from '../../helper/const'
5
5
 
6
6
  const hackEffectScope = () => {
7
7
  EffectScope.prototype.pause = function () {
@@ -60,17 +60,23 @@ export default function install (Vue) {
60
60
  data: {
61
61
  get () {
62
62
  return Object.assign({}, this.$props, this.$data)
63
- }
63
+ },
64
+ enumerable: true,
65
+ configurable: true
64
66
  },
65
67
  dataset: {
66
68
  get () {
67
69
  return collectDataset(this.$attrs, true)
68
- }
70
+ },
71
+ enumerable: true,
72
+ configurable: true
69
73
  },
70
74
  id: {
71
75
  get () {
72
76
  return this.$attrs.id || ''
73
- }
77
+ },
78
+ enumerable: true,
79
+ configurable: true
74
80
  }
75
81
  })
76
82
 
@@ -46,4 +46,4 @@ export {
46
46
  export {
47
47
  provide,
48
48
  inject
49
- } from './apiInject'
49
+ } from './inject'
@@ -1,39 +1,30 @@
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
- export function initAppProvides (appOptions) {
15
- const provideOpt = appOptions.provide
8
+ export function initAppProvides (provideOpt, instance) {
16
9
  if (provideOpt) {
17
10
  const provided = isFunction(provideOpt)
18
- ? callWithErrorHandling(provideOpt.bind(appOptions), appOptions, 'createApp provide function')
11
+ ? callWithErrorHandling(provideOpt.bind(instance), instance, 'createApp provide function')
19
12
  : provideOpt
20
13
  if (isObject(provided)) {
21
- providesMap.__app = provided
14
+ appProvides = provided
22
15
  } else {
23
16
  warn('App provides must be an object or a function that returns an object.')
24
17
  }
25
18
  }
26
19
  }
27
20
 
28
- function resolvePageId (context) {
29
- if (context && isFunction(context.getPageId)) {
30
- 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))
31
26
  }
32
- }
33
-
34
- function resolvePageProvides (context) {
35
- const pageId = resolvePageId(context)
36
- return providesMap.__pages[pageId] || (providesMap.__pages[pageId] = Object.create(null))
27
+ return provides
37
28
  }
38
29
 
39
30
  export function provide (key, value) {
@@ -42,8 +33,7 @@ export function provide (key, value) {
42
33
  warn('provide() can only be used inside setup().')
43
34
  return
44
35
  }
45
- // 小程序无法实现组件父级引用,所以 provide scope 设置为组件所在页面
46
- const provides = resolvePageProvides(instance.target)
36
+ const provides = resolveProvides(instance)
47
37
  provides[key] = value
48
38
  }
49
39
 
@@ -53,11 +43,11 @@ export function inject (key, defaultValue, treatDefaultAsFactory = false) {
53
43
  warn('inject() can only be used inside setup()')
54
44
  return
55
45
  }
56
- const provides = resolvePageProvides(instance.target)
57
- if (key in provides) {
46
+ const provides = instance.parent && instance.parent.provides
47
+ if (provides && key in provides) {
58
48
  return provides[key]
59
- } else if (key in providesMap.__app) {
60
- return providesMap.__app[key]
49
+ } else if (key in appProvides) {
50
+ return appProvides[key]
61
51
  } else if (arguments.length > 1) {
62
52
  return treatDefaultAsFactory && isFunction(defaultValue)
63
53
  ? defaultValue.call(instance && instance.target)