Server Component 思想在应用中心的改良

原文:Server Component 思想在应用中心的改良[1]

如果你还不懂什么是 Server Component,请一定要看:

1.React Server Components[2]2.精读《React Server Component》[3]3.什么是Server Component?

背景

应用中心是一个许多场景都常见的需求。近段时间,React Server Component 逐渐火了起来。虽然暂时没法上生产,但也给应用的实现方式带来了一种可能。本文面向加密应用的场景,对 Server Component 的实现做个简单的改造和适配。

与 React 的区别是什么

方向不同

React Server Component 可能更重在优化渲染的性能。带来的好处如:

•可以减少 bundle 体积•搭配传统 SSR 解决服务端 renderToString 造成的拖慢 QPS 问题•可以直接在 Node 环境中访问后端,提升开发舒适度,解决网络 IO 速度慢的问题

于是 React 在从一开始设计编写的方向,就是打算开发一套全新的开发模式。但此模式也暴露出一些弊端。如:无法很优雅地存储 Server Component 的状态,起码这在当前的开发模式中是无法忍受的(在未来也无法忍受)

下文这套方案的初衷方向,是为了组件加密执行。Server Component 的 bundle 并不落前端浏览器,于是也不会存在源码泄露问题,从源头上解决了泄密问题。对于一些开放平台的收费应用,此方向有很棒的效果。

所以在设计此方案时,考虑在 Node 侧使用沙箱选择了屏蔽一些 Node 的 API(具体看下文),像fs、http等。同时又开放了一些自定义的 API 在里面。这些都是为特殊场景做的努力

技术方案的差别

对于状态管理,React Server Component 的初衷是希望组件不要拥有自己的状态,于是 hook 功能失效。但考虑到开发便捷性,下文方案还是选择了对组件树中的 hook 状态做了单独剥离出来,同时有安全存取的方案。

简述

Server Component 协调渲染器

作用

主渲染器的主要作用,是将对应的组件通过 Reconciler (协调渲染器) 生成 DSL(这里也可以简单地理解为vdom一类的东西,但不映射任何dom元素),接着返回给前端进行递归渲染。

举个例子,我的组件代码是下图这样编写。那么在经过 Reconciler 之后,会变成对应的DSL。返回给前端,拿到 DSL 之后,才可以进行自定义的递归渲染。

c167788559c53c8076b1f973d780f1f9.png
image.png

上图为一套简单的完整实现流程。可以看到不同于Server Component的是,将hook状态树完全独立地剥离了出来,解决了 Server Component 组件状态的麻烦问题。下面就让我们一步步来解析其实现原理

第一步:初始化渲染

在我们定义好一个组件之后,我们就可以将其与状态树进行组合,交给渲染器。在这一步,我们的 effects 只有一个:initialize 初始化。

effects 的含义是事件记录。例如初始化事件,或者按钮的onclick事件。

98c45f28d34310f0755615f3d05fab4a.png
image.png
a34627ffe03f63188cd7a9c7e9aeab85.png
image.png

在准备好后,就可以开始渲染了。

1. 生成fiber节点

我们将拿到的 effect,进行遍历组合执行,如果是 initialize 状态,第一步则将当前的组件转化为fiber节点。这里的fiber节点不仅存放了组件的类型、子组件fiber、key,还存放了当前组件hook的值。

520c31c46c41a5c04e32a8d70b4e0fbd.png
image.png
2. 生成 DSL

在拿到fiber之后,简单地递归生成DSL。这一步没什么好说的,代码很简单

868ac0c126d09693b7881cdf077838c4.png
image.png
3. 生成状态树

当然,生成DSL之后,还要生成一颗独立的状态树存放组件的状态。这里的实现也很简单:从fiber当中递归拿到对应的hook值,根据组件的key存放在JS对象当中

acbf24dded7b57bad42c190f5c7e6b16.png
image.png

到这一步,最后DSL的完整生成就结束了。这里贯穿全程的是一个 reconcilerState 实例。其作用是:

•存放当前批量 effect 的更新队列(如果有多个effect,可以存放在队列里面。甚至在执行effect的时候碰到新的 effect 产出)•标记更新队列开关•存放当前执行到的 fiber 节点

第二步:事件触发更新

这套技术方案与 Server Component 相比,最香的地方可能莫过于使用 hook 了。由于设计时独立出来一颗状态树,使得在组件中使用 hook 也存在可能。

1. 生成fiber节点驱动更新触发

这一步依然重复上面的 fiber 生成。但生成的同时,也会去检查当前的 effect 队列有没有匹配的事件。如果有,则触发事件:

48dfcb19a934fe156bb1f0a1710a1142.png
image.png
2. 执行事件

如果事件当中,碰到了有状态更新,则会调用之前定义的 useState 的更新事件。hook更新函数则会扔到effect队列中一条 action 执行事件。

在原本的 event 事件(按钮点击事件)流程走完后,会获得一条 action 事件(状态更新事件),这就是 hook 的更新事件

6bfba161ac652618550ad9c7268ece96.png
image.png
89236d19c2b4bc71210c7879f3dc7914.png
image.png
3. action 更新

这一步的状态,和初始化渲染要执行的函数逻辑是一样的。同样是构建fiber节点,创建 DSL,创建状态树。最后结束返回 DSL 到前端。

一些小心思
1. 组件key的生成

这里使用的是深度 + 层级数字自增的key。同时为了避免生成有问题,搭配对应的babel插件进行编译时添加。

2. hook为什么是对象而不是链表

考虑到数据需要进行传递,链表的数据打印后层级会很深,不便于数据进行序列化和反序列化,于是还是选择了对象拍平的方式存储值。

Node Sandbox

在 React Server Component 中,提倡组件可以直接访问后端,甚至通过Node Api操作db。但在开放场景的情况下,此操作是非常高危的,需要屏蔽一些模块访问

于是选择基于开源的 isolated-vm[4] 做沙箱方案。isolated-vm 是基于c++ 编写的 Node 原生模块。安全,多线程,还可以控制内存大小和超时时间,甚至可以接入 Chrome Devtools 做调试。

但有一个缺点:不支持模块化。不过也有方案解决。我们可以将上面的方案通过构建工具打包为一个整体的 bundle,然后再交给 Sandbox 执行。

fbdbde4d815d64d14c54231f4fe1502f.png
image.png

状态加密

对于hook的状态,这部分可以利用一些第三方加密服务进行存取。大体逻辑如下:

1.在服务端,将状态存储进加密服务中,返回密钥给前端2.前端再次发起事件请求,将密钥给后端3.后端带着密钥,将上一次状态从加密服务中取出。

缺点

目前来看,因为 Server Component 始终执行在 Node 端,所以概念上是无法操作 DOM 的。这也进一步受限了它的场景。不过可以通过在 Client Component 侧封装一些需要操作 DOM 的组件,例如拖动 Panel 等,在Server Component 定义,在前端渲染。

引用链接

[1] Server Component 思想在应用中心的改良: https://github.com/Janlay884181317/blog/issues/3
[2] React Server Components: http://www.ayqy.net/blog/react-server-components/
[3] 精读《React Server Component》: https://github.com/ascoders/weekly/blob/master/%E5%89%8D%E6%B2%BF%E6%8A%80%E6%9C%AF/193.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Server%20Component%E3%80%8B.md
[4] isolated-vm: https://github.com/laverdet/isolated-vm

167d9e0e16388e40fc654a4592e669bd.png

往期推荐

大厂面试过程复盘(微信/阿里/头条,附答案篇)

88bff4403fe793b48c82260c4bdb03c3.png

面试题:说说事件循环机制(满分答案来了)

c233b66abe2c6fd846d5141aaf7569f6.png

专心工作只想搞钱的前端女程序员的2020

c39050f5de5b5d6d275f392b290f9aef.png


最后

  • 欢迎加我微信,拉你进技术群,长期交流学习...

  • 欢迎关注「前端Q」,认真学前端,做个专业的技术人...

b61a60e0459e0c8f6831c687d02caa6d.png

56ac8d9d02eb948aa061b8d97176664e.png

点个在看支持我吧

15971b132b4152c8c301e07eb915ff38.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值