引言

  1. 熟悉jquery的同学一定对ready函数非常熟悉,然而又有多少人对这个ready有个很好的认识呢。
    什么才算DOMContentLoaded?
  2. 现在webpack流行,打包css为一个bundle有什么缺点?
    为什么css文件必须要放在html头上,放在尾部会有什么后果

举几个例子

  1. setTimeout(()=>{},0)
    上面的这个函数肯定会在DOMContentLoaded之后触发
    重新摘录MDN对DOMContentLoaded事件的解释:
    The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed,
    without waiting for stylesheets, images, and subframes to finish loading.
    重新摘录MDN对lLoad事件的解释:
    A very different event - load - should be used only to detect a fully-loaded page.

  2. 第二个问题着实让我很头大。因为最近写了slide-swipe移动端手势库,
    发现如果采用webpack方案会让offsetWidth在ios模拟器里面取值出错,chrome和火狐浏览器是不会报错的。
    这个很小的bug最后的解决方案是把css提取出来放在html头部。

再思考

  1. 毕竟前端代码很多时候脱离不了浏览器,所以理解浏览器运行机制必不可少。

    之前我翻译过浏览器的运行机制文章。正如文章所说: 渲染首屏页面依赖于cssOM和DOM两块,
    如果没有css信息,最典型的一点就是没有layout布局。
    有的浏览器是可以在加载外部css文件的时候触发重绘重排的,但是有的则要在下个循环做这件事。
    虽然在js执行的过程中,如果对DOM执行setter和getter会触发重绘重排,但是这样得到的数据往往只是中间值。
    比如element.offsetWidth等等,它的值是不精确的。

  2. 针对上述问题,最好的方式莫过于把css放在头部,保证dom的layout信息比较完备,已经适合js来获取相关信息了。

    这个问题实在是比较纠结,最好的方式还是看浏览器的渲染执行线。

学习过程中顺便发现的资料

css优化策略

前言

做过移动开的同学都知道,苹果手机在滚动的时候是具有弹性距离的。
最难处理的一点就是,你要在页面里面的某一块做垂直滚动的处理。
这个时候往往会触发整个屏幕的整体移动,这个是个非常头疼的问题

解决问题

笔者最近在封装一套移动端的手势库,对于这个问题做了一定的研究,下面给出解决方案。

解决原理

  • document的touchmove事件在scroll之前触发
  • 事件是可以冒泡的

针对以上解决原理,解决移动端的hack方案如下

1
2
在全局环境下,加上如下代码即可
document.addEventListener('touchmove',()=>{})

总结

是不是很神奇?笔者针对这个hack方案做了不少实验,包括事件冒泡顺序及scroll和touchmove处理。
最后的结论就是通过冒泡,让你针对的dom节点优先移动,然后document的事件才会执行。
由于你自己写了touchmove,因此劫持了原生的处理方案,使得scroll没被触发。

1、测试移动端事件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<div id="d" style="width: 100px; height: 100px; border: 1px solid black;">
</div>
<button id="reset">reset</button>
<div id="container"></div>
</body>
<script type="text/javascript">
$('#reset').bind('click',function(){
$('#container').empty();
});
var startTime;
var log = function (msg,e) {
var clientX="clientX ";
if(msg.match('touchmove')){
clientX+=(e['targetTouches'][0].clientX+'').substr(0,3);
}
var div = $('<div></div>');
div.html((new Date().getTime()+'').substring(6) + ': ' + (new Date().getTime() - startTime) + ': ' + clientX+msg)
$('#container').append(div);

};
var addListener= function (type,func) {
var d = document.getElementById('d');
d.addEventListener(type,func)
};
var touchStart = function (e) {
startTime = new Date().getTime();
log('touchStart',e);
};
var touchMove = function (e) {
log('touchmove',e);
};
var touchEnd = function (e) {
log('touchEnd',e);

};
var mouseDown = function () {
log('mouseDown');
};
var click = function () {
log('clickend');
};
var mouseClick = function () {
log('mouseClick');
};
var mouseUp = function () {
log('mouseUp');
};
var mouseOver = function () {
log('mouseOver');
};
addListener('mousedown', mouseDown);
addListener('mouseover', mouseOver);
addListener('click', mouseClick);
addListener('mouseup', mouseUp);
addListener('touchstart', touchStart);
addListener('touchend', touchEnd);
addListener('click', click);
addListener('touchmove', touchMove);

快速点击下的测试结果

华为荣耀手机(网页性能超乎预期,点击会在touchEnd后立即执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//test1
touchStart 1
touchEnd 74
mouseOver 82
mouseDown 86
mouseUp 93
mouseClick 94
clickEnd 95
//test2
touchStart 1
touchEnd 107
mouseOver 117
mouseDown 121
mouseUp 129
mouseClick 131
clickEnd 133

三星手机(点击事件相比touch大致会有200ms的延迟)

1
2
3
4
5
6
7
8
//test1
touchStart 1
touchEnd 63
mouseOver 258
mouseDown 260
mouseUp 265
mouseClick 271
clickEnd 275

iphone 4s(点击事件相比touch大致会有300ms的延迟,同时mouseover将会劫持后续的click事件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//测试1
touchStart 1
touchEnd 36
mouseDown 394
mouseUp 404
mouseClick 405
clickEnd 406
touchStart 1
touchEnd 63
mouseDown 251
mouseUp 255
mouseClick 257
clickEnd 259
//测试2
touchStart 1
touchEnd 59
mouseOver 418

iphone 6

1
2
3
4
5
6
7
8
9
10
11
//测试1
touchStart 1
touchEnd 48
mouseOver 412
//测试2
touchStart 1
touchEnd 50
mouseDown 420
mouseUp 428
mouseClick 429
clickEnd 430

点击小结 手机点按触发顺序touch会在mouseEvents之前


2、手指移动测试结果

测试数据暂时不给出,大致的情况是touchmove事件的触发往往和触发click事件互斥

手机内置浏览器环境单一(webkit = webCore + JSCore)

  • iphone
  • Android
  • Nokia S60
  • webOS
  • linux
  • windows(webkit 发展较晚)

css支持程度

  • flexBox布局【兼容性写法】:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     <div class="flex-parent">
    <div class="flex-son"></div>
    <div class="flex-son"></div>
    <div class="flex-son"></div>
    </div>
    .flex-parent {
    /*设置父元素为伸缩容器*/
    display: -webkit-box; /*老版本:iOS 6-, Safari 3.1-6*/
    display: -webkit-flex; /*新版本:Chrome*/
    display: flex; /*标准规范:Opera 12.1, Firefox 20+*/
    /*设置父元素内部的伸缩子元素项目换行规则:水平排列(row)不换行(nowrap)*/
    -webkit-box-orient: horizontal; /*老版本:iOS 6-, Safari 3.1-6*/
    -webkit-flex-flow: row nowrap; /*新版本:Chrome*/
    flex-flow: row nowrap; /*标准规范:Opera 12.1, Firefox 20+*/
    }
    .flex-son {
    /*设置子元素伸缩项目的伸缩比例*/
    -webkit-box-flex: 1; /*老版本:iOS 6-, Safari 3.1-6*/
    -webkit-flex: 1; /*新版本:Chrome*/
    flex: 1; /*标准规范:Opera 12.1, Firefox 20+*/
    /*此处无需设置宽度,因为flexbox会自动伸缩*/
    height: 2rem;
    margin: 0 0.5rem;
    background: #000;
    }

flex参考w3c官方标准

  • css3

    我们可以把移动设备分为低端系统(IE9、android2.1~3.0、ios3.2)和高端系统(ios4+、 android4.1~4.2),
    针对低端系统,采用普通方案,并使用hack兼容
    针对高端系统,可以采用更丰富更炫酷的效果

浏览器事件机制

  • 这个得专门开一篇博客来研究

参考cnblog的整理资料

浏览器内核概念(渲染引擎+语法解释)

浏览器内核负责对网页语法进行解释,并渲染网页

1、IE内核浏览器概览

  • Trident:IE内核

    2005年前的Trident几乎没有更新,导致了与W3c的标准脱节
    补充说明:IE从版本11开始支持webGL
    IEd8的js引擎为Jscript
    IE9开始用chakra(速度和标准化层面更出色)

  • 内核在浏览器中发展概览

    IE6-IE8(Trident 4.0),IE9(Trident 5.0),IE10(Trident 6.0)
    360浏览器(6.0为Trident+Webkit,7.0为Trident+Blink)
    猎豹和360极速浏览器(7.5之前为Trident+Webkit,7.5为Trident+Blink)
    百度和世界只穿(早期为IE内核,2013年采用chrome+IE)

好消息:

2016年1月12日,微软官方宣布对IE8、9、10停止支持,未来的开发者可以更加专注于逻辑和展现,而不用处理更多兼容性问题

总结

部分浏览器的新版本是“双核”甚至是“多核”,其中一个内核是Trident,然后再增加一个其他内核。国内的厂商一般把其他内核叫做“高速浏览模式”,而Trident则是“兼容浏览模式”,用户可以来回切换


2、Gecko火狐内核

  • js引擎 : SpiderMonkey
  • 发展

    源自微软的开发人员为支持W3C标准而生
    基本覆盖面来自火狐浏览器

3、presto为opera早期内核

Opera现已改用Google Chrome的Blink内核

4、webkit内核(开源)

  • 组成: webCore排版引擎+JavaScriptCore解析引擎
  • 内核覆盖范围

    google的chrome浏览器(脚本理解使用V8)
    360及搜狗的极速模式
    手机客户端!!(android && iphone)

Blink是一个由Google和Opera Software开发的浏览器排版引擎(13年4月发布)


排版引擎(概览)

目前来看可以专注于webCore排版引擎,之后chrome出品的Blink值得关注
对于移动端开发者来说,可以暂时专注于webCore

前言

css的性能问题,在前端项目中很少被关注到,这是个很大的遗漏点。
实际上如果启动GPU加速,优化main线程和layout线程,可以让页面动画更加顺畅呢!

主线程和layout线程

  • 主线程主要负责以下工作:

    • 运行JavaScript
    • 计算HTML元素的CSS样式
    • 布局页面
    • 把页面元素绘制成一个或多个位图
    • 把这些位图移交给排版线程
  • 排版线程主要负责以下工作:

    • 通过GPU渲染位图,并显示在屏幕上
    • 向主线程请求更新位图的可见部分或即将可见的部分
    • 判断出当前页面处于可见的部分
    • 判断出即将通过页面滚动而可见的部分
    • 随着用户滚动页面来移动这些部分(译者注:可见部分的和即将可见的部分)

线程如何合作

当用户滚动的时候,layout线程向主线程请求更新显示部分位图.
当页面变化时,layout线程尝试以每秒60帧的速度重绘页面。

GPU

GPU定义: 图形处理器(graphics processing unit),是显卡的处理器

处理transition

1
2
3
4
5
6
7
8
9

div {
height: 100px;
transition: height 1s linear;
}

div:hover {
height: 200px;
}

启用transform

1
2
3
4
5
6
7
8
9

div {
transform: scale(0.5);
transition: transform 1s linear;
}

div:hover {
transform: scale(1.0);
}

图解浏览器对css动画的处理过程

  • transition处理过程
  • transform处理过程

前言

做前端的同学大部分都用过ellipsis,一般用于对字符串进行展示并添加’…’以进行展示

1
2
3
white-space:nowrap; //文本不会换行,文本会在在同一行上继续,直到遇到 <br> 
text-overflow:ellipsis;
overflow:hidden

以上代码的局限性在于仅仅对一行文本做这种处理,不能处理多行文本。

解决方案

笔者经过网上资料的查找,发现webkit-line-clamp在webkit内核浏览器中,非常适合对多行文本进行处理。
设置方式如下

1
2
3
4
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;

gulp再研究

  • 术语

    • glob模式: shell 所使用的简化了的正则表达式。可以参考npm的glob模块
  • api

    • gulp.src(globs[, options]) 按照globs规则定义输入数据流
    • gulp.dest(path[, options]) 定义输出流
    • gulp.task(name[, deps], fn) 定义任务
    • gulp.watch(glob[, opts, cb]) 添加事件监听
  • 管道

    • pipe(gulp的每个操作返回一个stream),调用node的fs模块方法,进行IO操作

前沿

前端框架react+webpack在利用ES6/ES7的语法层面,相比于AMD规范借用requireJS
之流,可以避免预先配置脚本依赖等繁琐工作,而更快速的开发网站。
webpack里面有个重要的loaders对象用来解析相关脚本或者css
其中对js的解析用到一个很重要的loader====> babel

喜当爹的babel6

在半年前,babel还是5开头的版本号,使用起来真是简单方便
没想到,babel的开发人员为了js的未来,于15年末将babel推向了6系列。
比较坑爹的一点是,babel6和babel5系列,配置方式基本不兼容!!!
对于不明真相、用惯babel5系列的同学简直就是坑了个爹

babel官网的相关论述

  • 本来只需要安装一个babel5的东西被拆分成了babel-cli+ babel-core
  • babel6需要根据需求进行插件(plugins)安装和预设(presets)
  • 相同的一点是plugins和presets都可以在.babelrc中配置

babel 组成

  • babel6本身就是由一大堆的插件组成体系
  • 对于细节化的插件可以通过babel-preset-es2015与babel-preset-react完成
  • stage选项被 类似{“presets”: [“stage-2”]}代替

终极方案.babelrc(with npm install the listed dependencies)

1
2
3
{
"presets": ["es2015",'react']
}

webpack之loader

  • 链式(类似gulp使用中的pipe方法,最后一项返回结果)
  • 接受query参数,用于传递给相关loader
  • Plugins可以带给相关loader更多特性

ES6在IE下的兼容性问题

对照列表