Skip to content

Commit

Permalink
276
Browse files Browse the repository at this point in the history
  • Loading branch information
ascoders committed Mar 27, 2023
1 parent 5003106 commit c76fe87
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

前端界的好文精读,每周更新!

最新精读:<a href="./可视化搭建/275.%E7%BB%84%E4%BB%B6%E5%80%BC%E6%A0%A1%E9%AA%8C.md">275.组件值校验</a>
最新精读:<a href="./可视化搭建/276.keepAlive%20%E6%A8%A1%E5%BC%8F.md">276.keepAlive 模式</a>

素材来源:[周刊参考池](https://github.com/ascoders/weekly/issues/2)

Expand Down Expand Up @@ -309,6 +309,7 @@
- <a href="./可视化搭建/273.%E7%BB%84%E4%BB%B6%E5%80%BC%E4%B8%8E%E8%81%94%E5%8A%A8.md">273.组件值与联动</a>
- <a href="./可视化搭建/274.%E5%AE%9A%E4%B9%89%E8%81%94%E5%8A%A8%E5%8D%8F%E8%AE%AE.md">274.定义联动协议</a>
- <a href="./可视化搭建/275.%E7%BB%84%E4%BB%B6%E5%80%BC%E6%A0%A1%E9%AA%8C.md">275.组件值校验</a>
- <a href="./可视化搭建/276.keepAlive%20%E6%A8%A1%E5%BC%8F.md">276.keepAlive 模式</a>

### SQL

Expand Down
58 changes: 58 additions & 0 deletions 可视化搭建/276.keepAlive 模式.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
由于 React 的特点,组件改变所在父级后会产生 Remount,而在可视化搭建场景存在两个特点:

1. 自由、磁贴、流式布局都可以通过拖拽轻松改变组件父元素。
2. 大数据量下组件 Remount 的消耗不容忽视。

结合上面两个特点,拖拽过程中或者松手时不可避免会产生卡顿,这就是我们这篇文章要解决的问题。

## 利用 createPortal 解决 Remount 问题

[createPortal](https://react.dev/reference/react-dom/createPortal) 可以将 React 实例渲染到任意指定 DOM 上,所以我们利用这个 API,将组件树的组件打平,但通过 createPortal 生成到嵌套的 DOM 树上,就同时实现了以下两点:

- 在 dom 结构上依然符合组件树的嵌套描述。
- 在 React 实例角度,没有嵌套关系。

实现分为三步:

1. 遍历组件树,根据组件树嵌套结构生成 createPortal 的目标 dom,我们姑且称为 keepElement,对需要挂载 keepElement 的容器位置生成 dom,称为 keepContainer。对于没有渲染的容器,可以先不挂载 keepElement,而是等到父容器 mount 后再将 keepElement 移过去,后面再展开说明。
2. 遍历组件树,一次性打平渲染所有树中 React 组件实例,并利用 createPortal 挂载到对应的 keepElement 上。
3. 当数据流产生变化导致父级变化,或者布局插件拖动改变父级时,我们仅利用 dom api 将 keepElement 在不同的 keepContainer 之间移动,而在 React 实例视角没有发生任何变化。

<img width=500 src="https://s1.ax1x.com/2023/03/25/ppDLFSO.png">

## 协议做到用户无感知

因为实现了 dom 结构与 React 实例结构分离,因此开启 keepAlive 模式不需要改变 `componentTree` 描述,也不会影响任何逻辑功能,我们只需要标记一下 `keepAlive` 参数即可开启:

```jsx
import { createDesigner } from 'designer'

const { Designer, Canvas, useDesigner } = createDesigner()

const App = () => {
<Designer keepAlive={true} />
}
```

## 渲染增加了额外 dom 嵌套

keepAlive 模式唯一对功能产生的影响是增加了额外 dom 嵌套,分别是 keepContainer 与 keepElement,产生这两层 dom 的原因分别是:

- keepElement: 因为 React 实例 Remount 的作用范围是该组件自身 return 的所有虚拟 dom 最终映射的真实 dom,为了保证 React 映射 dom 与 React 树结构的对应,为了不产生 Remount 就必须要用额外的游离态 dom 作为 createPortal 的挂载节点。
- keepContainer: 由于不仅要知道组件产生移动时,应该将 keepElement 移动到哪个 keepContainer 下,还需要在比如容器代码 `return children` 位置突然 `return null` 并恢复时,重新构建 keepElement,所以我们需要监听每一个 keepContainer 生命周期,所以需要额外生成一个 dom。

因此 keepAlive 模式势必会打乱原有应用的 dom 结构,新增的 dom 结构在比如流式布局时可能产生意外的定位错误,所以 keepAlive 模式尽量与绝对定位的布局方式结合。

## 总结

keepAlive 模式可以在不改变任何协议、应用代码的情况下,解决跨父级移动导致的 Remount 问题,但这种设计也会引入新增 dom 结构的问题,只要尽量采用绝对定位的布局策略,就可以避免负面影响。

> 讨论地址是:[精读《可视化搭建 - keepAlive 模式》· Issue #475 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/475)
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**

> 关注 **前端精读微信公众号**
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">

> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)

0 comments on commit c76fe87

Please sign in to comment.