@mpxjs/core 2.6.114-alpha.3 → 2.7.0-alpha

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.
@@ -8,4 +8,4 @@ declare let __mpx_env__: string
8
8
  declare module '*?resolve' {
9
9
  const resourcePath: string
10
10
  export default resourcePath
11
- }
11
+ }
package/@types/index.d.ts CHANGED
@@ -8,6 +8,14 @@
8
8
  /// <reference path="./global.d.ts" />
9
9
  /// <reference path="./node.d.ts" />
10
10
 
11
+ // @ts-ignore
12
+ import VueI18n from 'vue-i18n'
13
+
14
+ declare module 'vue-i18n' {
15
+ export default interface VueI18n {
16
+ mergeMessages(messages: {[index: string]:VueI18n.LocaleMessageObject}): void;
17
+ }
18
+ }
11
19
  // declare Store types
12
20
  type StoreOpt<S, G, M, A, D extends MpxStore.Deps> = MpxStore.StoreOpt<S, G, M, A, D>
13
21
 
@@ -1,6 +1,11 @@
1
1
  declare namespace MpxStore {
2
2
  type UnboxDepField<D, F> = F extends keyof D ? D[F] : {}
3
3
 
4
+ interface compContext{
5
+ __mpxProxy: object;
6
+ [key: string]: any
7
+ }
8
+
4
9
  interface Deps {
5
10
  [key: string]: Store | StoreWithThis
6
11
  }
@@ -88,7 +93,21 @@ declare namespace MpxStore {
88
93
  mapActions(depPath: string, maps: string[]): {
89
94
  [key: string]: (...payloads: any[]) => any
90
95
  }
96
+ // 下面是新增的异步store的接口类型
97
+ mapStateToInstance<K extends keyof S>(maps: K[], context: compContext): void
98
+ mapStateToInstance(depPath: string, maps: string[], context: compContext): void
99
+
100
+ // mapState support object
101
+ mapStateToInstance<T extends { [key: string]: keyof GetAllMapKeys<S, D, 'state'> }>(obj: T, context: compContext): void
102
+
103
+ mapGettersToInstance<K extends keyof G>(maps: K[], context: compContext): void
104
+ mapGettersToInstance(depPath: string, maps: string[], context: compContext): void
105
+
106
+ mapMutationsToInstance<K extends keyof M>(maps: K[], context: compContext): Pick<GetMutations<M>, K>
107
+ mapMutationsToInstance(depPath: string, maps: string[], context: compContext): void
91
108
 
109
+ mapActionsToInstance<K extends keyof A>(maps: K[], context: compContext): Pick<GetActions<A>, K>
110
+ mapActionsToInstance(depPath: string, maps: string[], context: compContext): void
92
111
  }
93
112
  type GetComputedSetKeys<T> = {
94
113
  [K in keyof T]: T[K] extends {
@@ -275,6 +294,32 @@ declare namespace MpxStore {
275
294
  mapActions<T extends { [key: string]: string }>(obj: T): {
276
295
  [I in keyof T]: (...payloads: any[]) => any
277
296
  }
297
+ // 异步store api
298
+ mapStateToInstance<K extends keyof S>(maps: K[], context: compContext): void
299
+ mapStateToInstance<T extends string, P extends string>(depPath: P, maps: readonly T[], context: compContext):void
300
+ mapStateToInstance<T extends mapStateFunctionType<S & UnboxDepsField<D, 'state'>, GetComputedType<G> & UnboxDepsField<D, 'getters'>>>(obj: ThisType<any> & T, context: compContext): void
301
+ // Support chain derivation
302
+ mapStateToInstance<T extends { [key: string]: keyof GetAllMapKeys<S, D, 'state'> }>(obj: T, context: compContext): void
303
+ mapStateToInstance<T extends { [key: string]: keyof S }>(obj: T, context: compContext): void
304
+ mapStateToInstance<T extends { [key: string]: string }>(obj: T, context: compContext): void
305
+
306
+ mapGettersToInstance<K extends keyof G>(maps: K[], context: compContext): void
307
+ mapGettersToInstance<T extends string, P extends string>(depPath: P, maps: readonly T[], context: compContext): void
308
+ // Support chain derivation
309
+ mapGettersToInstance<T extends { [key: string]: keyof GetAllMapKeys<GetComputedType<G>, D, 'getters'> }>(obj: T, context: compContext): void
310
+ mapGettersToInstance<T extends { [key: string]: keyof G }>(obj: T, context: compContext): void
311
+ // When importing js in ts file, use this method to be compatible
312
+ mapGettersToInstance<T extends { [key: string]: string }>(obj: T, context: compContext): void
313
+
314
+ mapMutationsToInstance<K extends keyof M>(maps: K[], context: compContext): void
315
+ mapMutationsToInstance<T extends string, P extends string>(depPath: P, maps: readonly T[], context: compContext): void
316
+ mapMutationsToInstance<T extends { [key: string]: keyof M }>(obj: T, context: compContext): void
317
+ mapMutationsToInstance<T extends { [key: string]: string }>(obj: T, context: compContext): void
318
+
319
+ mapActionsToInstance<K extends keyof A>(maps: K[], context: compContext): void
320
+ mapActionsToInstance<T extends string, P extends string>(depPath: P, maps: readonly T[], context: compContext): void
321
+ mapActionsToInstance<T extends { [key: string]: keyof A }>(obj: T, context: compContext): void
322
+ mapActionsToInstance<T extends { [key: string]: string }>(obj: T, context: compContext): void
278
323
  }
279
324
 
280
325
  type StoreWithThis<S = {}, G = {}, M = {}, A = {}, D extends Deps = {}> = IStoreWithThis<S, G, M, A, D> & CompatibleDispatch
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/core",
3
- "version": "2.6.114-alpha.3",
3
+ "version": "2.7.0-alpha",
4
4
  "description": "mpx runtime core",
5
5
  "keywords": [
6
6
  "miniprogram",
@@ -43,5 +43,5 @@
43
43
  "url": "https://github.com/didi/mpx/issues"
44
44
  },
45
45
  "sideEffects": false,
46
- "gitHead": "951dd877934ec6233bfb67452b790dc982317ccd"
46
+ "gitHead": "00ed5b8ad381d9b718828b5e169b1ce5cc45c3a6"
47
47
  }
@@ -3,52 +3,92 @@ import {
3
3
  getByPath
4
4
  } from '../helper/utils'
5
5
 
6
- import { warn } from '../helper/log'
6
+ import { warn, error } from '../helper/log'
7
7
 
8
8
  function mapFactory (type, store) {
9
9
  return function (depPath, maps) {
10
10
  maps = normalizeMap(depPath, maps)
11
11
  const result = {}
12
- for (let key in maps) {
12
+ Object.entries(maps).forEach(([key, value]) => {
13
13
  result[key] = function (payload) {
14
- const value = maps[key]
15
- if (type === 'mutations') {
16
- return store.commit(value, payload)
17
- } else if (type === 'actions') {
18
- return store.dispatch(value, payload)
19
- } else {
20
- let getterVal = getByPath(store.getters, value, '', '__NOTFOUND__')
21
- if (getterVal === '__NOTFOUND__') {
22
- warn(`Unknown getter named [${value}].`)
23
- getterVal = ''
24
- }
25
- return getterVal
14
+ switch (type) {
15
+ case 'state':
16
+ if (typeof value === 'function') {
17
+ return value.call(this, store.state, store.getters)
18
+ } else {
19
+ let stateVal = getByPath(store.state, value, '', '__NOTFOUND__')
20
+ if (stateVal === '__NOTFOUND__') {
21
+ warn(`Unknown state named [${value}].`)
22
+ stateVal = ''
23
+ }
24
+ return stateVal
25
+ }
26
+ case 'getters':
27
+ let getterVal = getByPath(store.getters, value, '', '__NOTFOUND__')
28
+ if (getterVal === '__NOTFOUND__') {
29
+ warn(`Unknown getter named [${value}].`)
30
+ getterVal = ''
31
+ }
32
+ return getterVal
33
+ case 'mutations':
34
+ return store.commit(value, payload)
35
+ case 'actions':
36
+ return store.dispatch(value, payload)
26
37
  }
27
38
  }
28
- }
39
+ })
29
40
  return result
30
41
  }
31
42
  }
32
43
 
44
+ function checkMapInstance (args) {
45
+ const context = args[args.length - 1]
46
+ const isValid = context && typeof context === 'object' && context.__mpxProxy
47
+ if (!isValid) {
48
+ error(`调用map**ToInstance时必须传入当前component实例this`)
49
+ }
50
+
51
+ args.splice(-1)
52
+
53
+ return {
54
+ restParams: args,
55
+ context
56
+ }
57
+ }
58
+
33
59
  export default function (store) {
34
60
  return {
35
61
  mapGetters: mapFactory('getters', store),
36
62
  mapMutations: mapFactory('mutations', store),
37
63
  mapActions: mapFactory('actions', store),
38
- mapState: (depPath, maps) => {
39
- maps = normalizeMap(depPath, maps)
40
- const result = {}
41
- Object.keys(maps).forEach(key => {
42
- const value = maps[key]
43
- result[key] = function () {
44
- if (typeof value === 'function') {
45
- return value.call(this, store.state, store.getters)
46
- } else if (typeof value === 'string') {
47
- return getByPath(store.state, value)
48
- }
49
- }
50
- })
51
- return result
64
+ mapState: mapFactory('state', store),
65
+ // 以下是map**ToInstance用于异步store的,参数args:depPath, maps, context
66
+ mapStateToInstance: (...args) => {
67
+ const { context, restParams } = checkMapInstance(args)
68
+ const mapStateFun = mapFactory('state', store)
69
+ const result = mapStateFun(...restParams)
70
+ // 将result挂载到mpxProxy实例属性上
71
+ context.__mpxProxy.options.computed = context.__mpxProxy.options.computed || {}
72
+ Object.assign(context.__mpxProxy.options.computed, result)
73
+ },
74
+ mapGettersToInstance: (...args) => {
75
+ const { context, restParams } = checkMapInstance(args)
76
+ const mapGetFun = mapFactory('getters', store)
77
+ const result = mapGetFun(...restParams)
78
+ context.__mpxProxy.options.computed = context.__mpxProxy.options.computed || {}
79
+ Object.assign(context.__mpxProxy.options.computed, result)
80
+ },
81
+ mapMutationsToInstance: (...args) => {
82
+ const { context, restParams } = checkMapInstance(args)
83
+ const mapMutationFun = mapFactory('mutations', store)
84
+ const result = mapMutationFun(...restParams)
85
+ Object.assign(context, result)
86
+ },
87
+ mapActionsToInstance: (...args) => {
88
+ const { context, restParams } = checkMapInstance(args)
89
+ const mapActionFun = mapFactory('actions', store)
90
+ const result = mapActionFun(...restParams)
91
+ Object.assign(context, result)
52
92
  }
53
93
  }
54
94
  }
package/src/core/proxy.js CHANGED
@@ -41,12 +41,14 @@ export default class MPXProxy {
41
41
  this.uid = uid++
42
42
  this.name = options.name || ''
43
43
  this.options = options
44
- // initial -> created -> mounted -> destroyed
45
- this.state = 'initial'
44
+ // beforeCreate -> created -> mounted -> destroyed
45
+ this.state = BEFORECREATE
46
46
  this.lockTask = asyncLock()
47
47
  this.ignoreProxyMap = makeMap(EXPORT_MPX.config.ignoreProxyWhiteList)
48
48
  if (__mpx_mode__ !== 'web' && __mpx_mode__ !== 'tenon') {
49
49
  this._watchers = []
50
+ this._namedWatchers = {}
51
+ this._computedWatchers = {}
50
52
  this._watcher = null
51
53
  this.localKeysMap = {} // 非props key
52
54
  this.renderData = {} // 渲染函数中收集的数据
@@ -61,16 +63,33 @@ export default class MPXProxy {
61
63
  this.initApi()
62
64
  this.callUserHook(BEFORECREATE)
63
65
  if (__mpx_mode__ !== 'web' && __mpx_mode__ !== 'tenon') {
64
- this.initState(this.options)
66
+ this.initState()
65
67
  }
66
68
  this.state = CREATED
67
69
  this.callUserHook(CREATED, params)
68
70
  if (__mpx_mode__ !== 'web' && __mpx_mode__ !== 'tenon') {
69
- // 强制走小程序原生渲染逻辑
70
- this.options.__nativeRender__ ? this.doRender() : this.initRender()
71
+ this.initRender()
71
72
  }
72
73
  }
73
74
 
75
+ reCreated (params) {
76
+ const options = this.options
77
+ this.state = BEFORECREATE
78
+ this.callUserHook(BEFORECREATE)
79
+ if (__mpx_mode__ !== 'web') {
80
+ this.initComputed(options.computed, true)
81
+ this.initWatch(options.watch)
82
+ }
83
+ this.state = CREATED
84
+ this.callUserHook(CREATED, params)
85
+ if (__mpx_mode__ !== 'web') {
86
+ this.initRender()
87
+ }
88
+ this.nextTick(() => {
89
+ this.mounted()
90
+ })
91
+ }
92
+
74
93
  renderTaskExecutor (isEmptyRender) {
75
94
  if ((!this.isMounted() && this.curRenderTask) || (this.isMounted() && isEmptyRender)) {
76
95
  return
@@ -117,6 +136,10 @@ export default class MPXProxy {
117
136
  this.callUserHook(DESTROYED)
118
137
  }
119
138
 
139
+ isDestroyed () {
140
+ return this.state === DESTROYED
141
+ }
142
+
120
143
  initApi () {
121
144
  // 挂载扩展属性到实例上
122
145
  proxy(this.target, this.options.proto, Object.keys(this.options.proto), true, (key) => {
@@ -142,6 +165,12 @@ export default class MPXProxy {
142
165
  // 强制执行render
143
166
  this.target.$forceUpdate = (...rest) => this.forceUpdate(...rest)
144
167
  this.target.$nextTick = fn => this.nextTick(fn)
168
+ this.target.$getPausableWatchers = () => this._watchers.filter(item => item.pausable)
169
+ this.target.$getWatcherByName = (name) => {
170
+ if (!this._namedWatchers) return null
171
+ return this._namedWatchers[name] || null
172
+ }
173
+ this.target.$getRenderWatcher = () => this._watcher
145
174
  }
146
175
  }
147
176
 
@@ -161,10 +190,15 @@ export default class MPXProxy {
161
190
  this.initWatch(options.watch)
162
191
  }
163
192
 
164
- initComputed (computedOpt) {
193
+ initComputed (computedOpt, reInit) {
165
194
  if (computedOpt) {
166
- this.collectLocalKeys(computedOpt)
167
- initComputed(this, this.data, computedOpt)
195
+ if (reInit) {
196
+ // target传递null以跳过computed挂载,仅重新初始化watchers
197
+ initComputed(this, null, computedOpt)
198
+ } else {
199
+ this.collectLocalKeys(computedOpt)
200
+ initComputed(this, this.data, computedOpt)
201
+ }
168
202
  }
169
203
  }
170
204
 
@@ -187,7 +221,9 @@ export default class MPXProxy {
187
221
  })
188
222
  Object.assign(this.data, dataFn.call(this.target))
189
223
  }
224
+ // 此时data中不包括props数据
190
225
  this.collectLocalKeys(this.data)
226
+ // 将props数据合并到data中
191
227
  Object.keys(initialData).forEach((key) => {
192
228
  if (!hasOwn(this.data, key)) {
193
229
  // 除了data函数返回的数据外深拷贝切断引用关系,避免后续watch由于小程序内部对data赋值重复触发watch
@@ -241,9 +277,7 @@ export default class MPXProxy {
241
277
  if (typeof EXPORT_MPX.config.hookErrorHandler === 'function') {
242
278
  EXPORT_MPX.config.hookErrorHandler(e, this.target, hookName)
243
279
  } else {
244
- setTimeout(() => {
245
- throw e
246
- })
280
+ throw e
247
281
  }
248
282
  }
249
283
  }
@@ -258,6 +292,7 @@ export default class MPXProxy {
258
292
  while (i--) {
259
293
  this._watchers[i].teardown()
260
294
  }
295
+ this._watchers.length = 0
261
296
  }
262
297
 
263
298
  render () {
@@ -409,9 +444,10 @@ export default class MPXProxy {
409
444
  }
410
445
 
411
446
  initRender () {
412
- let renderWatcher
447
+ if (this.options.__nativeRender__) return this.doRender()
448
+
413
449
  if (this.target.__injectedRender) {
414
- renderWatcher = new Watcher(this, () => {
450
+ this._watcher = new Watcher(this, () => {
415
451
  try {
416
452
  return this.target.__injectedRender()
417
453
  } catch (e) {
@@ -420,13 +456,12 @@ export default class MPXProxy {
420
456
  }
421
457
  this.render()
422
458
  }
423
- }, noop)
459
+ }, noop, { pausable: true })
424
460
  } else {
425
- renderWatcher = new Watcher(this, () => {
461
+ this._watcher = new Watcher(this, () => {
426
462
  this.render()
427
- }, noop)
463
+ }, noop, { pausable: true })
428
464
  }
429
- this._watcher = renderWatcher
430
465
  }
431
466
 
432
467
  forceUpdate (data, options, callback) {
@@ -17,7 +17,7 @@ export default function transferOptions (options, type) {
17
17
  }
18
18
  if (currentInject && currentInject.injectComputed) {
19
19
  // 编译计算属性注入
20
- options.computed = Object.assign({}, options.computed, currentInject.injectComputed)
20
+ options.computed = Object.assign({}, currentInject.injectComputed, options.computed)
21
21
  }
22
22
  if (currentInject && currentInject.injectOptions) {
23
23
  // 编译option注入,优先微信中的单独配置
@@ -197,13 +197,7 @@ export default class MpxScroll {
197
197
  )
198
198
  }
199
199
 
200
- pageScrollTo (
201
- {
202
- scrollTop,
203
- selector,
204
- duration = 300
205
- }
206
- ) {
200
+ pageScrollTo ({ scrollTop, selector, duration = 300 }) {
207
201
  let _scrollTop
208
202
 
209
203
  if (isDef(scrollTop)) {
package/src/index.js CHANGED
@@ -104,13 +104,6 @@ if (__mpx_mode__ === 'web') {
104
104
  watch = vm.$watch.bind(vm)
105
105
  const set = Vue.set.bind(Vue)
106
106
  const del = Vue.delete.bind(Vue)
107
- const remove = function (...args) {
108
- if (process.env.NODE_ENV !== 'production') {
109
- error('$remove will be removed in next minor version, please use $delete instead!', this.$rawOptions && this.$rawOptions.mpxFileResource)
110
- }
111
- return del.apply(this, args)
112
- }
113
- // todo 补齐web必要api
114
107
  APIs = {
115
108
  createApp,
116
109
  createPage,
@@ -124,15 +117,10 @@ if (__mpx_mode__ === 'web') {
124
117
  watch,
125
118
  use,
126
119
  set,
127
- remove,
128
120
  delete: del,
129
121
  getMixin,
130
122
  implement
131
123
  }
132
-
133
- InstanceAPIs = {
134
- $remove: remove
135
- }
136
124
  } else if (__mpx_mode__ === 'tenon') {
137
125
  const set = (target, key, value) => {
138
126
  return Reflect.set(target, key, value)
@@ -164,18 +152,10 @@ if (__mpx_mode__ === 'web') {
164
152
  }
165
153
 
166
154
  const vm = {}
167
-
168
155
  watch = function (expOrFn, cb, options) {
169
156
  return watchWithVm(vm, expOrFn, cb, options)
170
157
  }
171
158
 
172
- const remove = function (...args) {
173
- if (process.env.NODE_ENV !== 'production') {
174
- error('$remove will be removed in next minor version, please use $delete instead!', this.$rawOptions && this.$rawOptions.mpxFileResource)
175
- }
176
- return del.apply(this, args)
177
- }
178
-
179
159
  APIs = {
180
160
  createApp,
181
161
  createPage,
@@ -189,7 +169,6 @@ if (__mpx_mode__ === 'web') {
189
169
  watch,
190
170
  use,
191
171
  set,
192
- remove,
193
172
  delete: del,
194
173
  getMixin,
195
174
  implement
@@ -197,7 +176,6 @@ if (__mpx_mode__ === 'web') {
197
176
 
198
177
  InstanceAPIs = {
199
178
  $set: set,
200
- $remove: remove,
201
179
  $delete: del
202
180
  }
203
181
  }
@@ -20,10 +20,12 @@ export function initComputed (vm, target, computed) {
20
20
  noop,
21
21
  { lazy: true }
22
22
  )
23
- if (!(key in target)) {
24
- defineComputed(vm, target, key, userDef)
25
- } else {
26
- error(`The computed key [${key}] is duplicated with data/props, please check.`, vm.options.mpxFileResource)
23
+ if (target) {
24
+ if (!(key in target)) {
25
+ defineComputed(vm, target, key, userDef)
26
+ } else {
27
+ error(`The computed key [${key}] is duplicated with data/props, please check.`, vm.options.mpxFileResource)
28
+ }
27
29
  }
28
30
  }
29
31
  }
@@ -1,4 +1,5 @@
1
1
  import { isObject, noop } from '../helper/utils'
2
+ import { error } from '../helper/log'
2
3
  import Watcher from './watcher'
3
4
  import { queueWatcher } from './scheduler'
4
5
 
@@ -19,7 +20,26 @@ export function watch (vm, expOrFn, cb, options) {
19
20
 
20
21
  options = options || {}
21
22
  options.user = true
23
+
24
+ if (options.once) {
25
+ const _cb = cb
26
+ const onceCb = typeof options.once === 'function'
27
+ ? options.once
28
+ : function () { return true }
29
+ cb = function (...args) {
30
+ const res = onceCb.apply(this, args)
31
+ if (res) watcher.teardown()
32
+ _cb.apply(this, args)
33
+ }
34
+ }
35
+
22
36
  const watcher = new Watcher(vm, expOrFn, cb, options)
37
+ if (!vm._namedWatchers) vm._namedWatchers = {}
38
+ const name = options.name
39
+ if (name) {
40
+ if (vm._namedWatchers[name]) error(`已存在name=${name} 的 watcher,当存在多个 name 相同 watcher 时仅保留当次创建的 watcher,如需都保留请使用不同的 name!`)
41
+ vm._namedWatchers[name] = watcher
42
+ }
23
43
  if (options.immediate) {
24
44
  cb.call(vm.target, watcher.value)
25
45
  } else if (options.immediateAsync) {
@@ -24,6 +24,7 @@ export default class Watcher {
24
24
  this.deep = !!options.deep
25
25
  this.lazy = !!options.lazy
26
26
  this.sync = !!options.sync
27
+ this.name = options.name
27
28
  } else {
28
29
  this.deep = this.lazy = this.sync = false
29
30
  }
@@ -31,6 +32,10 @@ export default class Watcher {
31
32
  this.id = ++uid // uid for batching
32
33
  this.active = true
33
34
  this.immediateAsync = false
35
+ // 是否暂停,默认为否
36
+ this.paused = false
37
+ // 是否可被暂停,默认为否,不可被暂停
38
+ this.pausable = !!(options && options.pausable) || false
34
39
  this.dirty = this.lazy // for lazy watchers
35
40
  this.deps = []
36
41
  this.newDeps = []
@@ -115,7 +120,7 @@ export default class Watcher {
115
120
  // 支持临时将某个异步watcher修改为sync执行,在模拟setData时使用
116
121
  update (sync) {
117
122
  /* istanbul ignore else */
118
- if (this.lazy) {
123
+ if (this.lazy || this.paused) {
119
124
  this.dirty = true
120
125
  } else if (this.sync || sync) {
121
126
  if (sync) dequeueWatcher(this)
@@ -125,6 +130,21 @@ export default class Watcher {
125
130
  }
126
131
  }
127
132
 
133
+ pause () {
134
+ // pausable=false 不可暂停
135
+ if (!this.pausable) return
136
+ this.paused = true
137
+ }
138
+ resume () {
139
+ // pausable=false 不可恢复
140
+ if (!this.pausable) return
141
+ // paused 阶段被触发,则 resume 后执行一次run
142
+ this.paused = false
143
+ if (this.dirty) {
144
+ this.dirty = false
145
+ this.run()
146
+ }
147
+ }
128
148
  /**
129
149
  * Scheduler job interface.
130
150
  * Will be called by the scheduler.
@@ -35,7 +35,7 @@ export default function pageStatusMixin (mixinType) {
35
35
  [CREATED] () {
36
36
  const options = this.$rawOptions
37
37
  const hasPageShow = options.pageShow || options.pageHide
38
- const needPageLifetimes = options.pageLifetimes && (__mpx_mode__ === 'ali' || __mpx_mode__ === 'tt')
38
+ const needPageLifetimes = options.pageLifetimes && __mpx_mode__ === 'ali'
39
39
 
40
40
  if (hasPageShow || needPageLifetimes) {
41
41
  let currentPage
@@ -70,10 +70,13 @@ export default function proxyEventMixin () {
70
70
  }
71
71
  }
72
72
  if (__mpx_mode__ === 'ali') {
73
+ const getHandler = (eventName, props) => {
74
+ const handlerName = eventName.replace(/^./, matched => matched.toUpperCase()).replace(/-([a-z])/g, (match, p1) => p1.toUpperCase())
75
+ return props && (props['on' + handlerName] || props['catch' + handlerName])
76
+ }
73
77
  Object.assign(methods, {
74
78
  triggerEvent (eventName, eventDetail) {
75
- const handlerName = eventName.replace(/^./, matched => matched.toUpperCase()).replace(/-([a-z])/g, (match, p1) => p1.toUpperCase())
76
- const handler = this.props && (this.props['on' + handlerName] || this.props['catch' + handlerName])
79
+ const handler = getHandler(eventName, this.props)
77
80
  if (handler && typeof handler === 'function') {
78
81
  const dataset = collectDataset(this.props)
79
82
  const id = this.props.id || ''
@@ -92,6 +95,31 @@ export default function proxyEventMixin () {
92
95
  },
93
96
  detail: eventDetail
94
97
  }
98
+ handler.call(this, eventObj)
99
+ }
100
+ },
101
+ __proxyEvent (e) {
102
+ const type = e.type
103
+ const handler = getHandler(type, this.props)
104
+
105
+ if (handler && typeof handler === 'function') {
106
+ const dataset = collectDataset(this.props)
107
+ const id = this.props.id || ''
108
+ const targetData = Object.assign({}, e.target || {}, {
109
+ id,
110
+ dataset
111
+ })
112
+
113
+ const currentTargetData = Object.assign({}, e.currentTarget || {}, {
114
+ id,
115
+ dataset
116
+ })
117
+
118
+ const eventObj = Object.assign({}, e, {
119
+ target: targetData,
120
+ currentTarget: currentTargetData
121
+ })
122
+
95
123
  handler.call(this, eventObj)
96
124
  }
97
125
  }
@@ -22,7 +22,8 @@ const setComponentRef = function (target, ref, context, isAsync) {
22
22
  enumerable: true,
23
23
  configurable: true,
24
24
  get () {
25
- if (!cacheRef) {
25
+ // wx由于分包异步化的存在,每次访问refs都需要重新执行selectComponen,避免一直拿到缓存中的placeholder
26
+ if (__mpx_mode__ === 'wx' || !cacheRef) {
26
27
  return (cacheRef = context.__getRefNode(ref, isAsync))
27
28
  }
28
29
  return cacheRef
@@ -91,7 +92,7 @@ export default function getRefsMixin () {
91
92
  }
92
93
  const component = e.detail.component
93
94
  const destroyed = e.detail.destroyed
94
- const className = component.props.mpxClass || component.className
95
+ const className = component.props.className || component.className
95
96
  const identifiers = className ? className.trim().split(/\s+/).map(item => {
96
97
  return `.${item}`
97
98
  }) : []
@@ -84,23 +84,24 @@ function filterOptions (options, type) {
84
84
  }
85
85
 
86
86
  function initProxy (context, rawOptions, currentInject, params) {
87
- // 提供代理对象需要的api
88
- transformApiForProxy(context, currentInject)
89
- // 缓存options
90
- context.$rawOptions = rawOptions
91
- // 创建proxy对象
92
- const mpxProxy = new MPXProxy(rawOptions, context)
93
- context.__mpxProxy = mpxProxy
94
- context.__mpxProxy.created(params)
87
+ if (!context.__mpxProxy) {
88
+ // 提供代理对象需要的api
89
+ transformApiForProxy(context, currentInject)
90
+ // 缓存options
91
+ context.$rawOptions = rawOptions
92
+ // 创建proxy对象
93
+ context.__mpxProxy = new MPXProxy(rawOptions, context)
94
+ context.__mpxProxy.created(params)
95
+ } else if (context.__mpxProxy.isDestroyed()) {
96
+ context.__mpxProxy.reCreated(params)
97
+ }
95
98
  }
96
99
 
97
100
  export function getDefaultOptions (type, { rawOptions = {}, currentInject }) {
98
101
  const hookNames = type === 'component' ? ['onInit', 'didMount', 'didUnmount'] : ['onLoad', 'onReady', 'onUnload']
99
102
  const rootMixins = [{
100
103
  [hookNames[0]] (...params) {
101
- if (!this.__mpxProxy) {
102
- initProxy(this, rawOptions, currentInject, params)
103
- }
104
+ initProxy(this, rawOptions, currentInject, params)
104
105
  },
105
106
  deriveDataFromProps (nextProps) {
106
107
  if (this.__mpxProxy && this.__mpxProxy.isMounted() && nextProps && nextProps !== this.props) {
@@ -130,6 +131,7 @@ export function getDefaultOptions (type, { rawOptions = {}, currentInject }) {
130
131
  },
131
132
  didUpdate () {
132
133
  if (this.__mpxProxy) {
134
+ // todo: lockTask必要性待验证,属性更新触发自身setData时,updated执行与wx对齐,updated触发机制也考虑与wx对齐(props update && setData callback)
133
135
  this.__mpxProxy.lockTask(() => {
134
136
  this.__mpxProxy.updated()
135
137
  })
@@ -143,7 +145,7 @@ export function getDefaultOptions (type, { rawOptions = {}, currentInject }) {
143
145
  }
144
146
  },
145
147
  [hookNames[2]] () {
146
- this.__mpxProxy && this.__mpxProxy.destroyed()
148
+ if (this.__mpxProxy) this.__mpxProxy.destroyed()
147
149
  }
148
150
  }]
149
151
  rawOptions.mixins = rawOptions.mixins ? rootMixins.concat(rawOptions.mixins) : rootMixins
@@ -2,29 +2,32 @@ import mergeOptions from '../../../core/mergeOptions'
2
2
  import { initProxy, filterOptions } from '../wx/getDefaultOptions'
3
3
 
4
4
  export function getDefaultOptions (type, { rawOptions = {}, currentInject }) {
5
- const hookNames = ['attached', 'ready', 'detached']
6
- // 构造页面时统一使用onInit进行初始化
7
- if (type === 'page') {
8
- hookNames[0] = 'onInit'
9
- }
5
+ let hookNames = ['attached', 'ready', 'detached']
10
6
  // 当用户传入page作为构造器构造页面时,修改所有关键hooks
11
7
  if (rawOptions.__pageCtor__) {
12
- hookNames[1] = 'onReady'
13
- hookNames[2] = 'onUnload'
8
+ hookNames = ['onLoad', 'onReady', 'onUnload']
14
9
  }
15
- const rootMixins = [{
10
+
11
+ const rootMixin = {
16
12
  [hookNames[0]] (...params) {
17
- if (!this.__mpxProxy) {
18
- initProxy(this, rawOptions, currentInject, params)
19
- }
13
+ initProxy(this, rawOptions, currentInject, params)
20
14
  },
21
15
  [hookNames[1]] () {
22
- this.__mpxProxy && this.__mpxProxy.mounted()
16
+ if (this.__mpxProxy) this.__mpxProxy.mounted()
23
17
  },
24
18
  [hookNames[2]] () {
25
- this.__mpxProxy && this.__mpxProxy.destroyed()
19
+ if (this.__mpxProxy) this.__mpxProxy.destroyed()
26
20
  }
27
- }]
21
+ }
22
+
23
+ // 如构造页面,优先使用onInit进行初始化
24
+ if (type === 'page') {
25
+ rootMixin.onInit = function (...params) {
26
+ initProxy(this, rawOptions, currentInject, params)
27
+ }
28
+ }
29
+
30
+ const rootMixins = [rootMixin]
28
31
  rawOptions.mixins = rawOptions.mixins ? rootMixins.concat(rawOptions.mixins) : rootMixins
29
32
  rawOptions = mergeOptions(rawOptions, type, false)
30
33
  return filterOptions(rawOptions)
@@ -9,7 +9,6 @@ function filterOptions (options) {
9
9
  if (builtInKeysMap[key]) {
10
10
  return
11
11
  }
12
- // Tenon 使用的Vue3 语法中 data 配置需要为一个函数
13
12
  if (key === 'data' || key === 'dataFn') {
14
13
  newOptions.data = function mergeFn () {
15
14
  return Object.assign(
@@ -28,8 +27,7 @@ function initProxy (context, rawOptions) {
28
27
  // 缓存options
29
28
  context.$rawOptions = rawOptions
30
29
  // 创建proxy对象
31
- const mpxProxy = new MPXProxy(rawOptions, context)
32
- context.__mpxProxy = mpxProxy
30
+ context.__mpxProxy = new MPXProxy(rawOptions, context)
33
31
  context.__mpxProxy.created(Hummer.pageInfo && Hummer.pageInfo.params && [Hummer.pageInfo.params])
34
32
  }
35
33
 
@@ -38,7 +36,7 @@ export function getDefaultOptions (type, { rawOptions = {}, currentInject }) {
38
36
  const rootMixins = [{
39
37
  [hookNames[0]] (...params) {
40
38
  if (!this.__mpxProxy) {
41
- initProxy(this, rawOptions, currentInject, params)
39
+ initProxy(this, rawOptions, currentInject, params) // todo 确认参数是否需要
42
40
  }
43
41
  },
44
42
  [hookNames[1]] () {
@@ -24,30 +24,33 @@ function filterOptions (options) {
24
24
  }
25
25
 
26
26
  function initProxy (context, rawOptions, params) {
27
- // 缓存options
28
- context.$rawOptions = rawOptions
29
- // 创建proxy对象
30
- const mpxProxy = new MPXProxy(rawOptions, context)
31
- context.__mpxProxy = mpxProxy
32
- context.__mpxProxy.created(params)
27
+ if (!context.__mpxProxy) {
28
+ // 缓存options
29
+ context.$rawOptions = rawOptions
30
+ // 创建proxy对象
31
+ context.__mpxProxy = new MPXProxy(rawOptions, context)
32
+ context.__mpxProxy.created(params)
33
+ } else if (context.__mpxProxy.isDestroyed()) {
34
+ context.__mpxProxy.reCreated(params)
35
+ }
33
36
  }
34
37
 
35
38
  export function getDefaultOptions (type, { rawOptions = {} }) {
36
39
  const rootMixins = [{
37
40
  created () {
38
- if (!this.__mpxProxy) {
39
- const query = (global.__mpxRouter && global.__mpxRouter.currentRoute && global.__mpxRouter.currentRoute.query) || {}
40
- initProxy(this, rawOptions, [query])
41
- }
41
+ const query = (global.__mpxRouter && global.__mpxRouter.currentRoute && global.__mpxRouter.currentRoute.query) || {}
42
+ initProxy(this, rawOptions, [query])
43
+ // web中单独触发onLoad
44
+ this.onLoad && this.onLoad(query)
42
45
  },
43
46
  mounted () {
44
- this.__mpxProxy && this.__mpxProxy.mounted()
47
+ if (this.__mpxProxy) this.__mpxProxy.mounted()
45
48
  },
46
49
  updated () {
47
- this.__mpxProxy && this.__mpxProxy.updated()
50
+ if (this.__mpxProxy) this.__mpxProxy.updated()
48
51
  },
49
52
  destroyed () {
50
- this.__mpxProxy && this.__mpxProxy.destroyed()
53
+ if (this.__mpxProxy) this.__mpxProxy.destroyed()
51
54
  }
52
55
  }]
53
56
  // 为了在builtMixin中可以使用某些rootMixin实现的特性(如数据响应等),此处builtInMixin在rootMixin之后执行,但是当builtInMixin使用存在对应内建生命周期的目标平台声明周期写法时,可能会出现用户生命周期比builtInMixin中的生命周期先执行的情况,为了避免这种情况发生,builtInMixin应该尽可能使用内建生命周期来编写
@@ -117,14 +117,17 @@ export function filterOptions (options) {
117
117
  }
118
118
 
119
119
  export function initProxy (context, rawOptions, currentInject, params) {
120
- // 提供代理对象需要的api
121
- transformApiForProxy(context, currentInject)
122
- // 缓存options
123
- context.$rawOptions = rawOptions
124
- // 创建proxy对象
125
- const mpxProxy = new MPXProxy(rawOptions, context)
126
- context.__mpxProxy = mpxProxy
127
- context.__mpxProxy.created(params)
120
+ if (!context.__mpxProxy) {
121
+ // 提供代理对象需要的api
122
+ transformApiForProxy(context, currentInject)
123
+ // 缓存options
124
+ context.$rawOptions = rawOptions
125
+ // 创建proxy对象
126
+ context.__mpxProxy = new MPXProxy(rawOptions, context)
127
+ context.__mpxProxy.created(params)
128
+ } else if (context.__mpxProxy.isDestroyed()) {
129
+ context.__mpxProxy.reCreated(params)
130
+ }
128
131
  }
129
132
 
130
133
  export function getDefaultOptions (type, { rawOptions = {}, currentInject }) {
@@ -137,15 +140,13 @@ export function getDefaultOptions (type, { rawOptions = {}, currentInject }) {
137
140
  }
138
141
  const rootMixins = [{
139
142
  [hookNames[0]] (...params) {
140
- if (!this.__mpxProxy) {
141
- initProxy(this, rawOptions, currentInject, params)
142
- }
143
+ initProxy(this, rawOptions, currentInject, params)
143
144
  },
144
145
  [hookNames[1]] () {
145
- this.__mpxProxy && this.__mpxProxy.mounted()
146
+ if (this.__mpxProxy) this.__mpxProxy.mounted()
146
147
  },
147
148
  [hookNames[2]] () {
148
- this.__mpxProxy && this.__mpxProxy.destroyed()
149
+ if (this.__mpxProxy) this.__mpxProxy.destroyed()
149
150
  }
150
151
  }]
151
152
  rawOptions.mixins = rawOptions.mixins ? rootMixins.concat(rawOptions.mixins) : rootMixins
@@ -1,6 +1,6 @@
1
- import mpx from '../index'
1
+ const mpx = require('../index').default
2
2
 
3
- export default (type) => (...args) => {
3
+ module.exports = (type) => (...args) => {
4
4
  if (type === 'Behavior') {
5
5
  if (args[0]) {
6
6
  Object.defineProperty(args[0], '__mpx_behaviors_to_mixins__', {
@@ -1,3 +1 @@
1
- import mpx from '../index'
2
-
3
- export default mpx
1
+ module.exports = require('../index').default