@mpxjs/core 2.8.32 → 2.9.0-beta.0

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
@@ -128,6 +128,8 @@ interface ComponentOpt<D, P, C, M, Mi extends Array<any>, S extends Record<any,
128
128
 
129
129
  pageHide?: () => void
130
130
 
131
+ initData?: Record<string, any>
132
+
131
133
  [index: string]: any
132
134
  }
133
135
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/core",
3
- "version": "2.8.32",
3
+ "version": "2.9.0-beta.0",
4
4
  "description": "mpx runtime core",
5
5
  "keywords": [
6
6
  "miniprogram",
@@ -47,5 +47,5 @@
47
47
  "url": "https://github.com/didi/mpx/issues"
48
48
  },
49
49
  "sideEffects": false,
50
- "gitHead": "76bc52fa3cb4d7c9071d2fb154e23434e7fcb184"
50
+ "gitHead": "67b611aeae99f916d46ca8d5d9ed784ddf53f05c"
51
51
  }
@@ -10,6 +10,7 @@ export const ONLOAD = '__onLoad__'
10
10
  export const ONSHOW = '__onShow__'
11
11
  export const ONHIDE = '__onHide__'
12
12
  export const ONRESIZE = '__onResize__'
13
+ export const SERVERPREFETCH = '__serverPrefetch__'
13
14
 
14
15
  export const INNER_LIFECYCLES = [
15
16
  BEFORECREATE,
@@ -19,6 +20,7 @@ export const INNER_LIFECYCLES = [
19
20
  BEFOREUPDATE,
20
21
  UPDATED,
21
22
  BEFOREUNMOUNT,
23
+ SERVERPREFETCH,
22
24
  UNMOUNTED,
23
25
  ONLOAD,
24
26
  ONSHOW,
@@ -132,7 +132,9 @@ function extractObservers (options) {
132
132
  },
133
133
  deep: true,
134
134
  // 延迟触发首次回调,处理转换支付宝时在observer中查询组件的行为,如vant/picker中,如不考虑该特殊情形可用immediate代替
135
- immediateAsync: true
135
+ // immediateAsync: true
136
+ // 为了数据响应的标准化,不再提供immediateAsync选项,之前处理vant等原生组件跨平台转换遇到的问题推荐使用条件编译patch进行处理
137
+ immediate: true
136
138
  })
137
139
  }
138
140
  })
@@ -174,7 +176,9 @@ function extractObservers (options) {
174
176
  }
175
177
  },
176
178
  deep,
177
- immediateAsync: watchProp
179
+ // immediateAsync: watchProp
180
+ // 为了数据响应的标准化,不再提供immediateAsync选项,之前处理vant等原生组件跨平台转换遇到的问题推荐使用条件编译patch进行处理
181
+ immediate: watchProp
178
182
  })
179
183
  }
180
184
  })
@@ -348,7 +352,7 @@ function transformHOOKS (options) {
348
352
  const componentHooksMap = makeMap(convertRule.lifecycle.component)
349
353
  for (const key in options) {
350
354
  // 使用Component创建page实例,页面专属生命周期&自定义方法需写在methods内部
351
- if (typeof options[key] === 'function' && key !== 'dataFn' && key !== 'setup' && !componentHooksMap[key]) {
355
+ if (typeof options[key] === 'function' && key !== 'dataFn' && key !== 'setup' && key !== 'serverPrefetch' && !componentHooksMap[key]) {
352
356
  if (!options.methods) options.methods = {}
353
357
  options.methods[key] = options[key]
354
358
  delete options[key]
package/src/core/proxy.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { reactive } from '../observer/reactive'
2
- import { ReactiveEffect } from '../observer/effect'
2
+ import { ReactiveEffect, pauseTracking, resetTracking } from '../observer/effect'
3
3
  import { effectScope } from '../platform/export/index'
4
4
  import { watch } from '../observer/watch'
5
5
  import { computed } from '../observer/computed'
6
- import { queueJob, nextTick } from '../observer/scheduler'
6
+ import { queueJob, nextTick, flushPreFlushCbs } from '../observer/scheduler'
7
7
  import Mpx from '../index'
8
8
  import {
9
9
  noop,
@@ -36,6 +36,7 @@ import {
36
36
  BEFOREUPDATE,
37
37
  UPDATED,
38
38
  BEFOREUNMOUNT,
39
+ SERVERPREFETCH,
39
40
  UNMOUNTED,
40
41
  ONLOAD,
41
42
  ONSHOW,
@@ -119,6 +120,7 @@ export default class MpxProxy {
119
120
  // 下次是否需要强制更新全部渲染数据
120
121
  this.forceUpdateAll = false
121
122
  this.currentRenderTask = null
123
+ this.propsUpdatedFlag = false
122
124
  }
123
125
  this.initApi()
124
126
  }
@@ -170,7 +172,9 @@ export default class MpxProxy {
170
172
  }
171
173
 
172
174
  propsUpdated () {
175
+ this.propsUpdatedFlag = true
173
176
  const updateJob = this.updateJob || (this.updateJob = () => {
177
+ this.propsUpdatedFlag = false
174
178
  // 只有当前没有渲染任务时,属性更新才需要单独触发updated,否则可以由渲染任务触发updated
175
179
  if (this.currentRenderTask?.resolved && this.isMounted()) {
176
180
  this.callHook(BEFOREUPDATE)
@@ -477,6 +481,7 @@ export default class MpxProxy {
477
481
  return
478
482
  }
479
483
 
484
+ pauseTracking()
480
485
  // 使用forceUpdateData后清空
481
486
  if (!isEmptyObject(this.forceUpdateData)) {
482
487
  data = mergeData({}, data, this.forceUpdateData)
@@ -504,12 +509,30 @@ export default class MpxProxy {
504
509
  }
505
510
 
506
511
  this.target.__render(data, callback)
512
+ resetTracking()
513
+ }
514
+
515
+ toggleRecurse (allowed) {
516
+ if (this.effect && this.update) this.effect.allowRecurse = this.update.allowRecurse = allowed
517
+ }
518
+
519
+ updatePreRender () {
520
+ this.toggleRecurse(false)
521
+ pauseTracking()
522
+ flushPreFlushCbs(undefined, this.update)
523
+ resetTracking()
524
+ this.toggleRecurse(true)
507
525
  }
508
526
 
509
527
  initRender () {
510
528
  if (this.options.__nativeRender__) return this.doRender()
511
529
 
512
530
  const effect = this.effect = new ReactiveEffect(() => {
531
+ // pre render for props update
532
+ if (this.propsUpdatedFlag) {
533
+ this.updatePreRender()
534
+ }
535
+
513
536
  if (this.target.__injectedRender) {
514
537
  try {
515
538
  return this.target.__injectedRender()
@@ -522,10 +545,10 @@ export default class MpxProxy {
522
545
  }
523
546
  }, () => queueJob(update), this.scope)
524
547
 
525
- const update = this.update = this.effect.run.bind(this.effect)
548
+ const update = this.update = effect.run.bind(effect)
526
549
  update.id = this.uid
527
550
  // render effect允许自触发
528
- effect.allowRecurse = update.allowRecurse = true
551
+ this.toggleRecurse(true)
529
552
  update()
530
553
  }
531
554
 
@@ -619,6 +642,7 @@ export const onLoad = createHook(ONLOAD)
619
642
  export const onShow = createHook(ONSHOW)
620
643
  export const onHide = createHook(ONHIDE)
621
644
  export const onResize = createHook(ONRESIZE)
645
+ export const onServerPrefetch = createHook(SERVERPREFETCH)
622
646
  export const onPullDownRefresh = createHook('__onPullDownRefresh__')
623
647
  export const onReachBottom = createHook('__onReachBottom__')
624
648
  export const onShareAppMessage = createHook('__onShareAppMessage__')
package/src/index.js CHANGED
@@ -29,6 +29,7 @@ export {
29
29
  UPDATED,
30
30
  BEFOREUNMOUNT,
31
31
  UNMOUNTED,
32
+ SERVERPREFETCH,
32
33
  ONLOAD,
33
34
  ONSHOW,
34
35
  ONHIDE,
@@ -42,6 +43,7 @@ export {
42
43
  onUpdated,
43
44
  onBeforeUnmount,
44
45
  onUnmounted,
46
+ onServerPrefetch,
45
47
  onLoad,
46
48
  onShow,
47
49
  onHide,
@@ -4,6 +4,19 @@ import { PausedState } from '../helper/const'
4
4
 
5
5
  let uid = 0
6
6
 
7
+ let shouldTrack = true
8
+ const trackStack = []
9
+
10
+ export function pauseTracking () {
11
+ trackStack.push(shouldTrack)
12
+ shouldTrack = false
13
+ }
14
+
15
+ export function resetTracking () {
16
+ const last = trackStack.pop()
17
+ shouldTrack = last === undefined ? true : last
18
+ }
19
+
7
20
  export class ReactiveEffect {
8
21
  active = true
9
22
  deps = []
@@ -27,17 +40,21 @@ export class ReactiveEffect {
27
40
  // run fn and return value
28
41
  run () {
29
42
  if (!this.active) return this.fn()
30
- pushTarget(this)
43
+ const lastShouldTrack = shouldTrack
31
44
  try {
45
+ pushTarget(this)
46
+ shouldTrack = true
32
47
  return this.fn()
33
48
  } finally {
34
49
  popTarget()
50
+ shouldTrack = lastShouldTrack
35
51
  this.deferStop ? this.stop() : this.cleanupDeps()
36
52
  }
37
53
  }
38
54
 
39
55
  // add dependency to this
40
56
  addDep (dep) {
57
+ if (!shouldTrack) return
41
58
  const id = dep.id
42
59
  if (!this.newDepIds.has(id)) {
43
60
  this.newDepIds.add(id)
@@ -17,6 +17,7 @@ let postFlushIndex = 0
17
17
 
18
18
  const resolvedPromise = Promise.resolve()
19
19
  let currentFlushPromise = null
20
+ let currentPreFlushParentJob = null
20
21
 
21
22
  const RECURSION_LIMIT = 100
22
23
 
@@ -68,9 +69,9 @@ export function queueJob (job) {
68
69
  // if the job is a watch() callback, the search will start with a +1 index to
69
70
  // allow it recursively trigger itself - it is the user's responsibility to
70
71
  // ensure it doesn't end up in an infinite loop.
71
- if (
72
- !queue.length ||
73
- !queue.includes(job, isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex)
72
+ if ((!queue.length ||
73
+ !queue.includes(job, isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex)) &&
74
+ job !== currentPreFlushParentJob
74
75
  ) {
75
76
  if (job.id == null) {
76
77
  queue.push(job)
@@ -92,8 +93,9 @@ function queueFlush () {
92
93
  }
93
94
  }
94
95
 
95
- export function flushPreFlushCbs (seen) {
96
+ export function flushPreFlushCbs (seen, parentJob = null) {
96
97
  if (pendingPreFlushCbs.length) {
98
+ currentPreFlushParentJob = parentJob
97
99
  activePreFlushCbs = [...new Set(pendingPreFlushCbs)]
98
100
  pendingPreFlushCbs.length = 0
99
101
  if (isDev) seen = seen || new Map()
@@ -107,8 +109,9 @@ export function flushPreFlushCbs (seen) {
107
109
  }
108
110
  activePreFlushCbs = null
109
111
  preFlushIndex = 0
112
+ currentPreFlushParentJob = null
110
113
  // recursively flush until it drains
111
- flushPreFlushCbs(seen)
114
+ flushPreFlushCbs(seen, parentJob)
112
115
  }
113
116
  }
114
117
 
@@ -42,7 +42,7 @@ const processWatchOptionsCompat = (options) => {
42
42
  }
43
43
 
44
44
  export function watch (source, cb, options = {}) {
45
- let { immediate, deep, flush, immediateAsync } = processWatchOptionsCompat(options)
45
+ let { immediate, deep, flush } = processWatchOptionsCompat(options)
46
46
  const instance = currentInstance
47
47
  let getter
48
48
  let isMultiSource = false
@@ -139,8 +139,6 @@ export function watch (source, cb, options = {}) {
139
139
  if (cb) {
140
140
  if (immediate) {
141
141
  job()
142
- } else if (immediateAsync) {
143
- queuePreFlushCb(job)
144
142
  } else {
145
143
  oldValue = effect.run()
146
144
  }
@@ -63,7 +63,7 @@ export default function pageStatusMixin (mixinType) {
63
63
  },
64
64
  created () {
65
65
  // onLoad应该在用户声明周期CREATED后再执行,故此处使用原生created声明周期来触发onLoad
66
- const query = (global.__mpxRouter && global.__mpxRouter.currentRoute && global.__mpxRouter.currentRoute.query) || {}
66
+ const query = this.$root.$options?.router?.currentRoute?.query || {}
67
67
  this.__mpxProxy.callHook(ONLOAD, [query])
68
68
  }
69
69
  })
@@ -71,25 +71,27 @@ export default function pageStatusMixin (mixinType) {
71
71
 
72
72
  Object.assign(mixin, {
73
73
  [CREATED] () {
74
- const pageInstance = mixinType === 'page' ? this : getCurrentPageInstance()
75
- if (pageInstance) {
76
- this.$watch(() => pageInstance.mpxPageStatus, status => {
77
- if (!status) return
78
- if (status === 'show') this.__mpxProxy.callHook(ONSHOW)
79
- if (status === 'hide') this.__mpxProxy.callHook(ONHIDE)
80
- const pageLifetimes = this.__mpxProxy.options.pageLifetimes
81
- if (pageLifetimes) {
82
- if (/^resize/.test(status) && isFunction(pageLifetimes.resize)) {
83
- // resize
84
- pageLifetimes.resize.call(this, systemInfo)
85
- } else if (isFunction(pageLifetimes[status])) {
86
- // show & hide
87
- pageLifetimes[status].call(this)
74
+ if (isBrowser) {
75
+ const pageInstance = mixinType === 'page' ? this : getCurrentPageInstance()
76
+ if (pageInstance) {
77
+ this.$watch(() => pageInstance.mpxPageStatus, status => {
78
+ if (!status) return
79
+ if (status === 'show') this.__mpxProxy.callHook(ONSHOW)
80
+ if (status === 'hide') this.__mpxProxy.callHook(ONHIDE)
81
+ const pageLifetimes = this.__mpxProxy.options.pageLifetimes
82
+ if (pageLifetimes) {
83
+ if (/^resize/.test(status) && isFunction(pageLifetimes.resize)) {
84
+ // resize
85
+ pageLifetimes.resize.call(this, systemInfo)
86
+ } else if (isFunction(pageLifetimes[status])) {
87
+ // show & hide
88
+ pageLifetimes[status].call(this)
89
+ }
88
90
  }
89
- }
90
- }, {
91
- sync: true
92
- })
91
+ }, {
92
+ sync: true
93
+ })
94
+ }
93
95
  }
94
96
  }
95
97
  })
@@ -20,7 +20,7 @@ export default function proxyEventMixin () {
20
20
  setByPath(this, expr, value)
21
21
  },
22
22
  getOpenerEventChannel () {
23
- const router = global.__mpxRouter
23
+ const router = this.$root.$options && this.$root.$options.router
24
24
  const eventChannel = router && router.__mpxAction && router.__mpxAction.eventChannel
25
25
  return eventChannel
26
26
  }
@@ -1,7 +1,7 @@
1
1
  import transferOptions from '../core/transferOptions'
2
2
  import mergeOptions from '../core/mergeOptions'
3
3
  import builtInKeysMap from './patch/builtInKeysMap'
4
- import { makeMap, spreadProp } from '@mpxjs/utils'
4
+ import { makeMap, spreadProp, isBrowser } from '@mpxjs/utils'
5
5
  import * as webLifecycle from '../platform/patch/web/lifecycle'
6
6
  import Mpx from '../index'
7
7
 
@@ -35,7 +35,7 @@ export default function createApp (option, config = {}) {
35
35
  created () {
36
36
  Object.assign(this, Mpx.prototype)
37
37
  Object.assign(this, appData)
38
- const current = (global.__mpxRouter && global.__mpxRouter.currentRoute) || {}
38
+ const current = this.$root.$options?.router?.currentRoute || {}
39
39
  const options = {
40
40
  path: current.path && current.path.replace(/^\//, ''),
41
41
  query: current.query,
@@ -49,19 +49,24 @@ export default function createApp (option, config = {}) {
49
49
  hide: [],
50
50
  error: []
51
51
  }
52
- if (this.$options.onShow) {
53
- this.$options.onShow.call(this, options)
54
- global.__mpxAppCbs.show.push(this.$options.onShow.bind(this))
55
- }
56
- if (this.$options.onHide) {
57
- global.__mpxAppCbs.hide.push(this.$options.onHide.bind(this))
58
- }
59
- if (this.$options.onError) {
60
- global.__mpxAppCbs.error.push(this.$options.onError.bind(this))
52
+ if (isBrowser) {
53
+ if (this.$options.onShow) {
54
+ this.$options.onShow.call(this, options)
55
+ global.__mpxAppCbs.show.push(this.$options.onShow.bind(this))
56
+ }
57
+ if (this.$options.onHide) {
58
+ global.__mpxAppCbs.hide.push(this.$options.onHide.bind(this))
59
+ }
60
+ if (this.$options.onError) {
61
+ global.__mpxAppCbs.error.push(this.$options.onError.bind(this))
62
+ }
61
63
  }
62
64
  }
63
65
  })
64
66
  } else {
67
+ if (option.onAppInit) {
68
+ option.onAppInit()
69
+ }
65
70
  builtInMixins.push({
66
71
  onLaunch () {
67
72
  Object.assign(this, Mpx.prototype)
@@ -77,6 +82,10 @@ export default function createApp (option, config = {}) {
77
82
  global.__mpxOptionsMap = global.__mpxOptionsMap || {}
78
83
  global.__mpxOptionsMap[global.currentModuleId] = defaultOptions
79
84
  global.getApp = function () {
85
+ if (!isBrowser) {
86
+ console.error('[Mpx runtime error]: Dangerous API! global.getApp method is running in non browser environments')
87
+ return
88
+ }
80
89
  return appData
81
90
  }
82
91
  } else {
@@ -3,7 +3,7 @@ import mergeOptions from '../../../core/mergeOptions'
3
3
  import { diffAndCloneA, hasOwn } from '@mpxjs/utils'
4
4
  import { getCurrentInstance as getCurrentVueInstance } from '../../export/index'
5
5
  import MpxProxy, { setCurrentInstance, unsetCurrentInstance } from '../../../core/proxy'
6
- import { BEFORECREATE, BEFOREUPDATE, UPDATED, BEFOREUNMOUNT, UNMOUNTED } from '../../../core/innerLifecycle'
6
+ import { BEFORECREATE, BEFOREUPDATE, UPDATED, BEFOREUNMOUNT, UNMOUNTED, SERVERPREFETCH } from '../../../core/innerLifecycle'
7
7
 
8
8
  function filterOptions (options) {
9
9
  const newOptions = {}
@@ -79,6 +79,9 @@ export function getDefaultOptions (type, { rawOptions = {} }) {
79
79
  },
80
80
  destroyed () {
81
81
  if (this.__mpxProxy) this.__mpxProxy.callHook(UNMOUNTED)
82
+ },
83
+ serverPrefetch () {
84
+ if (this.__mpxProxy) this.__mpxProxy.callHook(SERVERPREFETCH)
82
85
  }
83
86
  }]
84
87
  // 为了在builtMixin中可以使用某些rootMixin实现的特性(如数据响应等),此处builtInMixin在rootMixin之后执行,但是当builtInMixin使用存在对应内建生命周期的目标平台声明周期写法时,可能会出现用户生命周期比builtInMixin中的生命周期先执行的情况,为了避免这种情况发生,builtInMixin应该尽可能使用内建生命周期来编写
@@ -38,7 +38,9 @@ const APP_HOOKS = [
38
38
  'onError',
39
39
  'onPageNotFound',
40
40
  'onUnhandledRejection',
41
- 'onThemeChange'
41
+ 'onThemeChange',
42
+ 'onSSRAppCreated',
43
+ 'onAppInit'
42
44
  ]
43
45
 
44
46
  export const LIFECYCLE = {
@@ -14,7 +14,8 @@ const APP_HOOKS = [
14
14
  'onError',
15
15
  'onPageNotFound',
16
16
  'onUnhandledRejection',
17
- 'onThemeChange'
17
+ 'onThemeChange',
18
+ 'onAppInit'
18
19
  ]
19
20
 
20
21
  const PAGE_HOOKS = [