@mpxjs/webpack-plugin 2.9.70-alpha.0 → 2.9.70

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 (99) hide show
  1. package/LICENSE +433 -0
  2. package/README.md +1 -1
  3. package/lib/config.js +0 -14
  4. package/lib/dependencies/ResolveDependency.js +0 -5
  5. package/lib/index.js +7 -38
  6. package/lib/json-compiler/helper.js +3 -3
  7. package/lib/loader.js +0 -53
  8. package/lib/parser.js +1 -1
  9. package/lib/platform/json/wx/index.js +21 -8
  10. package/lib/platform/style/wx/index.js +51 -54
  11. package/lib/platform/template/wx/component-config/button.js +2 -14
  12. package/lib/platform/template/wx/component-config/fix-component-name.js +15 -12
  13. package/lib/platform/template/wx/component-config/image.js +0 -4
  14. package/lib/platform/template/wx/component-config/index.js +1 -1
  15. package/lib/platform/template/wx/component-config/input.js +0 -4
  16. package/lib/platform/template/wx/component-config/rich-text.js +6 -2
  17. package/lib/platform/template/wx/component-config/scroll-view.js +0 -4
  18. package/lib/platform/template/wx/component-config/switch.js +0 -4
  19. package/lib/platform/template/wx/component-config/text.js +0 -4
  20. package/lib/platform/template/wx/component-config/textarea.js +0 -5
  21. package/lib/platform/template/wx/component-config/unsupported.js +1 -1
  22. package/lib/platform/template/wx/component-config/view.js +0 -4
  23. package/lib/platform/template/wx/index.js +1 -127
  24. package/lib/resolve-loader.js +1 -4
  25. package/lib/runtime/components/react/dist/getInnerListeners.js +5 -6
  26. package/lib/runtime/components/react/dist/mpx-canvas/html.js +2 -4
  27. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +5 -1
  28. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +96 -107
  29. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +26 -27
  30. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +480 -0
  31. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +68 -0
  32. package/lib/runtime/components/react/dist/mpx-swiper/type.js +1 -0
  33. package/lib/runtime/components/react/dist/mpx-view.jsx +6 -3
  34. package/lib/runtime/components/react/dist/pickerFaces.js +6 -12
  35. package/lib/runtime/components/react/dist/{pickerViewOverlay.jsx → pickerOverlay.jsx} +3 -5
  36. package/lib/runtime/components/react/dist/useAnimationHooks.js +7 -8
  37. package/lib/runtime/components/react/dist/utils.jsx +89 -60
  38. package/lib/runtime/components/react/event.config.ts +25 -26
  39. package/lib/runtime/components/react/getInnerListeners.ts +212 -169
  40. package/lib/runtime/components/react/mpx-button.tsx +9 -14
  41. package/lib/runtime/components/react/mpx-canvas/html.ts +2 -4
  42. package/lib/runtime/components/react/mpx-canvas/index.tsx +44 -46
  43. package/lib/runtime/components/react/mpx-checkbox-group.tsx +15 -13
  44. package/lib/runtime/components/react/mpx-checkbox.tsx +20 -21
  45. package/lib/runtime/components/react/mpx-form.tsx +15 -20
  46. package/lib/runtime/components/react/mpx-icon.tsx +2 -2
  47. package/lib/runtime/components/react/mpx-image.tsx +87 -47
  48. package/lib/runtime/components/react/mpx-input.tsx +24 -32
  49. package/lib/runtime/components/react/mpx-label.tsx +12 -14
  50. package/lib/runtime/components/react/mpx-movable-area.tsx +10 -16
  51. package/lib/runtime/components/react/mpx-movable-view.tsx +20 -24
  52. package/lib/runtime/components/react/mpx-navigator.tsx +2 -8
  53. package/lib/runtime/components/react/mpx-radio-group.tsx +13 -15
  54. package/lib/runtime/components/react/mpx-radio.tsx +19 -25
  55. package/lib/runtime/components/react/mpx-rich-text/html.ts +40 -0
  56. package/lib/runtime/components/react/mpx-rich-text/index.tsx +121 -0
  57. package/lib/runtime/components/react/mpx-root-portal.tsx +3 -5
  58. package/lib/runtime/components/react/mpx-scroll-view.tsx +40 -41
  59. package/lib/runtime/components/react/mpx-switch.tsx +19 -15
  60. package/lib/runtime/components/react/mpx-text.tsx +8 -16
  61. package/lib/runtime/components/react/mpx-textarea.tsx +11 -10
  62. package/lib/runtime/components/react/mpx-view.tsx +18 -20
  63. package/lib/runtime/components/react/mpx-web-view.tsx +94 -59
  64. package/lib/runtime/components/react/types/global.d.ts +2 -1
  65. package/lib/runtime/components/react/useAnimationHooks.ts +36 -12
  66. package/lib/runtime/components/react/utils.tsx +91 -60
  67. package/lib/runtime/components/web/mpx-web-view.vue +34 -20
  68. package/lib/runtime/optionProcessor.js +0 -22
  69. package/lib/style-compiler/index.js +1 -1
  70. package/lib/style-compiler/plugins/scope-id.js +30 -2
  71. package/lib/template-compiler/compiler.js +30 -26
  72. package/lib/utils/env.js +1 -6
  73. package/lib/utils/pre-process-json.js +9 -5
  74. package/package.json +4 -7
  75. package/lib/dependencies/AddEntryDependency.js +0 -24
  76. package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +0 -39
  77. package/lib/runtime/components/react/dist/mpx-swiper.jsx +0 -606
  78. package/lib/runtime/components/react/dist/pickerVIewContext.js +0 -9
  79. package/lib/runtime/components/react/dist/pickerViewMask.jsx +0 -18
  80. package/lib/runtime/components/tenon/getInnerListeners.js +0 -334
  81. package/lib/runtime/components/tenon/tenon-button.vue +0 -309
  82. package/lib/runtime/components/tenon/tenon-image.vue +0 -66
  83. package/lib/runtime/components/tenon/tenon-input.vue +0 -171
  84. package/lib/runtime/components/tenon/tenon-rich-text.vue +0 -26
  85. package/lib/runtime/components/tenon/tenon-scroll-view.vue +0 -127
  86. package/lib/runtime/components/tenon/tenon-switch.vue +0 -96
  87. package/lib/runtime/components/tenon/tenon-text.vue +0 -70
  88. package/lib/runtime/components/tenon/tenon-textarea.vue +0 -86
  89. package/lib/runtime/components/tenon/tenon-view.vue +0 -93
  90. package/lib/runtime/components/web/event.js +0 -105
  91. package/lib/runtime/optionProcessor.tenon.js +0 -84
  92. package/lib/style-compiler/plugins/hm.js +0 -20
  93. package/lib/tenon/index.js +0 -117
  94. package/lib/tenon/processJSON.js +0 -352
  95. package/lib/tenon/processScript.js +0 -203
  96. package/lib/tenon/processStyles.js +0 -21
  97. package/lib/tenon/processTemplate.js +0 -126
  98. package/lib/tenon/script-helper.js +0 -223
  99. package/lib/utils/get-relative-path.js +0 -25
@@ -1,6 +1,5 @@
1
- import { forwardRef, JSX, useEffect, useRef, useContext, useMemo } from 'react'
2
- import { noop, warn } from '@mpxjs/utils'
3
- import { View } from 'react-native'
1
+ import { forwardRef, JSX, useRef, useContext, useMemo, createElement } from 'react'
2
+ import { warn, getFocusedNavigation, isFunction } from '@mpxjs/utils'
4
3
  import { Portal } from '@ant-design/react-native'
5
4
  import { getCustomEvent } from './getInnerListeners'
6
5
  import { promisify, redirectTo, navigateTo, navigateBack, reLaunch, switchTab } from '@mpxjs/api-proxy'
@@ -31,26 +30,25 @@ interface WebViewProps {
31
30
  }
32
31
 
33
32
  interface PayloadData {
34
- data?: Record<string, any>
33
+ [x: string]: any
35
34
  }
36
35
 
37
36
  type MessageData = {
38
37
  payload?: PayloadData,
38
+ args?: Array<any>,
39
39
  type?: string,
40
40
  callbackId?: number
41
41
  }
42
42
 
43
- const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((props, ref): JSX.Element => {
44
- const { src, bindmessage = noop, bindload = noop, binderror = noop } = props
45
- if (!src) {
46
- return (<View></View>)
47
- }
43
+ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((props, ref): JSX.Element | null => {
44
+ const { src, bindmessage, bindload, binderror } = props
45
+ const mpx = global.__mpx
48
46
  if (props.style) {
49
47
  warn('The web-view component does not support the style prop.')
50
48
  }
51
49
  const pageId = useContext(RouteContext)
52
50
  const currentPage = useMemo(() => getCurrentPage(pageId), [pageId])
53
-
51
+ const webViewRef = useRef<WebView>(null)
54
52
  const defaultWebViewStyle = {
55
53
  position: 'absolute' as 'absolute' | 'relative' | 'static',
56
54
  left: 0 as number,
@@ -59,34 +57,14 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
59
57
  bottom: 0 as number
60
58
  }
61
59
 
62
- const webViewRef = useRef<WebView>(null)
63
60
  useNodesRef<WebView, WebViewProps>(props, ref, webViewRef, {
64
61
  style: defaultWebViewStyle
65
62
  })
66
63
 
67
- const _messageList = useRef<any[]>([])
68
- const handleUnload = () => {
69
- // 这里是 WebView 销毁前执行的逻辑
70
- bindmessage(getCustomEvent('messsage', {}, {
71
- detail: {
72
- data: _messageList.current
73
- },
74
- layoutRef: webViewRef
75
- }))
64
+ if (!src) {
65
+ return null
76
66
  }
77
67
 
78
- useEffect(() => {
79
- if (currentPage) {
80
- currentPage.__webViewUrl = src
81
- }
82
- }, [src, currentPage])
83
-
84
- useEffect(() => {
85
- // 组件卸载时执行
86
- return () => {
87
- handleUnload()
88
- }
89
- }, [])
90
68
  const _load = function (res: WebViewNavigationEvent) {
91
69
  const result = {
92
70
  type: 'load',
@@ -107,8 +85,33 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
107
85
  }
108
86
  binderror(result)
109
87
  }
88
+ const injectedJavaScript = `
89
+ if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
90
+ var _documentTitle = document.title;
91
+ window.ReactNativeWebView.postMessage(JSON.stringify({
92
+ type: 'setTitle',
93
+ payload: {
94
+ _documentTitle: _documentTitle
95
+ }
96
+ }))
97
+ Object.defineProperty(document, 'title', {
98
+ set (val) {
99
+ _documentTitle = val
100
+ window.ReactNativeWebView.postMessage(JSON.stringify({
101
+ type: 'setTitle',
102
+ payload: {
103
+ _documentTitle: _documentTitle
104
+ }
105
+ }))
106
+ },
107
+ get () {
108
+ return _documentTitle
109
+ }
110
+ });
111
+ }
112
+ `
110
113
  const _changeUrl = function (navState: WebViewNavigation) {
111
- if (currentPage) {
114
+ if (navState.navigationType) { // navigationType这个事件在页面开始加载时和页面加载完成时都会被触发所以判断这个避免其他无效触发执行该逻辑
112
115
  currentPage.__webViewUrl = navState.url
113
116
  }
114
117
  }
@@ -121,43 +124,79 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
121
124
  if (typeof nativeEventData === 'string') {
122
125
  data = JSON.parse(nativeEventData)
123
126
  }
124
- } catch (e) {
125
- data = {}
126
- }
127
+ } catch (e) {}
128
+ const args = data.args
127
129
  const postData: PayloadData = data.payload || {}
128
- switch (data.type) {
130
+ const params = Array.isArray(args) ? args : [postData]
131
+ const type = data.type
132
+ switch (type) {
133
+ case 'setTitle':
134
+ { // case下不允许直接声明,包个块解决该问题
135
+ const title = postData._documentTitle
136
+ if (title) {
137
+ const navigation = getFocusedNavigation()
138
+ navigation && navigation.setOptions({ title })
139
+ }
140
+ }
141
+ break
129
142
  case 'postMessage':
130
- _messageList.current.push(postData.data)
143
+ bindmessage && bindmessage(getCustomEvent('messsage', {}, { // RN组件销毁顺序与小程序不一致,所以改成和支付宝消息一致
144
+ detail: {
145
+ data: params[0]?.data
146
+ }
147
+ }))
131
148
  asyncCallback = Promise.resolve({
132
149
  errMsg: 'invokeWebappApi:ok'
133
150
  })
134
151
  break
135
152
  case 'navigateTo':
136
- asyncCallback = navObj.navigateTo(postData)
153
+ asyncCallback = navObj.navigateTo(...params)
137
154
  break
138
155
  case 'navigateBack':
139
- asyncCallback = navObj.navigateBack(postData)
156
+ asyncCallback = navObj.navigateBack(...params)
140
157
  break
141
158
  case 'redirectTo':
142
- asyncCallback = navObj.redirectTo(postData)
159
+ asyncCallback = navObj.redirectTo(...params)
143
160
  break
144
161
  case 'switchTab':
145
- asyncCallback = navObj.switchTab(postData)
162
+ asyncCallback = navObj.switchTab(...params)
146
163
  break
147
164
  case 'reLaunch':
148
- asyncCallback = navObj.reLaunch(postData)
165
+ asyncCallback = navObj.reLaunch(...params)
166
+ break
167
+ default:
168
+ if (type) {
169
+ const implement = mpx.config.webviewConfig.apiImplementations && mpx.config.webviewConfig.apiImplementations[type]
170
+ if (isFunction(implement)) {
171
+ asyncCallback = Promise.resolve(implement(...params))
172
+ } else {
173
+ /* eslint-disable prefer-promise-reject-errors */
174
+ asyncCallback = Promise.reject({
175
+ errMsg: `未在apiImplementations中配置${type}方法`
176
+ })
177
+ }
178
+ }
149
179
  break
150
180
  }
151
181
 
152
182
  asyncCallback && asyncCallback.then((res: any) => {
153
183
  if (webViewRef.current?.postMessage) {
154
184
  const test = JSON.stringify({
155
- type: data.type,
185
+ type,
156
186
  callbackId: data.callbackId,
157
187
  result: res
158
188
  })
159
189
  webViewRef.current.postMessage(test)
160
190
  }
191
+ }).catch((error: any) => {
192
+ if (webViewRef.current?.postMessage) {
193
+ const test = JSON.stringify({
194
+ type,
195
+ callbackId: data.callbackId,
196
+ error
197
+ })
198
+ webViewRef.current.postMessage(test)
199
+ }
161
200
  })
162
201
  }
163
202
  const events = {}
@@ -172,21 +211,17 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
172
211
  onError: _error
173
212
  })
174
213
  }
175
- if (bindmessage) {
176
- extendObject(events, {
177
- onMessage: _message
178
- })
179
- }
180
- return (<Portal>
181
- <WebView
182
- style={defaultWebViewStyle}
183
- source={{ uri: src }}
184
- ref={webViewRef}
185
- {...events}
186
- onNavigationStateChange={_changeUrl}
187
- javaScriptEnabled={true}
188
- ></WebView>
189
- </Portal>)
214
+ extendObject(events, {
215
+ onMessage: _message
216
+ })
217
+
218
+ return createElement(Portal, null, createElement(WebView, extendObject({
219
+ style: defaultWebViewStyle,
220
+ source: { uri: src },
221
+ ref: webViewRef,
222
+ javaScriptEnabled: true,
223
+ onNavigationStateChange: _changeUrl
224
+ }, events)))
190
225
  })
191
226
 
192
227
  _WebView.displayName = 'MpxWebview'
@@ -19,7 +19,8 @@ declare module '@mpxjs/utils' {
19
19
  bottom: number
20
20
  left: number
21
21
  right: number
22
- }
22
+ },
23
+ setOptions: (params: Record<string, any>) => void
23
24
  } | undefined
24
25
  }
25
26
 
@@ -83,12 +83,34 @@ const InitialValue: ExtendedViewStyle = Object.assign({
83
83
  const TransformOrigin = 'transformOrigin'
84
84
  // transform
85
85
  const isTransform = (key: string) => Object.keys(TransformInitial).includes(key)
86
+ // 多value解析
87
+ const parseValues = (str: string, char = ' ') => {
88
+ let stack = 0
89
+ let temp = ''
90
+ const result = []
91
+ for (let i = 0; i < str.length; i++) {
92
+ if (str[i] === '(') {
93
+ stack++
94
+ } else if (str[i] === ')') {
95
+ stack--
96
+ }
97
+ // 非括号内 或者 非分隔字符且非空
98
+ if (stack !== 0 || (str[i] !== char && str[i] !== ' ')) {
99
+ temp += str[i]
100
+ }
101
+ if ((stack === 0 && str[i] === char) || i === str.length - 1) {
102
+ result.push(temp)
103
+ temp = ''
104
+ }
105
+ }
106
+ return result
107
+ }
86
108
  // parse string transform, eg: transform: 'rotateX(45deg) rotateZ(0.785398rad)'
87
109
  const parseTransform = (transformStr: string) => {
88
- const values = transformStr.trim().split(/\s+/)
110
+ const values = parseValues(transformStr)
89
111
  const transform: {[propName: string]: string|number|number[]}[] = []
90
112
  values.forEach(item => {
91
- const match = item.match(/([/\w]+)\(([^)]+)\)/)
113
+ const match = item.match(/([/\w]+)\((.+)\)/)
92
114
  if (match && match.length >= 3) {
93
115
  let key = match[1]
94
116
  const val = match[2]
@@ -109,7 +131,7 @@ const parseTransform = (transformStr: string) => {
109
131
  break
110
132
  case 'matrix':
111
133
  case 'matrix3d':
112
- transform.push({ [key]: val.split(',').map(val => +val) })
134
+ transform.push({ [key]: parseValues(val, ',').map(val => +val) })
113
135
  break
114
136
  case 'translate':
115
137
  case 'scale':
@@ -120,8 +142,8 @@ const parseTransform = (transformStr: string) => {
120
142
  {
121
143
  // 2 个以上的值处理
122
144
  key = key.replace('3d', '')
123
- const vals = val.split(',', key === 'rotate' ? 4 : 3)
124
- // scale(.5) === scaleX(.5) scaleY(.5) 这里处理一下
145
+ const vals = parseValues(val, ',').splice(0, key === 'rotate' ? 4 : 3)
146
+ // scale(.5) === scaleX(.5) scaleY(.5)
125
147
  if (vals.length === 1 && key === 'scale') {
126
148
  vals.push(vals[0])
127
149
  }
@@ -218,12 +240,14 @@ export default function useAnimationHooks<T, P> (props: _ViewProps) {
218
240
  }
219
241
  // 添加每个key的多次step动画
220
242
  animatedKeys.forEach(key => {
221
- let toVal = (rules.get(key) || transform.get(key))
243
+ const ruleV = isTransform(key) ? transform.get(key) : rules.get(key)
222
244
  // key不存在,第一轮取shareValMap[key]value,非第一轮取上一轮的
223
- if (toVal === undefined) {
224
- toVal = index > 0 ? lastValueMap[key] : shareValMap[key].value
225
- }
226
- const animation = getAnimation({ key, value: toVal }, { delay, duration, easing }, needSetCallback ? setTransformOrigin : undefined)
245
+ const toVal = ruleV !== undefined
246
+ ? ruleV
247
+ : index > 0
248
+ ? lastValueMap[key]
249
+ : shareValMap[key].value
250
+ const animation = getAnimation({ key, value: toVal! }, { delay, duration, easing }, needSetCallback ? setTransformOrigin : undefined)
227
251
  needSetCallback = false
228
252
  if (!sequence[key]) {
229
253
  sequence[key] = [animation]
@@ -231,7 +255,7 @@ export default function useAnimationHooks<T, P> (props: _ViewProps) {
231
255
  sequence[key].push(animation)
232
256
  }
233
257
  // 更新一下 lastValueMap
234
- lastValueMap[key] = toVal
258
+ lastValueMap[key] = toVal!
235
259
  })
236
260
  // 赋值驱动动画
237
261
  animatedKeys.forEach((key) => {
@@ -327,6 +351,6 @@ export default function useAnimationHooks<T, P> (props: _ViewProps) {
327
351
  styles[key] = shareValMap[key].value
328
352
  }
329
353
  return styles
330
- }, Object.assign({}, originalStyle) as ExtendedViewStyle)
354
+ }, {} as ExtendedViewStyle)
331
355
  })
332
356
  }
@@ -18,8 +18,10 @@ export const HIDDEN_STYLE = {
18
18
  opacity: 0
19
19
  }
20
20
 
21
- const varDecRegExp = /^--.*/
21
+ const varDecRegExp = /^--/
22
22
  const varUseRegExp = /var\(/
23
+ const unoVarDecRegExp = /^--un-/
24
+ const unoVarUseRegExp = /var\(--un-/
23
25
  const calcUseRegExp = /calc\(/
24
26
  const envUseRegExp = /env\(/
25
27
 
@@ -37,7 +39,7 @@ function getSafeAreaInset (name: string) {
37
39
  }
38
40
 
39
41
  export function omit<T, K extends string> (obj: T, fields: K[]): Omit<T, K> {
40
- const shallowCopy: any = Object.assign({}, obj)
42
+ const shallowCopy: any = extendObject({}, obj)
41
43
  for (let i = 0; i < fields.length; i += 1) {
42
44
  const key = fields[i]
43
45
  delete shallowCopy[key]
@@ -77,7 +79,7 @@ export const parseInlineStyle = (inlineStyle = ''): Record<string, string> => {
77
79
  const [k, v, ...rest] = style.split(':')
78
80
  if (rest.length || !v || !k) return styleObj
79
81
  const key = k.trim().replace(/-./g, c => c.substring(1).toUpperCase())
80
- return Object.assign(styleObj, { [key]: global.__formatValue(v.trim()) })
82
+ return extendObject(styleObj, { [key]: global.__formatValue(v.trim()) })
81
83
  }, {})
82
84
  }
83
85
 
@@ -286,11 +288,15 @@ interface TransformStyleConfig {
286
288
 
287
289
  export function useTransformStyle (styleObj: Record<string, any> = {}, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight }: TransformStyleConfig) {
288
290
  const varStyle: Record<string, any> = {}
291
+ const unoVarStyle: Record<string, any> = {}
289
292
  const normalStyle: Record<string, any> = {}
293
+ const normalStyleRef = useRef<Record<string, any>>({})
294
+ const normalStyleChangedRef = useRef(false)
290
295
  let hasVarDec = false
291
296
  let hasVarUse = false
292
297
  let hasSelfPercent = false
293
298
  const varKeyPaths: Array<Array<string>> = []
299
+ const unoVarKeyPaths: Array<Array<string>> = []
294
300
  const percentKeyPaths: Array<Array<string>> = []
295
301
  const calcKeyPaths: Array<Array<string>> = []
296
302
  const envKeyPaths: Array<Array<string>> = []
@@ -299,7 +305,9 @@ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableV
299
305
 
300
306
  function varVisitor ({ key, value, keyPath }: VisitorArg) {
301
307
  if (keyPath.length === 1) {
302
- if (varDecRegExp.test(key)) {
308
+ if (unoVarDecRegExp.test(key)) {
309
+ unoVarStyle[key] = value
310
+ } else if (varDecRegExp.test(key)) {
303
311
  hasVarDec = true
304
312
  varStyle[key] = value
305
313
  } else {
@@ -308,25 +316,32 @@ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableV
308
316
  }
309
317
  }
310
318
  // 对于var定义中使用的var无需替换值,可以通过resolveVar递归解析出值
311
- if (!varDecRegExp.test(key) && varUseRegExp.test(value)) {
312
- hasVarUse = true
313
- varKeyPaths.push(keyPath.slice())
319
+ if (!varDecRegExp.test(key)) {
320
+ // 一般情况下一个样式属性中不会混用unocss var和普通css var,可分开进行互斥处理
321
+ if (unoVarUseRegExp.test(value)) {
322
+ unoVarKeyPaths.push(keyPath.slice())
323
+ } else if (varUseRegExp.test(value)) {
324
+ hasVarUse = true
325
+ varKeyPaths.push(keyPath.slice())
326
+ }
314
327
  }
315
328
  }
316
329
 
317
- // traverse var
330
+ // traverse var & generate normalStyle
318
331
  traverseStyle(styleObj, [varVisitor])
332
+
319
333
  hasVarDec = hasVarDec || !!externalVarContext
320
334
  enableVar = enableVar || hasVarDec || hasVarUse
321
335
  const enableVarRef = useRef(enableVar)
322
336
  if (enableVarRef.current !== enableVar) {
323
337
  error('css variable use/declare should be stable in the component lifecycle, or you can set [enable-var] with true.')
324
338
  }
325
- // apply var
339
+ // apply css var
326
340
  const varContextRef = useRef({})
327
341
  if (enableVarRef.current) {
342
+ // eslint-disable-next-line react-hooks/rules-of-hooks
328
343
  const varContext = useContext(VarContext)
329
- const newVarContext = Object.assign({}, varContext, externalVarContext, varStyle)
344
+ const newVarContext = extendObject({}, varContext, externalVarContext, varStyle)
330
345
  // 缓存比较newVarContext是否发生变化
331
346
  if (diffAndCloneA(varContextRef.current, newVarContext).diff) {
332
347
  varContextRef.current = newVarContext
@@ -334,70 +349,86 @@ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableV
334
349
  transformVar(normalStyle, varKeyPaths, varContextRef.current)
335
350
  }
336
351
 
337
- function envVisitor ({ value, keyPath }: VisitorArg) {
338
- if (envUseRegExp.test(value)) {
339
- envKeyPaths.push(keyPath.slice())
340
- }
352
+ // apply unocss var
353
+ if (unoVarKeyPaths.length) {
354
+ transformVar(normalStyle, unoVarKeyPaths, unoVarStyle)
341
355
  }
342
356
 
343
- function calcVisitor ({ value, keyPath }: VisitorArg) {
344
- if (calcUseRegExp.test(value)) {
345
- calcKeyPaths.push(keyPath.slice())
346
- }
357
+ const { clone, diff } = diffAndCloneA(normalStyle, normalStyleRef.current)
358
+ if (diff) {
359
+ normalStyleRef.current = clone
360
+ normalStyleChangedRef.current = !normalStyleChangedRef.current
347
361
  }
348
362
 
349
- function percentVisitor ({ key, value, keyPath }: VisitorArg) {
350
- if (hasOwn(selfPercentRule, key) && PERCENT_REGEX.test(value)) {
351
- hasSelfPercent = true
352
- percentKeyPaths.push(keyPath.slice())
353
- } else if ((key === 'fontSize' || key === 'lineHeight') && PERCENT_REGEX.test(value)) {
354
- percentKeyPaths.push(keyPath.slice())
363
+ const memoResult = useMemo(() => {
364
+ // transform can be memoized
365
+ function envVisitor ({ value, keyPath }: VisitorArg) {
366
+ if (envUseRegExp.test(value)) {
367
+ envKeyPaths.push(keyPath.slice())
368
+ }
355
369
  }
356
- }
357
370
 
358
- // traverse env & calc & percent
359
- traverseStyle(normalStyle, [envVisitor, percentVisitor, calcVisitor])
371
+ function calcVisitor ({ value, keyPath }: VisitorArg) {
372
+ if (calcUseRegExp.test(value)) {
373
+ calcKeyPaths.push(keyPath.slice())
374
+ }
375
+ }
360
376
 
361
- const percentConfig = {
362
- width,
363
- height,
364
- fontSize: normalStyle.fontSize,
365
- parentWidth,
366
- parentHeight,
367
- parentFontSize
368
- }
377
+ function percentVisitor ({ key, value, keyPath }: VisitorArg) {
378
+ if (hasOwn(selfPercentRule, key) && PERCENT_REGEX.test(value)) {
379
+ hasSelfPercent = true
380
+ percentKeyPaths.push(keyPath.slice())
381
+ } else if ((key === 'fontSize' || key === 'lineHeight') && PERCENT_REGEX.test(value)) {
382
+ percentKeyPaths.push(keyPath.slice())
383
+ }
384
+ }
369
385
 
370
- // apply env
371
- transformEnv(normalStyle, envKeyPaths)
372
- // apply percent
373
- transformPercent(normalStyle, percentKeyPaths, percentConfig)
374
- // apply calc
375
- transformCalc(normalStyle, calcKeyPaths, (value: string, key: string) => {
376
- if (PERCENT_REGEX.test(value)) {
377
- const resolved = resolvePercent(value, key, percentConfig)
378
- return typeof resolved === 'number' ? resolved : 0
379
- } else {
380
- const formatted = global.__formatValue(value)
381
- if (typeof formatted === 'number') {
382
- return formatted
386
+ // traverse env & calc & percent
387
+ traverseStyle(normalStyle, [envVisitor, percentVisitor, calcVisitor])
388
+
389
+ const percentConfig = {
390
+ width,
391
+ height,
392
+ fontSize: normalStyle.fontSize,
393
+ parentWidth,
394
+ parentHeight,
395
+ parentFontSize
396
+ }
397
+
398
+ // apply env
399
+ transformEnv(normalStyle, envKeyPaths)
400
+ // apply percent
401
+ transformPercent(normalStyle, percentKeyPaths, percentConfig)
402
+ // apply calc
403
+ transformCalc(normalStyle, calcKeyPaths, (value: string, key: string) => {
404
+ if (PERCENT_REGEX.test(value)) {
405
+ const resolved = resolvePercent(value, key, percentConfig)
406
+ return typeof resolved === 'number' ? resolved : 0
383
407
  } else {
384
- warn('calc() only support number, px, rpx, % temporarily.')
385
- return 0
408
+ const formatted = global.__formatValue(value)
409
+ if (typeof formatted === 'number') {
410
+ return formatted
411
+ } else {
412
+ warn('calc() only support number, px, rpx, % temporarily.')
413
+ return 0
414
+ }
386
415
  }
416
+ })
417
+ // transform number enum stringify
418
+ transformStringify(normalStyle)
419
+
420
+ return {
421
+ normalStyle,
422
+ hasSelfPercent
387
423
  }
388
- })
389
- // transform number enum stringify
390
- transformStringify(normalStyle)
424
+ }, [normalStyleChangedRef.current, width, height, parentWidth, parentHeight, parentFontSize])
391
425
 
392
- return {
393
- normalStyle,
394
- hasSelfPercent,
426
+ return extendObject({
395
427
  hasVarDec,
396
- enableVarRef,
397
428
  varContextRef,
398
429
  setWidth,
399
430
  setHeight
400
- }
431
+ }, memoResult)
401
432
  }
402
433
 
403
434
  export interface VisitorArg {
@@ -477,7 +508,7 @@ interface LayoutConfig {
477
508
  export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout, nodeRef }: LayoutConfig) => {
478
509
  const layoutRef = useRef({})
479
510
  const hasLayoutRef = useRef(false)
480
- const layoutStyle: Record<string, any> = !hasLayoutRef.current && hasSelfPercent ? HIDDEN_STYLE : {}
511
+ const layoutStyle = useMemo(() => { return !hasLayoutRef.current && hasSelfPercent ? HIDDEN_STYLE : {} }, [hasLayoutRef.current])
481
512
  const layoutProps: Record<string, any> = {}
482
513
  const enableOffset = props['enable-offset']
483
514
  if (hasSelfPercent || onLayout || enableOffset) {
@@ -565,7 +596,7 @@ export const useStableCallback = <T extends AnyFunc | null | undefined> (
565
596
  }
566
597
 
567
598
  export const usePrevious = <T, > (value: T): T | undefined => {
568
- const ref = useRef<T | undefined>(undefined)
599
+ const ref = useRef<T | undefined>()
569
600
  const prev = ref.current
570
601
  ref.current = value
571
602
  return prev