Skip to content

Commit eeb2d7c

Browse files
authored
refactor(react): 因升级react-reconciler而做的必要代码调整 (#13105)
* refactor(react): 因升级react-reconciler而做的必要代码调整 * chore: 兼容supportsMicrotasks类型报错的提示
1 parent 9dc467c commit eeb2d7c

File tree

9 files changed

+468
-137
lines changed

9 files changed

+468
-137
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
"@types/node": "^14.14.31",
107107
"@types/postcss-import": "^12.0.0",
108108
"@types/react": "^18.0.0",
109-
"@types/react-reconciler": "0.26.1",
109+
"@types/react-reconciler": "0.28.1",
110110
"@types/request": "^2.48.1",
111111
"@types/resolve": "1.19.0",
112112
"@types/sass": "^1.16.1",

packages/taro-plugin-react/src/webpack.mini.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ function setAlias (ctx: IPluginContext, framework: Frameworks, chain) {
2323
// 不是生产环境,且没有设置 debugReact,则使用压缩版本的 react 依赖,减少体积
2424
alias.set('react-reconciler$', 'react-reconciler/cjs/react-reconciler.production.min.js')
2525
alias.set('react$', 'react/cjs/react.production.min.js')
26-
alias.set('scheduler$', 'scheduler/cjs/scheduler.production.min.js')
2726
alias.set('react/jsx-runtime$', 'react/cjs/react-jsx-runtime.production.min.js')
2827

2928
// 在React18中,使用了exports字段约定了模块暴露路径,其中并未暴露 ./cjs/ 。这将使上面的alias在编译时报错。相当的tricky。

packages/taro-react/README.md

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,284 @@
11
# @tarojs/react
22

33
基于 `react-reconciler` 的小程序专用 React 渲染器,连接 `@tarojs/runtime 的 DOM 实例,相当于小程序版的 `react-dom`,暴露的 API 也和 `react-dom` 保持一致。
4+
5+
## HostConfig for Taro
6+
7+
Taro 对 HostConfig 进行了针对性的改造,以适应小程序环境。现对各字段进行阐述,具体实现以文件 `./src/reconciler.ts` 为准。
8+
9+
字段的含义描述来源于 [react-reconciler](https://www.npmjs.com/package/react-reconciler/v/0.27.0)
10+
11+
### getPublicInstance(instance)
12+
13+
> Determines what object gets exposed as a ref. If you don't want to do anything here, return instance.
14+
15+
Taro: 默认返回 instance 即可
16+
17+
### getRootHostContext(rootContainer)
18+
19+
> This method lets you return the initial host context from the root of the tree. If you don't intend to use host context, you can return null.
20+
21+
Taro: 默认返回 {}
22+
23+
### getChildHostContext(parentHostContext, type, rootContainer)
24+
25+
> Host context lets you track some information about where you are in the tree so that it's available inside createInstance as the hostContext parameter.
26+
27+
Taro: 默认返回 {}
28+
29+
### resetAfterCommit(containerInfo)
30+
31+
> This method is called right after React has performed the tree mutations. You can leave it empty.
32+
33+
Taro: 该方法置空
34+
35+
### prepareForCommit(containerInfo)
36+
37+
> This method lets you store some information before React starts making changes to the tree on the screen.
38+
39+
Taro: 返回 null 即可.
40+
41+
### createInstance(type, props, rootContainer, hostContext, internalHandle)
42+
43+
> This method should return a newly created node. For example, the DOM renderer would call document.createElement(type) here and then set the properties from props.
44+
45+
Taro: 在 ReactDOM 中会调用 document.createElement 来生成 dom,而在小程序环境中 Taro 中模拟了 document,直接返回 `document.createElement(type)` 即可
46+
47+
> 也因为 Taro 没有使用其他入参,因此方法 getRootHostContext / getChildHostContext 默认返回 {} 即可
48+
49+
### appendInitialChild(parentInstance, child)
50+
51+
> This method should mutate the parentInstance and add the child to its list of children. For example, in the DOM this would translate to a parentInstance.appendChild(child) call.
52+
53+
Taro: 直接 parentInstance.appendChild(child) 即可
54+
55+
### finalizeInitialChildren(instance, type, props, rootContainer, hostContext)
56+
57+
> In this method, you can perform some final mutations on the instance. There is a second purpose to this method. It lets you specify whether there is some work that needs to happen when the node is connected to the tree on the screen. If you return true, the instance will receive a commitMount call later. See its documentation below.
58+
59+
finalizeInitialChildren 在组件挂载到页面中前调用,更新时不会调用.
60+
61+
Taro: 遍历 props,更新到 instance 中即可,同时返回false,即不需要调用 `commitMount` 方法
62+
63+
#### prepareUpdate(instance, type, oldProps, newProps, rootContainer, hostContext)
64+
65+
> React calls this method so that you can compare the previous and the next props, and decide whether you need to update the underlying instance or not. If you need to update it, you can return an arbitrary object representing the changes that need to happen. Then in commitUpdate you would need to apply those changes to the instance.
66+
67+
prepareUpdate 的返回结果是 `commitUpdate` 的第二个入参。prepareUpdate 主要作用是render阶段计算出dom的哪些属性需要更新,这样在 commitUpdate 方法在commit阶段中可以快速更新dom,提高性能.
68+
69+
Taro: 对比 oldProps、newProps 计算出差异点,返回 `[prop1, value1, prop2, value2, ...]` .
70+
71+
### shouldSetTextContent(type, props)
72+
73+
> Some target platforms support setting an instance's text content without manually creating a text node.
74+
75+
Taro: taro 模拟的 text 支持直接赋值 textContent (`packages/taro-runtime/src/dom/text.ts`),该方法返回 false 即可.
76+
77+
> 因返回false,因此 resetTextContent 不会被调用
78+
79+
### createTextInstance(text, rootContainer, hostContext, internalHandle)
80+
81+
> Same as createInstance, but for text nodes. If your renderer doesn't support text nodes, you can throw here.
82+
83+
Taro: 模拟的 document 支持创建 text 节点, 返回 `document.createTextNode(text)` 即可.
84+
85+
### scheduleTimeout(fn, delay)
86+
87+
> You can proxy this to setTimeout or its equivalent in your environment.
88+
89+
Taro: setTimeout
90+
91+
### cancelTimeout(id)
92+
93+
> You can proxy this to clearTimeout or its equivalent in your environment.
94+
95+
Taro: clearTimeout
96+
97+
### noTimeout
98+
99+
> This is a property (not a function) that should be set to something that can never be a valid timeout ID. For example, you can set it to -1.
100+
101+
Taro: -1
102+
103+
### isPrimaryRenderer
104+
105+
> This is a property (not a function) that should be set to true if your renderer is the main one on the page.
106+
107+
Taro: true
108+
109+
### warnsIfNotActing
110+
111+
> Only used in development mode.
112+
113+
Taro: true
114+
115+
### supportsMutation
116+
117+
> If your target platform is similar to the DOM and has methods similar to appendChild, removeChild, and so on, you'll want to use the mutation mode.
118+
119+
Taro: true
120+
121+
### supportsPersistence
122+
123+
> If your target platform has immutable trees, you'll want the persistent mode instead.
124+
125+
Taro: false
126+
127+
### supportsHydration
128+
129+
> is support hydration
130+
131+
Taro: false
132+
133+
### getInstanceFromNode(hostRoot)
134+
135+
> 自定义获取 instance 的逻辑
136+
137+
Taro: 返回 null,React 内部会继续调用 `findFiberRoot` 方法获取.
138+
139+
### beforeActiveInstanceBlur
140+
141+
> flag enableCreateEventHandleAPI is false, never call it
142+
143+
Taro: 置空
144+
145+
### afterActiveInstanceBlur
146+
147+
> flag enableCreateEventHandleAPI is false, never call it
148+
149+
Taro: 置空
150+
151+
### preparePortalMount(containerInfo)
152+
153+
> This method is called for a container that's used as a portal target. Usually you can leave it empty.
154+
155+
Taro: 暂不支持 portal,置空即可
156+
157+
### prepareScopeUpdate
158+
159+
> flag enableScopeAPI is false, never call it
160+
161+
Taro: 置空
162+
163+
### getInstanceFromScope
164+
165+
> flag enableScopeAPI is false, never call it
166+
167+
Taro: 返回 null 即可
168+
169+
### getCurrentEventPriority
170+
171+
> The constant you return depends on which event, if any, is being handled right now.
172+
173+
Taro: 默认为点击事件,返回 `DefaultEventPriority` 即可
174+
175+
### detachDeletedInstance
176+
177+
> 当 fiber 被标记为 Deletion 时,dom被卸载后,从dom节点上删除 react 初始化的内容,如 __reactProps$xxxx 等. 详见[React源码](https://github.com/facebook/react/blob/973b90bdf6f4a1d9b3864d93985d4a204f233855/packages/react-dom-bindings/src/client/ReactDOMComponentTree.js#L54)
178+
179+
Taro: 因taro无法获取到 $xxxx 随机数,因此暂无法实现善后工作,该方法置空. 但可以根据字段是否 `__react` 开头来实现。
180+
181+
### supportsMicrotasks
182+
183+
> 是否支持微任务
184+
185+
Taro: true
186+
187+
### scheduleMicrotask
188+
189+
> 微任务方法 polyfill
190+
191+
Taro: 使用 promise.then 模拟实现,或直接使用 setTimeout
192+
193+
**以下字段因 `supportsMutation=true` 而需要被实现**
194+
### appendChild(parentInstance, child)
195+
196+
> This method should mutate the parentInstance and add the child to its list of children.
197+
198+
Taro: 调用 `parentInstance.appendChild(child)` 即可
199+
200+
### appendChildToContainer(container, child)
201+
202+
> Same as appendChild, but for when a node is attached to the root container.
203+
204+
Taro: 调用 `parentInstance.appendChild(child)` 即可
205+
206+
### commitTextUpdate(textInstance, prevText, nextText)
207+
208+
> This method should mutate the textInstance and update its text content to nextText.
209+
210+
Taro: 赋值 `textInstance.nodeValue` 即可
211+
212+
### commitMount(instance, type, props, internalHandle)
213+
214+
> This method is only called if you returned true from finalizeInitialChildren for this instance.
215+
216+
Taro: 因 `finalizeInitialChildren` 返回 false,该方法置空
217+
218+
### commitUpdate(instance, updatePayload, type, prevProps, nextProps, internalHandle)
219+
220+
> This method should mutate the instance according to the set of changes in updatePayload.
221+
222+
Taro: 根据 updatePayload,将属性更新到 instance 中,此时 updatePayload 是一个类似 `[prop1, value1, prop2, value2, ...]` 的数组
223+
224+
> 如果 updatePayload 为 null,该方法在 React 内部不会被调用
225+
226+
### insertBefore(parentInstance, child, beforeChild)
227+
228+
> This method should mutate the parentInstance and place the child before beforeChild in the list of its children.
229+
230+
Taro: 调用 `parentInstance.insertBefore(child)` 即可
231+
232+
### insertInContainerBefore(container, child, beforeChild)
233+
234+
> Same as insertBefore, but for when a node is attached to the root container.
235+
236+
Taro: 调用 `container.insertBefore(child)` 即可
237+
238+
### removeChild(parentInstance, child)
239+
240+
> This method should mutate the parentInstance to remove the child from the list of its children.
241+
242+
Taro: 调用 `parentInstance.removeChild(child)` 即可
243+
244+
### removeChildFromContainer(container, child)
245+
246+
> Same as removeChild, but for when a node is detached from the root container.
247+
248+
Taro: 调用 `container.removeChild(child)` 即可
249+
250+
### resetTextContent(instance)
251+
252+
> If you never return true from shouldSetTextContent, you can leave it empty.
253+
254+
Taro: 该方法置空
255+
256+
### hideInstance(instance)
257+
258+
> This method should make the instance invisible without removing it from the tree.
259+
260+
Taro: `display` 样式置为 `none` 即可
261+
262+
### hideTextInstance(textInstance)
263+
264+
> Same as hideInstance, but for nodes created by createTextInstance.
265+
266+
Taro: `textInstance.nodeValue = ''` 即可
267+
268+
### unhideInstance(instance, props)
269+
270+
> This method should make the instance visible, undoing what hideInstance did.
271+
272+
Taro: `display` 样式重置即可
273+
274+
### unhideTextInstance(textInstance, text)
275+
276+
> Same as unhideInstance, but for nodes created by createTextInstance.
277+
278+
Taro: `textInstance.nodeValue = text` 即可
279+
280+
### clearContainer(container)
281+
282+
> This method should mutate the container root node and remove all children from it.
283+
284+
Taro: 将 `container` 所有孩子节点 `nodeValue` 置空即可

packages/taro-react/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@
2626
"dependencies": {
2727
"@tarojs/runtime": "workspace:*",
2828
"@tarojs/shared": "workspace:*",
29-
"react-reconciler": "0.27.0",
30-
"scheduler": "^0.20.1"
29+
"react-reconciler": "0.27.0"
3130
},
3231
"devDependencies": {
3332
"react": "^18.2.0"

packages/taro-react/rollup.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const baseConfig = {
1414
exports: 'named'
1515
}
1616
],
17-
external: ['@tarojs/runtime', 'scheduler', 'react-reconciler', '@tarojs/shared'],
17+
external: ['@tarojs/runtime', 'react-reconciler', '@tarojs/shared'],
1818
plugins: [
1919
ts(),
2020
buble()

packages/taro-react/src/props.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,36 @@ function isEventName (s: string) {
1010
const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i
1111

1212
export function updateProps (dom: TaroElement, oldProps: Props, newProps: Props) {
13+
const updatePayload = getUpdatePayload(dom, oldProps, newProps)
14+
if(updatePayload){
15+
updatePropsByPayload(dom, oldProps, updatePayload)
16+
}
17+
}
18+
19+
export function updatePropsByPayload (dom: TaroElement, oldProps: Props, updatePayload: any[]){
20+
for(let i = 0; i < updatePayload.length; i += 2){ // key, value 成对出现
21+
const key = updatePayload[i]; const newProp = updatePayload[i+1]; const oldProp = oldProps[key]
22+
setProperty(dom, key, newProp, oldProp)
23+
}
24+
}
25+
26+
export function getUpdatePayload (dom: TaroElement, oldProps: Props, newProps: Props){
1327
let i: string
28+
let updatePayload: any[] | null = null
1429

1530
for (i in oldProps) {
1631
if (!(i in newProps)) {
17-
setProperty(dom, i, null, oldProps[i])
32+
(updatePayload = updatePayload || []).push(i, null)
1833
}
1934
}
2035
const isFormElement = dom instanceof FormElement
2136
for (i in newProps) {
2237
if (oldProps[i] !== newProps[i] || (isFormElement && i === 'value')) {
23-
setProperty(dom, i, newProps[i], oldProps[i])
38+
(updatePayload = updatePayload || []).push(i, newProps[i])
2439
}
2540
}
41+
42+
return updatePayload
2643
}
2744

2845
// function eventProxy (e: CommonEvent) {

0 commit comments

Comments
 (0)