@mpxjs/core 2.9.38 → 2.9.41-react.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.
Files changed (44) hide show
  1. package/package.json +37 -3
  2. package/src/convertor/convertor.js +4 -1
  3. package/src/convertor/getConvertMode.js +3 -1
  4. package/src/convertor/wxToReact.js +31 -0
  5. package/src/core/proxy.js +75 -7
  6. package/src/core/transferOptions.js +9 -1
  7. package/src/dynamic/astCache.js +25 -0
  8. package/src/dynamic/dynamicRenderMixin.js +77 -0
  9. package/src/dynamic/vnode/context.js +17 -0
  10. package/src/dynamic/vnode/css-select/cssauron.js +445 -0
  11. package/src/dynamic/vnode/css-select/index.js +116 -0
  12. package/src/dynamic/vnode/css-select/through.js +19 -0
  13. package/src/dynamic/vnode/css-select/tokenizer.js +371 -0
  14. package/src/dynamic/vnode/interpreter.js +449 -0
  15. package/src/dynamic/vnode/render.js +289 -0
  16. package/src/{vuePlugin.js → external/vuePlugin.js} +1 -1
  17. package/src/index.js +8 -2
  18. package/src/platform/builtInMixins/directiveHelperMixin.android.js +2 -0
  19. package/src/platform/builtInMixins/directiveHelperMixin.ios.js +15 -0
  20. package/src/platform/builtInMixins/directiveHelperMixin.js +3 -0
  21. package/src/platform/builtInMixins/index.js +20 -2
  22. package/src/platform/builtInMixins/proxyEventMixin.android.js +2 -0
  23. package/src/platform/builtInMixins/proxyEventMixin.ios.js +49 -0
  24. package/src/platform/builtInMixins/proxyEventMixin.js +12 -14
  25. package/src/platform/builtInMixins/refsMixin.android.js +2 -0
  26. package/src/platform/builtInMixins/refsMixin.ios.js +311 -0
  27. package/src/platform/builtInMixins/renderHelperMixin.js +2 -2
  28. package/src/platform/builtInMixins/styleHelperMixin.android.js +2 -0
  29. package/src/platform/builtInMixins/styleHelperMixin.ios.js +141 -0
  30. package/src/platform/builtInMixins/styleHelperMixin.js +3 -0
  31. package/src/platform/createApp.android.js +2 -0
  32. package/src/platform/createApp.ios.js +103 -0
  33. package/src/platform/createApp.js +3 -5
  34. package/src/platform/export/api.web.js +1 -1
  35. package/src/platform/patch/ali/getDefaultOptions.js +24 -4
  36. package/src/platform/patch/index.js +9 -6
  37. package/src/platform/patch/react/getDefaultOptions.android.js +1 -0
  38. package/src/platform/patch/react/getDefaultOptions.ios.js +270 -0
  39. package/src/platform/patch/react/getDefaultOptions.js +1 -0
  40. package/src/platform/patch/swan/getDefaultOptions.js +1 -1
  41. package/src/platform/patch/web/getDefaultOptions.js +13 -3
  42. package/src/platform/patch/wx/getDefaultOptions.js +21 -1
  43. /package/src/{vue.js → external/vue.js} +0 -0
  44. /package/src/{vue.web.js → external/vue.web.js} +0 -0
@@ -0,0 +1,289 @@
1
+ import cssSelect from './css-select'
2
+ // todo: stringify wxs 模块只能放到逻辑层执行,主要还是因为生成 vdom tree 需要根据 class 去做匹配,需要看下这个代码从哪引入
3
+ import stringify from '@mpxjs/webpack-plugin/lib/runtime/stringify.wxs'
4
+ import Interpreter from './interpreter'
5
+ import { dash2hump, isString, error } from '@mpxjs/utils'
6
+
7
+ const deepCloneNode = function (val) {
8
+ return JSON.parse(JSON.stringify(val))
9
+ }
10
+
11
+ function simpleNormalizeChildren (children) {
12
+ for (let i = 0; i < children.length; i++) {
13
+ if (Array.isArray(children[i])) {
14
+ return Array.prototype.concat.apply([], children)
15
+ }
16
+ }
17
+ return children
18
+ }
19
+
20
+ export default function _genVnodeTree (astData, contextScope, options) {
21
+ const { template = {}, styles = [] } = astData || {}
22
+ const { moduleId, location } = options || {}
23
+ // 解除引用
24
+ const templateAst = deepCloneNode(template)
25
+ // 获取实例 uid
26
+ const uid = contextScope[0]?.__mpxProxy?.uid || contextScope[0]?.uid
27
+ // 动态化组件 slots 通过上下文传递,相当于 props
28
+ const slots = contextScope[0]?.slots || {}
29
+
30
+ function createEmptyNode () {
31
+ return createNode('block')
32
+ }
33
+
34
+ function genVnodeTree (node) {
35
+ if (node.type === 1) {
36
+ // wxs 模块不需要动态渲染
37
+ if (node.tag === 'wxs') {
38
+ return createEmptyNode()
39
+ } else if (node.for && !node.forProcessed) {
40
+ return genFor(node)
41
+ } else if (node.if && !node.ifProcessed) {
42
+ return genIf(node)
43
+ } else if (node.tag === 'slot') {
44
+ return genSlot(node)
45
+ } else {
46
+ const data = genData(node)
47
+ let children = genChildren(node)
48
+ // 运行时组件的子组件都通过 slots 属性传递,样式规则在当前组件内匹配后挂载
49
+ if (node.dynamic) {
50
+ data.slots = resolveSlot(children.map(item => genVnodeWithStaticCss(deepCloneNode(item))))
51
+ children = []
52
+ }
53
+ return createNode(node.tag, data, children)
54
+ }
55
+ } else if (node.type === 3) {
56
+ return genText(node)
57
+ }
58
+ }
59
+
60
+ function evalExps (exps) {
61
+ const interpreter = new Interpreter(contextScope)
62
+ // 消除引用关系
63
+ let value
64
+ try {
65
+ value = interpreter.eval(JSON.parse(JSON.stringify(exps)))
66
+ } catch (e) {
67
+ const errmsg = e.message
68
+ console.warn(errmsg)
69
+ error('interprete the expression wrong: ', location, {
70
+ errType: 'mpx-dynamic-interprete',
71
+ errmsg
72
+ })
73
+ }
74
+ return value
75
+ }
76
+
77
+ function createNode (tag, data = {}, children = []) {
78
+ if (Array.isArray(data)) {
79
+ children = data
80
+ data = {}
81
+ }
82
+ if (typeof tag === 'object') {
83
+ return tag
84
+ }
85
+
86
+ // 处理 for 循环产生的数组,同时清除空节点
87
+ children = simpleNormalizeChildren(children).filter(node => !!node?.nt)
88
+
89
+ return {
90
+ nt: tag,
91
+ d: data,
92
+ c: children
93
+ }
94
+ }
95
+
96
+ /**
97
+ *
98
+ * 样式隔离的匹配策略优化:
99
+ *
100
+ * 条件1: 子组件不能影响到父组件的样式
101
+ * 条件2: slot 的内容必须在父组件的上下文当中完成样式匹配
102
+ * 条件3: 匹配过程只能进行一次
103
+ *
104
+ * 方案一:根据 moduleId 即作用域来进行匹配
105
+ * 方案二:根据虚拟树来进行匹配
106
+ */
107
+ // function createDynamicNode (moduleId, data = {}, children = []) {
108
+ // const { template = {}, styles = [] } = staticMap[moduleId]
109
+ // data.$slots = resolveSlot(children) // 将 slot 通过上下文传递到子组件的渲染流程中
110
+ // const vnodeTree = _genVnodeTree(template, [data], styles, moduleId)
111
+ // return vnodeTree
112
+ // }
113
+
114
+ function resolveSlot (children) {
115
+ const slots = {}
116
+ if (children.length) {
117
+ for (let i = 0; i < children.length; i++) {
118
+ const child = children[i]
119
+ const name = child.d?.slot
120
+ if (name) {
121
+ const slot = (slots[name] || (slots[name] = []))
122
+ if (child.tag === 'template') {
123
+ slot.push.apply(slot, child.children || [])
124
+ } else {
125
+ slot.push(child)
126
+ }
127
+ } else {
128
+ (slots.default || (slots.default = [])).push(child)
129
+ }
130
+ }
131
+ }
132
+ return slots
133
+ }
134
+
135
+ function genData (node) {
136
+ const res = {
137
+ uid,
138
+ moduleId
139
+ }
140
+ if (!node.attrsList) {
141
+ return res
142
+ }
143
+
144
+ node.attrsList.forEach((attr) => {
145
+ if (attr.name === 'class' || attr.name === 'style') {
146
+ // class/style 的表达式为数组形式,class/style的计算过程需要放到逻辑层,主要是因为有逻辑匹配的过程去生成 vnodeTree
147
+ const helper = attr.name === 'class' ? stringify.stringifyClass : stringify.stringifyStyle
148
+ let value = ''
149
+ if (attr.__exp) {
150
+ let valueArr = evalExps(attr.__exp)
151
+ valueArr = Array.isArray(valueArr) ? valueArr : [valueArr]
152
+ value = helper(...valueArr)
153
+ // dynamic style + wx:show
154
+ const showStyle = valueArr[2]
155
+ if (showStyle) {
156
+ value = value + ';' + showStyle
157
+ }
158
+ } else {
159
+ value = attr.value
160
+ }
161
+ res[attr.name] = value
162
+ } else {
163
+ res[dash2hump(attr.name)] = attr.__exp
164
+ ? evalExps(attr.__exp)
165
+ : attr.value
166
+ }
167
+ })
168
+
169
+ return res
170
+ }
171
+
172
+ function genChildren (node) {
173
+ const res = []
174
+ const children = node.children || []
175
+ if (children.length) {
176
+ children.forEach((item) => {
177
+ res.push(genNode(item))
178
+ })
179
+ }
180
+ return res
181
+ }
182
+
183
+ function genNode (node) {
184
+ if (node.type === 1) {
185
+ return genVnodeTree(node)
186
+ } else if (node.type === 3 && node.isComment) {
187
+ return ''
188
+ // TODO: 注释暂不处理
189
+ // return _genComment(node)
190
+ } else {
191
+ return genText(node) // 文本节点统一通过 _genText 来生成,type = 2(带有表达式的文本,在 mpx 统一处理为了3) || type = 3(纯文本,非注释)
192
+ }
193
+ }
194
+
195
+ function genText (node) {
196
+ return {
197
+ nt: '#text',
198
+ ct: node.__exp ? evalExps(node.__exp) : node.text
199
+ }
200
+ }
201
+
202
+ function genFor (node) {
203
+ node.forProcessed = true
204
+
205
+ const itemKey = node.for.item || 'item'
206
+ const indexKey = node.for.index || 'index'
207
+ const scope = {
208
+ [itemKey]: null,
209
+ [indexKey]: null
210
+ }
211
+ const forExp = node.for
212
+ const res = []
213
+ let forValue = evalExps(forExp.__exp)
214
+
215
+ // 和微信的模版渲染策略保持一致:当 wx:for 的值为字符串时,会将字符串解析成字符串数组
216
+ if (isString(forValue)) {
217
+ forValue = forValue.split('')
218
+ }
219
+
220
+ if (Array.isArray(forValue)) {
221
+ forValue.forEach((item, index) => {
222
+ // item、index 模板当中如果没申明,需要给到默认值
223
+ scope[itemKey] = item
224
+ scope[indexKey] = index
225
+
226
+ contextScope.push(scope)
227
+
228
+ // 针对 for 循环避免每次都操作的同一个 node 导致数据的污染的问题
229
+ res.push(deepCloneNode(genVnodeTree(deepCloneNode(node))))
230
+
231
+ contextScope.pop()
232
+ })
233
+ }
234
+
235
+ return res
236
+ }
237
+
238
+ // 对于 if 而言最终生成 <= 1 节点
239
+ function genIf (node) {
240
+ if (!node.ifConditions) {
241
+ node.ifConditions = []
242
+ return {} // 一个空节点
243
+ }
244
+ node.ifProcessed = true
245
+
246
+ const ifConditions = node.ifConditions.slice()
247
+
248
+ let res = {} // 空节点
249
+ for (let i = 0; i < ifConditions.length; i++) {
250
+ const condition = ifConditions[i]
251
+ // 非 else 节点
252
+ if (condition.ifExp) {
253
+ const identifierValue = evalExps(condition.__exp)
254
+ if (identifierValue) {
255
+ res = genVnodeTree(condition.block === 'self' ? node : condition.block)
256
+ break
257
+ }
258
+ } else { // else 节点
259
+ res = genVnodeTree(condition.block)
260
+ break
261
+ }
262
+ }
263
+ return res
264
+ }
265
+
266
+ // 暂时不支持作用域插槽
267
+ function genSlot (node) {
268
+ const data = genData(node) // 计算属性值
269
+ const slotName = data.name || 'default'
270
+ return slots[slotName] || null
271
+ }
272
+
273
+ function genVnodeWithStaticCss (vnodeTree) {
274
+ styles.forEach((item) => {
275
+ const [selector, style] = item
276
+ const nodes = cssSelect(selector, { moduleId })(vnodeTree)
277
+ nodes?.forEach((node) => {
278
+ // todo style 合并策略问题:合并过程中缺少了权重关系 style, class 的判断,需要优化
279
+ node.d.style = node.d.style ? style + node.d.style : style
280
+ })
281
+ })
282
+
283
+ return vnodeTree
284
+ }
285
+
286
+ const interpreteredVnodeTree = genVnodeTree(templateAst)
287
+
288
+ return genVnodeWithStaticCss(interpreteredVnodeTree)
289
+ }
@@ -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 () {
package/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import Vue from './vue'
1
+ import Vue from './external/vue'
2
2
  import { error, diffAndCloneA, hasOwn, makeMap } from '@mpxjs/utils'
3
3
  import { APIs, InstanceAPIs } from './platform/export/api'
4
4
 
@@ -60,6 +60,8 @@ export {
60
60
 
61
61
  export { getMixin } from './core/mergeOptions'
62
62
 
63
+ export { dynamic } from './dynamic/astCache'
64
+
63
65
  export function toPureObject (obj) {
64
66
  return diffAndCloneA(obj).clone
65
67
  }
@@ -143,7 +145,11 @@ Mpx.config = {
143
145
  hostWhitelists Array 类型 支持h5域名白名单安全校验
144
146
  apiImplementations webview JSSDK接口 例如getlocation
145
147
  */
146
- webviewConfig: {}
148
+ webviewConfig: {},
149
+ /**
150
+ * react-native 相关配置,用于挂载事件等,如 onShareAppMessage
151
+ */
152
+ rnConfig: {}
147
153
  }
148
154
 
149
155
  global.__mpx = Mpx
@@ -0,0 +1,2 @@
1
+ import directiveHelperMixin from './directiveHelperMixin.ios'
2
+ export default directiveHelperMixin
@@ -0,0 +1,15 @@
1
+ import { warn, type } from '@mpxjs/utils'
2
+ export default function directiveHelperMixin () {
3
+ return {
4
+ methods: {
5
+ __getWxKey (item, key) {
6
+ const value = key === '*this' ? item : item[key]
7
+ if (typeof value === 'string' || typeof value === 'number') {
8
+ return value
9
+ } else {
10
+ warn(`wx:key's value should return a string or a number, received: ${type(value)}`, this.__mpxProxy.options.mpxFileResource)
11
+ }
12
+ }
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,3 @@
1
+ export default function directiveHelperMixin () {
2
+ return {}
3
+ }
@@ -10,10 +10,21 @@ import pageScrollMixin from './pageScrollMixin'
10
10
  import componentGenericsMixin from './componentGenericsMixin'
11
11
  import getTabBarMixin from './getTabBarMixin'
12
12
  import pageRouteMixin from './pageRouteMixin'
13
+ import { dynamicRefsMixin, dynamicRenderHelperMixin, dynamicSlotMixin } from '../../dynamic/dynamicRenderMixin'
14
+ import styleHelperMixin from './styleHelperMixin'
15
+ import directiveHelperMixin from './directiveHelperMixin'
16
+ import { isReact } from '@mpxjs/utils'
13
17
 
14
18
  export default function getBuiltInMixins (options, type) {
15
- let bulitInMixins = []
16
- if (__mpx_mode__ === 'web') {
19
+ let bulitInMixins
20
+ if (isReact) {
21
+ bulitInMixins = [
22
+ proxyEventMixin(),
23
+ directiveHelperMixin(),
24
+ styleHelperMixin(type),
25
+ refsMixin()
26
+ ]
27
+ } else if (__mpx_mode__ === 'web') {
17
28
  bulitInMixins = [
18
29
  proxyEventMixin(),
19
30
  refsMixin(),
@@ -41,6 +52,13 @@ export default function getBuiltInMixins (options, type) {
41
52
  showMixin(type),
42
53
  i18nMixin()
43
54
  ])
55
+ if (__mpx_dynamic_runtime__) {
56
+ bulitInMixins = bulitInMixins.concat([
57
+ dynamicRenderHelperMixin(),
58
+ dynamicSlotMixin(),
59
+ dynamicRefsMixin()
60
+ ])
61
+ }
44
62
  }
45
63
  }
46
64
  return bulitInMixins.filter(item => item)
@@ -0,0 +1,2 @@
1
+ import proxyEventMixin from './proxyEventMixin.ios'
2
+ export default proxyEventMixin
@@ -0,0 +1,49 @@
1
+ import { error } from '@mpxjs/utils'
2
+ import Mpx from '../../index'
3
+
4
+ export default function proxyEventMixin () {
5
+ const methods = {
6
+ __invoke (rawEvent, eventConfig = []) {
7
+ if (typeof Mpx.config.proxyEventHandler === 'function') {
8
+ try {
9
+ Mpx.config.proxyEventHandler(rawEvent)
10
+ } catch (e) {
11
+ }
12
+ }
13
+ const location = this.__mpxProxy.options.mpxFileResource
14
+
15
+ let returnedValue
16
+ eventConfig.forEach((item) => {
17
+ const callbackName = item[0]
18
+ if (callbackName) {
19
+ const params = item.length > 1
20
+ ? item.slice(1).map(item => {
21
+ if (item === '__mpx_event__') {
22
+ return rawEvent
23
+ } else {
24
+ return item
25
+ }
26
+ })
27
+ : [rawEvent]
28
+ if (typeof this[callbackName] === 'function') {
29
+ returnedValue = this[callbackName].apply(this, params)
30
+ } else {
31
+ error(`Instance property [${callbackName}] is not function, please check.`, location)
32
+ }
33
+ }
34
+ })
35
+ return returnedValue
36
+ }
37
+ // __model (expr, $event, valuePath = ['value'], filterMethod) {
38
+ // const innerFilter = {
39
+ // trim: val => typeof val === 'string' && val.trim()
40
+ // }
41
+ // const originValue = valuePath.reduce((acc, cur) => acc[cur], $event.detail)
42
+ // const value = filterMethod ? (innerFilter[filterMethod] ? innerFilter[filterMethod](originValue) : typeof this[filterMethod] === 'function' ? this[filterMethod](originValue) : originValue) : originValue
43
+ // setByPath(this, expr, value)
44
+ // }
45
+ }
46
+ return {
47
+ methods
48
+ }
49
+ }
@@ -1,5 +1,11 @@
1
1
  import { setByPath, error, dash2hump, collectDataset } from '@mpxjs/utils'
2
2
  import Mpx from '../../index'
3
+ import contextMap from '../../dynamic/vnode/context'
4
+
5
+ function logCallbackNotFound (context, callbackName) {
6
+ const location = context.__mpxProxy && context.__mpxProxy.options.mpxFileResource
7
+ error(`Instance property [${callbackName}] is not function, please check.`, location)
8
+ }
3
9
 
4
10
  export default function proxyEventMixin () {
5
11
  const methods = {
@@ -12,7 +18,6 @@ export default function proxyEventMixin () {
12
18
  }
13
19
  const location = this.__mpxProxy.options.mpxFileResource
14
20
  const type = $event.type
15
- const emitMode = $event.detail && $event.detail.mpxEmit
16
21
  if (!type) {
17
22
  error('Event object must have [type] property!', location)
18
23
  return
@@ -33,22 +38,15 @@ export default function proxyEventMixin () {
33
38
  }
34
39
  const eventConfigs = target.dataset.eventconfigs || {}
35
40
  const curEventConfig = eventConfigs[type] || eventConfigs[fallbackType] || []
41
+ // 如果有 mpxuid 说明是运行时组件,那么需要设置对应的上下文
42
+ const rootRuntimeContext = contextMap.get(target.dataset.mpxuid)
43
+ const context = rootRuntimeContext || this
36
44
  let returnedValue
37
45
  curEventConfig.forEach((item) => {
38
46
  const callbackName = item[0]
39
- if (emitMode) {
40
- $event = $event.detail.data
41
- }
42
47
  if (callbackName) {
43
48
  const params = item.length > 1
44
49
  ? item.slice(1).map(item => {
45
- // 暂不支持$event.xxx的写法
46
- // if (/^\$event/.test(item)) {
47
- // this.__mpxTempEvent = $event
48
- // const value = getByPath(this, item.replace('$event', '__mpxTempEvent'))
49
- // // 删除临时变量
50
- // delete this.__mpxTempEvent
51
- // return value
52
50
  if (item === '__mpx_event__') {
53
51
  return $event
54
52
  } else {
@@ -56,10 +54,10 @@ export default function proxyEventMixin () {
56
54
  }
57
55
  })
58
56
  : [$event]
59
- if (typeof this[callbackName] === 'function') {
60
- returnedValue = this[callbackName].apply(this, params)
57
+ if (typeof context[callbackName] === 'function') {
58
+ returnedValue = context[callbackName].apply(context, params)
61
59
  } else {
62
- error(`Instance property [${callbackName}] is not function, please check.`, location)
60
+ logCallbackNotFound(context, callbackName)
63
61
  }
64
62
  }
65
63
  })
@@ -0,0 +1,2 @@
1
+ import refsMixin from './refsMixin.ios'
2
+ export default refsMixin