大家好,我卡颂。
要说 React
有什么其余框架没有的、举世无双的个性,那肯定是 并发更新。围绕并发更新,存在两个很有意思的景象:
- 很多开发者据说过他
- 很少开发者间接应用过他
这两个景象看似矛盾,其实很好解释 —— React
18 之后的新个性,次要是面向下层框架的(次要是Next.js
)。
换句话说,这些新个性(比方并发更新)次要是供框架集成,而不是开发者间接应用。
比方,并发更新的两个外围 API
—— useTransition
和useDeferredValue
,都是针对 视图切换 的场景。
而在前端交互中,最次要的 视图切换 场景就是 路由切换,所以蕴含路由性能的前端框架就会集成这两个API
。
而当初,一个试验性浏览器 API
—— View Transitions API
将原生实现 视图切换 性能。
他到底有什么用?如果其余框架应用它,是不是能取得 React
同样的并发更新能力?
欢送围观朋友圈、退出人类高质量前端交换群,带飞
什么是视图切换?
不论是 View Transitions API
,还是React
的useTransition
,都是为了进步 视图切换 场景下的用户体验。
什么是 视图切换?以 view-transitions Demo 举例。这是个简略的相册Demo
,点击右边图片缩略图,左边会显示大图:
整个过程简略来说包含 3 个步骤:
- 点击缩略图
- 申请大图数据
- 大图申请胜利后,显示大图
从步骤 1 到 3 的过程就是个典型的 视图切换。整个过程有很多能够优化体验的中央,比方:
- 从旧图到新图的突变过渡成果
- 点击缩略图发动图片申请后,大图区域能够先显示旧图(而不是立即显示
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
后,会顺次做:
- 对页面进行截图,作为旧视图
- 执行传递给
document.startViewTransition
的回调 DOM
更新后,对更新后的页面进行截图,作为新视图- 结构一棵代表过渡成果的伪元素树,挂载在根元素(
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)
中
对于上述相册示例,挂载的伪元素树结构如下:
之所以要挂载一棵伪元素树,次要是因为两个起因:
- 开发者能够对微元素利用
CSS
规定
比方,上述两个 保留了新 / 旧视图的截图 的伪元素,相似于 img
标签,开发者能够对他们利用CSS 动画
,当新 / 旧视图切换时,实现自定义的过渡成果。
- 不便对整个页面中不同 视图切换 分组
比方,在上述相册示例中,视图切换的元素包含两局部:
- 新 / 旧视图之间的切换(下图红框局部)
- 新 / 旧图片名称的切换(下图绿框局部)
相册对应的 HTML
构造如下:
img
标签对应视图局部(下图红框局部)figcaption
标签对应图片名称局部(下图绿框局部)
当咱们为 figcaption
元素设置不同的view-transition-name
:
figcaption {view-transition-name: figure-caption;}
会失去一棵新的伪元素树,其中 视图局部 和图片名称局部 伪元素是分来到的:
通过给页面中不同 HTML
元素定义不同的 view-transition-name
属性,就能独立管制这个元素是视图切换时的过渡成果。
与 React 的区别
浏览器原生的 View Transitions API
与React
中的 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
后,与其说实现的是 视图切换,不如说实现的是:
- 首先,实现状态的切换
React
外部再将状态变动映射到视图变动
实质来说,操作视图的是React
,而不是开发者。所以,开发者很难管制过渡成果。
动效库 Framer
的作者认为,因为开发者很难管制并发更新的残缺生命周期,所以很难在并发更新时表白 animation
成果:
简略来说就是,开发者很难为并发更新定制过渡成果(用 CSS
或JS
)。
总结
能够认为,View Transitions API
是比 useTransition
形象水平更高、开发者可控性更高的 API
。useTransition
能实现的,他能够。useTransition
不能实现的,他也能够。
要说毛病,View Transitions API
是 Web
平台独有的,而 useTransition
依赖 React
外围的并发更新能力,是跨端的。
以后,View Transitions API
的兼容性并不好:
然而,一旦他变成能够大规模应用的 API
,那么其余前端框架只有接入他,就能轻松取得比React
消耗大量精力实现的useTransition
(以及底层的并发更新个性)更弱小的视图切换能力。