CSS大会的总结

本次大会的ppt都在w3ctech上: http://www.w3ctech.com/topic/1463
个人具体的学习和总结如下:

1.手机淘宝CSS实践启示录


1.1 勾三股四讲了Web Components,主要是做前端组件,实现高度复用。资料如下:
Web Components 是 W3C 定义的新标准,目前还在草案阶段。
Web Components 的核心思想就是把 UI 元素组件化,即将 HTML、CSS、JS 封装起来,使用的时候就不需要这里贴一段 HTML,那里贴一段样式,最后再贴一段 JS 了。一般来说,它其实是由四个部分的功能组成的:

  • HTML Imports
  • HTML Templates
  • Custom Elements
  • Shadow DOM

###模板
使用template标签来新建模板:

<template id="appTmpl">
... 和之前一样的内容 ...
</template>

使用下面的 JS 就可以访问到模板,并将其插入 DOM 中。

var tmpl = document.querySelector('#appTmpl');
// 取到 t 以后,可以像操作 DOM 一样随意修改其中的内容
// 然后需要从模板创建一个深拷贝(Deep Copy),将其插入 DOM
var clone = document.importNode(tmpl.content, true);
// 创建深拷贝还可以使用下面的方法:
// var clone = tmpl.content.cloneNode(true);
document.body.appendChild(clone);

主要有四个特性:

  • 惰性:在使用前不会被渲染;
  • 无副作用:在使用前,模板内部的各种脚本不会运行、图像不会加载等;
  • 内容不可见:模板的内容不存在于文档中,使用选择器无法获取;
  • 可被放置于任意位置:即使是 HTML 解析器不允许出现的位置,例如作为 select 的子元素。

###Shadow DOM
Shadow DOM 是一个 HTML 的新规范,其允许开发者封装自己的 HTML 标签、CSS 样式和 JavaScript 代码。使用 Shadow DOM,我们需要在一个元素上创建一个根(Root),然后将模板内文档添加到这个根上即可。

<template id="appTmpl">
  <style>
  /* ... 将 CSS 移动到模板内 ... */
  </style>
  ... 原来的模板内容 ...
</template>
<div class="app"></div> 
var tmpl = document.querySelector('#appTmpl');
var host = document.querySelector('.app');
var root = host.createShadowRoot();
root.appendChild(document.importNode(tmpl.content, true));

###自定义元素Custom Elements
自定义元素允许开发者定义新的 HTML 元素类型。带来以下特性:

  • 定义新元素
  • 元素继承
  • 扩展原生 DOM 元素的 API
    使用 document.registerElement() 创建一个自定义元素:
var Helloworld = document.registerElement("hello-world", {
  prototype: Object.create(HTMLElement.prototype)
});

document.body.appendChild(new Helloworld());
还可以进行元素继承,扩展新的api:
var MyButton = document.registerElement("my-button", {
  prototype: Object.create(HTMLButtonElement.prototype)
});
var MyButtonProto = Object.create(HTMLButtonElement.prototype);

MyButtonProto.sayhello = function() {
  alert("hello");
};

var MyButton = document.registerElement("my-button", {
  prototype: MyButtonProto
});


var myButton = new MyButton();
document.body.appendChild(myButton);

myButton.sayhello(); // alert: "hello"
使用new来进行实例化
var myButton = new MyButton();
myButton.innerHTML = 'click me!';
document.body.appendChild(myButton);
或者在页面使用标签:
<my-button>click me!</my-button>

生命周期
元素可以定义特殊的方法,来注入其生存周期内的关键时间点。生命周期的回调函数名称和时间点对应关系如下:

  • createdCallback: 创建元素实例时
  • attachedCallback: 向文档插入实例时
  • detachedCallback: 从文档移除实例时
  • attributeChangedCallback(attrName, oldVal, newVal): 添加,移除,或修改一个属性时

    var MyButtonProto = Object.create(HTMLButtonElement.prototype);
    
    MyButtonProto.createdCallback = function() {
      this.innerHTML = 'Click Me!';
    };
    
    MyButtonProto.attachedCallback = function() {
      this.addEventListener('click', function(e) {
        alert('hello world');
      });
    };
    
    var MyButton = document.registerElement('my-button', {
      prototype: MyButtonProto
    });
    
    var myButton = new MyButton();
    document.body.appendChild(myButton);
    

    ###HTML Imports

    通过<link>标签来引入 HTML 文件,使得我们可以用不同的物理文件来组织代码。
    <link rel="import" href="http://example.com/component.html" >
    

    资料:跟 Web Components 打个啵
    http://zorro.io

1.2 勾股还讲了关于手机淘宝的响应式开发处理方案


em为单位:
这种技术需要一个参考点,一般都是以<body> 的”font-size”为基准。比如说我们使用”1em”等于”10px”来改变默认值”1em=16px”,这样一来,我们设置字体大小相当于“14px”时,只需要将其值设置为”1.4em”。
计算公式是:1 ÷ 父元素的font-size × 需要转换的像素值 = em值。
Rem为单位
CSS3的出现,他同时引进了一些新的单位,包括我们今天所说的rem。在W3C官网上是这样描述rem的——“font size of the root element” 。我在根元素 <html> 中定义了一个基本字体大小为62.5%(也就是10px。设置这个值主要方便计算,如果没有设置,将是以“16px”为基准)。从上面的计算结果,我们使用“rem”就像使用“px”一样的方便,而且同时解决了“px”和“em”两者不同之处。
lib-flexible 可伸缩布局方案
https://github.com/amfe/lib-flexible
https://www.npmjs.com/package/px2rem

  • break by-self
  • focus on the scenes
  • find a platform
  • choose the “right”
  • use the “power”
  • keep walking fast

2.New W3C CSS Checker


Mike Smith讲w3c在开搞新的css checker https://csschecker.w3.org。顺便说了下新语言Rust,确实太新了,14年底刚刚出来。顺带还透露Servo, Mozilla’s next-generation browser engine。撸码圈又多了新的语言了。。。

3.CSS 预处理器与 CSS 后处理器


###CSS 预处理器
广义上说,目标格式为 CSS 的预处理器是CSS 预处理器,但本文特指以最终生成 CSS 为目的的领域特定语言。Sass、LESS、Stylus是目前最主流的 CSS预处理器。编译前与编译后是完全不同的语言。

###CSS 后处理器
CSS 后处理器是对 CSS 进行处理,并最终生成 CSS 的预处理器,它属于广义上的CSS 预处理器。我们很久以前就在用CSS 后处理器了,最典型的例子是CSS 压缩工具(如clean-css),只不过以前没单独拿出来说过。还有最近比较火的Autoprefixer,以Can I Use上的浏览器支持数据为基础,自动处理兼容性问题。编译前与编译后的代码都是 CSS。

###优秀的 CSS 后处理器框架
1.Rework
2.PostCSS
PostCSS是一个CSS后处理器框架,允许你通过 JavaScript 对 CSS 进行修改。是从Autoprefixer项目中抽象出的框架。

###PostCSS有以下特点:
它和Rework非常相似,但提供了更高级的API,更易扩展它可以在现有Source Map的基础上生成新的Source Map在原有 CSS 格式的保留方面做的更好,便于开发编辑器插件比Rework更年轻,还只有Autoprefixer一个成功案例其实Autoprefixer最初是基于Rework做的,但后来作者有更多需求(上面的列表),就造了PostCSS这个轮子。

###用法:
安装nodejs
安装npm

npm install gulp -g
npm install postcss -g
npm install gulp-postcss -g 
npm install cssnext -g
npm install autoprefixer -g

npm install gulp --save--dev
npm install postcss --save--dev 
npm install gulp-postcss --save--dev 
npm install cssnext --save--dev
npm install autoprefixer --save--dev

新建gulpfile.js,设置解析方式:

var gulp = require('gulp');
var postcss = require('gulp-postcss');
var autoprefixer = require('autoprefixer');
var cssnext = require('cssnext');
var opacity = function (css, opts) {
    css.eachDecl(function(decl) {
        if (decl.prop === 'opacity') {
            decl.parent.insertAfter(decl, {
                prop: '-ms-filter',
                value: '"progid:DXImageTransform.Microsoft.Alpha(Opacity=' + (parseFloat(decl.value) * 100) + ')"'
            });
        }
    });
};

gulp.task('css', function () {
    var processors = [
        autoprefixer({browsers: ['last 1 version']}),
        opacity,
        cssnext()
    ];
    return gulp.src('./*.css')
        .pipe(postcss(processors))
        .pipe(gulp.dest('./dest'));
});

运行gulp进行处理,后面带上task的名称:

gulp css

text.css:

.container{
    display: flex;
    opacity: .2;
}
/* 变量 */
:root {
--fontSize: 14px;
--mainColor: #333;
--highlightColor: hwb(190, 35%, 20%);
}
/* 自定义 media queries */
@custom-media --viewport-medium (min-width: 760px) and (max-width: 990px);
    /* 变量引用 以及使用 calc() 运算*/
    body {
    color: var(--mainColor);
    font-size: var(--fontSize);
    line-height: calc(var(--fontSize) * 1.5);
    padding: calc((var(--fontSize) / 2) + 1px);
    }
    /* 颜色处理函数 */
    a {
    color: color(var(--highlightColor) blackness(+20%));
    background: color(red a(80%))
    }
    /* 使用自定义 media queries */
    @media (--viewport-medium) {
    .foo {
    display: flex;
    font-size: calc(var(--fontSize) * 2 + 6px);
    }
}

生成的代码

.container{
    display: -webkit-flex;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    opacity: .2;
    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
}
/* 变量 */
/* 自定义 media queries */
/* 变量引用 以及使用 calc() 运算*/
body {
color: #333;
font-size: 14px;
line-height: 21px;
padding: 8px;
}
/* 颜色处理函数 */
a {
color: rgb(89, 142, 153);
background: #FF0000;
background: rgba(255, 0, 0, 0.8)
}
/* 使用自定义 media queries */
@media (min-width: 760px) and (max-width: 990px) {
    .foo {
        display: -webkit-flex;
        display: -webkit-box;
        display: -ms-flexbox;
        display: flex;
        font-size: 34px;
    }
}

4.Web高性能动画


该ppt深入讲解了提高网页动画性能的各种方法,关于layout,paint,composite 有个比较具体的介绍:

浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点。在渲染DOM的时候,浏览器所做的工作实际上是:
1. 获取DOM后分割为多个图层
2. 对每个图层的节点计算样式结果(Recalculate style--样式重计算)
3. 为每个节点生成图形和位置(Layout--回流和重布局)
4. 将每个节点绘制填充到图层位图中(Paint Setup和Paint--重绘)
5. 图层作为纹理上传至GPU
6. 符合多个图层到页面上生成最终屏幕图像(Composite Layers--图层重组)

Chrome中满足以下任意情况就会创建图层:
* 3D或透视变换(perspective transform)CSS属性
* 使用加速视频解码的 video 节点
* 拥有3D(WebGL)上下文或加速的2D上下文的 canvas 节点
* 混合插件(如Flash)
* 对自己的opacity做CSS动画或使用一个动画webkit变换的元素
* 拥有加速CSS过滤器的元素
* 元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)
* 元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)

需要注意的是,如果图层中某个元素需要重绘,那么整个图层都需要重绘。比如一个图层包含很多节点,其中有个gif图,gif图的每一帧,都会重回整个图层的其他节点,然后生成最终的图层位图。所以这需要通过特殊的方式来强制gif图属于自己一个图层(translateZ(0)或者translate3d(0,0,0)),CSS3的动画也是一样(好在绝大部分情况浏览器自己会为CSS3动画的节点创建图层)

简化一下上述过程,每一帧动画浏览器可能需要做如下工作:
1. 计算需要被加载到节点上的样式结果(Recalculate style--样式重计算)
2. 为每个节点生成图形和位置(Layout--回流和重布局)
3. 将每个节点填充到图层中(Paint Setup和Paint--重绘)
4. 组合图层到页面上(Composite Layers--图层重组)

如果我们需要使得动画的性能提高,需要做的就是减少浏览器在动画运行时所需要做的工作。最好的情况是,改变的属性仅仅印象图层的组合,变换(transform)和透明度(opacity)就属于这种情况

setTimeout的奇葩坑:

60fps是动力也是压力,因为它意味着我们只有16.7毫秒(1000 / 60)来绘制每一帧。如果使用setTimeout或者setInterval(以下统称为timer)来控制绘制,问题就来了。

首先,Timer计算延时的精确度不够。延时的计算依靠的是浏览器的内置时钟,而时钟的精确度又取决于时钟更新的频率(Timer resolution)。IE8及其之前的IE版本更新间隔为15.6毫秒。假设你设定的setTimeout延迟为16.7ms,那么它要更新两个15.6毫秒才会该触发延时。这也意味着无故延迟了 15.6 x 2 - 16.7 = 14.5毫秒。

            16.7ms
DELAY: |------------|

CLOCK: |----------|----------|
          15.6ms    15.6ms
所以即使你给setTimeout设定的延时为0ms,它也不会立即触发。目前Chrome与IE9+浏览器的更新频率都为4ms(如果你使用的是笔记本电脑,并且在使用电池而非电源的模式下,为了节省资源,浏览器会将更新频率切换至于系统时间相同,也就意味着更新频率更低)。

具体的介绍:http://www.infoq.com/cn/articles/javascript-high-performance-animation-and-page-rendering

5.重拾 CSS 的乐趣(上)


https://github.com/cssmagic/blog/issues/52