[译] Houdini:也许是你不曾听过的最振奋人心的 CSS 进化

原文连接:Houdini: Maybe The Most Exciting Development In CSS You’ve Never Heard Of
更多译文将陆续推出,欢迎点赞+收藏+关注个人专栏,未完待续……css

你是否曾经想要使用一些特别的CSS特性,却由于不曾获得全部浏览器的支持而选择放弃?又或者是,这些特性获得了全部浏览器的支持,但总会伴随着奇怪的bug,表现不一致甚至相互矛盾?若是这些事情都曾发生在你身上——我敢打赌——你应该关注一下Houdini前端

Houdini是W3C的一项新的任务,其宗旨在于解决上面所说的问题。它计划经过提供一系列的API,使开发者得以自定义扩展CSS,并把这些样式直接放入浏览器的渲染引擎中渲染出来。git

但这事实上到底意味着什么?这真的是一个好主意吗?以及它会如何帮助咱们开发现代的和面向将来的页面呢?github

在这篇文章中,我将尝试回答上述问题。在此以前,明白当今存在什么问题,以及须要作出什么改变,是很是重要的。待会我将更加详细地介绍Houdini是如何解决问题的,并将列出一些在目前开发过程当中遇到的酷炫的特性。文章的最后,我会提供一些实实在在的能让咱们这些web开发者为Houdini成为现实的作法。web

Houdini想要尝试解决的是什么问题?

每当我撰写文章或者制做DEMO,用于展现一些新的CSS特性的时候,不可避免的总有人评论或者在Twitter留言说:“真是酷炫狂霸叼炸天!但糟糕的是在将来十年内咱们都没法使用它们。”segmentfault

就像上面那些负能量满满又毫无建设性的评论那样,我深觉得然。在历史上,新特性的草案每每通过多年才被广泛接受。正由于如此,而且在纵观web的发展史后,让新特性草案真正成为CSS标准的惟一办法就是让其走一遍标准流程。api

标准流程
标准流程中的每一步跨域

面对这所谓的标准流程我无力反抗,但必须认可这浪费了大量的时间!浏览器

比方说,flexbox第一次被提议是在2009年,但开发者们仍然在抱怨直到如今都没法使用它,由于只有少数浏览器支持这一特性。好在随着大部分浏览器都支持自动更新,这个问题正在慢慢得以改善;然而,即便拥有着现代浏览器,从草案到成为可用标准之间,依然存在着迟延。并发

有趣的是,这并不是是web开发中全部领域都出现的问题。让咱们看看Javascript是怎么作的:

js方案
Js中写polyfill的步骤

在这种状况下,一个方案从构思到在生产环境使用,每每只不过几天时间。个人意思是,在生产环境中我已经在使用async/await方法了,即便这个方法未被任何一个浏览器所支持!

你也能够感觉到这两个社区的巨大情绪差别。在Javascript社区中,你会看到一些抱怨JS发展太快的文章。与之相反的,在CSS社区你会听到一些感叹,在可以真正使用以前,学习任何新的特性都是徒劳的。

那么,为何咱们不本身去写一些CSS的polyfill呢?

乍这么一想,写CSS的polyfill彷佛是现成的答案。伴随着优秀的polyfill,CSS将会发展得跟Javascript同样快,对吗?

很惋惜,这并无那么简单。为CSS进行polyfill很是困难,更多的时候每每会毁掉全部的性能。

Javascript是一门动态语言,这意味着你能够用JS去polyfill它自身。也正由于它动态的特性,它是很是易于扩展的。从另外一个角度来讲,CSS几乎不能被本身所polyfill。在某些状况下,你能够经过构建的方法实现CSS的polyfill(POSTCSS就是干这个的);然而当你想要polyfill任何依赖于DOM结构的,或者某一元素样式或位置的东西的时候,你不得不在客户端运行你的polyfill逻辑。

不幸的是,浏览器很难实现这个需求。

下面的图片展现的是浏览器对于一个HTML文档从接收到渲染的基本过程。其中蓝色区域就是Javascript有能力做出控制的步骤:

xx
Javascript在浏览器渲染进程中的控制权

这幅图挺让人沮丧的。做为一个开发者,你没法控制浏览器是如何将HTML和CSS解析为DOM 和 CSS 对象模型(CSSOM)的;没法控制整个渲染过程;没法控制浏览器是如何选择把元素渲染到DOM上面的,或者如何把内容填充到屏幕上展示给用户;你也没法控制浏览器是如何排版的。

你惟一可以彻底控制的只有DOM这一块。在这种时候CSSOM是可用的;即使如此,引用Houdini网站的一句话,这是“蹩脚的,浏览器之间不一致的,缺少论证的特性。”

举个例子,在现在浏览器中的CSSOM,不会告诉你跨域样式表的规则,而且很容易就会抛弃掉它看不懂的CSS语法或者声明——这意味着当你想要polyfill一些浏览器不支持的特性的时候,你没法使用CSSOM。取而代之的,你只能手动遍历DOM,找到<style>或者(和)<link>标签,解析它,重写它,再把它从新添加回DOM。

固然,更新DOM意味着浏览器将会完整地从新进行一遍及局、绘制、排版的重绘过程。

x
使用Javascript在浏览器渲染阶段进行polyfill

当完整地渲染页面的时候也许并不会形成如此大的性能冲击(尤为对于一些网站来讲),不怎么需考虑发生的可能性。若是你的polyfill逻辑须要在事件响应中进行,好比滚动事件,窗口大小改变事件,鼠标移动事件,键盘事件——每时每刻都在发生这些事情的时候——性能方面的影响就会很是大,有时候甚至会卡顿,崩溃。

更糟糕的是,你会发现现在绝大多数的CSS polyfill都包含了它们本身的CSS解析器以及运行逻辑。与此同时,因为解析和运行都是很是复杂的事情,因此这些polyfill要么太大,要么太容易有bug。

综上所述,若是你但愿浏览器去作一些它并不懂如何去作的工做(好比使用你的自定义CSS),那么你必须假装一些指令给浏览器,能够经过手动更新和修改DOM来实现。除此以外你没有任何办法影响渲染过程当中的其余阶段。

既然如此,为何我没有想到修改浏览器内部的渲染引擎呢?

对我来讲,这个问题是这篇文章的关键。若是以前的内容你只是粗略浏览的话,那么请认真仔细地阅读接下来的这一段文字。

通过上一小节,我敢确定有部分读者已经在想:“我不须要这个!我只是在制做普通的页面而已。我并无准备把浏览器给黑了或者弄一些什么创意啊,实验啊,尖端产品之类的玩意儿。”

若是你这么想,我强烈建议你回顾一下这些年来你在开发时所用技术的发展史。但愿可以进入并修改浏览器样式渲染过程的想法并不是为了酷炫的demo——而是让开发者或者框架更大的权利去作两件主要的事情:

  • 消除定义的样式在浏览器之间的差别

  • 发明或者polyfill新的属性,从而让人们可以在今天就使用它们

若是你曾经使用过诸如jQuery的Javascript库,你已经从中获益了!事实上,这正是现在几乎全部前端库和框架的卖点。Github上的五个最受欢迎的Javascript和DOM操做框架——AngularJS,D3,JQuery,React和Ember——它们全都为浏览器兼容作了许多的工做,从而让你根部不须要考虑浏览器兼容性。它们都经过向外提供的API就能直接被使用。

如今,让咱们回到CSS以及它的跨浏览器兼容问题。即便是流行如Bootstrap和Foundation这两个声称主打兼容性的CSS框架也没法避免跨浏览器带来的bug——它们只是避开了bug而不是解决它。同时,CSS跨浏览器带来的bug不只仅是过去的事情。即时在今天,面对如flexbox之类的新特性,咱们仍然面临着跨浏览器带来的不一致性问题。

最后,想像一下若是你确信你能使用任何的CSS属性,而且在不一样浏览器中它们都能正常运行,你的开发体验将会多么温馨。再设想一下,任何你在博客、大会上听到的新特性——好比CSS grids,CSS snap points和sticky positioning,都可以经过某种方式像原生CSS同样获得完美运行,全部的一切只须要你从Github上复制一段代码,那将会多么美好。

这正是Houdini的目标,这正是其组织所为之奋斗的将来。

所以,即便你并不打算写CSS的polyfill或者开发一个实验性质的新特性,你也极可能会但愿别人去作这些——由于一旦这些polyfills被实现了,全部人都能从中获益。

在如今的开发中Houdini究竟是什么?

我在上文提到过,开发者仅有一点点权利去操做浏览器的渲染过程。确实,只有DOM和CSSOM可以被开发者操做。

为了解决这个问题,Houdini小组提供了一些新的方法,第一次让开发者得以进入到渲染过程当中的其余步骤。下面的图片展现的是渲染过程以及新的方法是如何被使用而修改当中的步骤的。(注意灰色部分的方法是计划中的但仍在修改的。)

xx
Houdini的新方法影响浏览器渲染过程的位置

接下来的几个小节将会简要介绍每个新方法及其所拥有的功能。我要说明的是,仍然有一些新方法并未收录到这篇文章当中,完整的方法列表请查阅GitHub repository of Houdini’s drafts

CSS属性-值API

CSS已经能够自定义属性了,正如我以前说的那样,对于这个可能性的实现我表示很是兴奋。CSS属性-值API更是让自定义属性向前迈了一步,使其在添加属性操做的时候更加实用。

有许多很棒的事情可以在自定义属性中实现,但最大的卖点也许是可以让开发者们在transition和animate当中,使用如今并不支持的自定义属性。

想像一下下面的例子:

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过渡到darkred。若是你想如今就这么作,你不得不手动地为每个元素添加这些过渡代码,由于你没法让他们自动完成这些工做。

这个API另一个已经肯定的特性是将容许注册一个“调用钩子”,一个容许开发者在渲染进程结束之后修改自定义属性最终值的方法,这个方法在polyfill的时候会很是有用。

CSS TYPED OM

CSS TYPED OM能被视为当前CSSOM的2.0版本。其目标是为了解决大量的关于现阶段的规范,以及包括经过新的CSS解析API和CSS属性-值API所带来的问题。

另一个目标是为了提高性能。把当前的CSSOM转化成有意义的JS表达式,可以带来大幅度的性能提高。

CSS LAYOUT API

CSS LAYOUT API容许开发者自定义布局模块。“布局模块”指的是任何带有CSSdisplay属性的元素。这将会是第一次为开发者带来性能媲美原生布局的方式,好比display: flexdisplay: table

做为实际使用的例子,Masonry layout library展现了开发者是多么想要实现复杂的使人惊叹的布局,却没法仅仅经过CSS来实现的情景。

不幸的是,他们被性能问题所困扰,尤为是在一些能力稍差的设备上。

CSS LAYOUT API提供一个registerLayout方法给开发人员,该方法接收一个布局的名字做为参数(一个接下来将会用在CSS当中的名字),以及一个包含着全部布局逻辑的Javascript类。这里展现的是一个经过registerLayoutAPI注册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而且一切都将正常运行:

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

CSS PAINT API

这个API很是像上文所说的LAYOUT API。它提供一个registerLayout方法。开发者可以在
CSS的任何地方使用paint()方法,接下来一张图片会被生成并放置在所注册的名称上面。
这里是一个简单的描绘了带有颜色的圆的例子:

registerPaint('circle', class {
  static get inputProperties() { return ['--circle-color']; }
  paint(ctx, geom, properties) {
    // Change the fill color.
    const color = properties.get('--circle-color');
    ctx.fillStyle = color;
    // Determine the center point and radius.
    const x = geom.width / 2;
    const y = geom.height / 2;
    const radius = Math.min(x, y);
    // Draw the circle \o/
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
    ctx.fill();
  }
});

在CSS中能够这么使用:

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

如今.bubble元素将会以一个蓝色的圆为背景被展现出来。无论发生什么,这个圆都会等大居中地放置在元素当中。

WORKLETS

前文的大部分的API都经过代码块来展现(好比registerLayoutregisterPaint)。若是你想知道这些代码应该放在哪里运行,答案是放在WORKLET脚本中。

Worklets就像web workers,它们容许你引用脚本文件,而且在渲染过程的任意时刻运行当中的Javascript代码,同时它也是独立于主线程的。

Worklet脚本严格制约着你可以进行的操做,这正是保证高性能的关键。

复合的Scrolling和Animation

即便到目前为止仍然没有关于composited scrolling and animation的官方说明,但这确实是Houdini众多特性中最广为人知并不是常有但愿实现的特性之一。最后的这个API容许开发者们把逻辑运行在脱离主线程的负责排版的worklet当中,同时也支持修改一个DOM元素子集的属性值。这个子集仅仅包含了可以被读写的属性,却不会强迫渲染引擎去从新计算布局或样式(举个例子,transform,opacity,scroll offset等。)

这将会容许开发者们去建立高性能的基于scroll-和input-的动画,好比sticky scroll headers and parallax effects。你能够在Github找到更多的关于这个API试图去解决的问题的例子

即便仍旧缺少官方文档,可是有经验的开发者们已经在Chrome浏览器中开始尝试。事实上,Chrome团队在最近已经开始基于这些最终将会发布的API来实现CSS snap pointssticky positioning。实在是振奋人心,由于这意味着Houdini API有着足够的性能,以致于让新的Chrome特性可以基于它们来实现。若是你还在担忧Houdini不如原生那么有效率,以上事实也许能够在某种程度上打消你的疑虑。

看看真实的例子吧。Surma发布了一个视频,展现了一个运行在基于Chorme的浏览器内部的demo。Demo模仿了原生Twitter APP的用户头像变化行为动画。想知道它是怎么实现的吗?请看源码

如今,你能够作些什么?

根据上面提到的,我以为任何一个web开发者都应该关心Houdini;它将会让咱们的开发生涯更加方便。即便你从未直接使用过Houdini,但你颇有可能已经使用过基于它而开发出来的东西。

尽管这个将来并不会立刻到来,但极可能比咱们想象的都更加接近了。全部主流浏览器供应商的表明们在今年年初汇集在最后一次的Houdini面对面交流会上,会上对于如何去创建并发展Houdini的问题,已经基本达成了共识。

我能说的,不是Houdini是否可以最终实现的问题,而是你们可以在什么时候,在何地可以参与进来的问题。

浏览器供应工商,就像任何一个软件开发者同样,必须重点发展新的特性。而且优先发展的每每是常常用户强调须要的新特性。

因此,若是你关心web开发中样式和布局的扩展,若是你想要直接使用新的CSS特性而无需通过漫长的等待,请告诉你所使用的浏览器的开发成员。

另外一个你可以出力的地方,就是开发一些真实可用的案例——好比去实现一些在现在难以被实现的样式或布局。在Github上有几个实际案例,你能够提交并发布pull request去贡献你的想法。若是文档不存在,你能够新建一个。

Houdini小组的成员(通常来讲叫W3C)很是期待来自开发者们的想法和建议。任何参与标准制定的都是浏览器开发工程师。他们一般不是专业的web开发者,这意味着他们并不是时刻清楚痛点在哪儿。

他们须要咱们去告诉他们。

参考资料

特别鸣谢Houdini的成员Ian Kilpatrick和Shane Stephens审核了这篇文章。


文章内容较多,未进行高质量的审核校对,仅以原文为准,若有错漏欢迎指出。不按期发布开发体验,学习心得,墙外干货,欢迎关注个人专栏。感谢你的阅读,我是Jrain,下次见!

相关文章
相关标签/搜索