神刀安全网

Houdini:CSS 领域最令人振奋的革新

想要使用某种 CSS 特性,但是因为浏览器兼容性问题而没法使用?更糟糕一点,所有浏览器都支持这种特性,但支持度不完全,在某些情况下会有 bug 出现、支持性不一致,甚至于完全不兼容。如果你曾经遇到过上述情况(我肯定你一定遇到过),那你得好好关注 Houdini

Houdini 是 W3C 新成立的一个任务小组,它的终极目标是实现 css 属性的完全兼容。Houdini 提出了一个前无古人的的设想:开放 CSS 的 API 给开发者,开发者可以通过这套接口自行扩展 CSS,并提供相应的工具 允许开发者介入浏览器渲染引擎的样式和布局流程中

但是……这意味着什么呢?这个方案靠谱吗?不管是现在还是将来,Houdini 对开发者进行万展开发有什么帮助呢?

我将在后面的文章里尽可能地回答上面这三个问题。但在开始回答之前,要先搞清楚我们现在面临的问题是什么,以及为什么出现 Houdini 这样的解决方案。弄明白问题是什么之后,我再告诉大家 Houdini 将会如何解决这些问题,以及它目前的进展。最后,开发者们,你们还能了解具体如何做才能让 Houdini 尽早落地。

Houdini 能解决什么问题?

写文章也罢做 demo 也罢,每一次当我想要炫耀点 CSS 新花样的时候,总有人会说“这效果真是屌炸天!然而我们现在并不能好好用它,至少等个 10 年吧”。

虽然每每我都觉得这样的评论让人生气又没啥建设性,但依然承认大家的担心有理有据。纵观 CSS 历史,每一份特性草案都是经过了许多年才被广泛应用。而之所以会是“许多年”,就是因为要允许一个新特性被写入 CSS 标准需要经过一整套标准制定流程,然而就过了很多年……

Houdini:CSS 领域最令人振奋的革新

我对于这个标准制定的流程肯定是毫无异议的,但你得承认,走完整个流程花儿都谢了。

flexbox 大概就是最好的例子,2009年,关于 flexbox 的草案第一次被提出,但直到今天开发者们还在抱怨着这个属性的浏览器兼容性问题。不过感谢上帝,随着现代浏览器的自动更新机制,这个问题总有解决的那一天。但是,按照现在的新属性发布流程,最新的浏览器也会和新属性提案仍然存在一个时间差。

不过同样在 web 世界里,对于如今的 JavaScript 来说好像不是什么事儿了:

Houdini:CSS 领域最令人振奋的革新

在上面的流程图中,我们可以看到从想到一个新的 js 特性到运用在生产环境,大概只需要几天时间。讲真,我已经在使用 asyncawait 了,然而目前没有浏览器天生支持这两个方法吧。

估计现在你也大概感受到了 CSS 社区和 JS 社区的画风不同了:在 JS 社区里,你总能听到人们在抱怨它一天一个样 -- 跑得太快追得太累;而 CSS 社区,人们却在叹息着花精力去学习新事物是件多么吃力不讨好的事儿 -- 天知道什么时候才能用上新玩意呢。

既然如此,为什么我们不能用上 CSS Polyfill ?

脑海里闪过的第一个解决方案就是 CSS polyfill,只要 CSS polyfill 足够强大,相信 CSS 的发展速度赶超 JavaScript 不是梦。

然而,给 CSS 打补丁做起来有多难你都想不到,而且在大部分情况下,只要这么做了,性能肯定会受到影响。

JavaScript 是一种动态语言,它是如此之“动态”以至于有着让人惊叹的可扩展性,所以用 JavaScript 给 JavaScript 打补丁是可以轻松实现的,但 CSS 不是动态的呀。在某些情况下,你可以在构建过程中将一种形式的 CSS 转译成另一种形式( PostCSS 就是一个典型的例子)。而一旦你的补丁是依赖于的 DOM 结构、某一个元素的布局或者它的定位的话,那补丁程序就需要在本地运行了。

不幸的是,要在浏览器中实现这种方案挺难的。

让我们来看看一个 HTML 文档从被浏览器接收到显示在屏幕上的全过程,下面这张图里被标为蓝色的部分就是 JavaScript 可以染指的环节了:

Houdini:CSS 领域最令人振奋的革新

作为开发者,看着这张图心都凉了,我们根本控制不了浏览器解析 HTML 和 CSS 的过程,只能看着它生成 DOMCSS object model (CSSOM)。没法控制级联(cascade)、没法控制浏览器布局元素的方式(layout)、也没法控制元素在屏幕上显示的过程(paint)、最后的合成(composite)也无能为力。

整个过程中,开发者能完全控制的唯一环节就是 DOM,另外 CSSOM 也可以部分控制到。即使如此,引用 Houdini 官网上的话来说,这种程度的暴露是“不确定的、兼容性不稳定的以及缺乏对关键特性的支持的”。

举个例子,浏览器中的 CSSOM 是不会告诉你它是如何处理跨源的样式表的,而且对于浏览器无法解析的 CSS 语句它的处理方式就是不解析了,也就是说——如果你要用 CSS polyfill 让浏览器去支持它尚且不支持的属性,那就不能在 CSSOM 这个环节做,只能自行解析一遍 DOM 树,找到 <style><link rel="stylesheet"> 标签,获取其中的 CSS 样式、解析、重写,最后再加回 DOM 树中。

DOM 树都刷新了,得,渲染页面步骤重新走一遍。

Houdini:CSS 领域最令人振奋的革新

(上图括号里面的文字:JavaScript Polyfills 只能去更改 DOM 和 CSSOM,大部分这样的操作,都会导致页面重新渲染。)

好吧,可能你会说这种别无选择的方法,也并不会对性能造成多大伤害(对某些网站来说,是的),但想想这个重渲染过程会多么频繁地发生,如果你的 polyfill 是需要应对页面上的所有交互呢?scroll 事件、窗口缩放、鼠标移动还有键盘输入……随时都会被触发的重渲染会把页面拖得无敌慢,用户绝对会发现的。

雪上加霜的是……大部分的 CSS polyfills 都是各有各的解析器和层级逻辑,而且“解析器”和“层级逻辑”又是两个非常复杂的东西,所以这些 polyfills 不是文件太大就是有太多 bug。

简要概括一下上面的内容:如果你想要浏览器做出它本来做不到事情(比如让它解析你给的样式,不管它能不能实现该样式),而渲染流程里你无法插手其他步骤,所以只能通过手动更新和改变 DOM 的方式。

如果我想要更改浏览器内部的渲染引擎呢?

我认为,这个问题是这篇文章的关键所在,如果你草草略过了前文,千万要在这里停下!仔细看这部分!

在看完上面那一节之后,我确定有些人干脆因噎废食地想“我不需要这个!我只想要中规中矩地写页面,并不想侵入浏览器内核然后实现一些特别前卫的效果”。

如果你是这么想的,那我强烈建议你看看自己这些年用于实现页面效果的技术。我们想要“干涉”浏览器解析样式的目的并不仅仅是为了炫技,更是为了框架作者以及开发者们能做到下面两件事情:

  • to normalize cross-browser differences,
  • 统一跨浏览器行为,
  • to invent or polyfill new features so that people can use them today.
  • 开发新特性或者给新特性打补丁,让人们可以立刻使用到新特性。

如果你曾使用过像 jQuery 那样的 JS 库,那你已经从中受惠了!事实上,良好的兼容性正是绝大多数前端库活着框架的卖点之一。Github 上受欢迎度排名前五的 JavaScript 和 DOM 仓库 — AngularJS、D3、 jQuery、 React 还有 Ember,面对使用它们的人来说,只要搞明白如何使用那些 API,就能成功达到想要的目的了,但是它们背后,在兼容各浏览器上下了多少功夫,恐怕是使用者几乎从未考虑过的。

现在,想想 CSS 在跨浏览器上的问题。甚至像 Bootstrap 或者 Foundation 这样宣称兼容性良好的 CSS 框架也都没有把浏览器 bug 标准化,而只是尽量去避免它们。不要以为 CSS 的兼容性问题只是个老毛病,就拿 flexbox 来说,我们也还面对着各种各样的 跨浏览器兼容问题 。(译者注:flexbox 的兼容问题,现在在主流的移动端页面开发上,已经有所缓解,译者曾整理过一个 gist 用于移动端 html 5 页面的 flexbox 效果,欢迎使用纠错)

试想一下,你可以随心所欲地使用想用的 CSS 属性,在每个浏览器上,你的页面长得和你设想的一样(,这职业生涯得过的多欢脱啊。你看到的那些酷炫的属性都能在 保证性能的前提下 使用,比如网格布局(  CSS grids )、对齐( CSS snap points )还有 sticky 定位 …… 而要实现这一切,你只需要把代码从 Github 上下载下来而已。

好吧,上面描绘的是 Houdini 的蓝图,Houdini 小组想要将这个梦想实现。

也许你从未想过写个 CSS polyfill 或者开发一些实验性的特性,但你可能会希望其他人能做这些事情,毕竟一旦有了这些工具,受益的可是全部开发者啊。

Houdini 的目前进展

在前面,我曾提到过开发者是很难干涉浏览器的渲染过程的,除了 DOM 和 CSSOM 这两个环节外。

Houdini 小分队为了解决这个问题引入了一些新的标准,首次给了开发者介入另外几个渲染环节的权限。下面这张图片展示的是每个环节对应的新标准,开发者可以用这些标准来控制对应的环节。(注意:灰色区块是还在实现中的标准,目前暂时无法使用。)

Houdini:CSS 领域最令人振奋的革新

在接下来的几节内容中,我将大概介绍一下每一种新规范以及它们能做到什么,其他规范这里就不再赘述了,感兴趣的朋友可以看 这里

CSS Parsing API

CSS Parser API 还没有被写入规范,所以下面我要说的内容随时都会有变化,但是它的基本思想不会变:允许开发者自由扩展 CSS 词法分析器,引入新的结构(constructs),比如新的媒体规则、新的伪类、嵌套、 @extends@apply 等等。

只要新的词法分析器知道如何解析这些新结构,CSSOM 就不会直接忽略它们,而是把这些结构放到正确的地方。

CSS 属性与值 API

我曾 提过 CSS 已经有自定义属性了,这太让人兴奋了,CSS 将解锁多少新技能啊。 CSS Properties and Values API 的出现进一步推动了自定义属性,它还允许自定义属性添加不同的类型,大大增强了自定义属性的能力。

在自定义属性中加入不同的类型是很棒啦,不过这个 API 最大的的卖点是,开发者可以在自定义属性上做!动!画!而仅凭现在的技术,我们是做不到的……

来看看这个例子:

body {   --primary-theme-color: tomato;   transition: --primary-theme-color 1s ease-in-out; } body.night-theme {   --primary-theme-color: darkred; } 

night-theme 类被加到 <body> 元素上之后,页面所有有 --primary-theme-color 的元素属性值都会慢慢从 tomato 变成 darked 。如果今天你想要在自己的页面上实现这个效果,那就需要费劲儿的一个个给元素添加过渡动画。

译者注:为什么我满脑子想的都是性能,页面全部重绘似乎是不可避免得了,毕竟从 body 元素的颜色都换了嘛。不过话又说回来,主题都换了,reflow 也是理所应当的

这个 API 的另一个卖点是注册一个 “apply hook”,也就是开发者可以在 cascade 步骤结束的时候,通过一个钩子更改一个元素的自定义属性值,这个特性对于 polyfills 开发可是很有意义的。

CSS Typed OM

你可以把 CSS Typed OM 视为 CSSOM 2.0,它的目的在于解决目前模型的一些问题,并实现 CSS Parsing API 和 CSS 属性与值 API 相关的特性。

提升性能是 Typed OM 的另一重任。将现在 CSSOM 的字符串值转成有意义的 JS 表达式可以有效的提升性能。

CSS Layout API

开发者可以通过 CSS Layout API 实现自己的布局模块(layout module),这里的“布局模块”指的是 display 的属性值。也就是说,这个 API 实现以后,开发者首次拥有了像 CSS 原生代码(比如 display:flexdisplay:table )那样的布局能力。

让我们来看一个用例,在 Masonry layout library 上大家可以看到开发者们是有多想实现各种各样的复杂布局,其中一些布局光靠 CSS 是不行的。虽然这些布局会让人耳目一新印象深刻,但是它们的页面性能往往都很差,在一些低端设备上性能问题犹为明显。

CSS Layout API 暴露了一个 registerLayout 方法给开发者,接收一个布局名(layout name)作为后面在 CSS 中使用的属性值,还有一个包含有这个布局逻辑的 JavaScript 类。假如你想要用这个方法定义一个 masonry 的类,可以这么写:

registerLayout('masonry', class {   static get inputProperties() {     return ['width', 'height']   }   static get childrenInputProperties() {     return ['x', 'y', 'position']   }   layout(children, constraintSpace, styleMap, breakToken) {     // Layout logic goes here.   } } 

如果上面这个例子你看不明白也用不着担心。关键在下面的代码,当你下载好 masonry.js 后,将它加入你的站点,然后这么来写 CSS 你就能得到一个 masonry 布局的样式了:

body {   display: layout('masonry'); } 

CSS Paint API

CSS Paint API 和上面说到的 Layout API 非常相似。它提供了一个 registerPaint 方法,操作方式和 registerLayout 方法也很相似。当想要构建一个 CSS 图像的时候,开发者随时可以调用 paint() 函数,也可以使用刚刚注册好的名字。

下面这段代码展示时如何画一个有颜色的圆型:

registerPaint('circle', class {   static get inputProperties() { return ['--circle-color']; }   paint(ctx, geom, properties) {     // 改变填充颜色     const color = properties.get('--circle-color');     ctx.fillStyle = color;     // 确定圆心和半径     const x = geom.width / 2;     const y = geom.height / 2;     const radius = Math.min(x, y);     // 画圆     ctx.beginPath();     ctx.arc(x, y, radius, 0, 2 * Math.PI, false);     ctx.fill();   } }); 

在 CSS 中可以这样使用它:

.bubble {   --circle-color: blue;   background-image: paint('circle'); } 

你将在页面上看到一个以蓝色圆形为背景的元素,它的类是 .bubble ,这个圆形背景将始终占满 .bubble 元素。

Worklets

你已经看过了一些和规范相关的代码(比如 registerLayoutregisterPaint ),估计现在你想知道的是,这些代码得放在哪里呢?答案就是 worklet 脚本(工作流脚本文件)。

Worklets 的概念和 web worker 类似,它们允许你引入脚本文件并执行特定的 JS 代码,这样的 JS 代码要满足两个条件:第一,可以在渲染流程中调用;第二,和主线程独立。

Worklet 脚本严格控制了开发者所能执行的操作类型,这就保证了性能。

复合滚动和动画

虽然关于 composited scrolling and animation 还没有官方的规范出来,但它可以算是 Houdini 项目中相当广为人知且颇被期待的特性之一。在设想中,这个 API 将会使得开发者能在合成器(compositor)的 worklet (而不是在主线程)中执行程序,还能更改一个 DOM 元素的属性,不过是不会引起渲染引擎重新计算布局或者样式的属性,比如 transformopacity 或者滚动条位置(scroll offset)。

开发者可以通过这个 API 创建高性能的滚动和输入动画,比如滚动头效果、视差效果。你可以在 Github 上看到更多这个 API 试图实现的 效果

虽说正式规范还没有确定,但 Chrome 已经在实验性工具中加上了它。事实上 Chrome 的工程师们正在使用这些 API 最终会暴露的语言基元(primitives)来实现 CSS snap pointssticky 定位 。这说明了什么?Houdini API 的性能已经足够说服 Chrome 在它之上实现新特性了。单单这一点应该就能说服一直在担心性能问题的的你了吧。

Surma 在 Youtube 上发布了一个模拟 Twitter 应用头部滚动效果的 demo ,源码可以在 这里 查看。

那么现在我们能做什么?

在开头我就说了,我认为所有的 web 开发者都应该关注 Houdini,这个项目将会大大改善我们的未来。即使你可能不会直接接触到 Houdini 规范,但你肯定也会间接享受到它为你带来的便利,毕竟很多特性将基于它被构建出来。

虽然所描绘的美妙未来暂时还不会到来,但它也不会有你想象中那么遥远。今年早些时候,所有主流浏览器厂商都派代表参加了 Houdini 在悉尼的线下会议,在那次会议上,厂商们对于 Houdini 的方向和进程基本都达到了一致。

相信从我说的这些话里,你应该能相信 Houdini 的到来只是时间问题,它一定会成为正式的规范。

浏览器也是软件之一,它当然不可能一次性把所有的特性全加上,肯定是有给特性划分重要程度的。而这个划分方式常常是取决于用户对某个特性的需求度。

所以如果你真的很在意浏览器上样式和布局的可扩展性,如果你真的很想活在 CSS 新特性一出就能直接用进项目的世界里,快去和浏览器的开发者团队联系,告诉他们你真的很需要 Houdini。

另一种参与方式是,把现在不容易或根本不可能实现的、但你希望有一天可以用 CSS 实现的效果列出来, W3C 有提供一个 用例文档 ,你可以向那个 repo 提 pr。如果有的浏览器没有提供那样的文档,那干脆你来帮他们新建一个!

Houdini 项目小组(也可以说是 W3C 的所有成员)真的非常希望听到全世界 web 开发者的声音。事实上很多开发浏览器的人他们本身并不是职业的 web 开发者,像 c++ 程序员是真的不容易明白 web 开发者的痛点啊。

他们需要我们!

原文参考资料

特别鸣谢 Houdini 小组成员 Ian Kilpatrick 和 Shane Stephens 帮我 review 这篇文章。

外刊君: “不用去硅谷,来打一场码农的翻身仗吧~”

Houdini:CSS 领域最令人振奋的革新

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Houdini:CSS 领域最令人振奋的革新

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址