乐趣区

关于前端:浏览器要原生实现React的并发更新了

大家好,我卡颂。

要说 React 有什么其余框架没有的、举世无双的个性,那肯定是 并发更新。围绕并发更新,存在两个很有意思的景象:

  • 很多开发者据说过他
  • 很少开发者间接应用过他

这两个景象看似矛盾,其实很好解释 —— React18 之后的新个性,次要是面向下层框架的(次要是Next.js)。

换句话说,这些新个性(比方并发更新)次要是供框架集成,而不是开发者间接应用。

比方,并发更新的两个外围 API —— useTransitionuseDeferredValue,都是针对 视图切换 的场景。

而在前端交互中,最次要的 视图切换 场景就是 路由切换,所以蕴含路由性能的前端框架就会集成这两个API

而当初,一个试验性浏览器 API —— View Transitions API 将原生实现 视图切换 性能。

他到底有什么用?如果其余框架应用它,是不是能取得 React 同样的并发更新能力?

欢送围观朋友圈、退出人类高质量前端交换群,带飞

什么是视图切换?

不论是 View Transitions API,还是ReactuseTransition,都是为了进步 视图切换 场景下的用户体验。

什么是 视图切换?以 view-transitions Demo 举例。这是个简略的相册Demo,点击右边图片缩略图,左边会显示大图:

整个过程简略来说包含 3 个步骤:

  1. 点击缩略图
  2. 申请大图数据
  3. 大图申请胜利后,显示大图

从步骤 1 到 3 的过程就是个典型的 视图切换。整个过程有很多能够优化体验的中央,比方:

  1. 从旧图到新图的突变过渡成果
  2. 点击缩略图发动图片申请后,大图区域能够先显示旧图(而不是立即显示 loading 成果),待新图申请胜利后再过渡到新图

这里解释下第二点,对于切换类的交互,相比于 当视图切换时立即显示 loading 成果,待新视图加载实现后过渡到新视图 当视图切换时先显示旧视图,待新视图加载实现后过渡到视图 在提早不高的状况下体验会更好。

除了上述这些 体验优化的点,视图切换的实现还有很多细节须要思考,比方:

  • 如何解决新旧视图切换时的过渡成果?
  • 如何解决新视图加载时的 loading 成果?
  • 当正在申请新视图数据时(此时视图处在旧视图中),用户又对旧视图产生交互怎么办?
  • 视图切换时如何解决页面滚动地位、光标聚焦(focus)地位?
  • 对于应用屏幕阅读器的盲人,视图切换时阅读器会朗诵什么?

除此之外,不同场景下的 视图切换 实现细节也不同。比方,如何在切换页面时优化视图切换成果?

SPA(单页利用)呈现之前,网站通常是由多个页面组成。比方上面网站的每个Tab 栏,对应一个独立网页,其中:

  • bramus Tab对应 https://http203-playlist.netlify.app/with-bramus/
  • cassie Tab对应https://http203-playlist.netlify.app/with-cassie/

Tab 之间切换,浏览器会:

  • 卸载之前的页面
  • 申请新页面数据
  • 加载新页面

页面卸载 页面加载 之间的白屏间隙会造成屏幕闪动。

要优化这种场景下优化视图切换成果,以后惟一做法是利用 history API 接管路由操作,将网页变成一个SPA

既然 视图切换 是如此常见的需要,且有这么多须要思考的因素,那浏览器为什么不原生实现呢?

于是,View Transitions API应运而生。

以后 View Transitions API 不反对跨页面的视图切换,但将来会反对

View Transitions 的应用

View Transitions API 的应用很简略,只须要用 document.startViewTransition 包裹视图切换后的回调函数即可。

对于上述相册示例,回调函数的逻辑是 将 img 标签 src 属性更新为新图片地址

const transition = document.startViewTransition(() => {galleryImg.src = /* 新图片地址 */;});

剩下所有跟过渡相干的实现,都由 Transitions API 解决。

View Transitions 实现原理

在视图切换时,存在 2 个概念:

  • 切换前的旧视图
  • 切换后的新视图

当应用 View Transitions 后,会顺次做:

  1. 对页面进行截图,作为旧视图
  2. 执行传递给 document.startViewTransition 的回调
  3. DOM更新后,对更新后的页面进行截图,作为新视图
  4. 结构一棵代表过渡成果的伪元素树,挂载在根元素(html元素)下,构造相似如下:
::view-transition
└─ ::view-transition-group(root)
   └─ ::view-transition-image-pair(root)
      ├─ ::view-transition-old(root)
      └─ ::view-transition-new(root)

其中:

  • 旧视图保留在 ::view-transition-old(root)
  • 新视图保留在 ::view-transition-new(root)

对于上述相册示例,挂载的伪元素树结构如下:

之所以要挂载一棵伪元素树,次要是因为两个起因:

  1. 开发者能够对微元素利用 CSS 规定

比方,上述两个 保留了新 / 旧视图的截图 的伪元素,相似于 img 标签,开发者能够对他们利用CSS 动画,当新 / 旧视图切换时,实现自定义的过渡成果。

  1. 不便对整个页面中不同 视图切换 分组

比方,在上述相册示例中,视图切换的元素包含两局部:

  • 新 / 旧视图之间的切换(下图红框局部)
  • 新 / 旧图片名称的切换(下图绿框局部)

相册对应的 HTML 构造如下:

  • img标签对应视图局部(下图红框局部)
  • figcaption标签对应图片名称局部(下图绿框局部)

当咱们为 figcaption 元素设置不同的view-transition-name

figcaption {view-transition-name: figure-caption;}

会失去一棵新的伪元素树,其中 视图局部 图片名称局部 伪元素是分来到的:

通过给页面中不同 HTML 元素定义不同的 view-transition-name 属性,就能独立管制这个元素是视图切换时的过渡成果。

与 React 的区别

浏览器原生的 View Transitions APIReact中的 useTransition 相比,谁更弱小呢?

毫无疑问,前者更弱小。

这是因为,对于 View Transitions API,通过操作伪元素树,开发者能够自定义过渡成果(就像对img 元素应用 CSS 过渡动画 一样简略)。即便不应用 CSS Transition,应用JS Transition 也齐全没问题。

document.startViewTransition办法会返回一个 transition 对象实例:

const transition = document.startViewTransition(() => {galleryImg.src = /* 新图片地址 */;});

该实例蕴含了一系列视图切换生命周期对应的promise,比方:

  • ViewTransition.ready:伪元素树结构实现,筹备开始过渡时
  • ViewTransition.finished:过渡成果实现后,此时新视图曾经能够响应用户交互

而在 React 中,应用 useTransition 后,与其说实现的是 视图切换,不如说实现的是:

  1. 首先,实现状态的切换
  2. React外部再将状态变动映射到视图变动

实质来说,操作视图的是React,而不是开发者。所以,开发者很难管制过渡成果。

动效库 Framer 的作者认为,因为开发者很难管制并发更新的残缺生命周期,所以很难在并发更新时表白 animation 成果:

简略来说就是,开发者很难为并发更新定制过渡成果(用 CSSJS)。

总结

能够认为,View Transitions API是比 useTransition 形象水平更高、开发者可控性更高的 APIuseTransition 能实现的,他能够。useTransition不能实现的,他也能够。

要说毛病,View Transitions APIWeb 平台独有的,而 useTransition 依赖 React 外围的并发更新能力,是跨端的。

以后,View Transitions API的兼容性并不好:

然而,一旦他变成能够大规模应用的 API,那么其余前端框架只有接入他,就能轻松取得比React 消耗大量精力实现的useTransition(以及底层的并发更新个性)更弱小的视图切换能力。

退出移动版