这篇文章是关于 HTML 渲染的首屏优化的,
主要是针对 web app 这种相对交互较多的应用场景,
首屏优化主要为了改善用户对于页面的感知,
而服务端渲染(SSR)是 web app 优化首屏依赖的重要手段.
昨晚录了视频, 解释了一遍, 可以先看个大概:
http://weibo.com/1651843872/E...
为什么说渐进式渲染?
以往基于字符串拼接的模板引擎技术, 性能还可以, 但是前端不方便,
而现在基于 Virtual DOM 实现的后端渲染, 前端舒服, 后端却变慢,
原因是 Virtual DOM 难以做静态分析进行预编译, 最终难以提高性能.
另外组件级别 Caching 方案也不够成熟, 以后比较难确定有效果.
既然提高性能短期做不到, 那么考虑对服务端渲染的工作进行缩减.
比如说, 首屏渲染多少内容? 整页渲染性能低, 能不能只渲染部分,
比如说只渲染第一屏主体内容, 而下方或者更详细数据在客户端抓取.
就是说, 服务端渲染一部分, 客户端加载一部分, 从而做出效果.
当然, 并不是新的东西, 只是说这套方案在 Virtual DOM 上要重新实现一遍.
再说"渐进式", 上述过程"服务端/客户端"渲染工作分割的比例如何?
比如说定义 Level 0~5 的等级, 服务端渲染的部分可以有多种情况,
最主要的比如说 App Shell 情况, 也就是页面的静态框架部分,
那么渲染过程就是: 服务端渲染局部, 客户端渲染局部,
而且随着服务端性能提高, 可以分配更多工作到服务器上. 这样的"渐进",
怎样对渲染进行分割?
举个例子, 如果用纯数字的层级, 可以这样来判断服务端:
(def level 2)
(def html-result
(div {}
(if (> level 0)
(div {} (text "content 0"))
(if (> level 1)
(div {} (text "content 1"))
(if (> level 2)
(div {} (text "content 2"))
(if (> level 3)
(div {} (text "content 3"))
(if (> level 4)
(div {} (text "content 4")))))
而客户端拿到 level
之后, 可以计算差量, 算出缺失的部分,
这个其实就是 Virtual DOM 做 Diffing 的思路... 不难理解.
单纯从原理上, 这是行得通的, 也就是我视频当中展示的.
从具体的使用的场景, 不同的 Level 实际上对应不同的页面内容,
论坛是一个比较清晰的例子, 想象一个论坛:
网页的静态部分, HTML 固定的内容, 比如导航栏和底部
页面首屏的内容, 比如一个论坛的话题
页面首屏看不到的内容, 比如话题下面多少回复
切换路由才会显示的页面, 比如导航的另一个页面
对于这样的情况, 显然有若干种可行的渲染分割的方案:
全在客户端渲染
1, 2, 3 在服务端渲染, 4 等到用户点击从浏览器抓
1, 2 在服务器渲染, 评论由客户端加载
只有 1 在服务端渲染, 动态的数据全部由客户端抓取.
而这些方案对于服务端来说, 性能的开销各不相同, 形成一个梯度,
而最后一种情况, 服务端预编译页面就好了, 几乎没有渲染负担.
根据实际的场景, 可以有更多 Level 可以设计.. 只是没这么简单罢了...
怎样处理数据依赖?
数据依赖问题是复杂的一个原因, 可能还是比较重要的一个.
对于稍微复杂的单页面应用, 抓取数据可能会涉及多个接口,
考虑到前后端分离之类的因素, 很可能是跨机器去抓数据的,
当然这样也就需要注意减少抓数据等待, 以便尽快返回.
但是这中间有一些误区, 前端常用的办法是组件 didMount 开始抓数据,
而实际上, 如果想要合并请求, 就要处理多个组件的 didMount,
甚至存在嵌套的情况, 数据 A 加载完, 渲染, 然后需要加载 B, 结果就复杂了.
好一个点的办法是借助 router 或者其他的 DSL 分析出缺失数据,
比如 Falcor 当中可以借鉴的一些方案... 目前说不上多少.
处理数据依赖的问题在前端抓数据已经遇到, 服务端只是其中一个场景,
我个人来说现在没有满意的方案, 只是说简单情况强制写就算了.
这一点 GraphQL, Falcor 这样的方案也在思考中, 希望尽快有结果.
展望渐进式渲染
笼统一点讲, Gulp 可以预编译 HTML 的头部尾部, 也是渲染,
那么页面渲染的步骤就有, Gulp -> Server -> Client 三步了,
而 Client 中还有收到用户点击而产生的渲染,
因而这一串的渲染步骤其实是有很长的, 有很多的文章可以做.
希望我们会有足够灵活的方案, 来完成中间各种方式的处理.
现在呢只能算一个试验的方案, 目测各大框架都没有提供直接的支持...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。