大家好,我卡颂。

要说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(以及底层的并发更新个性)更弱小的视图切换能力。