Giter VIP home page Giter VIP logo

blog's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

规范里的词汇/术语

W3C 通过 status code 来表示 specifications 的 maturity。
CSS WG 使用以下状态码,从 least 到 most stable:

  • ED Editors' Draft 编辑草案(不是 W3C Technical Report)
  • FPWD First Public Working Draft 首个公开工作草案 ✔
  • WD Working Draft 工作草案 ✔(0 || 多个)
    • 不稳定,也不完整
    • 目的是:创建当前规范的一个快照,也能征求 W3C 和公众的意见
  • LC LCWD Last Call Working Draft
    • 工作组认为规范是完整的,且所有 issues 都是要解决的
    • 此规范将会转向CR,除非有重大问题提出
    • 此阶段是 others 提交 issues 的最后机会,在变成CR之前
  • CR Candidate Recommendation 候选推荐标准 ✔✔
    • 所有的已知 issues,都被解决了
    • 向 implementor 征集实现
    • AC正式审查,可能有三种结果:成为标准、返回工作组继续完善、废弃
      • 此阶段的很有可能成为标准,且如有改动,则需给出改动原因
  • PR Proposed Recommendation 提案推荐标准 ✔✔
    • 要从CRPR,需要全面的 test suite 和实现报告,以证明每个特性都在至少2款浏览器里实现了
    • 意味着其质量足以成为REC
    • 此时,W3C 成员再最后一次 review 下规范
      • 一般不会有实质性的改动;若有,则只能再发布一个新的WDCR
    • 其它相关
      • PER Proposed Edited Recommendation 已修订的提案推荐标准
  • REC Recommendation 推荐标准,通常称之为 standard,即事实标准 ✔✔
    • 最终阶段:a standard,一个标准
    • 此时,就不会有太多变动了,当然依然可以收勘误
    • 它可能成为
      • Edited Recommendation 编辑推荐标准。微小改动,然后生成推荐的 Revised Edition
      • Amended Recommendation 修订推荐标准。不增加新功能的实质性更改
      • SPSD Superseded Recommendation 被取代的推荐标准(缺少足够的市场相关性)

  • ret Retired 退役的
  • Rescinded Recommendation 撤销的推荐标准。不再认可的,认为不会再恢复到标准状态了
  • Note Group Note 工作组说明,不打算成为标准的文档。已经停止使用了
    • 通常记录规范以外的信息,eg.
      • 规范的用例及其最佳实践
      • 解释规范被弃用的原因

https://www.w3.org/Style/2011/CSS-process
https://www.w3.org/2018/Process-20180201/#Reports

Immutable JavaScript

immutable 不可变的
immutability 不变性
mutating data 变异数据

编写 immutable 的 JS 代码,是个比较好的实践。已经有库 Immutable.js 可以帮我们实现这个特性了。这篇博客来聊下,在 ES6+ 里如何使用 Immutable 特性。

问题

为什么 immutability 很重要?因为,变异数据会让代码不易阅读,也容易引入bug。

对于基本类型(比如 number 和 string),书写 immutable 代码是非常简单的,因为基本类型自己不会变。指向基本类型的变量一直指向实际的值,如果你把它赋值给了另一个变量,那另一个变量是得到了该值的一个新拷贝。

对象(和数组)就不一样了,因为它们传的是引用。在这种情况下,新变量和原始变量是指向的同一个对象。不论你修改新变量还是原始变量,都会 mutate 该对象。先来感受下。

const person = {
  name: 'anjia',
  age: 18
};

const newPerson = person;

newPerson.age = 30;

console.log(newPerson === person);   // true
console.log(newPerson, person);  // age 都是30了

这就是问题的所在。当你修改了 newPerson,我们竟然也自动修改了旧对象 person。在大多数情况下,这是不希望的行为,也是不好的编码实践。

那下面,我们看看如何解决这个问题。

Immutable

对象

不传引用,我们可以创建一个全新的对象。

const person = {
  name: 'anjia',
  age: 18
};

const newPerson = Object.assign({}, person, {
  age: 30
});

console.log(newPerson === person);   // false
console.log(newPerson, person);  // newPerson 是30岁,person 还是18岁

Object.assign 是个 ES6 的新特性,它把所有对象合并到第一个。

这样,就保持了旧变量 person 的独立性和完整性,我们把它称之为 immutable。

在 ES6 里,我们有更简洁的写法。可以用 object spread 操作符 ...,这样:

const person = {
  name: 'anjia',
  age: 18
};

const newPerson = {
  ...person,
  age: 30
};

console.log(newPerson === person);   // false
console.log(newPerson, person);  // newPerson 是30岁,person 还是18岁。同上

那么,如何删除一个属性呢?当然,不能用 delete,因为它会 mutate 到原始值。我们可以这样:

const person = {
  name: 'anjia',
  gender: 'female',
  age: 18
};

const property = 'age';   // 删除age属性

const newPerson = Object.keys(person).reduce((obj, key) => {
  if(key !== property){
    return {...obj, [key]: person[key]}
  }
  return obj
}, {});


console.log(newPerson === person);   // false
console.log(newPerson); // 只有 name 和 gender
console.log(person);    // 有 name, gender, age

呃,好吧,删除的话我们需要自己写整个逻辑代码。你可以把它封装成一个公共方法。

数组

下面的例子,以 mutating 的方式向数组里添加新项。

const names = ['an', 'jia'];

const newNames = names;
newNames.push('zora');

console.log(newNames === names);  // true
console.log(newNames, names);   // 都是 ["an", "jia", "zora"]

解决办法,思路同上。

const names = ['an', 'jia'];

const newNames = [...names, 'zora'];

console.log(newNames === names);  // false
console.log(newNames);   // ["an", "jia", "zora"]
console.log(names);      // ["an", "jia"]

这样我们就创建了一个新数组newNames,而且还能让老数组names保持自身的独立性和完整性。

关于数组的,还有一些方法也能非常方便地生成新数组,而不影响老值。比如 map filter 等。详见之前的一篇博客数组遍历。看代码(点进去链接看看),有没有觉得写 immutable 的代码更方便了。再配合着箭头函数,就更简洁了。它们每次都返回一个全新的数组。

当然,有一个例外就是sort()

const names = ['zora', 'anjia', 'an', 'jia'];

const newNames = names.sort();

console.log(newNames === names);  // true
console.log(newNames);   // 都输出 ["an", "anjia", "jia", "zora"]
console.log(names);

有没有解决办法呢?有,如下:

const names = ['zora', 'anjia', 'an', 'jia'];

const newNames = names.slice().sort();   // 利用 slice(),虽然有点 hacky

console.log(newNames === names);  // false
console.log(newNames);   // ["an", "anjia", "jia", "zora"]
console.log(names);      // ["zora", "anjia", "an", "jia"] 原值

如上,现代JS,可以让我们轻松实现 immutability。良好的编码,可以避免让JS变得不可预测。

性能

每次都创建新对象会耗费时间和内存哦?嗯~是的,它会带来一点开销。但是,与它带来的优势来比,这点缺点可以忽略了。

  • JS 里有个更复杂的操作是:追踪对象是否改变。Object.observe(object, callback) 是非常重的,且开销也很大。如果是 immutable,那可以用 oldObject === newObject 来判断,会很省内存哦
  • 第二个好处是:代码质量。确保声明是 immutable,会迫使自己更好的思考自己的程序结构,然后写出更健壮的代码。

REF:https://wecodetheweb.com/2016/02/12/immutable-javascript-using-es6-and-beyond/

Vue

目录

cookie, localStorage, sessionStorage

概况

相同点:

  • 都是客户端存储
  • 都遵循同 origin 策略
  • 都只支持 string 类型的存储
    • key 和 value 都是 String

不同点:

  cookie localStorage sessionStorage
作用域 origin + path
当 path 是 '/' 时,则是 origin
origin origin + tab
失效期 一般由服务器生成,并设置失效时间;若在客户端设置,默认是浏览器关闭/max-age 持久型/主动删除 当前页签关闭
大小限制 每条 cookie 约 4KB / 个数限制 大约 5MB
HTTP 请求是否携带
安全 尽量加密 / HTTP only 若取, 注意XSS攻击
使用时 有库-封装 也可封, eg.方便存 JSON, Array 等

客户端存储时,别人都可随便修改
需注意安全方面。eg.加密、防止 XSS 攻击、防止恶意修改
当涉及敏感信息时,尤其注意。eg.钱、订单

大小限制,不同浏览器的实现会略不一样。
RFC 2965 的推荐标准:

  • cookie 总个数不超过 300 个,每个 origin 下不超过 20 个,每个 cookie 大小不超过 4KB
  • localStorage、sessionStorage 可达 8M

cookie

cookie 的设计比较早,它作为 HTTP 协议的一种扩展,数据会自动在客户端和服务器之间传输。

所以,要在 cookie 里放数据时,尤其考虑此特性。以及安全性。

cookie 的限制:

  • 个数:每个域名大约 30/50 个,也有浏览器不限制个数
    • 若超过个数,一般按最近最少使用策略剔除老的
  • 大小限制:每条大约 4KB
    • 若超过大小了,再设置会被直接忽略

sessionStorage

它和 localStorage 的唯一区别,就在于有效期。

  • 仅当前的会话窗口有效,刷新页面时存在
  • 若是不同的页签,即便是相同的 origin 也无法共享

localStorage

说说处理它时的注意事项:

  • 注意安全错误。因为用户可能将其浏览器配置为 specified origin 时禁止访问
  • setItem()时记得捕获异常
    • 存储空间满了,会抛异常
    • 隐身模式下,有些浏览器会抛出异常。eg. Mobile Safari 会将配额设置为 0
  • 浏览器兼容问题
    • 它的大小,不同浏览器会略不同
    • 低版本浏览器可能不支持
    • 隐身模式下,不同浏览器处理也不同

这里有个 localStorage 的在线例子,可以感受下浏览器兼容性
https://mdn.github.io/dom-examples/web-storage/

CSS Typed OM

简介

我们都知道,CSSOM 是 CSS 的对象模型,它提供了一系列方法来访问元素上各个属性的值。

CSS Typed OM(CSS 类型化的对象模型),单纯看名字,它只比 CSSOM 多了个 Typed。它是 CSS 的 object-based API,把 CSS value 的字符串值转成了 JS 对象。是 CSS Houdini #23 的一部分。

CSS OM CSS Typed OM
介绍 CSS Typed OM
values 都是 string values 都是 JS object
  给 CSS values 增加了 types,
methods 和 a proper object model
element.style 元素 .attributeStyleMap
样式规则.styleMap
  两方法都返回StylePropertyMap对象
是个 map-like object,可以用 get/set/keys/values/entries
写法简单 写法冗长了点
  代码更健壮
性能更好

接下来,我们重点看下它是如何让代码更健壮性能更好的。

关键特性

特性检测 window.CSS && CSS.number

  1. 取CSS值时,再也不用担心 string 和 number 的误用了
    • 如果给定的 CSS 属性支持 number,那么 typed OM 会返回一个 number,即便输入的值是string
    • 是个 JSON 结构的,内含 value 和 unit
    el.attributeStyleMap.set('opacity', '0.3');
    el.attributeStyleMap.get('opacity').value === 0.3  // true
    el.attributeStyleMap.get('opacity').value += 0.1   // 再也不用担心值是 0.30.1 了
  2. 它会自动把值处理到有效范围之内
    el.attributeStyleMap.set('z-index', CSS.number(15.4))
    el.computedStyleMap().get('z-index').value === 15    // 自动四舍五入,处理成 15
    
    el.attributeStyleMap.set('opacity', 3)
    el.computedStyleMap().get('opacity').value === 1  // 值始终在有效范围之内
  3. 取属性值的时候,再也不用纠结要不要用驼峰了。Typed OM 里是在 CSS 里怎么写,就在 JS 里怎么取
    // 之前的写法
    el.style.backgroundColor
    el.style['background-color']
  4. 性能更好
    • 浏览器不用花费大量的时间去做字符串的序列化/反序列化工作
    • 在JS和C++之间就可以对 CSS values 有一致的解释和理解了
    • 好处是:
      • 有人统计了下,Typed OM 比单纯的 CSSOM 操作快了 30%
      • 这对处理快速的脚本动画效果至关重要,eg.requestAnimationFrameanimationWorklet
  5. 方便了算术运算和单位转换,eg.处理绝对长度单位时,见 Arithmetic operations
  6. 新的解析方法,能让 CSS 里处理 error 了,见 Error Handling

更详细的 API,见 API Basics

https://developers.google.com/web/updates/2018/03/cssom

CSS 规范

理解 CSS 规范

要理解 CSS specifications,需要理解规范构建的 context, vocabulary 和 fundamental concepts。

  1. 每个规范都有它的上下文,可以读 CSS Snapshot, 以及 CSS Design Principles
  2. 规范是如何组织的,即规范的行文结构
  3. 描述规范对实现者的期望的词汇,详细见 #18
    • eg. MUST, MUTS NOT, SHOULD, SHOULD NOT
  4. 仔细阅读以下规范,里面有重要的 rules 和 concepts
    • Cascading and Inheritance
    • Display
    • Box dimensions
    • visual formatting model
    • Controlling box generation
    • positioning schemes
    • containing block

注意:

  • errata 勘误表,在每个 spec 的顶部
  • corrections 修正

另外:

https://www.w3.org/Style/CSS/read

date

var now = new Date()

now.toLocaleString()       // 2019/1/7 下午12:30:13
now.toLocaleDateString()   // 2019/1/7
now.toLocaleTimeString()   // 下午12:30:13

now.toDateString()    // Mon Jan 07 2019
now.toGMTString()     // Mon, 07 Jan 2019 04:30:13 GMT
now.toISOString()     // 2019-01-07T04:30:13.857Z
now.toJSON()          // 2019-01-07T04:30:13Z

now.toString()      // Mon Jan 07 2019 12:30:13 GMT+0800 (**标准时间)
now.toTimeString()  // 12:30:13 GMT+0800 (**标准时间)
now.toUTCString()   // Mon, 07 Jan 2019 04:30:13 GMT

CSS 是怎样确定图像大小的?

今天,和大家聊聊 CSS 是如何确定图像的显示大小的。

热热身

先来看个例子,热热身。

上面这张图像的原始尺寸是:宽 54px 高 49px。
那么,在以下代码中,每张图像显示的最终尺寸是多少?

https://p1.ssl.qhimg.com/t01068da1826ad05875.png 是该图片的 url

图像由<img src="">指定,代码如下:

<img src="https://p1.ssl.qhimg.com/t01068da1826ad05875.png">
<img src="https://p1.ssl.qhimg.com/t01068da1826ad05875.png" width="30" height="30">
<img src="https://p1.ssl.qhimg.com/t01068da1826ad05875.png" width="30">
<img src="https://p1.ssl.qhimg.com/t01068da1826ad05875.png" height="30">
点击查看:显示的尺寸依次是...
  1. 宽 54px,高 49px
  2. 宽 30px,高 30px
  3. 宽 30px,高 30/(54/49)=27.22px
  4. 宽 30*(54/49)=33.06px,高 30px

图像由background-image指定,代码如下:

<style>
.img {
    display: inline-block;
    background-color: #eee;
    background-image: url('https://p1.ssl.qhimg.com/t01068da1826ad05875.png');
    background-repeat: no-repeat; 
    background-size: auto; /*auto 是默认值*/
}
</style>
<span class="img" style="width: 100px; height: 100px;"></span>
<span class="img" style="width: 30px; height: 30px;"></span>
<span class="img" style="width: 30px; height: 30px; background-size: 10px 10px;"></span>
<span class="img" style="width: 30px; height: 30px; background-size: contain;"></span>
<span class="img" style="width: 100px; height: 100px; background-size: cover;"></span>
点击查看:显示的尺寸依次是...
  1. 宽 54px,高 49px
  2. 宽 54px,高 49px
  3. 宽 10px,高 10px
  4. 宽 30px,高 27.22px
  5. 宽 100px,高 100px

以上 9 道题,你都答对了吗?

确定图像大小的过程

为了方便讨论,我们先来认识四个术语:

  1. 固有尺寸:是固有宽度、固有高度和固有宽高比的集合。对于特定对象,这三个尺寸可能都存在,也可能都不存在。比如光栅图像同时拥有这三个,SVG 图像只有固有宽高比,CSS 渐变就没有任何固有尺寸
  2. 指定大小:是通过width height background-size中的一个或多个指定的
  3. 默认对象大小:是一个具有确定宽高的矩形。在既没有固有尺寸,也没有指定大小时生效
  4. 具体对象大小:是对象最终显示的大小,即有明确宽度和高度值的矩形

通常情况,计算图像大小的默认过程是:

  1. 优先使用指定大小,得到要显示的宽和高
  2. 如果只指定了一个宽度,或只指定了一个高度,那么
    2.1 如果有固有宽高比,则用它和给出的那个,计算出来另一个
    2.2 否则,就取固有尺寸里的
    2.3 如果也没有固有尺寸,那就取默认对象大小的
  3. 如果没有指定大小
    3.1 先用固有尺寸里的
    3.2 如果没有固有尺寸,那就取默认对象大小的

初听起来有些绕,我们用刚才提到的例子具象下这个过程。

光栅图像是有三个固有尺寸的。以开头给出的 Logo 图像为例,它的固有尺寸分别是:

  • 固有宽度 54px
  • 固有高度 49px
  • 固有宽高比 54/49

<img>

我们先来看用<img src="">指定的那四个:

demo1.

<img src="https://p1.ssl.qhimg.com/t01068da1826ad05875.png">

没有指定大小,那就用固有尺寸。所以,最终显示的尺寸是宽 54px,高 49px

demo2.

<img src="https://p1.ssl.qhimg.com/t01068da1826ad05875.png" width="30" height="30">

有指定的宽和高,所以,最终显示的尺寸是宽 30px,高 30px

demo3.

<img src="https://p1.ssl.qhimg.com/t01068da1826ad05875.png" width="30">

只有一个指定的宽,那就用固有宽高比计算出高来。所以,最终显示的尺寸是宽 30px,高 30/(54/49)=27.22px

demo4.

<img src="https://p1.ssl.qhimg.com/t01068da1826ad05875.png" height="30">

只有一个指定的高,那就用固有宽高比计算出宽来。所以,最终显示的尺寸是宽 30*(54/49)=33.06px,高 30px

以上四个示例的 UI 依次是:

background-image

接下来,看用background-image指定的五个。

在这个上下文里,背景区域的大小即图像的默认对象大小,background-size属性提供指定大小。

background-size有两个关键字containcover,它们分别对应包含约束和覆盖约束。

  • 包含约束(contain constraint )遵循固有宽高比,宽高都小于等于背景区域,然后尽可能的大。
  • 覆盖约束(cover constraint)遵循固有宽高比,宽高都大于等于背景区域,然后尽可能的小。

demo1.

<span class="img" style="width: 100px; height: 100px;"></span>

autobackground-size的默认值,意思是按图像的固有尺寸来,所以最终显示的尺寸是宽 54px,高 49px

demo2.

<span class="img" style="width: 30px; height: 30px;"></span>

同上,auto按图像的固有尺寸来,所以最终显示的尺寸是宽 54px,高 49px

demo3.

<span class="img" style="width: 30px; height: 30px; background-size: 10px 10px;"></span>

这里background-size: 10px 10px给出了具体的指定大小,所以最终显示显示的尺寸就是宽 10px,高 10px

demo4.

<span class="img" style="width: 30px; height: 30px; background-size: contain;"></span>

根据contain的含义,包含约束要在背景框里尽可能的大。背景框宽 30px 高 30px,图像固有宽 54px 固有高 49px,所以包含约束取宽 30px,高根据固有宽高比算出来 30/(54/49)=27.22px,也就是指定大小是宽 30px 高 27.22px。对于背景区域未覆盖到的地方将会用背景色来填充。所以最终显示的尺寸是宽 30px 高 27.22px。

demo5.

<span class="img" style="width: 100px; height: 100px; background-size: cover;"></span>

根据cover的含义,覆盖约束要在大于等于背景框的前提下尽可能的小。背景框宽 100px 高 100px,图像固有宽 54px 固有高 49px,所以覆盖约束取高 100px,宽根据固有宽高比算出来 100*(54/49)=110.20px,也就是指定大小是宽 110.20px 高 100px。 对于超出背景区域的部分,会被裁切掉,所以最终显示的尺寸是宽 100px 高 100px。

以上五个示例的 UI 依次是:

总结

  1. 理解四个尺寸
    • 固有尺寸:固有宽度、固有高度、固有宽高比
    • 指定大小:width height background-size
    • 默认对象大小:依据不同的上下文而定
    • 具体对象大小:它就是最终要显示的大小
  2. 计算图像大小的过程,即算图像最终要显示的大小,也就是算它最终的“具体对象大小”
    • 根据固有尺寸 + 指定大小 + 默认对象大小,计算具体对象大小
    • 使用的优先级:指定大小 > 固有尺寸 > 默认对象大小
    • 两种比较常见的指定大小约束:包含约束、覆盖约束
  3. 当计算出的“具体对象大小”和图像的“固有尺寸”不一致时,图像就要调整自身,以匹配“具体对象大小”
    • 调整图像大小的属性background-size
      • 当是<img>时,也有个相应的属性可以调整图像大小,即 object-fit
    • 图像超出背景区域的部分,会被裁剪掉;覆盖不全的部分,会用背景色来填充

参考

CSS Images Module Level 3

CSS 的原生嵌套

CSS 的原生嵌套

嗨,你好,我叫安佳,是 360 搜索事业部的一名前端开发工程师。今年5月,我有幸加入了 W3C 的 CSS 工作组,成为其中的一员。第一次参与工作组讨论的是关于 [css-grid-2] Allow minmax where max wins over min,很开心自己的提议被标准采纳了,预计会在 CSS Grid Layout Module Level 2 里实现。

期间,看到了一个关于要 CSS 支持原生嵌套的议题 [css-nesting] request to pick up the css-nesting proposal。当时觉得这个特性很好,于是就表达了自己的观点。一周之后,CSS 工作组在周会上对它进行了讨论,但是打的标签依然是 unknown/future spec。又过了两周,看起来并没有要在下一个模块里实现此特性的计划,也没有要商讨的安排。

这么有用的嵌套功能和scope特性,为什么一直坐在冷板凳上呢?于是,我就想探究下原因。这篇文章就是研读此 Issue 及相关规范的成果,主要有这三部分:

  1. 来自 Web 开发者的呼声:介绍此 Issue 的相关背景
  2. CSS 工作组都干了什么:介绍工作组的工作内容
  3. 未来的原生嵌套:介绍嵌套语法

来自 Web 开发者的呼声

2012年4月13日,CodePen 的联合创始人 Chris Coyier 抱怨 CSS 的类名不支持命名空间,导致要写好多重复的选择器。

2016年2月2日,微软的项目经理 Kenneth Auchenberg 说如果 CSS 支持了变量和嵌套,他将不再使用预处理器。

2016年12月8日,《CSS揭秘》的作者 Lea Verou 调研了使用 CSS 预处理器的首要原因(单选题),有 1838 个人参与了投票,最终并列第一的两个理由是嵌套和变量。她觉得是时候该重新考虑 CSS 原生嵌套的问题了。

2017年7月13日,集设计和开发才能于一身的 UI/UX 自由工作者 Sara Soueidan 说嵌套是她最想要的 CSS 功能。

2017年8月15日,node-inspect 的作者 Jan Olaf Kremscssnext 把嵌套定义成了“明天的 CSS”,但他还是想看到原生的 CSS 嵌套,毕竟 JS 的生态系统已经证明避免“每个人都使用自己的半标准语言”绝对是健康的。

2018年2月23日,Lea Verou 再次发声,说她现在还在用 CSS 预处理器写嵌套,一旦 CSS 支持了原生嵌套,她就果断弃用预处理。

2018年5月25日,postcss-preset-env 的作者 Jonathan Neal 再次提议重新考虑下让 CSS 支持原生嵌套(也就是本文章的切入点),这引来了一波热议。

CSS 工作组都干了什么

其实,早在2014年4月3日,W3C 就发布过一个 CSS范围(scoping)模块 的工作草案;2015年9月23日,谷歌的工程师 Tab Atkins 也发布过一个 CSS嵌套模块 的编辑草案。那个时候,CSS 工作组也讨论过嵌套的问题,但并未通过社区的同意(见会议纪要)。

针对 Jonathan Neal 这次的提议,CSS 工作组的讨论流程如下:

图1. CSS工作组的讨论流程

  1. 要支持原生嵌套
  • 嵌套的样式规则是一个普遍的诉求
  • 现存的 CSS 预处理器都支持写嵌套,且它是最受欢迎的功能之一
  • 有了原生嵌套,就可以不用预处理器了
  1. 决定仅增加嵌套语法糖
  • 开发人员已经习惯了预处理器中的嵌套,嵌套选择器不应该有特殊的优先级
  • 局部样式是有用的,但scope能否真正满足开发人员的需求还不明朗
  • 嵌套和scope是两个维度的特性,建议先实现已经比较成熟的嵌套
  1. 嵌套语法,详见下节

最终的结论是,新增 CSS 嵌套模块,默认 ED(Editor Draft,编辑草案) 阶段,由 Tab Atkins 担任编辑,并收集相关 Issues,直到该特性成为 FPWD(First Public Working Draft,首个公开工作草案)。

ED Editor Draft 编辑草案
WD Working Draft 工作草案
CR Candidate Recommendation 候选推荐标准
PR Proposed Recommendation 提议推荐标准
REC Recommendation 推荐标准(最稳定的)

关于标准的诞生过程,可查看 World Wide Web Consortium Process Document

未来的原生嵌套

CSS Nesting Module Level 3 里定义了 CSS 嵌套,它新增了一个新的选择器:嵌套选择器 &

a, b {
    & c { color: blue; }
}

/* 等价于 */
a c,
b c { color: blue; }

看了上面的写法,我想肯定有小伙伴要问了:那个前缀 & 能省略不写吗?

对此,草案里的解释是:现有的 CSS 解析都是通过一个单独的前瞻符(lookahead token)来区分各种选择器的,如果新增的嵌套语法不写前缀的话,那一段文本就没法提前知道它到底是一个 CSS 声明还是一个 CSS 选择器了,这会非常不利于浏览器的实现。

前瞻符,诸如:

  • # ID 选择器
  • . 类选择器
  • [] 属性选择器
  • * 通用选择器
  • : 伪类
  • :: 伪元素

css-declaration
图2. CSS 声明

另外,如果省略了 & 也就没法区分 #foo { .bar {} } 到底是复合选择器 #foo.bar 还是组合选择器 #foo .bar 了。

草案里定义了两种嵌套方法:直接嵌套和 @nest 规则

直接嵌套

直接嵌套,即直接以嵌套选择器 & 开头

.foo {
  color: blue;
  & > .bar { color: red; }
}
/* 等价于
   .foo { color: blue; }
   .foo > .bar { color: red; }
 */

.foo {
  color: blue;
  &.bar { color: red; }
}
/* 等价于
   .foo { color: blue; }
   .foo.bar { color: red; }
 */

.foo, .bar {
  color: blue;
  & + .baz, &.qux { color: red; }
}
/* 等价于
   .foo, .bar { color: blue; }
   .foo + .baz,
   .bar + .baz,
   .foo.qux,
   .bar.qux { color: red; }
 */


/**
 * 以下写法都是无效的
 */
.foo {
  color: red;
  .bar { color: blue; }
}
/* 无效原因:没有嵌套选择器 & */

.foo {
  color: red;
  .bar & { color:blue; }
}
/* 无效原因:& 没有在组合选择器的第一位 */


.foo {
  color: red;
  &.bar, .baz { color: blue; }
}
/* 无效原因:列表的第二个选择器里没有嵌套选择器 & */

@nest 规则

.foo {
  color: red;
  @nest & > .bar {
    color: blue;
  }
}
/* 等价于
   .foo { color: red; }
   .foo > .bar { color: blue; }
 */

.foo {
  color: red;
  @nest .parent & {
    color: blue;
  }
}
/* 等价于
   .foo { color: red; }
   .parent .foo { color: blue; }
 */

.foo {
  color: red;
  @nest :not(&) {
    color: blue;
  }
}
/* 等价于
   .foo { color: red; }
   :not(.foo) { color: blue; }
 */


/**
 * 以下写法都是无效的
 */
.foo {
  color: red;
  @nest .bar {
    color: blue;
  }
}
/* 无效原因:没有嵌套选择器 & */

.foo {
  color: red;
  @nest & .bar, .baz {
    color: blue;
  }
}
/* 无效原因:列表里并非所有的选择器都包含嵌套选择器 & */

致谢

在此特别感谢,感谢 @cncuckoo(李松峰老师)对本文提出的大量指导建议,让我学到了很多,尤其是极其严谨的工作态度,以及以读者为出发点的行文思路。

你有想说的吗?

看完本篇文章,你有什么想说的吗?欢迎留言。

当然,你也可以去 CSS 工作组的官方 github 上 w3c/csswg-drafts 提 Issue。若是和嵌套相关的,则 Issue 的标题格式是“[css-nesting]...”;若是和scope相关的,则标题格式是“[css-scoping]...”。诚邀大家提出建设性的意见/建议。

相关规范:

本文章仅代表个人观点,与 CSS 工作组无关。特此说明

CSS Houdini

Houdini 的相关模块及各自进展

  • CSS Houdini 的九个API
    • 该网页是 Houdini 早期时的状态,只是个草案清单。不完整,有些也只是先占个坑
    • 它里面的规范链接有些已经过时了。规范的最新链接,见下表格中的
    • “成熟度”列的说明
      • 只有 FPWD WD CR REC 等,才是官方规范
      • 其余的 Draft, Idea 等,说明该 API 目前尚不成熟,先占个坑供大家讨论。即初期阶段
  规范 成熟度 说明
1 CSS Painting API 1 CR 用JS自定义CSS图像类型
2 CSS Layout API Level 1 FPWD 用JS自定义布局
  Worklets 1 FPWD 在渲染引擎的各个阶段执行脚本,独立于主JS
3 CSS Animation Worklet 1 FPWD 可控制一组动画效果的脚本动画
4 CSS Typed OM 1 WD 将CSSOM的值的字符串转成JS对象,性能好点
5 CSS Properties and Values API 1 WD 注册新的CSS属性,可定义类型/继承/赋初始值
  Box Tree API 1 Idea 访问 boxes 信息的 API
  Font Metrics API Idea 提供基本的字体指标
  CSS Parser API 提供CSS解析的API,移到了 WICG 仓库

7个今年还在更新维护中
Draft:Draft Community Group Report,社区小组报告草案
Idea:还在收集想法当中
WICG:Web Platform Incubator Community Group,Web平台孵化器社区小组

结合浏览器的实现情况及标准的进展,按成熟度降序排列:

CSS WG

Schedule 见 TPAC 2018 / CSS WG

Proposed Agenda

  1. csswg-drafts 有 4+n 个 Issues
    • Initial Letter Layout 和 other Inline Layout 相关的
    • 3118, [css-text-decor-4] Rethinking text-underline-offset,
    • 1744, 提议把 lang 作为 font-face 描述符, 理想情况下用 I18n Core
    • 2875, Apple's color-filter proposal (status gathering)
    • 3117, src:local() font 唯一名字匹配 ambiguous & restricts matched locale
  2. Tuesday: Constructable Stylesheet Objects Idea
    • 共12个issues
      • 相同结构的样式表,能在两个不同的的文档里使用吗?
      • 需要定义从 sheet 中获取的各种加载的内容
      • 在 adoptStyleSheets 里,我们可以从<style>/<link>里使用 CSSStyleSheets 吗?
      • adoptedStyleSheets, 我们是用 FrozenArray 还是 StyleSheetList?
    • 要把这个 spec 从 WICG 移到 CSS WG 吗?
    • Blink 有实现的兴趣
  3. CSS Environment Variables Module Level 1 ED
  4. CSS Text Module Level 4 text-spacing属性
  5. CSS Scrollbars Module Level 1,FPWD
    • 标准化了 color scrollbars,它是 Windows IE 5.5 在 2000 年引入的
    • 实验性的 implementation? 还要点 demos?
  6. CSS Basic User Interface Module Level 4 ED 共4个
    • [css-ui-4] [Agenda+ F2F] 的2个 Issues
    • 及其它2个
  7. Spatial Navigation
  8. fxtf-drafts 有2个 Issues
    • FX Task Force 是和 graphics 相关的资源,CSS WG 和 SVG WG
    • 282, 根元素的 filter 效果
    • 53, Backdrop filters
  9. 确定 CSS A11Y task force 的新代表人

https://wiki.csswg.org/planning/tpac-2018

Servo 的设计架构

  • Quantum 初探:介绍了 Quantum 项目的由来和概况,也顺便介绍了 Servo 的小历史
  • Servo 的设计架构:即本篇文章,将介绍 Servo 的基于任务的设计架构,重点介绍并行并发的策略

Servo 的设计架构

对,我就是 Servo 的官方 Logo

Servo 是一款现代化的高性能浏览器引擎,既支持常规应用,也支持嵌入使用。官网 https://servo.org

她由 Mozilla 开发,由三星集团移植到 Android 系统和 ARM 处理器,旨在创造一个大规模并行计算的环境,Servo 也与 Rust 编程语言有着共生的关系。源自 https://zh.wikipedia.org/wiki/Servo

1. 设计简介

Servo 是一个新的 Web 浏览器引擎。她的目标是创建一个多层级的高并发架构,同时在架构层面消除与错误的内存管理、数据竞争相关的常见 bug 和安全漏洞。

因为 C++ 不适合处理这类问题,所以 Servo 是用 Rust 语言编写的。Rust 在设计的时候充分考虑了 Servo 的需求,它提供了任务并行的基础架构和强类型系统,从而保证了内存安全、避免了数据竞争。

在设计的时候,Servo 的架构师们会优先考虑现代 Web 平台的以下特性:高性能、动态、富媒体应用,可能会牺牲一些无法优化的特性。他们想知道一个快速响应的 Web 平台是什么样子的,然后再实现它。

Servo 专注于实现一个功能完备的 Web 浏览器引擎和可靠的嵌入式引擎,前者(Web 浏览器引擎)使用了基于 HTML 的用户界面 Browser.html。尽管 Servo 最初只是一个研究型项目,但在开发它的时候就以提供可用于生产环境的代码为目标。目前,Servo 的一些组件已经迁移到了 Firefox 浏览器。

关于集成到 Firefox 中的 Servo 组件,可查看 Jack Moffitt 的演讲视频 Web Engines Hackfest

并行并发的策略

并发是拆分任务以便交叉执行;并行是同时执行多个任务以提高速度。Servo 在以下环节中用到了并行和并发。

  • 基于任务的架构:系统的主要组件应该有独立的堆,以便有明确的失败/恢复的边界。这也让整个系统的耦合度降低,以便可以轻松地替换掉某些组件,供我们实验和研究。
  • 并发渲染:将渲染和合成从布局中分离出来,以保证良好的响应性。渲染和合成都是单独的线程;合成线程手动管理自己的内存,以避免垃圾回收暂停。
  • 瓦片渲染:将屏幕划分成瓦片网格,并行渲染每一个瓦片。暂且忽略由此带来的收益,移动端渲染的时候是需要这种瓦片的。
  • 层渲染:将显示列表分成子树,并行渲染子树,并将其内容保留在 GPU 上。
  • 选择器匹配:这是一个令人尴尬的并行问题。与 Gecko 不同,Servo 在流树结构的单独传递中进行选择器匹配,这样会让并行更容易。
  • 并行布局:通过并行遍历 DOM 来构建流树,这种遍历遵守由元素(比如浮动元素)生成的顺序依赖关系。
  • 文本形状:作为内联布局的关键部分,文本形状的成本非常高,它很有并行的潜力。未实现。
  • 解析:用 Rust 新写了一个 HTML 解析器,专注于安全性和符合规范。尚未在解析中添加预测性和并行性。
  • 图像解码:并行解码多个图像非常简单。
  • 其他资源的解码:这可能不如图像解码重要,但页面加载的所有内容都是可以并行处理的,比如解析整个样式表、解码视频。
  • GC JS 和布局的并发:在大多数具有并发 JS 和布局的设计中,当查询布局的时候,JS 有时需要等待,而且有可能是非常频繁的。这将是运行 GC 的最佳时机。

GC,Garbage Collection,垃圾回收

挑战

  • 库不利于并行:用到的一些第三方库在多线程环境下不能很好地运行;字体尤其困难;即使从技术角度讲库是线程安全的,但是,通常是通过库的互斥锁来实现线程安全的,而这不利于实现并行。
  • 线程太多:如果在各个方面都抛到最大的并行量和并发量,那么最终会因为线程太多而压垮系统。

2. 任务架构

tasks-sup
图1. 任务监管图,源自 servo/wiki/Design

tasks-comm
图2. 任务通信图,源自 servo/wiki/Design

  • 每一个框代表一个 Rust 任务 (注:一个任务就是一个线程)
  • 蓝色框是浏览器管道里的主要任务
  • 灰色框是浏览器管道的辅助任务
  • 白色框是 worker 任务,它表示会有多个任务,具体的任务数要根据工作量来确定
  • 虚线表示主管关系
  • 实线表示通信信道

说明

我们可以把每个 constellation(见“附录.术语”小节)实例看做是浏览器的单个页签或者窗口,它管理着接收输入的任务管道,针对 DOM 运行 JavaScript,执行布局,构建显示列表,将显示列表渲染到瓦片上,最后把最终图像合成到屏幕上。

这个管道由四个主要任务组成:

  • 脚本(Script):创建和拥有 DOM,执行 JavaScript 引擎。它接收来自多个源的事件,包括导航事件。当内容任务(Content)需要查询布局相关的信息时,脚本任务必须向布局任务发送一个请求。每个内容任务都有自己的 JavaScript 运行时。
  • 布局(Layout):获取 DOM 快照,计算样式,构造主要的布局数据结构-流树(flow tree)。流树用于计算节点的布局,从它那可以构建显示列表显示列表会被发送到渲染任务。
  • 渲染(Renderer):接收显示列表,并将可见部分渲染到一个或多个瓦片上,尽可能并行。
  • 合成(Compositor):合成渲染的瓦片,并将它们发送到屏幕上进行显示。作为 UI 线程,合成任务也是 UI 事件的第一个接收器,UI 事件通常会被立即发送到内容任务以供处理(尽管一些事件,比如滚动事件,首先由合成任务处理并响应)。

管道中的多任务通信涉及到两种复杂的数据结构:DOM 和显示列表。DOM 从内容传到布局,显示列表从布局传到渲染。找出一种有效且类型安全的方式来表示、共享和传递这两种数据结构是该项目的诸多挑战之一。

写时复制 DOM

Servo 的 DOM 树节点是有版本控制的,它们可以在单个 writer 和多个 reader 之间共享。DOM 使用写时复制(copy-on-write)的策略允许当有多个 reader 时 writer 也能修改 DOM。writer 总是内容任务,reader 总是布局任务或其子任务。

DOM 节点是 Rust 值(Rust value),而 Rust 值的生命周期由 JavaScript 垃圾收集器管理。JavaScript 直接访问 DOM 节点,而没有依赖 XPCOM 或其它类似的基础设施。

DOM 接口目前不是类型安全的,这可能会导致不正确的节点管理。消除这类不安全是该项目的一个必要的高优先级目标;由于 DOM 节点具有复杂的生命周期,这将会带来一些挑战。

显示列表

Servo 的渲染完全由显示列表驱动,显示列表是由布局任务创建的一系列高级绘图命令。Servo 的显示列表是完全不可变的,因此它可以被同时运行的多个渲染任务所共享。这与 Webkit 和 Gecko 的渲染器不同:WebKit 的渲染器没有使用显示列表;Gecko 的渲染器使用了显示列表,但它在渲染期间还会查询额外的信息。

3. JavaScript 和 DOM 绑定

目前,Servo 使用的脚本引擎是 SpiderMonkey(可插拔引擎是一个长期的、低优先级的目标)。每个内容任务都有自己的 JavaScript 运行时。DOM 绑定使用原生的 JavaScript 引擎 API 而不是 XPCOM。从 WebIDL 自动生成绑定是一个高优任务。

4. 多进程架构

与 Chromium 和 WebKit2 类似,Servo 的架构师们打算做一个可信任的应用程序进程和多个不太可信的引擎进程。高级 API 实际上是基于 IPC 的,非 IPC 实现可能用于测试和单进程用例(虽然预计最糟糕的时候也会用于多进程)。引擎进程将使用操作系统沙箱工具来限制对系统资源的访问。

目前,Servo 并不打算像 Chromium 那样采用极端沙箱(extreme sandboxing),主要是因为锁定沙箱会导致大量的开发工作(特别是在 Windows XP 和旧版 Linux 等低优先级的平台上),并且该项目的其它方面的优先级更高一点。Rust 的类型系统还为内存安全漏洞增加了一层重要的防御功能,虽然仅凭这一点并不能使沙箱在防御不安全代码、类型系统中的错误以及第三方/主机库等方面变得不那么紧迫,但相对于其他浏览器引擎它确实能显著减少 Servo 的攻击面。此外,Servo 的架构师们对某些沙箱技术有性能方面的顾虑(例如,将所有 OpenGL 调用代理到单独的进程)。

5. I/O和资源管理

网页依赖于各种各样的外部资源,而这些资源具有很多的检索和解码机制。这些资源会被缓存在多处,比如磁盘、内存。在并行浏览器的设置中,这些资源一定会在并发的多个 worker 之间调度。

通常,浏览器是单线程的,会在“主线程”上执行 I/O,而“主线程”同时又担负着大部分的计算任务,这就会导致延迟问题。而 Servo 中没有“主线程”,所有外部资源的加载都由一个资源管理任务来处理。

浏览器有很多缓存,而 Servo 的基于任务的架构意味着它可能会拥有比现有浏览器引擎还多的缓存(例如,我们在拥有全局任务缓存的同时,也拥有着一个本地任务缓存,它存储着来自全局缓存的结果,以通过调度程序来保存往返记录)。Servo 应该有一个统一的缓存机制,以便在低内存的环境中也运行良好。

附录. 术语

  • constellation:该线程控制相关网页内容。在支持多页签的浏览器中,可以把它当做单个页签的拥有者;它封装了会话历史记录,知道 frame 树中的所有 frame,是每个 frame 管道的拥有者。
  • 管道(pipeline):为特定文档封装了脚本线程、布局线程和渲染线程之间的通信。每个管道都有一个全局唯一的 id,可以从 constellation 里访问到它。
  • 脚本线程/脚本任务(script thread/script task):这个线程执行 JavaScript,并存储同下所有文档的 DOM 表示。它可以把从 constellation 接收到的输入事件转换为规范里定义的 DOM 事件,也可以在收到新页面的时候调用 HTML 解析,也可以为事件评估 JS。
  • 布局线程(layout thread):这个线程负责将 DOM 树布局到特定文档的层(layer)上。它会收到来自脚本线程的命令,要么是为渲染线程生成一个新的显示列表,要么是为脚本线程返回页面的布局结果。
  • 显示列表(display list):一个具体的渲染说明(高级绘图命令)列表。显示列表是发生在布局之后的,因此所有的项都有相对堆叠上下文的像素位置,并且已经应用了 z-index,所以后加入显示列表的项将始终在其它项的上面。
  • 渲染线程/绘制线程(renderer thread/paint thread):这个线程负责将显示列表转换成一系列的绘图命令。该绘图命令会将关联文档的内容渲染在一个缓冲区里,之后会被发送到合成器。
  • 合成/合成器(Compositor):负责 Web 内容的合成渲染,并将它们尽可能快地显示在屏幕上。也负责从操作系统接收输入事件,并将它们转发到 constellation 线程。

小结

本文主要介绍了 Servo 的设计概况,重点介绍了它基于任务的整体架构及其四个主要任务(也称“线程”,在 Servo 的这个上下文里),即脚本任务、布局任务、渲染任务、合成任务。下图便是对上述内容的一个总结,希望对大家有所帮助和启发。

servo
图3. Servo 概况

下一步

后续,我会继续探索更多详细内容,敬请期待。关于 Quantum 和 Servo,如果您有其它更想知道的,欢迎留言。

致谢

感谢 @cncuckoo(李松峰老师)和 @liuguanyu(二哥)对本文提出的指导意见和建议。手动送花花。🌹

参考

常用缩写

WIP, Work In Progress, 开发中(work in progress, do not merge yet)
PTAL, Please Take A Look, 帮我看下(请别人 review 自己的 PR)
LGTM, Looks Good To Me, 看起来不错, 没有问题(别人 review 完 PR 之后)
RFC, Request For Comments, 请求评论, i.e. I think this is a good idea, lets discuss

AFAIK / AFAICT, As Far As I Know / Can Tell, 据我所知
IMHO, In My Humble Opinion, 以我的拙见(多用于邮件和网络)

FYI, For your information, 供你参考
CC, Carbon Copy, 抄送(邮件)
AFK, Away From the Keyboard, 稍后回来

ACK, ACKnowledgement, 同意, i.e. agreed/accepted change
NACK/NAK, Negative ACKnowledgement, 不同意, i.e. disagree with change and/or concept

Rust

快速入门

$ vim main.rs  # 新建文件
$ rustc main.rs # 编译
$ ls
main.rs # 源文件
main    # 可执行文件
$ ./main # 执行
Hello, world!
  • 入口main函数,它是每个可执行的 Rust 程序首先执行的代码
  • rustfmt 自动格式化工具(后续会将它包含在标准 Rust 发行版中)
  • 风格
    • 文件名以下划线 _ 分隔
    • 缩进,4个空格,而不是1个tab
    • 大部分代码以;结尾
  • 输出
    • println! 调用了一个 Rust 宏
    • println 是调用函数
    • 记住:当看到!时,就是宏,而不是普通函数
  • 编译和运行,是彼此独立的
    • 编译:rustc Rust 的编译器(类似于C/C++的gcc/clang
    • 运行:编译成功后,Rust 会输出一个二进制的可执行文件
      • 在 Windows 下是.ext后缀的,其它平台,无后缀
      • .pdb后缀是包含调试信息的
    • Rust 预编译静态类型的语言,ahead-of-time compiled
      • 即编译后,将可执行的二进制的文件发送给第三个人,他们就可以运行了(不需要安装 Rust),类似C语言

Servo 的 style 系统

Servo 的 style 系统

  1. selector 的实现
    • style 系统通用了很多东西,为了能和 Servo 的 layout 系统、Stylo 共享(Stylo 项目旨在把 Servo 的 style 系统集成到 Gecko 中)
    • 主要的通用特性是选择器的 SelectorImpl,这里有解析伪元素和其它伪类的所有逻辑。对应的规范是 Selectors Level 4 / Tree-Structural pseudo-classes
    • Servo 扩展了这一特性,以便在 Stylo 和 Servo 间分享更多的东西
    • 主要的 Servo 实现(用在常规构建中)是 SelectorImpl
  2. DOM glue(胶水)
    • 为了让 DOM、layout 和 style 能在不同的模块库里,这里涉及了一些特性
    • style 的 dom 特性TDocument TElement TNode TRestyleDamage)就是 layout 和 style 之间的主要的“墙”
    • layout 的 wrapper 模块可确保 layout 的特性
  3. stylelist
  4. properties 模块
    • properties 模块是一个 mako 模板,所有 properties、computed value 的计算和层叠逻辑都在那
    • 它是一个包含大量代码的复杂模板。主函数是 cascase 函数,这里执行所有计算
  5. 伪元素的处理
    • 介绍
      • 伪元素是 style 系统中棘手的一块。并非所有的伪元素都非常常见,因此有些伪元素可能会跳过层叠
      • 截止目前,Servo 有五个伪元素
      • 两个 ::-servo-details- 伪类都是私有的,i.e. 它们只解析来自 User-Agent 样式表里的
      • Servo 有三个不同的方式去层叠伪元素,它们定义在 PseudoElementCascadeType
    • Eager 层叠
      • 这个模块计算 computed values 给定节点的伪类,通过 style 系统的第一个 pass
      • 这用于所有的伪类元素,到目前为止,它是唯一方式一个公共的伪元素应该被层叠(关于它的解释见下方)
    • Precomputed 层叠
      • 根本就没有层叠。被标记为这样的伪元素不被层叠
      • 应用于这类伪类元素的唯一规则是全局规则(*|*选择器的规则),它们被直接应用到元素的 style,如果展示
      • ::-servo-details-content是这种伪类元素的一个例子,在 UA 样式表中的所有规则-有选择器 *|*::-servo-details-content(也仅有这些)被评估通过元素的style(除去display的值,那会被layout重写)
      • 私有伪类元素的首选类型(尽管它们中的而一些可能需要选择器,往下看)
    • Lazy 层叠
      • Lazy 层叠允许懒计算伪元素的样式,即,仅在需要的时候才会计算
      • 目前(对于 Servo,对于 stylo 而言不是那么多),支持这种伪元素的选择器只是可以在布局树上匹配的选择器的子集,它不能保存来自 DOM 树的所有数据
      • 该子集包括标签和属性选择器,足以制作::-servo-details-summary lazy伪元素(关于它,只需要知道它是否在open细节元素中)
      • 由于没有其他选择器适用于它,这(至少现在)不是公共伪元素的可接受类型,但应考虑用于私有伪元素

https://github.com/servo/servo/blob/master/docs/components/style.md

CSS display

CSS 是一门语言,描述 HTML、XML 等 structured documents 在 screen, paper 等媒介上的 rendering。

CSS Display Module Level 3

https://drafts.csswg.org/css-display

这个模块描述了:

  • 如何从 document element tree 生成 CSS formatting box tree
  • 定义了控制它的 display 属性

处于 at-rist 状态的特性,可能会在CR阶段删掉而不用再发布新的CR了:

  • 在 run-ins 存在时,用 ::first-letter
  • display: run-in
  • display 的所有 multi-keyword 值

at-rist 是 W3C Process 的一个术语,并不是说这个特性后续会被 dropped 或者 delayed。
而是 WG 觉得此特性实现成本略高,在短期内实现有难度,
所以在将本模块升级为 PR 时,可以删除该功能,而不用再发布一个新的 CR 了。

介绍

CSS 处理的源文档,是组织成 elements 和 text nodes 的 tree。CSS 将它 render 到 canvas 上(比如 screen, paper 或者 audio stream)。为了 render,CSS 会生成一个中间结构 box tree ,代表渲染文档的 formatting structure。

  • box tree 里的每一个 box ,代表着 canvas 上与之对应的 element 和 pseudo-element
  • box tree 里的每一个 text run ,代表着 text-nodes 里的内容

在创建 box tree 树之前,CSS 会先用 cascading 和 inheritance,将每个 CSS 属性的计算值分配给中的每个 element 和 text-node。

接着,对每个 element,CSS 会根据其 display 的值生成零个或多个 boxes

  • 通常,一个 element 生成一个 box
  • 但也有 display 的值会生成多个 box
    • eg. display:list-item 会生成一个 principal block box 和一个 marder box
  • 有的 display 值会让该元素及其后代不生成任何 box
    • eg. display:none dipaly:contents

Boxes 经常就叫它们的 display 类型,比如由display:block元素生成的 box 就叫 block box 或者 block。元素的 principal box 的 parent box 是它最近的祖先元素 principal box

box 和生成它的 element 有相同的样式,除非另有说明。通常,可继承的属性,会分配给 principal box,然后通过 box tree 继承到由同一个 element 生成的其它全部 box。不可继承的属性,默认会应用到 principal box;但是当 element 生成多个 boxes 的时候,它有时会定义成应用到不同的 box,比如:应用到 table 元素的 border 属性,会应用到它的 table box,但不会应用到它的 principal table wrapper box。如果值计算的过程改变了这些 boxes 的样式,那么元素的样式会被 requested (比如通过 getComputedStyle()),element reflects,对每个属性,值从 box 到应用该属性的 box。

类似的,每个相邻 text nodes 的连续序列生成一个包含它们内容的 text run ,它的样式和生成它们的 text nodes 相同。当然,如果序列不包含文本,那它就不生成 text run 了。

anonymous box 不和任何 element 关联。在有些情况下生成 anonymous boxes,是为了修复 box tree,i.e, 当它需要特定的嵌套结构,但是 element tree 里生成的 boxes 没有提供这种嵌套结构时。e.g. table cell box 就需要一个特定类型的父框 table row box,即便它的父不是 table row box。不同于生成元素的 boxes(它的样式通过 element tree 严格继承),anonymous boxes 是从它的 box tree 父系来继承的。

在 layout 的过程中,boxes 和 text runs 会被分成多个 fragments 。这当什么时候会发生呢?比如,当一个 inline box 或者 text run 跨行时,或者一个 block box 跨页/列时。所以一个 box 由一个或者多个 box fragments 组成;一个 text run 由一个或者多个 text fragments 组成。更多关于 fragmentation 的,可以查看 [css2-break]。

关于 "aural" box tree,可查看 CSS Speech Module
ps. CSS WG 已经停止了在该模块上的工作


  1. 模块交互
    • 此模块替换/扩展了 CSS2 里的 display 属性定义
    • 此模块里的所有属性,都不适用于伪元素::first-line::first-letter
    • 此规范遵循 CSS2 里的 CSS property definition conventions
    • 本规范中未定义的值类型在 CSS Values and Units Module Level 3 中定义
    • 其他 CSS 模块可以扩展这些值类型的定义
    • 除了列出的之外,此规范中定义的所有属性还接受 CSS-wide keywords 关键字作为其属性值
      • initial
      • inherit
      • unset 若属性可继承,则值同inherit, 否则同initial

有趣的观点

CSS WG

florian ... but what we need to worry about is its effects on users, rather than on the mood of developers.

dbaron: Have to think about what happens when web pages start using these things.
... But either way we need a spec about them, so we know what to do about them.

fantasai: In many cases, a feature started out in one camp, was adopted and further developed by another, and passed back and forth until it wound up in specs and implementations.

fantasai: Most modules, however, are a mix. And the design of many features is the result of input from multiple sources.

  • implementers
  • web designers
  • standardization experts
  • web authors (~added by myself @anjia)

Vue CLI 3

https://cli.vuejs.org/zh/guide/installation.html

依赖 Node.js 8.9+, 推荐 8.11.0+ / 10.14.0+ brew upgrade node

npm uninstall vue-cli -g   # 若安装了旧版的 vue-cli,卸载下
npm install -g @vue/cli    # Vue CLI 3
vue --version     # 3.x

vue 3.1.3

Vue CLI 本身使用了一套基于插件的架构

用 Vue CLI 3.1.3 创建项目:

若保存了 preset,会在文件~/.vuerc
这样,下次就不用命令行提示了,直接修改此文件即可

Vue CLI 里,@vue/cli-service安装了vue-cli-service命令

三个脚本:都有各自的 --options

  • serve: 开发环境,构建。
    • 启动一个开发服务器(基于 webpack-dev-server)并附带开箱即用的模块热重载(Hot-Module-Replacement)
    • 命令行参数传参,也可以在vue.config.js里的devServer字段配置开发服务器
  • build: 线上,构建。会在dist/目录产生一个可以用于生产环境的包
    • 带 HTML CSS JS 的压缩
    • 自动做的 vendor chunk splitting(更好的缓存)
  • inspec: 可以审查 Vue CLI 项目的 webpack config
  • lint:
  • 其他命令:npx vue-cli-service help

更多配置 https://cli.vuejs.org/zh/config/

Servo 中的 Crates

style

  • 从DOM树和样式表集合中,计算 specifiedcomputed values
  • 具体说,这个库包含属性的定义,以及将属性解析为 specified 值、再根据 specified 值计算 computed 值的相关代码,当然还有序列化 specified 和 computed 值的代码
  • 主入口是函数 style::traversal::recalc_style_at
    • style::traversal::recalc_style_at 函数,计算单个节点的样式
    • style::traversal 模块,遍历DOM树
    • style 是个 Crate
  • 主要依赖是 cssparserselectors
模块 说明
animation CSS 的 transition 和 animation
applicable_declarations applicable(适用的)声明管理
attr 解析 DOM attributes 的表示
author_styles 一组作者样式表及其计算表示,例如用于 ShadowRoot 和 XBL 的
bezier 贝塞尔曲线
bloom 样式 bloom filter 是一项优化手段,用在深层后代选择器匹配时
computed_values computed 值 的类型
context 计算样式的上下文
counter_style @counter-style 规则
custom_properties 支持层叠变量的自定义属性,CSS Custom Properties for Cascading Variables Module Level 1
data 样式计算中的每个节点的数据
dom 用于从样式计算中访问DOM的类型和特征
dom_apis 一些 DOM API 的通用实现,以便它们可以在 Servo 和 Gecko 间共享
driver 遍历 DOM 树。遍历以顺序模式开始,当它发现 work 的时候有选择的并行
element_state 声明元素可以在
encoding_support 从字节解析样式表(不是从字符串)
error_reporting 用于报告解析错误的类型
font_face @font-face 规则
font_metrics 从样式系统访问字体指标
hash 在 Gecko 模式下重新导出 hashglobe 类型,在 Servo 模式下重新导出 stdlib hashmap
invalidation 与无效样式相关的不同代码
logical_geometry 在 flow-relative 空间中的几何信息
macros 各种宏助手
matching CSS 选择器匹配的高级接口
media_queries 媒体查询
parallel 并行遍历 DOM 树的实现
parser 解析 CSS 代码的上下文
properties 样式系统支持的 CSS 属性。由 build.rs 从 properties.mako.rs 模板生成
rule_cache 从规则节点到 computed 值的缓存,以便缓存重置属性
rule_tree 规则树
scoped_tls Stack-scoped thread-local storage for rayon thread pools
selector_map 一种数据结构,可通过本地名称,ID和哈希有效地索引包含选择器的结构
selector_parser 样式系统支持的伪类和伪元素
servo 样式系统的和 servo 相关的
shared_lock 受同一个锁保护的不同对象
sharing 与样式共享缓存相关的代码,这种优化允许类似节点共享样式而无需两次运行选择器匹配
str 字符串相关的方法,用于 attribute 等类似的东东
style_adjuster 一个结构,用于封装计算样式所需的所有样式修正和标记传播,以使其符合 CSS 规范
style_resolver 给定元素或伪元素的样式解析
stylesheet_set 一组集中的文档样式表
stylesheets 样式表及其 CSS 规则
stylist 选择器匹配
thread_state 支持动态断言,关于什么类型的线程正在运行以及它处于什么状态
timer 定时器模块,用于定义由脚本控制的 Timer 类型
traversal 遍历 DOM 树;bloom 过滤器
traversal_flags 控制遍历过程的标志
values CSS 中用到的常用值-values

http://doc.servo.org/style/index.html

CSS WG 每月动态

  • CSS WG 每月动态 #34
  • W3C 每月动态 #19

文档的状态码的说明,详细见 #18 术语
诸如 ED, FPWD, WD, CR, PR, REC


ways to keep up to date with new publications

want to help

JSON

  1. JSON.stringify()
  2. JSON.parse()

Servo 的环境搭建

需要安装的软件

以 MAC 为例:

  1. Homebrew
    • 一个软件包管理工具,让 Mac 上安装软件变得方便
    • 它是将软件安装到自己的目录,然后再软链到 /usr/local
  2. Python 和 pip
    • 若是用 brew install python 安装的 python,则会自动安装 pip
    • pip 是 Python 的包管理工具
    • 版本的灵活选用,非常方便
      • python --version 是电脑里默认的 python 版本
      • python3 --version 则是直接调用 python3
      • pip3 --version 则是直接调用与 python3 匹配的包管理 pip
  3. Rust 和 Cargo
    • 安装 rustup,它是 Rust 语言的 installer
    • rustup 会自动安装 Rust 和 Cargo
    • Cargo 是 Rust 的包管理(非常方便且强大的工具,类似 npm 于 node)
  4. 安装其它依赖,详见 https://github.com/servo/servo#other-dependencies
    • brew install automake pkg-config python cmake yasm
    • pip3 install virtualenv,或者需要加选项 --user
  5. 构建 Servo,详见 https://github.com/servo/servo#building
    • ./mach build --dev 0:38:02
    • ./mach build --release 0:42:16

virtualenv 能创建一个隔绝的 python 环境(新的文件夹),里面包含所有必要的可执行文件,诸如对应的 Python 和 pip 等。

e.g.

# 1. 安装 homebrew
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

# 2. 安装 Python 和 pip
brew install python # Homebrew 会安装 pip3, pip3 是 Homebrew 版 Python 3 的 pip 的别名
python3 --version # Python 3.5.2
pip3 --version    # pip 18.0 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)

# 4. 安装 virtualenv
pip3 install virtualenv
virtualenv --version # 16.0.0

# 安装 proxychains-ng
brew install proxychains-ng # pywb 要用代理模式 
# run with proxychains4, 

e.g. Python

python   # 将打开 python 解释器
python2  # python2 解释器
python3  # python3 解释器
python --version
python3 --version

e.g. virtualenv

virtualenv -p python3 venv  # 指定python的版本,用virtualenv创建一个虚拟环境,虚拟环境的名字是“venv”,会创建一个文件夹,包含了python的可执行文件+pip库的拷贝,这样就随便安装其它包了)
source venv/bin/activate # 激活虚拟环境
pip3 install git+https://github.com/ikreymer/pywb.git

# 4.1 为工程创建一个虚拟环境
cd my_project_folder
virtualenv my_project # 会在当前的目录中创建一个文件夹
# 包含了Python可执行文件,以及 pip 库的一份拷贝,这样就能安装其他包了。
# 虚拟环境的名字(my_project)可以是任意的,若省略则会将文件均放在当前目录
# or
virtualenv -p /usr/bin/python2.7 my_project  # 指定python解释器

# 4.2 开始使用虚拟环境,需要先被激活
source my_project/bin/activate

# 4.3 停用虚拟环境,如果您在虚拟环境中暂时完成了工作
$ deactivate # 这将会回到系统默认的Python解释器,包括已安装的库也会回到默认的。
rm -rf my_project # 要删除一个虚拟环境,只需删除它的文件夹

# 然后过了一段时间,你可能会有很多个虚拟环境散落在系统各处,有可能已经忘了它们的名字/位置
# 还有其它工具,方便你管理虚拟环境。略。

e.g. Rust 的 Hello world

## cargo 和 rust 语言及其编译器 rustc 本身的各种特性紧密结合
## 基本上 rust 开发管理中所需的手段,cargo 都有

$ cargo new hello_world --bin
# 使用 cargo new 在当前目录下新建了基于 cargo 管理的 rust 项目
# 项目名称为 hello_world
# --bin 表示该项目将生成可执行文件

$ cd hello_world
$ tree .  # 瞄眼目录结构

$ cargo build # cargo 自动构建好全部
$ cargo run  # 运行

$ cargo clean  # 清理 target 文件夹
$ cargo update # 根据 toml 描述文件重新检索并更新各种依赖项的信息,并写入 lock 文件
$ cargo install  # 实际的生产部署


## 两个核心文件:
## cargo.homl 项目数据描述文件,直接面向rust开发人员,决定了项目如何构建、测试、运行
## cargo.lock 项目依赖详细清单文件,一般不管

TPAC 2018 参会总结

今年,我有幸参加了 2018 年的 TPAC 大会。第一次参会,感触颇多。

所以,想在这里分享一些信息,希望对大家有所帮助。

目录:

  1. TPAC 是什么
  2. TPAC 2018 的行程安排
  3. CSS 工作组的新提议
  4. CSS Houdini 的最新进展
  5. WICG 及 W3C:对新技术和 W3C 标准感兴趣的朋友看过来

TPAC 是什么

TPAC,全称 W3C Combined Technical Plenary / Advisory Committee Meetings Week,是 W3C 的年度重要技术会议之一。届时,W3C 的技术架构组(Technical Architecture Group,TAG)、顾问委员会理事会(Advisory Board,AB)、会员代表组成的顾问委员会(Advisory Committee,AC)、W3C 各技术小组以及公众邀请专家等,将进行为期五天的紧密合作,共同商讨未来 Web 的技术方向,深入讨论 W3C 的组织策略(摘自 W3C**)。

这里,我们划三个关键词:W3C、参会人员、年度技术大会。

W3C

W3C(World Wide Web Consortium,万维网联盟)旨在制定开放的 Web 标准,确保 Web 的长期发展。目前共有 1161 个 Web 标准和草案,包括WD CR PR PER REC ret Note 这 7 种。

  • WD Working Draft 工作草案 ✔
    • 不稳定也不完整。目的是创建当前规范的一个快照,也能征求 W3C 和公众的意见
  • CR Candidate Recommendation 候选推荐标准 ✔
    • 所有的已知 issues 都被解决了,向 implementor 征集实现
    • AC 正式审查,可能有三种结果:成为标准、返回工作组继续完善、废弃
      • 此阶段的很有可能成为标准,且如有改动,则需给出改动原因
  • PR Proposed Recommendation 提案推荐标准 ✔
    • CRPR需要全面的 test suite 和实现报告,以证明每个特性都在至少2款浏览器里实现了
    • 意味着其质量足以成为REC。此时,W3C 成员再最后一次 review 下规范
      • 一般不会有实质性的改动;若有,则只能再发布一个新的WDCR
  • PER Proposed Edited Recommendation 已修订的提案推荐标准
  • REC Recommendation 推荐标准,通常称之为 standard,即事实标准 ✔
    • 此时,就不会有太多变动了,当然依然可以收勘误
    • 它可能成为
      • Edited Recommendation 编辑推荐标准。微小改动,然后生成推荐的 Revised Edition
      • Amended Recommendation 修订推荐标准。不增加新功能的实质性更改
      • SPSD Superseded Recommendation 被取代的推荐标准(缺少足够的市场相关性)
  • ret Retired 退役的
  • Note Group Note 工作组说明
    • 不打算成为标准的文档。已经停止使用了
    • 通常记录规范以外的信息,eg.规范的用例及其最佳实践、解释规范被弃用的原因

W3C 是一个国际社区,它没有单一的物理总部,而是由四个机构共同主持的。分别是:美国的麻省理工学院、法国的 ERCIM、日本的庆应义塾大学、**的北京航空航天大学。这四个 Host 里的成员,即 W3C 员工,目前共 62 个人。

此外,W3C 还在世界各地设有 19 个 W3C Offices;它也积极和企业进行合作,截至 2018 年 11 月 4 日,W3C 有 475 个会员单位

其中,**有 18+ 家会员单位,包括 360、阿里、百度、腾讯、华为、**移动、白鹭科技、遨游、21CN、北京知道创宇、北京文因互联、博彦集智、**信通院、**电子科技集团公司信息科学研究院、**电子标准化研究所、中科院、西南科技大学、浙江大学等。

参会人员

符合以下条件之一的,便可参加 TPAC:

  • W3C TAGAB成员
    • TAG Technical Architecture Group 技术架构组
    • AB Advisory Board 顾问委员会理事会
  • W3C 会员单位的AC代表
    • AC Advisory Committee 会员代表组成的顾问委员会
  • W3C 各技术小组的成员
    • WG Working Group 工作组
    • IG Interest Group 兴趣组
    • BG Business Group 商务组
    • CG Community Group 社区组
  • W3C 会员单位的员工
  • 受邀嘉宾
  • W3C 或 W3C Office 的员工

历年 TPAC

从 2010 年开始,TPAC 已经过了 9 个年头。

  • TPAC 2018 法国,里昂(今年)
  • TPAC 2017 美国,加州
  • TPAC 2016 葡萄牙,里斯本
  • TPAC 2015 日本,札幌
  • TPAC 2014 美国,加州
  • TPAC 2013 **,深圳
  • TPAC 2012 法国,里昂
  • TPAC 2011 美国,加州
  • TPAC 2010 法国,里昂

更多信息,可查看 W3C/TPAC

TPAC 2018 的行程安排

TPAC 期间将举行小组会议、AC 会议,以及技术开放日。其中,技术开放日采取分组开放讨论的形式,讨论与 W3C 未来技术相关的话题,对公众及所有参会者开放,其会议纪要也对外公开。顾问委员会会议及小组会议通常仅对 W3C 会员和特邀专家开放。TPAC 给参与的各方提供了一次面对面讨论的机会,这是非常难得的。

下表是 2018 年 TPAC 的行程安排。

  周一 周二 周三 周四 周五
7:00-8:30   ChairsBreakfast  
8:00-8:30 注册 注册 注册 注册 注册
8:30-18:00 WG17个 WG17个 技术开放日 WG19个 WG18个
15:00-18:00   AC会议 AC会议
8:30-17:30 CG4个 CG4个 CG4个 CG3个
晚上 开发者见面会
18:30-22:30
AC晚宴
19:00-21:30
TP招待会
18:30-21:00
  • 小组会议,Group Meetings
    • WG Working Group 工作组
    • CG Community Group 社区组
  • AC 会议,Advisory Committee Meeting,顾问委员会会议
  • 技术开放日,Technical Plenary Day,TP

Chairs-Breakfast 是 TPAC 主席们的早餐时间,也是一次非正式会议,边吃边聊。项目负责人用大约 5 分钟的时间介绍下项目概况及最新进展。

注册。之前已经在线注册过的,在前台根据护照/ID换取胸卡即可。TPAC 也支持现场注册。

小组会议,包括WG(工作组)和CG(社区组)。是在周一、周二、周四、周五这四天里举行的,详情可参阅 TPAC 2018 时间表(点击某个工作组的标题,则可看到它详细的议题列表)。我是参加了 CSS 和 Houdini 的小组会议,这在后面会介绍。

AC 会议是在周二和周四举行的,仅限 AC 代表或其委托人参加。

周一晚上的开发者见面会,有 12 个主题的 Demo 演示,展示了正在进行中的和还在探索中的标准化工作,包括 CSS Houdini、WebAuth、WebAudio、VR/AR、WoT、网络支付、WebML、WebVMT 等,可去 Demo 演示 页面查看主题详情。之后,有五个小演讲,分别是关于 CSS 新布局、Web 隐私、MDN 与 Web 标准、CSS 工作组和 Web 国际化的,感兴趣的朋友可以去页面 W3C开发者见面会 查看相应的介绍和在线 PPT。

周三的技术开放日,上午是 Jeff Jaffe 的欢迎致辞、TAG 组的介绍及公开答疑;中午和下午是话题分组讨论,共有 13*4-2=50 个话题,你可以挑选自己感兴趣的四个进行参与。当天的在线 PPT、议题列表及对应的会议纪要都在 TPAC 2018 Wiki 里列出来了,感兴趣的小伙伴可自取。

分组讨论的话题里,有两个来自**企业:

  • Quick App and W3C,快应用和 W3C,来自华为
  • Web Accessibility Communication,Web 可访问性通信和 AI,来自**移动

据说快应用的“现场讨论非常激烈,并且在 Google 负责 PWA 的 Alex Russell 也对快应用非常感兴趣” 摘自彭星的《小程序应和Web一样开放 | W3C TPAC Lyon 2018 参会感悟》

CSS 工作组的新提议

CSS 工作组的小组会议是在周一和周二进行的,共有 47+ 个话题。会议纪要可以在线查看,如下。

周一.会议纪要 周二.会议纪要

进入会议纪要页面,点击你感兴趣话题,便可定位到彼时的讨论细节。值得一提的是,在纪要里一般都会给出对应的 Github 地址。点开 Github 地址便可看到关于此话题的来龙去脉,包括背景、要解决的问题、对应的规范以及在线讨论等非常有价值的信息,而且线下讨论的内容也会同步在评论区。非常推荐大家去 Github 上 w3c/csswg-drafts 垂直跟进自己感兴趣的 CSS 特性。

这里,我想和大家分享五个有意思的案例。

Spatial Navigation,空间导航。是在 Web 页面上使用方向键,根据元素的显示位置来进行导航。它是在 TPAC 2017 的技术开放日那天第一次和大家见面的,是 LG 的工程师将一个特定于电视领域的问题抽象出来,扩展在了 Web 上,详细的会议纪要见 2017/11/08-spatnav-irc。在之后的一年里,LG 的 Web 研究员 Jihye Hong 和 W3C 的特邀专家 Florian Rivoal 共同起草了这份编辑草案,并带着相应的 Web Polyfill 再一次来到了 TPAC 2018。本次会议上已将此规范纳入了 CSS WG ED,并会向FPWD推进。

  • ED Editors' Draft 编辑草案(不是 W3C 技术报告/规范)
  • FPWD First Public Working Draft 首个公开工作草案

Web 上的现有导航策略是,根据元素的tabindex属性或在 HTML 代码里的位置(无tabindex时),按 Tab 键来导航。不得不说,在无鼠标操作的场景下,Spatial Navigation 还是很有优势的。

Constructable Stylesheet Objects,可构造的样式表对象。它定义了 CSSOM 的附加功能,使 StyleSheet 对象可以直接构造,也提供了在自定义元素中方便操作样式表的 API。它解决的是 Web 组件操作 Shadow DOM 样式的性能问题。

Aspect Ratio,宽高比。目的是让图片有良好的响应性。本次提议是增加一个 CSS 属性 aspect-ratio,让它来控制图片的宽高比。之前还有个提议是给替换元素设置一个 intrinsic size,是考虑到替换元素自带宽高比信息。在此会议上,对该问题尚未给出最终解决方案。如果你有什么想法,欢迎去 Issues.Aspect Ratio 留言。

CSS Environment Variables,CSS 环境变量。在开会之前,我是第一次见这个特性,于是便简单地调研了下什么是 CSS 环境变量,结果发现它最初想要解决的竟然是 iPhone 的“刘海”问题,然后就不禁疑惑:这特定浏览器下的问题,也能提成 Web 规范?针对是否要将此草案升级为FPWD的问题,大家进行了激烈的讨论。最后,综合各方意见,结论是升为FPWD

有个有说服力的观点:虽然问题是产生于特定浏览器,但场景是通用的,规范应该推动这类特性的发展

Scroll Linked Animations,滚动链接动画。该提案目前是在 WICG 里的。它定义了一种纯 CSS 写法,可以控制滚动类动画,实现的功能如 demos 所示。本次的诉求是将此规范从 WICG 移到 CSS WG。会议中,特邀专家 fantasai 确定后续会 review 此规范,且浏览器厂商们均对此提案表达了兴趣。相信此特性在后续的推进上,会比较流畅。想想用纯 CSS 就可以实现视差滚动了,有没有很兴奋?

demos 需要在 Chrome Canary 里运行,因为它目前是用 Animation Worklet 模拟的。Animation Worklet 是 Houdini 的一部分,感兴趣的小伙伴,请移驾下节。

CSS Houdini 的最新进展

Houdini 的小组会议是在周四进行的,议程安排见 TPAC 2018 F2F,会议纪要见 2018-10-25

从规范的整体进度上看,有这两处改动:

  1. CSS Layout 的 API 做了调整,比较重大的改动有:
    • API 是基于 async 函数,而不是 generators 了。详见 Run a Work Queue
    • 之前返回 dictionary,现在是返回带有 dictionary 的 FragmentResult 构造函数
    • 传给 layout 的 Edges 对象现在也会包含滚动条的 padding
  2. CSS Animation Worklet 升级为 FPWD

至此,CSS Houdini 的相关规范及其最新进度如下:

  规范 进度 说明
1 CSS Painting API 1 CR 用JS自定义CSS图像类型
2 CSS Layout API Level 1 FPWD 用JS自定义布局
3 CSS Animation Worklet 1 FPWD 可控制一组动画效果的脚本动画
4 CSS Typed OM 1 WD 将CSSOM的值的字符串转成JS对象,性能好点
5 CSS Properties and Values API 1 WD 注册新的CSS属性,可定义类型/继承/赋初始值
  Font Metrics API 提供基本的字体指标
  CSS Parser API 提供CSS解析的API

最后,再分享两个有用的链接:

WICG 及 W3C

WICG,全称 Web Incubator Community Group,Web 孵化器社区组。它提供了一个轻量场所,方便开发人员提出 Web 新特性、新提议,也方便大家在 Github 上公开讨论。当提案变稳定和成熟了之后,就会考虑迁移到 W3C 工作组。

所以,对 Web 特性有新提议或者想关注新特性的小伙伴,就去逛 WICG 吧。

要将草案提交到 WICG,须至少有一个浏览器厂商对此 API 有兴趣

关于 W3C 的规范和标准。身为前端开发人员,当遇到一个新出炉的 Web 特性时,很多人难免会问“浏览器支持度如何?”。这完全能够理解。但在这里,我想说,就技术层面,浏览器厂商之间还是很有爱的。如果一个特性深受广大开发者的喜爱,大家都乐意实现它。

所以,下次当你发现一个好玩/有趣/有价值/有意义的 Web 新特性时,在感叹完“浏览器兼容性”之后,不要犹豫,就去 github.com/w3c 上表达你的喜爱吧~!有用的 Web 特性,让更多的人知道它的价值,是你的义务,更是你的权利。

Web 的持续发展,离不开每一个人的意见和建议。

结束语

经过这五天的面对面会议,深感我们还能做更多。我们,也要做更多。

最后,感谢 360 公司、奇舞团、360 搜索、360 导航对 360 W3C 工作组 的大力支持。


图为来到 TPAC 现场的 CSS 工作组成员。拍摄于法国里昂,2018.10.23

Terminology

Object-Sizing

  • intrinsic dimensions
  • specified size
  • default object size
  • concrete object size

intrinsic dimensions 内在/固有尺寸

intrinsic dimensions 是指 intrinsic height, intrinsic width 和 intrinsic aspect ratio(宽高比)的集合。对于特定对象,每个都是有可能存在,也可能不存在。intrinsic dimensions 代表物体本身的 preferred 或者 natural size,它与上下文无关。CSS 没有定义如何找出 intrinsic dimensions。

举例:

  • 有3个的:raster images(光栅图像)是同时拥有这三个 intrinsic dimensions 的对象
  • 有1个的:SVG image 可能只有一个,那就是 intrinsic aspect ratio
  • 有0个的:
    • CSS gradients,它就没有一个 intrinsic dimensions
    • embedded documents 也没有,比如 HTML 里的 <iframe> 元素
  • 有2个的:
    • 通常,对象不能只有两个 intrinsic dimensions,因为任何两个都会自动算出第三个维度
    • 但是,有些 replaced elements(比如 form 控件)就只有两个,有 intrinsic width 和 intrinsic height 而没有 intrinsic aspect ratio

如果一个对象(比如 icon)有多个 sizes,则将最大的 size(按面积)作为它的 intrinsic size。如果在那个 size 上它有多个 aspect ratios,或者有多个 aspect ratios 但是没 size,则就使用最接近 default object size 的 aspect ratio 的 aspect ratio。通过使用 contain constraint(包含约束)fit 来看哪一个 aspect ratio 在 default object size 里 fitting 后的面积最大;如果最大面积有多个 sizes,那就选 the widest size 作为它的 intrinsic size。

specified size 指定大小

一个对象的 specified size 是由 CSS 指定的,比如通过 width, height, 或者 background-size 属性。
specified size 可以是确定的 width 和 height,可以是一组约束,也可以是它们的组合。

default object size 默认对象大小

default object size 是一个具有确定 height 和 width 的矩形,用来在既没有 intrinsic dimensions 也没有 specified size 的时候,确定 concrete object size 的。

concrete object size 具体对象大小

concrete object size 是综合对象的 intrinsic dimensions、specified size 以及 default object size,从而生成一个有明确 width 和 height 的矩形。

https://drafts.csswg.org/css-images-3/#sizing-terms

image type

CSS Images Module Level 3

https://drafts.csswg.org/css-images-3

此模块定义了 CSS3 里与<image>type 和一些 replaced elements 相关的特性。

  • 给 representing 2D images 引入了其它值,比如 gradient
  • 也定义了一些 properties,可以
    • manipulating raster images
    • sizing / positioning replaced elements, 比如在 CSS layout 算法里确定 box 里的图像

在 CSS1/CSS2 里,image values 只能是 url value

<image> type 的值,可以用在 background-image, cursor, list-style-image, content 等属性上

此模块里定义的属性,只有 image-rendering 能用在 ::first-line::first-letter

目录:

  1. <image> Type: 2D Image Values
  2. Gradients
  3. Sizing Images and Objects in CSS
  4. Image Processing

Servo 的目录结构

Servo 的目录结构

  • components
    • bluetooth
      • 实现蓝牙线程
    • bluetooth_traits
      • 考虑到构建速度,提取出来的一些 API
    • canvas
      • 实现绘制 2d 和 WebGL 的线程
    • canvas_traits
      • 考虑到构建速度,提取出来的一些 API
    • compositing
      • 集成OS窗口/渲染和事件循环
    • constellation
      • 管理高级浏览上下文(比如页签)的资源
    • devtools
      • 进程内服务器,允许通过远程Firefox开发人员工具客户端操作浏览器实例
    • devtools_traits
      • 考虑到构建速度,提取出来的一些 API
    • gfx
      • 绘制布局页面的结果,并将结果发送到合成器
    • gfx_traits
      • 考虑到构建速度,提取出来的一些 API
    • layout
      • 将页面内容转换成定位的样式框,并将结果传送到渲染器
    • layout_thread
      • 运行布局线程,并和脚本线程进行通信,同时调用布局包进行布局
    • layout_traits
      • 考虑到构建速度,提取出来的一些 API
    • msg
      • 共享API,用于特定线程和crates间的通信
    • net
      • 网络协议的实现,状态和资源管理(缓存,cookie等)
    • net_traits
      • 考虑到构建速度,提取出来的一些 API
    • plugins
      • 语法扩展,自定义属性和lint
    • profile
      • 内存和时间的分析器
    • profile_traits
      • 考虑到构建速度,提取出来的一些 API
    • script
      • DOM 的实现(原生 Rust 代码,绑定到 SpiderMonkey)
    • script_layout_interface
      • 脚本包提供给布局包的 API
    • script_traits
      • 考虑到构建速度,提取出来的一些 API
    • servo
      • 入口,包括 servo 应用程序和 libservo 嵌入库
    • style
      • API,为解析 CSS、样式表和样式元素间的交互
    • style_traits
      • 考虑到构建速度,提取出来的一些 API
    • util
      • 整个项目中常用的各种实用方法和类型
    • webdriver_server
      • 进程内服务器,允许通过 WebDriver 客户端操作浏览器实例
  • etc
    • 对开发人员有用的工具和脚本
  • mach
    • 一个帮助开发人员执行任务的命令行工具
  • ports
    • glutin
      • glutin 窗口库的实现
  • python
    • servo
      • servo 专用的 mach 命令的实现
    • tidy
      • 在合并更改之前自动运行的 Python 包代码 lints
  • resources
    • 运行时使用的文件。在分发二进制构建时需要以某种方式包含
  • support
    • android
      • 构建 Android 平台时,需要特殊处理的库
    • rust-task_info
      • 用于获取进程内存使用情况信息的库
  • target
    • debug
      • 构建由 ./mach build --debug 生成的东东
    • doc
      • 当运行 ./mach doc 时,由 rustdoc 工具生成的文档
    • release
      • 构建由 ./mach build --release 生成的东东
  • tests
    • dromaeo
      • 自动运行 Dromaeo 测试套件的 Harness
    • heartbeats
      • 心跳检测的工具
    • html
      • 手动测试和实验
    • jquery
      • 自动运行 jQuery 测试套件的 Harness
    • power
      • 用于测量功耗的工具
    • unit
      • 单元测试,使用 rust 的内置测试线
    • wpt
      • W3C web-platform-tests 和 csswg-tests,以及运行它们和预期错误的工具

主要依赖

参考

https://github.com/servo/servo/blob/master/docs/ORGANIZATION.md

CSS Animation Worklet

简介

CSS Animation Worklet API 是 CSS Houdini 的一部分。关于 Houdini 的介绍,可查看 #23

这个 API 扩展了 Web 动画堆栈。具体来说:

  • 它扩展了 timelines,让网页开发人员能编排动画效果
  • 它可以控制 stateful(有状态) 的动画效果了。这是驱动动画的一种新方式
    • eg. scroll-driven 滚动驱动的动画
    • eg. 还有其它形式的,还在赶来的路上... 敬请期待

举几个例子,大家来感受下什么是 有状态 的动画。

比如 Chrome 的顶部地址栏(点此链接查看动画效果),它的显隐不仅取决于滚动位置,还取决于滚动方向。i.e. 当上拉页面向下滚动时,它就隐藏了;当下拉页面向上滚动时,它又回来了,且不管是否有没有滚动到页面的顶部。

比如视差滚动,目前在 Web 上实现不太容易,详见 Performant Parallaxing

比如自定义滚动条样式,让猫作为滚动条,要么需要我们自己监听滚动事件,然后还要确保动画流畅又不耗性能;要么实现起来不容易

有了 Animation Worklet API,我们就可以非常直接且简单地控制此类动画效果了。

https://developers.google.com/web/updates/2018/10/animation-worklet

TPAC 2018 准备

TPAC:W3C Combined Technical Plenary / Advisory Committee Meetings Week

参与人员:

  • TAG Technical Architecture Group 技术架构组
  • AB Advisory Board 理事会
  • AC Advisory Committee 会员代表组成的顾问委员会
  • W3C各技术小组
    • WG Working Group 工作组
    • IG Interest Group 兴趣组
    • BG Business Group 商务组
    • CG Community Group 社区组
  • 公众邀请专家

会议安排:5天 10.22-26

  • 5天的白天,8:30-18:00,午餐2h(不同组会错开用餐)
    • 4天 WG会议、AC会议
      • WG会议,周一二四五 全天(观察者座位有限,且旁边没电源)
      • AC会议,周二四 下午
    • 1天 Technical Plenary Day 技术开放日(周三)
  • 5天的晚上,18:30-22:30
    • 周一 W3C Developer Meetup,开发者聚会,全体
    • 周二/四 AC 晚宴(白天下午有AC会议)
    • 周三 Reception,大会晚宴,全体

Schedule

每天的详细安排,见官网,点击相应的工作组标题,可查看详情(内含议题)。

时间表如下:

  周一.22 周二.23 周三.24 周四.25 周五.26
8:00-8:30   Chairs Breakfast 7:00-8:30  
8:30-18:00WG WG17个 WG17个 TP Day & Breakout WG19个 WG18个
  CSS CSS Houdini
Performance
WoT
Performance
WoT
15:00-18:00AC   AC Meeting AC Meeting
8:30-17:30CG CG4个 CG4个 CG4个 CG3个
  Meetup 18:30-22:30 AC Dinner 19:00-
21:30
TPReception 18:30-
21:00

旁听周五的 Automotive WG

WG Working Group Meetings
CG Community Group
Meetup W3C Developer Meetup
TP Technical Plenary Day

http://www.chinaw3c.org/TPAC2018-Overview.html
https://www.w3.org/2018/10/TPAC
https://www.w3.org/2018/10/TPAC/schedule.html


  1. 各个地点
    • Lyon-Saint Exupéry Airport (LYS), Colombier-Saugnieu 里昂圣埃克絮佩里机场
    • WG 在 Cité Centre de Congrès de Lyon
      • 即 Lyon, France at the Lyon Convention Center, 又 Congress center
    • Meetup 在 Université de Lyon, 里昂大学
  2. 周一晚上的 W3C Developer Meetup
  3. 周三全天的 Technical Plenary Day
  4. CSS WG

  • Meetup 有12个主题展示和6个talks
  • Plenary day 有22个提议
  • 各个 WG
    • CSS 列了19个主题,涵盖了TR和issues
    • Perf 列了10个主题,涵盖70个issues
    • WoT 列了3大主题,及较长的schedule

CSS Painting API

今天和大家分享一个非常酷炫的 API CSS Painting API。它是 CSS Houdini #23 的一部分。

简介

它能做什么呢?简单点说,它可以让网页开发人员干预浏览器的绘制(Paint)环节。

为什么要干预绘制环节呢?干预绘制,意味着开发人员可以自行决定页面要绘制成的样子,而不一定非要等到浏览器支持才行。

举个例子,CSS3 的新属性conic-gradient圆锥形渐变:

<style>
    div {
        display: inline-block;
        width: 150px; height: 150px; margin: 10px; 
        border-radius: 50%;
    }
    .color-palette {
        background: conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
    }
    .color-rgb {
        border: 1px solid #999;
        background: conic-gradient(red 0, red 16%,white 16%, white 32%,green 32%, green 48%,white 48%, white 64%,blue 64%, blue 80%,white 80%, white);
    }
</style>

<div class="color-palette"></div>
<div class="color-rgb"></div>

以上代码的运行效果如下,也可在线预览(Chrome 69+):

根据 Can I use,目前仅 Chrome 支持conic-gradient。但是,有了CSS Painting API,我们就可以自己画出类似效果,然后在项目中使用了,而不用等到所有的浏览器都支持conic-gradient

当然,除了充当 CSS3 新特性的 polyfill 之外,我们还可以用它画任意形状。比如钻石状的 Div

比如,符合 Google Material Design波纹效果

浏览器支持情况

截止目前,CSS Painting API 的浏览器支持情况如下:

  • Chrome 65+ 和 Opera 52+ 已经支持
  • Firefox 有实现的意愿
  • Safari 还在考虑中
  • Edge 暂无反馈

也有相应的 CSS Paint Polyfill 供我们选择。

W3C 标准层面

CSS Painting API Level 1 已于今年8月9日成为候选推荐标准,这意味着该模块的所有已知 Issues 均已被解决,并且已经开始向浏览器厂商征集实现。

pywb

pywb

https://pywb.readthedocs.io/en/latest/

Webrecorder pywb 是 python 的一个功能完备的、高级 web 归档捕获和重播框架。它提供了命令行工具,高保真 Web 存档访问。版本 2.0

pywb 是功能完备的 web archive replay 系统,也叫 wayback machine,提供 replay, view, archived。

pywb 有很多命令行,先来看两个入门的

  • wb-manager 命令行工具,管理常见 collection 的操作,查看更多
  • wayback(pywb) 启动一个 web server 提供访问 web archives,查看更多

想要创建一个 WARC 文件,可查看 Creating a Web Archive

Command-Line Apps

安装了 pywb tool-suite 之后,就能用下面的 command-line apps 了。所有的 serve 工具都有不同的默认端口,当然你也可以覆盖它。

  • cdx-indexer
    • 提供了一种方法 从 WARC/ARC 创建 CDX(J) 文件。该工具支持 classic-CDX 和 new CDXJ 格式
    • indexer 也提供了 options - 所有的 WARC 记录,从POST请求里合并数据
    • cdx-indexer -h 可以查看所有的 options
    • 以后可能就被移除了,因为有个独立的工具
  • wb-manager
    • wb-manager command-line tool 可以用来配置 collections 的目录结构和内容,pywb 会自动读取这些目录结构和内容里的 collections
    • 当 wayback 运行的时候,这个工具还能用,pywb 会自动检测很多变化
    • 它可以用来
      • 创建一个新的 collection wb-manager init <coll>
      • 把 WARCs 添加到 collection wb-manager add <coll> <warc>
      • 添加 override 模板
      • 给 collections 添加和移除 metadata,matadata.yaml
      • 列出所有的 collections
      • reindex 一个 collection
      • 迁移,旧的 CDX 到 CDXJ 风格的 indexes
    • 帮助 wb-manager -h
  • warcserver
  • wayback (pywb)
    • main pywb application 就像一个 wayback application 被安装 (pywb是同样的application,以后可能就都用它了)
    • 该app运行在 8080 端口,默认情况。配置是在 config.yaml
    • 详细信息可查阅 Configuring the Web Archive,包括配置选项和自定义
  • live-rewrite-server
    • 这个 cli 是 wayback 的快捷方式(shorcut), 但是只要 Live Web Collection
    • 端口 8090, 从 live web 重写内容,有利于测试
    • 这个app等价于 wayback --live ,除了没有使用 config.yaml 里的其它 collections

Configuring the Web Archive

pywb 提供了一种可扩展的 YAML , 基于配置格式,通过 config.yaml, 在每一个 web archive 的根目录。

https://pywb.readthedocs.io/en/latest/manual/configuring.html#configuring-pywb

  1. Framed vs Frameless Replay
    • 要 archived web content, pywb 支持不同的 modes
    • Frame Replay:
      • archived content 被加载到一个iframe里 actual content
      • top frame UI 提供了 info 和 metadata top frame url
    • Frameless Replay
      • archived content 直接加载,banner UI 被诸如到页面里
    • 如何选择:
      • 鉴于安全问题,在 framed 模式下运行 pywb【因为恶意(malicious)站点可能修改 banner】
      • 某些情况,frameless replay 是合适的(appropriate)
    • 也可以禁用 framed replay
    • 也支持 HTTP/S proxy mode
  2. 目录结构
    • pywb系统自动访问和管理web archive collections-(遵循定义的目录结构)
    • 配置
      • 默认配置 pywb/default_config.yamld
      • 自定义配置 config.yaml
    • 路径
      • index_paths 定义了 index files 的子目录,并决定每个archive collection的内容
        • index files 通常包含到 WARC file 的指针
      • archive_paths 指定 pywb 如何解析在index里列出的 WARC 文件
        • 可配置多个
  3. etc.

HTTP/S Proxy Mode Access

在没有客户端URL重写的情况下,提供可能更好的重放。 通过 --proxy,即 wayback --proxy my-web-archive

servo-warc-tests

sudo pip3 install pywb  --user

## 1. 运行已经存在的
wayback --proxy WBEZ --port 8321
proxychains4 /Users/anjia/quantum/servo/mach run -r --certificate-path proxy-certs/pywb-ca.pem https://www.wbez.org/

## 2. 录制自己的
wb-manager init 360
wayback --proxy 360 --live --proxy-record --autoindex --port 8321

proxychains4 /Users/anjia/quantum/servo/mach run -r --certificate-path proxy-certs/pywb-ca.pem http://localhost:8321/360/record/https://www.360.cn/

proxychains4 /Users/anjia/quantum/servo/mach run -r --certificate-path proxy-certs/pywb-ca.pem https://www.360.cn/

## 3. 测试 
wayback --proxy 360 --port 8321
proxychains4 /Users/anjia/quantum/servo/mach run -r --certificate-path proxy-certs/pywb-ca.pem https://www.360.cn/

e.g.

# 运行一个已存在的 archive
wayback --proxy WBEZ --port 8321 
# WEBZ 是一个已经存在的文件夹,也称为 Collection
# --port 8321 是配置的端口,在 proxychains.conf 里

## 就可以配置浏览器 Proxy Settings 到特定的端口了,再加载特定的 url,就会加载最新的 archive copy 了
wayback --proxy my-web-archive  # 启动pywb,开启代理模式访问

proxychains4 ${SERVO_DIRECTORY}/mach run -r --certificate-path proxy-certs/pywb-ca.pem https://www.wbez.org/
proxychains4 ./mach run -r --certificate-path proxy-certs/pywb-ca.pem https://www.wbez.org/

## other
wayback --record --live -a --auto-interval 10  #
http://localhost:8321/360/record/https://www.360.cn/  # 在浏览器中访问

Servo 快速上手

介绍如何编译、运行、调试 Servo 环境
setup environment to compile, run, debug

  1. build
    • ./mach build -d
    • ./mach build -r
  2. run
    • servo的二进制在target/debug/servotarget/release/servo目录下
    • 可以直接运行二进制,但是更推荐用 ./mach
      • ./mach run -d -- http://github.com 等价于
      • ./target/debug/servo http://github.com
  3. ./mach
    • mach 是一个 python 的 utility
      • 它做了很多事情,让我们操作起来更轻松,eg.build, run, run tests, update dependencies
      • 应用范围:除了编辑文件和操作git之外,其它的事情都是通过mach完成的
    • ./mach --help
    • ./mach run -d [mach options] -- [servo options]
      • -- 把 mach options 和 servo options 分开
        • -- 不必须,但是建议用它。因为 mach 和 servo 有一些同名的选项,eg. --help --debug
    • 需知晓
      • mach commands ./mach --help
      • servo options ./mach run -- --help
  4. Rust
  5. Cargo 和 Crates
    • 一个 Rust library 就叫 crate
    • Servo 用了大量的 crate(依赖项),它们位于 Cargo.toml 这些文件里
    • Servo 分为 components 和 ports(有同名文件夹)
      • Cargo.toml 每个文件夹下都有自己的依赖项
      • Cargo.lock 依赖项的特定版本
    • 若要升级 git
      • 正常的 Rust 项目,cargo update -p std_image
      • 但是 Servo 里,./mach cargo-update -p std_image
    • 关于 Cargo 及相关的两个文件
  6. Working on a Crate
  7. Debugging
  8. Test
    • 没有 test 的 PR 不会被合并。均在 tests 目录下
    • 更新一个测试
    • 新增一个测试
  9. Documentation
  10. Ask questions

以上仅为快照,更多详情见 Hacking Quickstart

网络相关

Cookie

document.cookie

  • 当是 HTTP only 时,通过 JS 取不到。只能通过 HTTP 请求自己传(安全考虑)
  • 想要种/取 cookie,不能是 ip,需要是域名:前端、数据 API
  • 想要让请求服务器的数据 API 也带 cookie,需要设置 Request 的 凭证
    • credentials: "include"withCredentials: true

前端路由和数据接口,均配置 host

function escapeRe(str) {
    return str.replace(/[.*+?^$|[\](){}\\-]/g, '\\$&');
}

// Get the cookie value by key.
function get(key, decoder = decodeURIComponent) {
    if ((typeof key !== 'string') || !key) {
        return null;
    }

    const reKey = new RegExp(`(?:^|; )${escapeRe(key)}(?:=([^;]*))?(?:;|$)`);
    const match = reKey.exec(document.cookie);
    if (match === null) {
        return null;
    }

    return typeof decoder === 'function' ? decoder(match[1]) : match[1];
}

export default {
    get: get
}

深入理解 CSS linear-gradient

渐变

渐变(Gradient)是 CSS3 引入的特性,其定义详见 CSS Images Module Level 3。它本质上是一个 2D 图像,可以对 background-image、list-style-image 和 border 等进行细微着色。

语法是:

<gradient> = <linear-gradient()> | <repeating-linear-gradient()> |
             <radial-gradient()> | <repeating-radial-gradient()> |
             <conic-gradient()>  | <repeating-conic-gradient()>

渐变会绘制到一个叫渐变框(gradient box)的矩形框里。渐变框有具体的大小,而渐变本身是没有固有尺寸的。

固有尺寸(intrinsic dimensions)是固有宽度、固有高度、固有宽高比的集合。对于特定的对象,这三个尺寸都有可能存在或者不存在。比如:光栅图像(image)同时拥有这三个,SVG image 只有一个固有宽高比,CSS 渐变就没有任何固有尺寸。

举个例子,理解下。

在下面的代码中,渐变框就是.demo容器的 padding box,宽 220px 高 120px。

<style>
.demo {
    width: 200px; 
    height: 100px;
    padding: 10px;

    background-color: #eaeaea;
    background-image: linear-gradient(red, green);  /* 线性渐变 */
    background-repeat: no-repeat;
    background-position: center;
}
</style>

<div class="demo"></div>

如果你给 .demo 再增加一行下面的 CSS,那么,渐变框就变成了宽 50px 高 50px。

.demo {
    background-size: 50px 50px;
}

如果你是增加的下面这行 CSS,那么,渐变框就变成了宽 440px 高 240px。

.demo {
    background-size: 200% 200%;
}

同一个渐变,在渐变框大小不同时呈现的不同效果:

220px * 120px 50px * 50px 440px * 240px

理解 1. 渐变本身是没有固有尺寸的,虽然渐变框有具体大小

要想指定一个渐变效果,只需定义这三个元素,即可:

  • 渐变线(gradient line)
  • 渐变线上的起始点(starting point)和结束点(ending point)
  • 在起始点和结束点上的颜色值

这样,颜色就会平滑地填充渐变线上的其余部分。

不同的渐变类型,会定义如何使用渐变线上的颜色们来生成实际的渐变。

渐变类型,在几何上,可以是:

  • 线段(line):对应 CSS 的线性渐变(linear gradient)
  • 射线(ray):对应 CSS 的径向渐变(radial gradient)
  • 螺旋(spiral):对应 CSS 的圆锥渐变(conic gradient)

理解 2. 渐变三元素:渐变线、起始点和结束点、颜色序列

Quantum 初探

  • Quantum 初探:即本篇文章,将介绍 Quantum 项目的由来和概况,引出它的强大“后台” Servo
  • Servo 的设计架构:介绍了 Servo 的基于任务的设计架构,重点介绍了并行并发的策略

Quantum 初探

quantum 这个词来自拉丁语 quantus,意思是 how great - 多么伟大

Quantum 是 Mozilla 为了构建下一代 Web 引擎的项目。在正式介绍它之前,我们需要先了解一些浏览器的相关知识。

浏览器相关

浏览器的结构

图1. 浏览器结构,源自 How Browsers Work

  • 用户界面(User Interface):除了用于显示网页内容的视口之外,你能看到的其它所有部分都属于用户界面,诸如地址栏、前进/后退按钮、书签菜单等。
  • 浏览器引擎(Browser Engine):在用户界面和渲染引擎之间传送指令。
  • 渲染引擎(Rendering Engine):负责显示浏览器请求回来的资源内容。资源可以是 HTML 文档、PDF、图片或其它类型,它们的位置都是用 URI 指定的。如果请求的资源是 HTML,那渲染引擎就负责解析 HTML 和 CSS,并将解析后的内容显示在屏幕上。
  • 网络(Networking):负责网络调用,比如 HTTP 请求。不同平台的实现会有所差异。
  • JS 解释器(Javascript Interpreter):用于解析和执行 JavaScript 代码。
  • 用户界面后端(UI Backend):用于绘制基础小部件,比如组合框和窗口等。此层会公开平台无关的通用接口,而它的底层则是调用操作系统的用户界面方法。
  • 数据持久化存储(Data Persistence):浏览器的客户端持久化存储,诸如 Cookie、Local Storage、Session Storage、 IndexDB、WebSQL 等。

渲染引擎

渲染引擎(Rendering Engine)也叫布局引擎(Layout Engine)。浏览器的渲染引擎,有时也简称为浏览器引擎(Browser Engine)。

注意:这里的 Browser Engine 不同于图1中的 Browser engine

所以,以下名词往往是等价的:
浏览器的渲染引擎 ~ 浏览器引擎 ~ 渲染引擎 ~ 布局引擎

主流浏览器用到的渲染引擎如下:

浏览器 渲染引擎(开发语言) 脚本引擎(开发语言)
Chrome Blink (c++) V8 (c++)
Opera Blink (c++) V8 (c++)
Safari WebKit (c++) JavaScript Core (nitro)
FireFox Gecko (c++) SpiderMonkey (c/c++)
Edge EdgeHTML (c++) Chakra JavaScript Engine (c++)
IE Trident (c++) Chakra JScript Engine (c++)

之所以列出“脚本引擎”,旨在强调渲染引擎不负责 JS。对 JS 的解释和执行是由独立的引擎完成的,比如大名鼎鼎的 V8 引擎。

对 HTML 文档来说,渲染引擎的主要任务就是解析 HTML 和 CSS,再把最终的结果绘制到屏幕上。

下面是各个渲染引擎的时间线,我们可以很直观地看出她们的生日和年龄。其中,Trident、KHTML、Presto 已经停止更新;Gecko、WebKit、Blink 和 Edge 依然在持续更新中。

图2. 浏览器引擎,源自维基百科 Browser engine

Quantum

背景

2013年,Mozilla 启动了一项研究型项目 Servo。这是一个从零开始设计的浏览器引擎,目标是提高并发性和并行性,同时减少内存安全漏洞。它是由 Rust 语言编写的,而 Rust 有更好的内存安全属性和并发功能。

2016年4月,考虑到 Servo 还需要几年才能成为功能完备的浏览器引擎。所以,Mozilla 决定启动 Quantum 项目,将 Servo 的稳定部分带到 Firefox 里。

关于集成到 Firefox 中的 Servo 组件,可查看 Jack Moffitt 的演讲视频 Web Engines Hackfest

Quantum 项目

Gecko 是 Mozilla 的一个成熟且功能健全的 Web 浏览器引擎,它起源于1997年的 Netscape。Mozilla 采用渐进式的方法,将 Servo 里已稳定的组件迁移到 Gecko 中,用户将不必等很长时间就能看到 Firefox 在稳定性和性能上的显著改进。

2017年11月发布的 Firefox 57 是第一版启用了 Servo 组件的浏览器,之后便在此版本的基础上进行迭代开发。

Quantum 以 Gecko 引擎为基础,同时利用了 Rust 的良好并发性和 Servo 的高性能组件,为 Firefox 带来了更多的并行化和 GPU 运算,让 Firefox 更快更可靠。

多个子项目

Quantum 是一个将 Mozilla 的多个社区及其代码仓库联系在一起的大项目,它包含多个子项目:

  1. rust-bindgen:是 Rust 语言的 C++ 绑定生成器。Quantum 用它生成的代码,可用在 Firefox 的 C++ 代码和 Servo 的 Rust 组件之间。感兴趣的朋友可以查看 Contributing to rust-bindgen
  2. Quantum CSS:也叫 Stylo,旨在把 Servo 的并行 CSS 系统集成到 Gecko 中,可以充分利用多核 CPU 的硬件特性。
  3. Quantum Render:旨在将 WebRender 作为 Firefox 的图形后端。WebRender 是 Servo 针对 GPU 渲染进行了优化的下一代渲染器,它用保留模式模型替换立即模式绘制模型,通过利用 CSS/DOM 和场景图的相似性,让 GPU 加速更容易。
  4. Quantum Compositor:将 Gecko 现有的合成器移到自己的进程中,将图形驱动程序相关的崩溃和浏览器页签隔离开来,以让 Firefox 更稳定。此特性已经在 Firefox 53 里发布了。
  5. Quantum DOM:在 DOM 中使用协同调度线程来提高响应速度,而不用增加进程数和内存,这会让 Gecko 更具响应性,尤其是当有大量后台页签打开的时候。不同页签(也可能是不同 iframe)的 JS 代码将运行在独立的线程中,某些后台页签的代码将永远不会被运行。更多可查看 Bill McCloskey 的博客
  6. Quantum Flow:探讨了目前尚未被涵盖的性能改进,例如 UI 性能优化。

下一步

至此,大家对 Quantum 项目的由来和概况有了初步的认识。后续,我会继续探索更多详细内容。敬请期待。

参考

前言

HTTP,Hypertext Transter Protocol,超文本传输协议。

Transfer 翻译成了“传输”
Transfer 是表示“(状态的)转移”,依据 HTTP 制定者之一的博士论文

它有很多应用,但最著名的是用于 Web 浏览器和 Web 服务器之间的双工通信。

在这本书中,我们会:

  • 梳理 HTTP 中相互关联且常被误解的规则,介绍 HTTP 各方面的特性
  • 介绍 HTTP 是怎样做的,同时也解释了它为什么要这样做
  • 介绍了必需且重要的非 HTTP 技术

目标读者:

  • 希望理解 HTTP 和 Web 底层结构的
  • HTTP 及相关 Web 技术参考书
  • 系统架构师:了解如何设计、实现、管理复杂的网络架构
  • 性能工程师:理解缓存和性能优化
  • 咨询专家:理解 Web 技术前景

本书:

  • 澄清了一些常见误解,推荐了各种业内诀窍,提供了便捷的参考资料
  • 阐述了标准规范
  • 详细探讨了 Web 正常工作所必需的相关技术

5 个逻辑部分

本书共 21 个章节,分为5 个逻辑部分,每个部分都是一个技术专题。

  1. HTTP:Web 的基础
    • 第1章 入门
    • 第2章 URL、URN
    • 第3章 HTTP 报文
    • 第4章 HTTP 连接管理
  2. HTTP 结构
    • 第5章 Web 服务器结构
    • 第6章 HTTP 代理服务器。它是个中间服务器,可以作为 HTTP 服务与控制平台
    • 第7章 Web 缓存。缓存是...来提高性能、减少流量的设备
    • 第8章 网关和应用服务器。HTTP 可以与协议不同的软件进行通信,eg. SSL 加密协议
    • 第9章 Web 上的各种客户端类型。eg.浏览器、机器人、网络蜘蛛、搜索引擎
    • 第10章 HTTP-NG 协议:还在研究中的 HTTP 协议
  3. 识别、认证、安全
    • 第11章 识别用户
    • 第12章 验证用户身份、HTTP 认证机制
    • 第13章 摘要认证
    • 第14章 密码体系、数字证书、SSL
  4. 实体、编码、国际化
    • 第15章 HTTP 内容的结构
    • 第16章 Web 标准
    • 第17章 协商可接受内容
  5. 内容发布与分发
    • 第18章 在网络托管环境中部署服务器、HTTP 对虚拟网站托管
    • 第19章 创建 Web 内容,并将其装载到 Web 服务器中去
    • 第20章 流量分发到一组服务器
    • 第21章 日志格式、常见问题

8 个附录

包含参考资料、相关技术介绍

  • 附录 A:URI 方案支持的协议
  • 附录 B:HTTP 响应代码
  • 附录 C:HTTP Header 字段
  • 附录 D:MIME 类型
  • 附录 E:HTTP 认证中的 Base-64 编码
  • 附录 F:HTTP 认证方式的实现
  • 附录 G:HTTP Header 的语言标签值
  • 附录 H:支持国际化 HTTP 的字符编码

《HTTP权威指南》 读书笔记

Object

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object

1. Object.keys()

返回:新的字符串数组

  • 由可枚举属性组成
  • 属性的顺序同使用for-in
const arr = ['an', 'jia', 'zora'];
console.log(Object.keys(arr));  // ["0", "1", "2"]

const obj = {
  0: 'an',
  1: 'jia',
  2: 'zora'
};
console.log(Object.keys(obj));  // ["0", "1", "2"]

const person = Object.create({}, {
  // 是不可枚举的,所以不输出
  getName: {
    value: function() { return this.name; }
  }
  
});
person.name = 'anjia';
console.log(Object.keys(person));  // ["name"]

// 基本数据类型
console.log(Object.keys('anjia'));  // ["0", "1", "2", "3", "4"]
/**
 * 说明:ES5会报错,ES2015会将非对象参数强制转换成对象
 */

nginx

简介

Nginx 是 HTTP Server。常用来做:

  • 静态内容服务器:放置静态资源
  • 代理服务器:转发请求给后面的应用服务器

它和 Tomcat 虽然都叫 web server,但二者有本质的不同。Tomcat 是 Application Server,常用来做应用容器,让 Java App 运行在其中。

HTTP Server 关心的是 HTTP 协议层面的传输和访问控制,所以 Nginx 有代理、负载均衡等功能。客户端通过 HTTP Server 访问服务器上存储的资源(HTML文件、图片文件等)。通过 CGI 技术,也可以将处理过的内容通过 HTTP Server 分发。但是,HTTP Server 始终只是把服务器上的文件,如实的通过 HTTP 协议传输给客户端。

而 Application Server,则是应用执行的容器。它首先需要支持开发语言的 Runtime(对于 Tomcat 而言,就是 Java),保证应用能正常运行。其次,需要支持应用相关的规范,比如类库、安全方面的特性(对于 Tomcat 就是要提供 JSP/Sevlet 运行需要的标准类库等)。为了方便,Application Server 也会集成 HTTP Server 的功能,但不如专业的 HTTP Server 那么强大。所以 Application Server 往往是运行在 HTTP Server 背后,执行应用,将动态内容转化为静态内容之后,通过 HTTP Server 分发到客户端。

Nginx 的优点是占用资源少。

安装

在 Mac 下

brew install nginx
sudo brew services start nginx  // 如果 http://localhost:8080 看到 Welcome to nginx! 页面,说明启动成功
sudo brew services stop nginx   // 关闭 nginx 服务
sudo brew services reload nginx   // 重新加载

nginx -v                   // 查看版本
nginx -s reload  // 重新加载
nginx -s stop    // 停止

sudo nginx -t  // sudo
// nginx 会测试配置文件的语法,告诉你配置文件是否正确,同时也会给出配置文件的路径

位置

  • 查看 nginx 的路径 ps aux | grep nginx
  • 查看 nginx 配置文件的路径 /..../nginx -t

semver

语义化版本控制规范

SemVer: Semantic Version, 语义化版本

  • 规范版本号的格式,来约束版本号的配置和增长
  • 实现版本号的解析和比较,eg. ^ ~ >= > <= <

主版本号.次版本号.修订号

  • 主版本号:当你做了不兼容的 API 修改。 不兼容的大改动
  • 次版本号:当你做了向下兼容的功能性新增。 功能性改动
  • 修订号:当你做了向下兼容的问题修正。 bugfix

x.y.z-alpha 内测版
x.y.z-beta 公测版
x.y.z-stable 稳定版

初始化开发版本 0.1.0
正式上线了一次,就 1.0.0

主版本号为0时,是为了做快速开发

Cargo

概述

Cargo 做了4件事情:

  1. 引入两个包含项目的各种信息元数据文件
  2. 拉取、构建项目的依赖(默认从crates.io上拉)
  3. 构建项目,使用正确的参数调用rustc或其它构建工具
  4. 生成约定俗成的文件目录,以便轻松入门 Rust 项目
cargo new hello_test --bin  # --bin 意思是 binary,即二进制项目,比如库文件
cd hello_test
tree .

cargo build # 获取依赖及其子依赖,编译它们,更新 Cargo.lock
./target/debug/hello_test  # hello_test 是个二进制文件

cargo run # 和以上2条命令等价。会自动检测-如果已编译过&文件未改变,则就不重新编译了

cargo build --release  # 会放在文件夹 ./target/release 下

cargo check  # 快速(检查)编译,但不生成可执行文件

cargo update  # 更新所有依赖
cargo update -p rand # 仅更新"rand"依赖

eg.

  • Cargo.toml 描述项目的依赖
  • Cargo.lock 由Cargo自动生成,不用手动修改
    • 如果构建其它项目依赖的库,那可将Cargo.lock放在.gitignore
    • 如果构建一个可执行的应用程序或者命令行工具,记得把Cargo.lock放在git的版本仓库里

eg. 来自 crates.io

  • semver 语义的版本解析和比较
  • semver-parser 解析semver规范
  • rustc_version 查询已安装的rustc编译器版本
  • libc 类型和绑定到本地C函数的库,通常是在libc或其他通用平台的库中

eg. 生成的目录结构

  • Cargo.tomlCargo.lock在项目的根目录
  • src放源码
    • src/lib.rs 默认的库文件
    • src/main.rc 默认的可执行文件
    • src/bin/*.rs 其它的可执行文件
  • test
  • examples
  • benches

eg. 关于测试的

cargo test # 会寻找两个地方,每个`src`文件和`tests/`目录
cargo test foo  # 将会运行名字里带有foo的所有测试
# 更多见 https://doc.rust-lang.org/book/testing.html

命令“cargo test”运行测试用例,寻找两个地方的:

  • 每个src文件,单元测试
  • tests/目录下的,集成测试
    • 可以用 Travis CI,.travis.yml
    • 也可以用 GitLab CI,.gitlab-ci.yml

eg.构建缓存

  • 在单个工作区里,Cargo 会在诸多包之间共享构建工件
  • 在不同的工作区里,Cargo不共享
    • 若还想共享,则可以用第三方工具sccache

https://doc.rust-lang.org/cargo/guide/index.html

补充

  • 绝大多数 Rust 项目都使用 Cargo。当项目变大时,需要控制各个方面,单纯的rustc编译就不方便了
  • cargo 还有个好处,可以抹平不同操作系统的差异,都用一样的命令
cargo build  # 开发-快速构建
cargo build --realease  # 发布-快速运行

.toml, Tom's Obvious, Minimal Language, 作者是 Tom Preston-Werner toml

CSS Layout API

简介

CSS Layout API 能让网页开发人员写自己的布局算法。它是 CSS Houdini #23 的一部分

这样,我们在 CSS 里写display属性值的时候,就不局限于浏览器已经支持的那几种布局了,诸如display: blockdisplay: flex等,我们可以写display: layout(myLayout)

Layout API 里的所有内容都在 Logical Coordinate System(逻辑坐标系统)里计算。这样的话,网站的书写模式就可以自动影响到你写的布局了。

对于熟悉文本从左到右书写的开发人员来说,Logical Coordinate System 到我们日常用到的名词的对应关系如下:

Logical 在CSS里写的
inlineSize width
inlineStart left
inlineEnd right
blockSize height
blockStart top
blockEnd bottom

知道这个名词对应关系有什么意义呢?等会在编写 Layout Worklet 的时候,你会看到代码里大量用到了第一列的属性值。

参考 https://github.com/w3c/css-houdini-drafts/blob/master/css-layout-api/EXPLAINER.md

数组遍历

  • 遍历数组
    • for 最古老的循环,效率高。处理大量循环的时
    • for-of 只遍历对象上的,ES6
    • forEach 数组内部的方法。稀疏数组
    • for-in 会遍历继承链的对象属性。遍历对象的属性,仅可枚举类型
  • 其它
    • map 产生新数组。数组的每项经过计算,产生的新值
    • filter 产生新数组。返回 true 的值
    • reduce 返回值。前项加后项,累计计算最终值
    • every 布尔值。遇到返回 false 的,就返回 false。 检测每一项是否符合条件
    • some 布尔值。遇到返回 true 的,就返回 true。 检查是否有符合条件的项

for > for-of > forEach > filter > map > for-in
every > some

const arr = [1,2,3,4,5];
// const arr = [{'name': 'anjia', 'score': { 'math':90, 'eng':100}}, 5, 'test'];

function log(msg, prefix){
    prefix ? console.log('\n' + msg) : console.log(msg);
    
}

log('for', true);
for(let i=0, len=arr.length; i<len; i++){
    log(arr[i]);
}

log('for-of', true);
for(let item of arr){
    log(item);
}

log('forEach', true);
arr.forEach(item => {
    log(item);
});

log('for-in', true);
for(i in arr){  // 下标
    log(arr[i]);
}
function log(msg, prefix){
    prefix ? console.log('\n'+msg) : console.log(msg);
}

log('map', true);
var arr = [1,2,3,4,5];
var newArr = arr.map(item => {
    log(item);
    return item*2;
});
log(newArr);


log('filter', true);
var arr = [1,2,3,4,5,6];
let greater = arr.filter(item =>{
    log(item);
    return item > 3;
});
log(arr);
log(greater);


log('reduce', true);
let scores = [10, 20, 30];
let total = scores.reduce((sum, item)=>{
    console.log('sum=', sum, '  item=', item);
    return sum + item;
}, 0);
log(total);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.