前端101 – Vertical Slides – Fragments



前端101 – Vertical Slides – Fragments

1 3


101

前端工程师进阶用PPT

On Github zmmbreeze / 101

前端 101

MZhou / @mmzhou

HTML

<!DOCTYPE html> <!-- HTML5 -->
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>标题</title>
</head>
    <body>内容</body>
</html>

HTML语义化

  • p - 段落
  • h1,h2,h3,h4,h5,h6 - 层级标题
  • strong,em - 强调
  • ins - 插入
  • del - 删除
  • abbr - 缩写
  • code - 代码标识
  • cite - 引述来源作品的标题
  • q - 引用
  • blockquote - 一段或长篇引用
  • ul - 无序列表
  • ol - 有序列表
  • dl,dt,dd - 定义列表
  • table - 表格,列表
  • article - 独立的文档、页面、应用、站点
  • section - 按主题将内容分组,层级标题,并非「语义化的 div」。当你希望这个元素的内容体现在文档的提纲 (outline) 中时,用 section 是合适的。
  • nav - 导航
  • aside - 与周围内容关系不太密切的内容 (eg. 广告 / 侧边栏内容)
  • header - 一组介绍性描述或导航信息(eg. 目录 / 搜索框 / logo)
  • footer - 底部元素,代表最近的父级区块内容的页脚
  • address - 联系人信息

更多详细信息

  • SEO
  • 有标准、不纠结
  • 高大上

CSS、JAVASCRIPT的引入

<link rel="stylesheet" href="page.css"/>
<script src="page.js"></script>

protocol-relative URL

<link rel="stylesheet" href="//qq.com/page.css">

使用 protocol-relative URL 引入 CSS,在 IE7/8 下,会发两次请求。

  • Stalled / Blocking - 请求发起之前等待的时间总和。包含了用于处理代理的时间。另外,如果有已经建立好的连接,那么这个时间还包括等待已建立连接被复用的时间,这个遵循 Chrome 对同一源最大6个TCP连接的规则。Chrome的bug导致stalled了21秒
  • Proxy Negotiation - 处理代理的时间
  • DNS Lookup - 查找DNS的时间,页面上每个新的域都需要一次完整的寻路来完成DNS查找
  • Initial Connection / Connecting - 建立链接的时间,包括TCP三次握手及重试握手,还有处理SSL
  • SSL - 处理 SSL 握手
  • Request Sent / Sending - 请求发送的时间
  • Waiting (TTFB) - Time To First Byte,等待首个字节返回的时间
  • Content Download / Downloading - 全部内容下载完成的时间

Performance API

多普勒测速

Yahoo的军规

<!-- BAD: 会block外部script请求 -->
<script src="//example.com/test.js"></script><p></p>

<!-- GOOD: 外部script会异步加载 --> <script> var script = document.createElement('script'); script.src = "//example.com/test.js"; document.getElementsByTagName('head')[0].appendChild(script); </script>

<link href="http://example.com/test.css?rtt=2" rel="stylesheet">
<!-- body 内容 -->
<script>
    var script = document.createElement('script');
    script.src = "http://example.com/test.js?rtt=1&a";
    document.getElementsByTagName('head')[0].appendChild(script);
</script>

<script>
    var script = document.createElement('script');
    script.src = "http://example.com/test.js?rtt=1&b";
    document.getElementsByTagName('head')[0].appendChild(script);
</script>

Javascript 可以操作 CSSOM,所以需要等到 css 完全加载解析完毕之后才能执行 script 标签

<link href="http://example.com/test.css?rtt=2" rel="stylesheet">
<!-- body 内容 -->
<script src="http://example.com/test.js?rtt=1&a"></script>
<script src="http://example.com/test.js?rtt=1&b" ></script>

浏览器(包括 IE8/9 和 Android 2.3/2.2)会预解析查找可以下载的外部文件,并行下载,串行执行

<!-- 现代浏览器使用 'async',旧IE使用 'defer' -->
<script src="//somehost.com/awesome-widget.js" async defer></script>

并行下载,不会 block DOM 不能确保执行顺序

No Rules! Just Tools!

布局

本质上是一个个长方形框的布局

盒模型

.box {
    margin: 10px;
    padding: 10px;
    width: 100px;
    height: 100px;
    border: 1px solid #CCC;
}

box-sizing

这是一个块级元素 display: block;

块级元素,新开始一行并且尽可能撑满容器

Lorem Ipsum is simply dummy text of the printing and typesetting industry. 这是一个行内元素 display: inline; 。Lorem Ipsum is simply dummy text of the printing and typesetting industry.

行内元素,一行之内横向的排列,宽高不起作用

Lorem Ipsum is simply dummy text of the printing and typesetting industry. 这是一个行内块级元素 display: inline-block; 。Lorem Ipsum is simply dummy text of the printing and typesetting industry.

行内块级元素,一行之内横向的排列,宽高起作用

<div class="demo">
    <p>Lorem Ipsum is simply <span>这是一个行内块级元素 display: inline;</span> Lorem.</p>
    <p>Lorem Ipsum is simply <span>这是一个行内块级元素 display: inline;</span> Lorem.</p>
</div>
Lorem Ipsum is simply 这是一个行内块
级元素 display: inline; Lorem.
Lorem Ipsum is simply 这是一个行内块
级元素 display: inline; Lorem.
  • 红色 - 块级框
  • 绿色 - 行框
  • 橙色 - 行内框

普通文档流,一般是从左至右、从上到下

浮动元素

.demo-box {
    margin: 20px;
    padding: 10px;
    border: 5px dashed #000;
}
.demo-float-r,
.demo-float {
    margin: 10px;
    float: left;    /* 左浮动 */
    width: 100px;
    height: 100px;
    background: red;
}
.demo-float-r {
    float: right;   /* 右浮动 */
}
left
right
浮动元素脱离文档流 对于它的父元素来说,浮动元素是不存在的(父元素不会自适应以包裹浮动元素,所以需要清除浮动) 一个浮动元素的位置会尽可能的靠近他父元素的左上角或者右上角 行内元素添加浮动属性会变成块级元素 浮动元素不会浮动出父元素的边界

Lorem Ipsum is simply dummy text of the printing .

1
2
3
4
5
6
7
8
9
浮动元素前面定义的元素会把浮动元素挤到下面 先声明的浮动元素有优先靠近父元素左上角或者右上角位置的权利 如果有多个相同方向的浮动元素,浮动元素也会尽可能的靠近左上角或者右上角,直到父元素宽度没法放下这个元素的时候,这个元素才会被挤下去

清除浮动

.demo-clear {
    clear: left;  /* 清除左浮动 */
    margin: 10px;
    width: 100px;
    height: 100px;
    background: blue;
}
1
2
3
clear
4
5
6
7
8
9
  • clear 属性定义了元素的左侧或右侧或全部不允许出现浮动元素
  • clear 属性仅仅应用于块级元素
<div class="demo-box demo-clearfix">
    <div class="demo-float">1</div>
    <div class="demo-float-r">2</div>

    <!-- 清除浮动 -->
    <div class="demo-clear"></div>
</div>
/**
 * http://nicolasgallagher.com/micro-clearfix-hack/
 * For modern browsers
 * 1. 处理 Opera 下 contenteditable 时候的bug
 * 2. 使用 table 是为了触发 BFC,解决顶部元素的 margin 折叠问题
 */
.demo-clearfix:before,
.demo-clearfix:after {
    content: ' ';   /* 1 */
    display: table; /* 2 */
}
.demo-clearfix:after {
    clear: both;
}

/**
 * IE 6/7 下触发 hasLayout 实现兼容
 */
.demo-clearfix {
    *zoom: 1;
}
1
2
3
4
5
6
7
8
9

学习CSS布局

CSS 兼容性 Hack

IE 条件注释

<!--[if IE 6]>
    这段文字只在IE6浏览器显示
<![endif]-->

属性前缀 Hack

Selector IE6(s) IE7(s) IE8(s) IE9(s) IE10(s) color:red Y Y Y Y Y color:red\0 N N Y Y Y color:red\9\0 N N N Y Y *color:red Y Y N N N _color:red Y N N N N

CSS Hack Table

CSS 会忽略不支持的属性或选择器

.test1 {
    background-color: #FFF;                    /* 不支持rgba */
    background-color: rgba(255, 255, 255, .8); /* 支持rgba */
}
.test2 {
    background-image: url(top.png);
    /* IE9+ 不支持多背景 */
    background-image: url(data:image/svg+xml;base64,....), none;
}

Selector

.portal .lbf-combobox #user-id.lbf-combobox-label {
    /* ... */
}

解析顺序:从右到左

#user-id {
    /* ... */
}

CSS selector matching is now reasonably fast for the absolute majority of common selectors that used to be slow at the time of the profiler implementation. This time is also included into the Timeline "Recalculate Style" event.

As such, I believe the CSS selector profiler is not as useful as it [might have been] used to and can safely be dropped. This will also reduce the fraction of developers trying to micro-optimize already fast selectors.

避免冲突

/* index_header.css */
.header .current {
    background: #FEFEFE;
}

/* index_list.css */
.current {
    background: blue;
}

OOCSS / SMACSS / BEM

BEM

/* Block */
.menu {
    display: block;
}
/* Element */
.menu__item {
    float: left;
}
/* Modifier */
.menu__item_current {
    background-color: #EFEFEF;
}

优点

  • 减少后代选择器
  • 易重用,可扩展

但是很丑

AMCSS

缺点

  • 各种 JS 库支持不够好
  • 开发者的习惯很难改

CSS Modules

/* components/submit-button.css */
.common {
    /* font-sizes, padding, border-radius */
}
.normal {
    composes: common;
    /* blue color, light blue background */
}
.error {
    composes: common;
    /* red color, light red background */
}
.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ }
.components_submit_button__normal__def6547 { /* blue color, light blue background */ }
.components_submit_button__error__1638bcd { /* red color, light red background */ }
/* util.less */
.clearfix() { /* 清除浮动代码 */ }
/* common.less */
/* `g-` 作为全局模块的前缀 */
.g-header {
    .clearfix();
}
/* page/index.less */
/* `page-` 作为页面class的前缀 */
.page-index {
    /* 页面模块Block名 */
    .header {
        .clearfix();

        /* 页面模块中的Element */
        &-menu {
            /* ... */
        }
        /* 尽可能使用标签,确保 HTML 语义化 */
        &-menu li {
            float: left;
        }
        /* Element的状态名 */
        /* 非Block命名尽量简写,`cur === current` */
        &-menu .header-menu-cur {
            float: left;
        }
    }
}

Javascript 用 ID 选择器,CSS 用 Class 选择器

尽量做到“行为和样式分离”

JAVASCRIPT 单线程

setTimeout(function () {
    console.log(1);
}, 0);
console.log(2);

字符串拼接

var result = ''
    + '<h1>' + title + '</h1>'
    + '<p>' + content + '</p>';
var result = [
    '<h1>' + title + '</h1>',
    '<p>' + content + '</p>'
].join('');

Array.join > +操作符

只是在旧浏览器下(IE7-),现代浏览器中差不多

性能测试

String.replace 或者模板引擎更好

var tpl = ''
    + '<h1>{title}</h1>'
    + '<p>{content}</p>';

var template = function (tpl, data) {
    return tpl.replace(/{([^}]+)}/g, function (r, $0) {
        return data[$0] || '';
    });
};

var result = template(tpl, {
    title: '标题',
    content: '内容'
});

优势

  • 直观可读性好
  • 模板字符串可复用
  • 模板默认提供转义,更安全
  • JS压缩引擎可以合并字符串,压缩后没有 + 操作符

ES2015 / ES6 的模板字符串

var data = {
    title: '标题',
    content: '内容'
};

var result = `<h1>${data.title}</h1>
<p>${data.content}</p>`;

正则表达式

11年 Twitter 改版

引入了无限滚动特性

页面滚动时速度变的很慢!

jQuery 1.4.2 升级到 1.4.4

定位bug

$(window).bind('scroll', function () {
    if (nearBottomOfPage()) {
        // load more tweets ...
    }
});
$details.find('.details-pane-outer');

jQuery 1.4.3开始选择器引擎 Sizzle 会优先使用 querySelectorAll

// 1.4.2
document.getElementsByClassName('details-pane-outer');
// 1.4.4
document.querySelectorAll('details-pane-outer');
var divs = document.getElementsByTagName("div");
var i = 0;

while(i < divs.length){
    document.body.appendChild(document.createElement("div"));
    i++;
}

这是一个死循环!

  • Live NodeList 快
  • Static NodeList 慢

为什么 getElementsByTagName() 比 querySelectorAll() 快?

  • DOM 查询结果需要重用时一定要缓存
  • 绑定重复触发的事件时(例如window scroll 事件)一定要做 throttle 或 debounce

throttle 和 debounce

  • debounce - 阻止事件触发直到N段时间后
  • throttle - 限制事件触发频率

throttle 和 debounce 的区别

记录页面的 A 标签的点击事件

$('a').click(function (e) {
    dosomething();
});
var links = document.getElementsByTagName('a');
for (var i = 0, l = links.length; i < l; i++) {
    links[i].onclick = (function (link) {
        dosomething();
    })(links[i]);
}

链接多了之后很慢!

事件代理

捕获与冒泡

document.body.addEventListener('click', function (e) {
    e.preventDefault();
    var target = e.target;
    var isLink = target.nodeName === 'A';
    if (isLink) {
        // 记录操作
    }
}, false);
$('body').on('click', 'a', function () {
    // 记录操作
});

优势

  • 能处理动态更新的DOM元素
  • DOM元素很多时,有性能优势

DOM 操作

  • relayout / reflow - 重新计算节点的位置
  • repaint - 重新绘制节点到屏幕上
  • composite - GPU 合成

渲染流程

layout thrashing

// 读
var height = element.clientHeight;
// 写
element.style.height = (height + 2) + 'px';
// 写
element.style.height = (height + 3) + 'px';

浏览器优化之后,会把DOM操作放到一个队列里面。在将来的某个时候执行(例如屏幕刷新前)

// 读
var height1 = element1.clientHeight;
// 写
element1.style.height = (height1 + 2) + 'px';
// 读
var height2 = element2.clientHeight;
// 写
element2.style.height = (height2 + 2) + 'px';

此时第二次读操作执行前,会强迫浏览器 relayout 一次,这样才能计算得到新的 height

// 读
var height1 = element1.clientHeight;
// 读
var height2 = element2.clientHeight;
// 写
element1.style.height = (height1 + 2) + 'px';
// 写
element2.style.height = (height2 + 2) + 'px';

读写分离,减少 layout thrashing

function call1() {
    // 读
    var height1 = element1.clientHeight;
    // 写
    element1.style.height = (height1 + 2) + 'px';
}
function call2() {
    // 读
    var height2 = element2.clientHeight;
    // 写
    element2.style.height = (height2 + 2) + 'px';
}

call1();
call2();

不一定 DOM 操作都在一个方法体里面

function call1() {
    // 读
    var height1 = element1.clientHeight;
    // 写
    requestAnimationFrame(function() {
        element1.style.height = (height1 + 2) + 'px';
    });
}
function call2() {
    // 读
    var height2 = element2.clientHeight;
    // 写
    requestAnimationFrame(function() {
        element2.style.height = (height2 + 2) + 'px';
    });
}

Fast dom

合成线程

css triggers

话说回来,一般调用没那么频繁。只是特殊情况下(动画)需要注意优化

No Rules! Just Tools!

Q & A

前端 101 MZhou / @mmzhou