CSS Typed Object Model

CSS

在 Houdini 实现的过程中(什么是 Houdini?),Chrome 已经在 66 中已经实现了一部分 CSS 样式的 Typed Object Modal 支持(支持的列表可以参考这里)。

实现之后,在 JavaScript 中就可以通过 window.CSS 对象上的各类属性 API,生成指定类型的 CSS 属性值。看一个简单的例子:

在以前的实现中,往往需要这么写代码:

const fontSize = +(element.style.fontSize.replace('px', ''));
element.style.fontSize = `${fontSize * 2}px`;
element.style.opacity = 0.1;

这样写,会存在几个问题:

  1. 读取和设置带单位数值的时候,需要在字符串和数字之间进行转化;
  2. CSS 的属性名称是用 - 连接的,但是在 CSSStyleDeclaration 中却需要写成小驼峰的形式(font-size 变成 fontSize);
  3. 如果设置违法的值,代码会默默失败,没有任何错误提示;
element.style.opacity = 0.1;
// no error! not success!
element.style.opacity = '?';
// output: 0.1
console.log(element.style.opacity);
  1. 即使设置的属性值是数字,但是实际拿到的时候,值又变成了字符串

如,上例中的 element.style.opacity,虽然设置的值是 1,但如果运行 typeof element.style.opacity 结果却是 string

element.style.opacity = 0.1;
// output: string
console.log(typeof element.style.opacity);

如果试图直接进行运算,则可能得不到预料中的结果。比如,下面的输出依然是 0.1 而不是 0.6,因为 element.style.opacity += 0.5 的结果是 0.10.5(字符串拼接),作为一个非法值,直接被浏览器抛弃了(见第三点)

element.style.opacity = 0.1;
element.style.opacity += 0.5;
// output: 0.1
console.log(element.style.opacity);

有了 CSS Typed Object Model 之后,代码可以改写成这样:

const fontSize = element.attributeStyleMap.get('font-size').value;
element.attributeStyleMap.set('font-size', CSS.px(fontSize * 2));
element.attributeStyleMap.set('opacity', 1);

不难看出,这样的写法,基本解决了上面提到的几个问题:

  1. 读取和设置带单位数值的时候,不再需要手动进行字符串和数值的转化。CSS.px 这个函数可以将数值转化成一个带单位的对象,用于给 attributeStyleMap 赋值。另外,由于这个值 toString 之后就是类似 16px 的字符串,因此也可以直接给 element.style.fontSize 进行赋值。同时,从 attributeStyleMap 中拿到的数据,也是带单位的对象,对象中的 value 就是数值,unit 是字符串,表示单位,不再需要手动解析;
  2. attributeStyleMap 的属性名称和 CSS 的属性名称是一致的,不需要像以前一样在 JavaScript 中手动改成小驼峰的写法;
  3. 如果设置了违法的值,代码会报错:
try {
  element.attributeStyleMap.set('opacity', '?');
} catch (e) {
  console.log(e);
}

以上代码会输出报错:TypeError: Failed to execute 'set' on 'StylePropertyMap': Invalid type for property

  1. 应该是数值的结果,拿到的时候也是数值,而不是字符串(因此数值计算也不会出错):
element.attributeStyleMap.set('opacity', 1);
// output: number
console.log(typeof element.attributeStyleMap.get('opacity').value);

当然,这里如果这么些,结果依然是数字:

element.attributeStyleMap.set('opacity', '1');
// output: number!
console.log(typeof element.attributeStyleMap.get('opacity').value);

另外,使用 CSS Typed OM 还有一些其他额外的好处,比如,浏览器不需要序列化和反序列化结果,因此性能更好(一个简单的性能检测可以查看这里,大概有 30% 左右的提升)。

更多更详细关于 CSS Typed OM 的介绍,可以参考 Google 的这篇 Blog

P.S. 目前,其他的浏览器支持情况依然不理想,可以参考 Is Houdini ready yet? 网站上最新的支持情况了解详情。就实际情况来看,可以在 Electron 3 (基于 Chrome 66,见这里)或以上版本使用,但暂时不建议在 Web 项目中引入。