ice.js 3 已经正式发布,期待更多的业务来使用、验证,一起建设更好的用户体验。
ice.js 3 地址:https://v3.ice.work/
本文将简单介绍 ice.js 3 中已经落地和正在进行的一些体验优化策略,以供探讨。Chrome 在去年成立了一个名为 Aurora 的项目,深入主流的框架和工具,比如 next.js、nuxt、 angular、webpack 等,探索如何结合框架和工具,帮助 Web 开发者用更低的成本,构建出更好的体验。ice.js 虽不在这个项目之中,却也是在设计之初就秉持着这种理念,来实现整个框架的。无论是路由方案、数据加载方案、渲染方案,都是围绕着体验而设计。这是因为,我们都越来越意识到,框架掌控着整个应用的生命周期,是最适合从流程上来做出优化、提升体验的。好的体验,应该成为业务开发时的一个顺其自然的产物。反观目前,我们的 Web 体验则还是一种 开发者重参与
的模式,这种方式往往是:
- 先开发后治理,在完成业务开发,甚至上线后,再进行性能的评估,分析诊断优化点,实施相应的优化策略
- 对开发者要求高,需要每个开发者都能熟练分析各种性能问题,掌握各类场景的体验优化方案
- 不可持续,容易随着业务迭代又发生退化
如何改变现状,借助于框架设计,普遍提升业务的体验基线,而不必要求人人成为 Web 体验的专家,这正是 ice.js 3 在探索解决的问题。本文会简单介绍 ice.js 3 中已经落地和正在进行的一些体验优化策略,以供探讨。加载策略
▐既是 MPA 也是 SPA
ice.js 3 没有区分 MPA 和 SPA,而是对二者做了融合优化。这是因为传统的 MPA 和 SPA 都存在着一定的局限性:
- MPA 应用,为每一张页面都独立构建 HTML 和 JS Bundle,页面间资源不共享,页面跳转时,共用的资源被重复加载和执行。
- SPA 应用,所有页面复用一张 HTML 和 入口 JS,依赖于主 Bundle 进行路由匹配后,才加载对应的页面,整个过程是串行。
在 ice.js 3 中,我们对此做出了改进:
- 每张页面都会构建产生自己的 HTML ,并在 HTML 中引入了当前页面所需的资源,从而避免资源的串行加载。(构建产物类 MPA)
- 页面间跳转时,只会加载下一跳页面特有的 Bundle,避免了资源的重复加载。(资源加载逻辑类 SPA)
框架希望通过这种方式,来将默认的资源加载逻辑调到最优。
▐区块的并行化加载
在 ice.js 3 中,一张页面,可以由多个 布局组件
和 一个 页面入口组件
嵌套而成,也称为 嵌套路由
。针对这类场景,ice.js 应用了以下优化,来让页面达成更好的性能体验:
- 布局组件和页面入口的
资源
和数据请求
会被并行加载,以达到最快的资源加载速度。 - 路由间跳转时,比如从
/sales/recommends
跳转到/sales/favorites
,框架只会加载差异化的组件favorites.tsx
进行渲染,而不会重新渲染他们共用的布局组件
。
利用框架对 嵌套路由
所做的优化,我们可以将页面中逻辑相对分离的部分,用 嵌套路由
的方式来组织,以获得更好的加载体验。
例如,下面这种常见的移动端营销页,可以对页面内容进行拆分:
将顶部通用的
Slider
抽象为布局组件
将不同 tab 下对应的瀑布流,抽象为
路由组件
。
这样,Slider
和 瀑布流
就可以做到并行加载,并且当切换 tab 时,新的 tab 内容将由框架触发按需加载和渲染。
▐数据的并行化加载
在常规的 React 或 Rax 应用 中,数据请求一般都会在组件首次 useEffect
时发起。这种组织方式,数据依赖于业务 Bundle 的加载、解析、执行,会在页面完成首次渲染后才发起,请求的时机是非常滞后的。
在 ice.js 3 中,框架对页面数据加载的编码规范做出了约定,来最大限度的提前页面的数据加载时机。页面数据请求通过 dataLoader
声明后,会由框架(或容器)统一发起,和业务 Bundle 的加载解析是并行、不阻塞的。在手淘等 PHA 容器下,这种标准化的数据请求,还会被进一步提升为数据预请求,而无需额外的配置。
基于这种模式开发的 Web 应用,天然获得了更好的性能体验。
两种编码方式的请求时机对比如下:
▐云端结合的预请求
数据的预请求,已经是体验优化的一种主流手段,它将页面的数据请求和资源请求并行化起来。
但其实结合云和端,页面的 资源加载
和 数据加载
过程,可以被更彻底的并行化为下面的形式:实现更彻底的预请求的前提是:当容器获得一个页面的 URL 地址后,就能知道这个 URL 对应的 Assets、数据接口信息,然后直接发起这些资源的请求。目前,ice.js 正结合云(服务端)和 端(手淘容器)在尝试打通这条链路,以帮助业务获得更好的性能体验。渲染策略
▐默认 HTML 不再空白
常规的 CSR 应用,构建的 HTML 一般只包含一个容器节点,页面的初始状态是空白的,具体内容依赖 JS 的渲染。
然而,我们的页面内容,并不总是全部依赖于动态数据的,页面的基本框架结构常常都是静态的,这部分内容是完全可以在构建时就生成的。这样,页面的白屏时间将被大大缩短,用户体感会从【白屏->页面加载完成】转变为【静态页面->动态数据渲染完成】。
出于这个原因,ice.js 3 默认开启了 SSG
,会在构建时,执行页面渲染逻辑,预先构建好页面中不依赖于数据的静态部分。
出于这个原因,ice.js 3 默认开启了 SSG
,会在构建时,执行页面渲染逻辑,预先构建好页面中不依赖于数据的静态部分。
▐开箱即用的 SSR
SSR 作为体验优化的杀手锏,已在商品详情、用户增长、淘菜菜等业务中普遍使用和验证,可以预估在未来还会有着更广泛的应用。因此,ice.js 3 是将 SSR 作为主要的渲染模式来保障的。
ice.js 3 的 SSR 设计,延续了 Rax 中结合 Midway FaaS 的一体化模式,业务可以在一个工程中完成页面逻辑和渲染服务的维护,以 Serverless 的形式发布 SSR 应用。同时框架还内置了环境变量模拟、错误降级等常用逻辑,来尽可能降低 SSR 应用的开发成本。
▐页面内容的流式渲染
建设中
传统的 SSR 渲染是一个同步的过程,需要等到所有的数据请求都完成后,再一次性进行整张页面的渲染,然后把渲染完成的 HTML 字符串下发给浏览器。这种模式下,如果应用中某个依赖的数据接口耗时比较久,就有可能拖慢整张页面的渲染时间,反映到用户体感上就是页面的白屏时间过长。因此,对页面的内容分批、流式的返回,会是一种用户体感更好的方案。比如,页面的某个区块响应耗时比较久,就可以先渲染 Loading 状态,等待 Server 端完成这个区块的渲染后,再下发这部分的内容。
目前,ice.js 和上下游团队,正在基于 React 18 提供的流式渲染能力,在业务中探索面向业务的解决方案。
▐Zero-Bundle-Size 的探索
准备开工
Zero Bundle Size
是 React 在推 Server Components
时提出的一个概念,意思是在 Server 端完成渲染的组件,就不需要再向 Client 端下发 JS Bundle 了。目前社区流行的 Islands Architecture
也可以看做是类似的方案。
这是相对于现有的 SSR 应用而言的,我们知道,SSR 应用即使在 Server 端完成了整张页面的渲染,在 Client 端依然需要下发完整的 JS Bundle,重新执行一遍,来完成 Hydrate
,从而让页面达到可交互状态。
而在 Server Components 方案中,页面被划分为两类组件:
Server Component,只在 Server 端执行的组件,用于渲染静态内容
Client Component,只在 Client 端执行的组件,用于处理有交互的内容
浏览器侧,只需要加载 Client Component
对应的 JS Bundle 即可,可以预期的是页面的资源大小将会大大减小。以 React 官方的 Server Component Demo 为例,Client 端加载的 Bundle 大小从 498k
减少到了 178k
。Client 端资源减少,带来的直接收益是,页面的可交互时间将被大大提升。
这将会是 ice.js 在下一阶段的一个主要探索方向,期待通过这种模式,能进一步的提升业务的用户体验。
Server Components
地址:https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html
Islands Architecture
地址:https://www.patterns.dev/posts/islands-architecture/Server Component Demo 地址:https://github.com/reactjs/server-components-demo
小结
以上就是 ice.js 3 已经和正在建设的一些体验优化能力,可以简单小结为两个方向:
- 在加载策略上,优化了默认的页面资源加载策略,提供了区块和数据的并行化加载能力,同时还在探索如果结合云和端,建设更彻底的预请求方案。
- 在渲染策略上,提供了 SSG 能力以优化默认的 HTML 构建产物,在现有 SSR 能力的基础上,还将继续探索流式渲染、Server Components 等方案,进一步提升 SSR 应用的体验。
目前 ice.js 3 已经正式发布,期待更多的业务来使用、验证,一起建设更好的用户体验。
ice.js 3 地址:https://v3.ice.work/