Skip to content

Commit 0f0212e

Browse files
liuzejialiuzejia
liuzejia
authored and
liuzejia
committed
feat: 补充半编译文档
1 parent b298e53 commit 0f0212e

File tree

6 files changed

+722
-129
lines changed

6 files changed

+722
-129
lines changed

docs/complier-mode.mdx

+359
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
---
2+
title: 半编译模式
3+
---
4+
5+
:::info
6+
Taro v3.6.23 开始支持,目前只支持 **React**,暂不支持 Vue。底层实现原理请参考 [RFC 文档](https://github.com/NervJS/taro/discussions/14708)。
7+
:::
8+
9+
在节点数量增多到一定量级时,Taro3 的渲染性能会大幅下降,出现白屏时间长、交互延时等问题。经排查发现是目前 Taro 的 `<template>` 模板语法所造成的,为此我们参考 Taro 1/2 的思路,提供了 **CompileMode** 渲染模式。`CompileMode` 适合长列表 Item 这类会被重复渲染多次的组件使用,在长列表场景能提升 **30%** 以上的首开速度,同时能有效减少节点过多时产生的交互延时问题。CompileMode 可以说是应对复杂页面性能优化的“银弹”。
10+
11+
## 使用方法
12+
13+
首先在 Taro 编译配置中开启使用半编译模式:
14+
15+
```js title="config/index.js"
16+
const config = {
17+
mini: {
18+
experimental: {
19+
compileMode: true
20+
}
21+
}
22+
// ...
23+
}
24+
```
25+
26+
然后只需要给 Taro 基础组件添加 `compileMode` 属性,该组件及其 children 将会被编译为单独的小程序模板:
27+
28+
```jsx
29+
function GoodsItem () {
30+
return (
31+
<View compileMode>
32+
...
33+
</View>
34+
)
35+
}
36+
```
37+
38+
更为详细的用法请看 [详细用法](#详细用法)
39+
40+
## 常见问题
41+
42+
### 1. 编译出的模板文件会增加包体积
43+
44+
半编译模式使用了空间来换时间,编译出模板会令包体积增大。增加的文件大小视 JSX 写法而定,可以在编译后的页面目录下找到对应的模板文件,如 `pages/index/index.jsx` 编译出的模板位置在 `dist/pages/index/index-templates.wxml`。因此开发者应权衡后使用。
45+
46+
### 2. 只能优化部分语法
47+
48+
编译阶段只能识别、优化部分语法,不支持的语法会自动回退到 Taro3 默认的渲染模式,具体支持的语法可以查阅 [RFC 文档](https://github.com/NervJS/taro/discussions/14708)。
49+
50+
有一种常见语法需要注意:编译阶段只能识别 Taro 基础组件,而 React、Vue 组件的渲染会自动回退到旧的渲染模式。如果这些 React、Vue 组件也需要使用半编译模式,需要在组件内部再次添加 `compileMode` 属性:
51+
52+
```jsx
53+
function Index () {
54+
return (
55+
<View compileMode>
56+
<Text>Hello</Text> {/* 能被编译阶段识别 */}
57+
<Foo /> {/*会自动回退到 Taro3 默认的渲染模式*/}
58+
</View>
59+
)
60+
}
61+
62+
function Foo () {
63+
return (
64+
// 如果希望 Foo 组件也使用半编译模式,需要在 Foo 组件内部再次添加 compileMode 属性
65+
<View compileMode>
66+
...
67+
</View>
68+
)
69+
}
70+
71+
```
72+
73+
## 详细用法
74+
### 条件表达式 + 自定义组件
75+
通过状态来控制展示哪一个自定义组件的场景在业务中是很常见的,比如以下场景
76+
```jsx
77+
export default function Index () {
78+
const [show, setShow] = useState(true)
79+
80+
return (
81+
<View compileMode>
82+
<Button onClick={()=>setShow(!show)}>toggle show</Button>
83+
<View>
84+
{
85+
show ? <Item/> : null
86+
}
87+
</View>
88+
</View>
89+
)
90+
}
91+
92+
function Item () {
93+
return (
94+
<View compileMode>
95+
item
96+
</View>
97+
)
98+
}
99+
```
100+
正常来说,上面这段代码是没问题的,但是由于 compileMode 得在编译的时候,给元素加上 compileIf 的属性,所以必须是一个确切的标签,所以以上写法暂不支持。后续计划设法把这个属性直接写入在 template 节点上,以支持以上写法。现阶段,先用以下的降级方法:
101+
```jsx
102+
export default function Index () {
103+
const [show, setShow] = useState(true)
104+
105+
return (
106+
<View compileMode>
107+
<Button onClick={()=>setShow(!show)}>toggle show</Button>
108+
<View>
109+
<Item show={show}/>
110+
</View>
111+
</View>
112+
)
113+
}
114+
115+
function Item (props) {
116+
const { show } = props
117+
return (
118+
show
119+
?
120+
<View compileMode>
121+
item
122+
</View>
123+
: null
124+
)
125+
}
126+
```
127+
即把组件的展示,放到子组件中去进行判断。
128+
129+
### 使用 jsx 变量
130+
直接使用 jsx 变量,在半编译的情况下是会,如以下代码:
131+
```jsx
132+
export default function Index () {
133+
134+
const item = (<View>item</View>)
135+
return (
136+
<View compileMode>
137+
<View>
138+
{item}
139+
</View>
140+
</View>
141+
)
142+
}
143+
```
144+
要改为 render 开头的渲染函数,如下:
145+
```jsx
146+
export default function Index () {
147+
148+
const renderItem = () => <View>item</View>
149+
return (
150+
<View compileMode>
151+
<View>
152+
{renderItem()}
153+
</View>
154+
</View>
155+
)
156+
}
157+
```
158+
不过这种写法,并不会把 `renderItem` 的返回值直接打入模版里面,所以这种写法对性能会有一定的消耗。
159+
160+
### 表单驱动 jsx 元素
161+
这个场景下,其实就是 「使用 jsx 变量」 的一个延伸,如以下代码:
162+
```jsx
163+
export default function Index () {
164+
165+
const itemMap = {
166+
a: <View compileMode>itemA</View>,
167+
b: <View compileMode>itemB</View>,
168+
c: <View compileMode>itemC</View>
169+
}
170+
return (
171+
<View compileMode>
172+
{itemMap.a}
173+
{itemMap.b}
174+
{itemMap.c}
175+
</View>
176+
)
177+
}
178+
```
179+
需要改为以下写法:
180+
```jsx
181+
export default function Index () {
182+
const itemMap = {
183+
renderA: ()=> <View compileMode>itemA</View>,
184+
renderB: ()=> <View compileMode>itemB</View>,
185+
renderC: ()=> <View compileMode>itemC</View>
186+
}
187+
return (
188+
<View compileMode>
189+
<View>
190+
{itemMap.renderA()}
191+
{itemMap.renderB()}
192+
{itemMap.renderC()}
193+
</View>
194+
</View>
195+
)
196+
}
197+
```
198+
199+
## 最佳实践
200+
总的来说,要最大限度的发挥半编译模式的优势,就是要把尽量把静态节点,尽可能的写到同一个 jsx 里面去。自我检查的最简单的方式就是看看编译后的模版数量是否足够少,每个模版是否包含了足够多节点。
201+
如果一个 template 只是包含了少数节点,那其实无法带来很大的提升。如以下代码:
202+
```jsx
203+
import { View, Image, Text } from '@tarojs/components'
204+
205+
const dataList = [
206+
{
207+
src: "https://media.tiffany.cn/is/image/Tiffany/EcomBrowseM/35189432_1009333_ED.jpg?defaultImage=NoImageAvailableInternal",
208+
title: "这是标题1",
209+
subTitle: "这是子标题1",
210+
tag: ["标签1", "标签2", "标签3"],
211+
des: "这是描述1",
212+
subDes:'这是子描述1',
213+
prices: {
214+
normal: {
215+
int: '86',
216+
float: '88'
217+
},
218+
line: 100
219+
}
220+
},
221+
{
222+
src: "https://media.tiffany.cn/is/image/Tiffany/EcomBrowseM/62866950_989218_ED.jpg?defaultImage=NoImageAvailableInternal",
223+
title: "这是标题2",
224+
subTitle: "这是子标题2",
225+
tag: ["标签1", "标签2", "标签3"],
226+
des: "这是描述2",
227+
subDes:'这是子描述2',
228+
prices: {
229+
normal: {
230+
int: '60',
231+
float: '70'
232+
},
233+
line: 100
234+
}
235+
},
236+
{
237+
src: "https://media.tiffany.cn/is/image/Tiffany/EcomBrowseM/62507586_989743_ED_M.jpg?defaultImage=NoImageAvailableInternal",
238+
title: "这是标题3",
239+
subTitle: "这是子标题3",
240+
tag: ["标签1", "标签2", "标签3"],
241+
des: "这是描述3",
242+
subDes:'这是子描述3',
243+
prices: {
244+
normal: {
245+
int: '85',
246+
float: '10'
247+
},
248+
line: 100
249+
}
250+
},
251+
{
252+
src: "https://media.tiffany.cn/is/image/Tiffany/EcomBrowseM/33263465_997778_ED.jpg?defaultImage=NoImageAvailableInternal",
253+
title: "这是标题4",
254+
subTitle: "这是子标题4",
255+
tag: ["标签1", "标签2", "标签3"],
256+
des: "这是描述4",
257+
subDes:'这是子描述4',
258+
prices: {
259+
normal: {
260+
int: '8',
261+
float: '88'
262+
},
263+
line: 100
264+
}
265+
},
266+
{
267+
src: "https://media.tiffany.cn/is/image/Tiffany/EcomBrowseM/60957401_1023440_ED.jpg?defaultImage=NoImageAvailableInternal",
268+
title: "这是标题5",
269+
subTitle: "这是子标题5",
270+
tag: ["标签1", "标签2", "标签3"],
271+
des: "这是描述5",
272+
subDes:'这是子描述5',
273+
prices: {
274+
normal: {
275+
int: '77',
276+
float: '88'
277+
},
278+
line: 100
279+
}
280+
}
281+
]
282+
283+
284+
export default function Index () {
285+
return (
286+
<View>
287+
{
288+
new Array(100).fill(0).map((_, index)=><Item key={index} itemIndex = {index}/>)
289+
}
290+
</View>
291+
)
292+
}
293+
294+
const Item = (props) =>{
295+
const { itemIndex } = props
296+
const selectIndex = itemIndex % 5
297+
const data = dataList[selectIndex]
298+
return (
299+
<View compileMode>
300+
<View className='item-body-wrap' >
301+
<View className='image-wrap'>
302+
<Image src={data.src} mode='aspectFill' className='image-wrap' />
303+
</View>
304+
<View className='body-left'>
305+
<View className='title-wrap'>
306+
<View className='title'>
307+
{data.title}
308+
</View>
309+
<View className='sub-title'>
310+
{data.subTitle}
311+
</View>
312+
</View>
313+
314+
<View className='des-wrap'>
315+
<View className='des'>
316+
{data.des}
317+
</View>
318+
<View className='sub-des'>
319+
{data.subDes}
320+
</View>
321+
</View>
322+
323+
<View className='tag-wrap'>
324+
{
325+
data.tag.map((e, index)=><View key={index} className='tag'>
326+
{e}
327+
</View>)
328+
}
329+
</View>
330+
<View className='price-wrap'>
331+
<View className='price-normal'>
332+
<Text className='price-normal-int'>{data.prices.normal.int}</Text>
333+
<Text className='price-normal-float'>.{data.prices.normal.float}</Text>
334+
</View>
335+
<View className='price-line'>
336+
{data.prices.line}
337+
</View>
338+
339+
</View>
340+
<View className='add'>
341+
<Image src='https://img12.360buyimg.com/imagetools/jfs/t1/169993/8/27041/5311/61b1b219E03cffee0/778c223bd7677925.png' mode='aspectFill' className='add-image' />
342+
</View>
343+
<View className='level1'>
344+
<View className='level2'>
345+
<View className='level3'>
346+
<View className='level4'>
347+
<View className='level5'>
348+
搞一个嵌套五层的浮动元素
349+
</View>
350+
</View>
351+
</View>
352+
</View>
353+
</View>
354+
</View>
355+
</View>
356+
</View>
357+
)
358+
}
359+
```

0 commit comments

Comments
 (0)