一、浏览器简介
- 分类:现在主要有五大主流浏览器: Chrome, Internet Explorer, Firefox, Safari and Opera.移动端上是Android Browser, iPhone, Opera Mini and Opera Mobile, UC Browser, the Nokia S40/S60 browsers,除了Opera,这些浏览器都是基于WebKit内核的(目前可能有变)。
- 功能 :根据W3C制定的一系列规范,从服务端请求并渲染资源
- 普遍外观:地址栏,前进后退,书签,刷新及取消,主页
- 深层结构:下边主要介绍———渲染引擎及JS引擎
- 用户界面(User Interface) :包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
- 浏览器引擎(Browser engine): 在用户界面和呈现引擎之间传送指令。
- 呈现引擎(Rendering engine): 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
- 网络(Networking) : 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
- 用户界面后端(UI backend):用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
- JavaScript 解释器(JS Interpreter):用于解析和执行 JavaScript 代码。
- 数据存储(Datapersistence):这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。
二、渲染引擎
-
分类:不同浏览器用不同渲染引擎。
Internet Explorer uses Trident, Firefox uses Gecko, Safari uses WebKit. Chrome and Opera (from version 15) use Blink, a fork of WebKit.WebKit 是一个开源渲染引擎,起初作为Linux platform的引擎,后被 Apple应用于Mac. 详见 webkit.org.
-
加载:
- 浏览器根据 DNS 服务器得到域名的 IP 地址
- 向这个 IP 的机器发送 HTTP 请求
- 服务器收到、处理并返回 HTTP 请求
- 浏览器得到返回内容
客户端:如在浏览器输入https://www.baidu.com/
,经过 DNS 解析,查到baidu.com
对应的 IP(不同时间、地点对应的 IP 可能会不同),浏览器向该 IP 发送 HTTP 请求。
服务端:服务器接收到 HTTP 请求,经计算,返回 HTTP 请求,内容如下:
-
渲染:
浏览器对请求的呈现。默认渲染引擎可以呈现html,xml及图片。(通过插件)也可以呈现其它数据,比如pdf等。 目前只考虑html和css方面。
渲染主流程:
<center> Figure : Rendering engine basic flow</center>
<center>Figure : WebKit main flow </center>
content tree:在此,渲染引擎解析html文档并将元素转换成DOM节点。
render tree:在此,渲染引擎解析style(外部css文件或内联style)并转换。
<mark> 这是一个渐进式过程. 为保证好的UED,渲染引擎尽早展现内容. 在开始构建并展现render树之前,它不会等所有html被解析。部分内容被解析并呈现,同时进程继续解析网络中不断请求到的其余内容。
具体渲染过程
- 根据 HTML 结构生成 DOM 树
- 根据 CSS 生成 CSSOM
- 将 DOM 和 CSSOM 整合形成 RenderTree
- 根据 RenderTree 开始渲染和展示
- 遇到
<script>
时,会执行并阻塞渲染
浏览器拿到了 server 端返回的 HTML 内容后,开始解析并渲染。最初拿到的内容就是一堆字符串,必须先结构化成计算机擅长处理的基本数据结构--- DOM 树 (最基本的数据结构之一)。
解析过程中,如果遇到<link href="...">
和<script src="...">
这种外链加载 CSS 和 JS 的标签,浏览器会<mark>异步下载</mark>,下载过程和上文中下载 HTML 的流程一样。只不过,这里下载下来的字符串是 CSS 或者 JS 格式的。实际应用中,常用媒体类型(media type)和媒体查询(media query)来解除对渲染的阻塞。
```//html
<link href="index.css" rel="stylesheet"> <--阻塞-->
<link href="print.css" rel="stylesheet" media="print"><--加载&& !阻塞,仅在print时实用-->
<link href="other.css" rel="stylesheet" media="(min-width: 30em) and (orientation: landscape)"><--符合条件时阻塞-->
- 查看下边一段代码:
```//html
<p>根据 HTML 结构生成 DOM 树,稍等。。。</p>
<script src="app.js"></script>
<p>根据 CSS 生成 CSSOM,稍等。。。</p>
<script>console.log("inline")</script>
<p>将 DOM 和 CSSOM 整合形成 RenderTree</p>
这段html被解析时会被js代码阻塞(加载+执行),因此常把css放在头部(保证渲染),把js放在底部(保证非阻塞)。
- defer 与 async(区别在于:加载完之后何时执行)
- defer(延迟):并行执行html解析与js加载,载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。
- async(异步):与 defer 的区别在于,如果已经加载好,就会开始执行——无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行,因此async脚本一定时阻塞load事件的。因此,多个 async-script 的执行顺序是不确定的。值得注意的是,<mark>向 document 动态添加 script 标签时,async 属性默认是 true. </mark>
```//js
document.createElement("script").async
// true
```//html
<script src="app1.js" defer></script>
<script src="app2.js" defer></script>
<script src="init1.js" async></script>
<script src="init2.js" async></script>
-
浏览器的容错机制
您在浏览 HTML 网页时从来不会看到“语法无效”的错误。这是因为浏览器会纠正任何无效内容,然后继续工作。
1. 明显不能在某些外部标签中添加的元素。在此情况下,我们应该关闭所有标签,直到出现禁止添加的元素,然后再加入该元素。
2. 我们不能直接添加的元素。这很可能是网页作者忘记添加了其中的一些标签(或者其中的标签是可选的)。这些标签可能包括:HTML HEAD BODY TBODY TR TD LI(还有遗漏的吗?)。
3. 向 inline 元素内添加 block 元素。关闭所有 inline 元素,直到出现下一个较高级的 block 元素。
4. 如果这样仍然无效,可关闭所有元素,直到可以添加元素为止,或者忽略该标记。
CSS 解析
和 HTML 不同,CSS 是上下文无关的语法,可以使用简介中描述的各种解析器进行解析。事实上,CSS 规范定义了 CSS 的词法和语法。
- WebKit CSS 解析器
WebKit 使用 Flex 和 Bison 解析器生成器,通过 CSS 语法文件自动创建解析器。正如我们之前在解析器简介中所说,Bison 会创建自下而上的移位归约解析器。Firefox 使用的是人工编写的自上而下的解析器。这两种解析器都会将 CSS 文件解析成 StyleSheet 对象,且每个对象都包含 CSS 规则。CSS 规则对象则包含选择器和声明对象,以及其他与 CSS 语法对应的对象。
<center>图:解析 CSS</center>
处理脚本和样式表的顺序
- 脚本
:遇到 <script>
文档的解析将停止,直到脚本执行完毕。如果脚本是外部的,那么解析过程会停止,直到从网络同步抓取资源完成后再继续。HTML5 增加了一个选项----“defer”,这样不会停止文档解析,而是等到解析结束才执行.
-
预解析
WebKit 和 Firefox 都进行了这项优化。在执行脚本时,其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM 树,而是将这项工作交由主解析器处理;预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。 -
样式表:
理论上来说,应用样式表不会更改 DOM 树,因此似乎没有必要等待样式表并停止文档解析 - render树构建:
在 DOM 树构建的同时,浏览器还会构建:render树---按照正确的顺序绘制内容。 - render树和 DOM 树的关系:呈现器是和 DOM 元素相对应的,但并非一一对应。非可视化的 DOM 元素不会插入呈现树中,例如“head”元素。如果元素的 display 属性值为“none”,那么也不会显示在呈现树中(但是 visibility 属性值为“hidden”的元素仍会显示)。
Web 安全
浏览器重新渲染(re-render):重绘(repaint)与重排(reflow)todo...
重新渲染,就需要重新生成布局和重新绘制。前者叫做"重排"(reflow),后者叫做"重绘"(repaint)。"重绘"不一定需要"重排","重排"必然导致"重绘"。重排和重绘会不断触发,这是不可避免的。但是,它们非常耗费资源,是导致网页性能低下的根本原因。提高网页性能,就是要降低"重排"和"重绘"的频率和成本,尽量少触发重新渲染。
- 触发重新渲染的常见情况:
- 修改DOM
- 修改样式表
- 用户事件(比如鼠标悬停、页面滚动、输入框键入文字、改变窗口大小等等)
- 访问元素属性:
- offsetTop/offsetLeft/offsetWidth/offsetHeight
- scrollTop/scrollLeft/scrollWidth/scrollHeight
- clientTop/clientLeft/clientWidth/clientHeight
- getComputedStyle()
- 提高性能的技巧:
-
css:
- display(none->block)
- position(absolute,fixed)
- visibility(hidden)
-
js:
- 使用优秀类库的Virtual DOM(React,Vue)
- 注意作用域
- 避免全局查找
- 避免with
- 选择正确的方法
- 避免没必要的查找(性能部分的一部分是和解决问题的算法和方法相关的)
- 优化循环
- 展开循环
- 避免双重解释
- 其它
- 原生方法较快:
- switch语句较快
- 位运算符较快
- 最小化语句数
- 多个变量声明
- 插入迭代值
- 使用数组和对象字面量
- 优化DOM交互(如下)
-
BOM:
- window.requestAnimationFrame
- window.requestIdleCallback
-
DOM:
- document.createDocumentFragment:最小化现场更新
- innerHTML:要不使用标准DOM方法创建同样的DOM快得多(内部方法是编译好而非解释执行的)。
- 事件代理:页面处理程序的数量和页面响应UED速度负相关。事件处理程序本质上是一种函数,是一种对象,存放在内存中,设置大量的事件处理程序会使内存中的对象变多,Web 程序的性能会变得越来越差,用户体验很不好。
- 注意HTMLCollection:最小化对它们的访问,以下会返回HTMLCollection对象
- getElementByTagName
- childNodes
- attributes
- 特殊集合:document.forms,document.images etc.
- cloneNode
- 集中DOM的读写操作
- 缓存样式数据
-
应用示例
-
js中应用到的简单算法
标记名称 | 描述 |
---|---|
O(1) | 常数:不管有多少值,执行时间恒定,一般表示简单值和存储在变量中的值(数组元素访问) |
O(logn) | 对数:执行总时间与数量相关,但要完成算法并不一定获取每个值。如二分查找 |
O(n) | 线性:执行总时间和数量直接相关。如遍历操作 |
O(n2) | 平方:执行总时间与数量有关,每个值至少要获取n次。如插入排序 |
-
代码示例
```//js
// 1. 避免全局查找
/ bad(3次全局查找) 测试https://www.google.com.hk/search?newwindow=1&safe=strict&hl=zh-CN&tbm=isch&source=hp&biw=936&bih=803&ei=9WB7W7HoA4r_8gWHnKTgDg&q=image&oq=image&gs_l=img.3..0l10.10919.12202.0.12340.8.8.0.0.0.0.123.485.4j1.5.0....0...1ac.1.64.img..3.5.483.0..35i39k1j0i10k1.0.pzyNxe_xbM0
/
function updateUI(){
console.time()
var images=document.getElementsByTagName('img')
for(var i=0,len=images.length;i<len;i++){
images[i].title=document.title+'image'+i;
}
return console.timeEnd()
}
// default: 0.5439453125ms
// good(一次全局查找)
function updateUI(){
console.time()
var doc = document
var images=doc.getElementsByClassName('img')
for(var i=0,len=images.length;i<len;i++){
images[i].title=doc.title+'image'+i;
}
return console.timeEnd()
}
// default: 0.120849609375ms
// 2.避免不必要的属性查找(见JS常见算法类型)
/ 最简单最便捷的算法是常数O(1)/
// 四次查找(5,value,10,sum)
var value = 5
var sum = value + 10
console.log(sum)
// default: 0.375ms
// 3.使用变量和数组要比访问对象上属性(O(n))更高效。
// good
var values = [1, 2, 3]
var sum = values[0] + values[1]
console.log(sum)
// default: 0.298095703125ms
// bad
var values = {a:1, b:2}
var sum = values.a + values.b
console.log(sum)
// default: 0.302978515625ms
// 4.注意获取单个值的属性查找
// bad
var query = window.location.href.substring(window.location.href.indexOf('?'))
// default: 0.02783203125ms
// good
var url = window.location.href
var query = url.substring(url.indexOf('?'))
// default: 0.02001953125ms
// 5. 优化循环
// bad
var values=new Array().fill(10000)
for(var i=0;i<values.length;i++){i}
// default: 0.101806640625ms
// good(使用减值迭代:将终止条件从values.length的O(n)调用简化成了O(1)调用)
for(var i = values.length-1;i>0;i--){}
// default: 0.01708984375ms (一个数量级)
// good(后测试循环:循环部分已完全优化,任何进一步的优化只能在处理语句中进行了),这种写法的弊端就是可读性很差
var i = values.length - 1
if(i > -1){
do{
//
}while(--i >= 0)
}
// default: 0.011962890625ms
// 6.避免双重解释(JS
代码运行的同时必须新启动一个解析器来解析新的代码。实例化一个新的解析器有不容忽视的开销)
eval("alert('hi')")// bad
var say = new Function("alert('hi')")// bad
setTimeout('alert("hi")',500)// bad
alert('hi')// good
var say = function (){
alert('hi')
}// good
setTimeout(function (){
alert('hi')
},500)// good
// 7. 最小化语句数
// 多个变量声明
var count = 0,
color = 'red',
values = [],
now = new Date()
// 插入迭代值
var value = values[i]; i ++ // bad
var value = values[i++] // good
// 使用数组和对象字面量
var values = new Array(); values[0] = 1; values[1] = 2 // bad(3个语句)
var person = new Object();
person.a = 1;
person.b = 2 // bad(3个语句)
var values = [1,2] // good(2个语句创建和初始化数组)
var person = {a:1,b:2}// good(2个语句创建和初始化对象)
// 8. 优化DOM交互(因DOM内部包含了非常多的属性及方法,DOM 交互非常耗时)
// DOM里的内容
var div = document.createElement('div'), result = ''
for(var i in div){
result += i + ' '
}
/
"align title lang translate dir dataset hidden tabIndex accessKey draggable spellcheck autocapitalize contentEditable isContentEditable inputMode offsetParent offsetTop offsetLeft offsetWidth offsetHeight style innerText outerText onabort onblur oncancel oncanplay oncanplaythrough onchange onclose oncontextmenu oncuechange ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onfocus oninput oninvalid onkeydown onkeypress onkeyup eddata edmetadata start onmousedown onmouseenter onmouseleave onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange onreset onresize onscroll onseeked onseeking onselect onstalled onsubmit onsuspend ontimeupdate ontoggle onvolumechange onwaiting onwheel onauxclick ongotpointercapture onlostpointercapture onpointerdown onpointermove onpointerup onpointercancel onpointerover onpointerout onpointerenter onpointerleave nonce click focus blur namespaceURI prefix localName tagName id className classList slot attributes shadowRoot assignedSlot innerHTML outerHTML scrollTop scrollLeft scrollWidth scrollHeight clientTop clientLeft clientWidth clientHeight onbeforecopy onbeforecut onbeforepaste oncopy oncut onpaste onsearch onselectstart previousElementSibling nextElementSibling children firstElementChild lastElementChild childElementCount onwebkitfullscreenchange onwebkitfullscreenerror setPointerCapture releasePointerCapture hasPointerCapture hasAttributes getAttributeNames getAttribute getAttributeNS setAttribute setAttributeNS removeAttribute removeAttributeNS hasAttribute hasAttributeNS getAttributeNode getAttributeNodeNS setAttributeNode setAttributeNodeNS removeAttributeNode closest matches webkitMatchesSelector attachShadow getElementsByTagName getElementsByTagNameNS getElementsByClassName insertAdjacentElement insertAdjacentText insertAdjacentHTML requestPointerLock getClientRects getBoundingClientRect scrollIntoView scrollIntoViewIfNeeded animate before after replaceWith remove prepend append querySelector querySelectorAll webkitRequestFullScreen webkitRequestFullscreen attributeStyleMap scroll scrollTo scrollBy createShadowRoot getDestinationInsertionPoints computedStyleMap ELEMENT_NODE ATTRIBUTE_NODE TEXT_NODE CDATA_SECTION_NODE ENTITY_REFERENCE_NODE ENTITY_NODE PROCESSING_INSTRUCTION_NODE COMMENT_NODE DOCUMENT_NODE DOCUMENT_TYPE_NODE DOCUMENT_FRAGMENT_NODE NOTATION_NODE DOCUMENT_POSITION_DISCONNECTED DOCUMENT_POSITION_PRECEDING DOCUMENT_POSITION_FOLLOWING DOCUMENT_POSITION_CONTAINS DOCUMENT_POSITION_CONTAINED_BY DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC nodeType nodeName baseURI isConnected ownerDocument parentNode parentElement childNodes firstChild lastChild previousSibling nextSibling nodeValue textContent hasChildNodes getRootNode normalize cloneNode isEqualNode isSameNode compareDocumentPosition contains lookupPrefix lookupNamespaceURI isDefaultNamespace insertBefore appendChild replaceChild removeChild addEventListener removeEventListener dispatchEvent "
/
// 最小化现场更新
// bad
console.time()
var list = document.createElement('ul'),
item,
i
for(i=0;i<10000;i++){
item = document.createElement('li')
list.appendChild(item)
item.appendChild(document.createTextNode('item' + i))
}
document.body.appendChild(list)
console.timeEnd()
// default: 26.902099609375ms
// good
console.time()
var list = document.createElement('ul'),
fragment = document.createDocumentFragment(),
item,
i
for(i=0;i<10000;i++){
item = document.createElement('li')
fragment.appendChild(item)
item.appendChild(document.createTextNode('item' + i))
}
document.body.appendChild(fragment)
console.timeEnd()
// VM7017:12 default: 29.4169921875ms ???
// good (使用innerHTML(编译好的而非解释执行的))
console.time()
var list = document.createElement('ul'),
html = '',
i
for(i=0;i<10000;i++){
// list.innerHTML += "<li>Item " + i + "</li>" // 避免
html += "<li>Item " + i + "</li>"
}
list.innerHTML = html
document.body.appendChild(list)
console.timeEnd()
//default: 25.760986328125ms
// 最小化对HTMLColletion的访问
console.time()
var images = document.getElementsByTagName('img'),
i,
image,
len,
temp
for(i = 0, len=images.length;i<len;i++){
image = images[i]// 保存当前image,避免了对images的访问
temp=image+Image
}
console.timeEnd()
// default: 0.06591796875ms
console.time()
var images = document.getElementsByTagName('img'),
i,
len,
temp
for(i = 0, len=images.length;i<len;i++){
temp=images[i]+images[i]
}
console.timeEnd()
// VM6253:9 default: 0.115966796875ms
# web性能优化(web performance)
## 为什么要性能优化
- 留住用户(Performance is about retaining users)
- 提升转化率(Performance is about improving conversions)
![](https://user-gold-cdn.xitu.io/2018/8/16/165405d2ffcd13ce?w=1531&h=1084&f=png&s=37539)
<center>转化率就是金钱</center>
- 关乎用户体验(Performance is about the user experience)
- 以人为本(Performance is about people)
## 如何去优化
### 要发送什么(Mind what resources you send)
> 构建高性能应用程序的有效方法是审核发送给用户的资源。虽然Chrome开发人员工具中的网络面板可以很好地总结给定页面上使用的所有资源,但如果您到目前为止尚未考虑性能,那么知道从哪里开始可能会令人生畏。以下是一些建议:
- 如果您使用Bootstrap或Foundation来构建UI,请问自己是否有必要。这些抽象添加了浏览器必须下载,解析和应用于页面的大量CSS,所有这些都是在特定于站点的CSS进入图片之前。 Flexbox和Grid在使用相对较少的代码创建简单和复杂布局方面非常出色。由于CSS是一种render阻塞资源,因此CSS框架的开销可能会显着延迟渲染。您应该通过消除不必要的开销来寻求加速渲染,而非尽可能依赖于浏览器中的工具。
- JavaScript库很方便,但并不总是必要的。以jQuery为例:由于`querySelector`和`querySelectorAll`等方法,元素选择得到了极大的简化。使用`addEventListener`可以轻松进行事件绑定。` classList,setAttribute`和`getAttribute`提供了使用类和元素属性的简便方法。如果你必须使用类库,研究更精简的替代品。例如,Zepto是一个较小的jQuery替代品,Preact是React的一个小得多的替代品。
- 并非所有网站都需要是单页面应用程序(SPA),因为它们经常广泛使用JavaScript。 JavaScript是我们在字节的web字节上提供的最昂贵的资源,因为它不仅必须下载,还必须解析,编译和执行。例如,具有优化前端架构的新闻和博客站点可以像传统的多页体验一样表现良好。
### 如何发送(Mind how you send resources)
当您知道需要为您的应用发送哪些资源以使其成为您想要的美观和功能时,请考虑下一步如何发送它们。与预见和预防一样,交付对于构建快速用户体验至关重要。
- 迁移到HTTP / 2。 HTTP / 2解决了HTTP / 1.1中固有的许多性能问题,例如并发请求限制和缺少头压缩。
- 使用资源提示加快资源交付。 rel = preload是一个这样的资源提示,它允许在浏览器发现它们之前提前获取关键资源。这可以对页面呈现产生明显的积极影响,并在明智地使用时降低交互时间。 rel = preconnect是另一个资源提示,可以掩盖为第三方域上托管的资源打开新连接的延迟。
- 现代网站平均提供大量JavaScript和CSS。在HTTP / 1环境中将样式和脚本捆绑到大型捆绑包中很常见。这样做是因为大量请求对性能有害。现在不再是HTTP / 2在场的情况,因为多个同时请求更便宜。考虑在webpack中使用代码拆分来限制下载的脚本数量,使其仅限于当前页面或视图所需的内容。将CSS分成较小的模板或特定于组件的文件,并仅包含可能使用它们的资源。
### 数据规模(Mind how much data you send)
> 通过一些关于哪些资源适合发送以及如何发送它们的想法,我们将介绍一些限制发送数据的建议:
1. 配置服务器以压缩资源。压缩会大大减少您发送给用户的数据量,尤其是在涉及文本资产的情况下。 GZIP在这个领域是一种非常优秀的格式,但Brotli压缩可以更进一步。但是,要理解压缩并不是性能问题的全部:一些隐式压缩的文件格式(例如,JPEG,PNG,GIF,WOFF等)不响应压缩,因为它们已经被压缩。
3. 优化图片以确保您的网站尽可能少地发送图片数据。由于图片构成了网络上每页平均有效负载的很大一部分,因此图片优化代表了提升性能的独特机会。
6. 客户端提示可用于根据当前网络条件和设备特征定制资源交付。 DPR,Width和Viewport-Width标头可以帮助您使用服务器端代码为设备提供最佳图像,并提供更少的标记。 Save-Data标头可以帮助您为明确要求您的用户提供更轻松的应用程序体验。
7. [NetworkInformation API](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation)公开有关用户网络连接的信息。此信息可用于修改较慢网络上的用户的应用程序体验。
8. 减少请求(HTTP requests reduction)
2. 文件压缩(File compression)
3. 优化web缓存(Web caching optimization)
> Web Caching Optimization reduces server load, bandwidth usage, and latency. CDNs use dedicated web caching software to store copies of documents passing through their system.
Leveraging the browser cache is crucial. It is recommended to have a max-age of 7 days in such cases. This saves server time and makes things altogether faster.
4. 简化代码(Code minification)
6. 矢量图替换位图(Replacement of vector graphics)
8. 避免301 重定向
> Redirects are performance killers. Avoid them whenever possible. A redirect will generate additional round-trip times and therefore quickly doubles the time that is required to load the initial HTML document before the browser even starts to load other assets.
9. 采用基于云的网站监控(Adopt Cloud-based Website Monitoring)
10. 预取重连Prefetch and reconnect
在用户实际关注链接之前,域名预取是已经解析域名的一个很好的解决方案。
11. SSL certificate/ HTTPS
13. 热链接保护(Hotlink protection)
限制HTTP引用,以防止他人将您的资源嵌入其他网站。 热链接保护将通过禁止其他网站显示您的图像来节省带宽。
14. 基础设施(Infrastructure)
16. 数据库优化(Database Optimization)
17. 使用[webworker](https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers)
## (前端)主流框架主流做法案例
### webpack 分片加载原理
### vue。。。
> todo...
#### 参考链接
- <https://www.html5rocks.com/zn/tutorials/internals/howbrowserswork/>
- <http://taligarsiel.com/Projects/howbrowserswork1.htm>
- <https://zhuanlan.zhihu.com/p/30134423>
- <https://blog.csdn.net/milado_njuvbnm,>
- [JavaScript高级程序设计(第3版)](http://online.fliphtml5.com/jfls/fgqp/#p=10)
- [Why Performance Matters](https://developers.google.com/web/fundamentals/performance/why-performance-matters/)
- [HTML5规范](https://html.spec.whatwg.org/multipage/webappapis.html)
- <http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html>
- [16 Web Performance Optimization Techniques | Apiumhub](https://apiumhub.com/tech-blog-barcelona/web-performance-optimization-techniques/)
- [Firefox 新的渲染引擎 WebRender ](https://hacks.mozilla.org/2017/10/the-whole-web-at-maximum-fps-how-webrender-gets-rid-of-jank/)
- [JavaScript 事件对内存和性能的影响](https://segmentfault.com/a/1190000007055651)