DNS(Domain Name System)
: 负责将域名URL转化为服务器主机IP。:把域名转换成为网络可以识别的ip地址
DNS查找流程:首先查看浏览器缓存是否存在,不存在则访问本机DNS缓存,再不存在则访问本地DNS服务器。所以DNS也是开销,通常浏览器查找一个给定URL的IP地址要花费20-120ms,在DNS查找完成前,浏览器不能从host那里下载任何东西。
TTL(Time To Live):表示查找返回的DNS记录包含的一个存活时间,过期则这个DNS记录将被抛弃
DNS优化两个方面:DNS缓存、DNS负载均衡
影响DNS缓存的因素
最佳实践
当客户端的DNS缓存为空时,DNS查找的数量与Web页面中唯一主机名的数量相等。所以减少唯一主机名的数量就可以减少DNS查找的数量。
然而减少唯一主机名的数量会潜在地减少页面中并行下载的数量,避免DNS查找降低了响应时间,但减少并行下载可能会增加响应时间。当页面的组件量比较多的时候,可以考虑将组件分别放到至少2-4个主机名,已获得最大收益
CDN (Content Delivery Network)
可直译成内容分发网络。CDN的本质仍然利用缓存技术缓存, 解决的是如何将数据快速可靠从源站传递到用户的问题。用户获取数据时,不需要直接从源站获取,通过CDN对于数据的分发,用户可以从一个较优的服务器获取数据,从而达到快速访问,并减少源站负载压力的目的
即 — 把资源放在离用户地理位置更近的地方
用户在通过浏览器访问未使用CDN加速的网站的大致过程:
CDN访问过程
使用CDN可以解决资源并行下载限制,处理静态资源Cookie同域名携带等问题;
CDN缓存和回源需要合理的设置静态资源hash;
接入CDN会引入多个域名,增加域名解析时间,可进行预解析域名
使用CDN服务的网站,只需将其域名的解析权交给CDN的负载均衡设备,CDN负载均衡设备将为用户选择一台合适的缓存服务器,用户通过访问这台缓存服务器来获取自己所需的数据。
由于缓存服务器部署在网络运营商的机房,而这些运营商又是用户的网络服务提供商,因此用户可以以最短的路径,最快的速度对网站进行访问。因此,CDN可以加速用户访问速度,减少源站中心负载压力。
一个http请求绝大多数的时间消耗在了建立连接跟等待的时间,优化的方法是减少http请求。
Gzip
5前端生产环境中将js、css、图片等文件进行压缩的好处显而易见,通过减少数据传输量减小传输时间,节省服务器网络带宽,加快加载文本的速度,提高前端性能
而且测试表明,压缩对网站还是起到优化性能的作用的,那些基于文本的响应,包括HTML,XML,JSON(Javascript Object Notation),Javascript,和CSS可以减少大约70%的大小。
目前比较通用的压缩方法是启用gzip压缩
。它会把浏览器请求的页面,以及页面中引用的静态资源以压缩包的形式发送到客户端,然后在客户端完成解压和拼装.
http协议对压缩文件的传输的支持
浏览器请求数据时,通过Accept-Encoding申明自己可以接受的压缩方法
服务端接收到请求后,选取Accept-Encoding中的一种对响应数据进行压缩
服务端返回响应数据时,在Content-Encoding字段中说明数据的压缩方式
浏览器接收到响应数据后根据Content-Encoding的响应头对结果进行解压
注:如果服务器没有对响应数据进行压缩,则不返回Content-Encoding,浏览器也不进行解压
时间 | 流程 | 说明 |
---|---|---|
服务端响应请求时 | 服务端接收请求后找到响应文件,并进行压缩,然后将压缩后的文件作为内容返回给客户端 | 压缩等级越高压缩效果越好,同时CPU消耗也越大,压缩时间也越长。为了减少响应时间这一目的,服务端响应请求时压缩等级不宜过高 |
构建时 | 项目构建时将文件压缩后发布 | 构建时压缩不占用响应时间,可以选择较高的压缩等级,生成压缩后的文件后部署到服务器 |
结合使用 | 请求根据文件类型,大小,请求频率等对压缩时间做策略选择 | 构建时对需要压缩的文件进行压缩;服务器在收到请求后首先查找对应的已压缩文件,找不到的情况下使用服务端压缩 |
nginx
webpack
cnpm install --save-dev compression-webpack-plugin
webpack
配置const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
plugins:[
new CompressionWebpackPlugin({ //这里对大于10k的js和css文件进行压缩,其它配置参考官方文档
filename: '[path].gz[query]', // 目标资源名称 [file]会被替换成原资源,[path]替换成原资源路径 [query]替换成原查询字符串
algorithm: 'gzip' // 算法
test: /\.(js|css)$/, // 压缩js与css
threshold: 10240, // 只处理比这个值大的资源,按字节计算
minRatio: 0.8 // 只有压缩率比这个值小的资源才会被处理
})
]
后台开启使用koa
const staticCache = require('koa-static-cache')
import config from './config'
const app = new Koa()
app.use(staticCache(path.resolve(_dirname, "../dist"), {
maxAge: 7 * 24 * 60 * 60,
gzip: true, // 开启
dynamic: true
}))
Tree shaking
—— 清除不使用的代码,找出使用的代码
Scope hoisting
——检查import链,并尽可能的将散乱的模块放到一个函数中,前提是不能造成代码冗余,所以只有被引用了一次的模块才会被合并
Code-splitting
—— 能够把代码分离到不同的bundle中,然后可以按需加载或并行加载这些文件。
基于 ES6 的静态引用,
Tree shaking
通过扫描所有 ES6 的 export,找出被 import 的内容并添加到最终代码中。 webpack 的实现是把所有 import 标记为有使用/无使用两种,在后续压缩时进行区别处理
使用Scope Hoisting
可以让代码体积更小并且可以降低代码在运行时的内存开销,同时它的运行速度更快。Scope Hoisting可以减少搜索时间。(变量从局部作用域到全局作用域的搜索过程越长执行速度越慢)
Code-splitting
可以用于获取更小的bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。
proxy_pass + gzip
添加Expires头,配置Etag...
6自动化缓存-处理大规模缓存:
http-Header(协议头部):基于http-Request(请求头部)和http-Response(响应头)来实现的缓存策略
对http请求来说,客户端缓存分三类:
Expires
,Cache-Control=
和appcache
Last-Modified/If-Modified-Since
,Etag/If-None-Match
Cache-Control:max-age=0/no-cache
现在所有的浏览器都会使用本地资源去缓存住那些被Cache一Control或者Expires头标记的资源,这些头能标记资源需要缓存的时间。
另外,ETag(实体标签)和Last一Modified头来标识当资源过期后是否需要重新请求,浏览器为了减少不必要的服务器请求,尽可能地从本地缓存中获取资源,并且将那些已经过期的、或者当缓存空间减小的时候将那些很久不用的资源进行清理。
浏览器缓存通常包括图片,CSS,Javascript代码,这些缓存能合理地提高网站的性能(比如为了支持后退和前进的按钮,使用一个单独的缓存来保存整个渲染的页面)
Expires
:指定缓存到期GMT的绝对时间,指服务端具体时间点,过期之前均从浏览器缓存读取数据。如果设了max-age,max-age就会覆盖expires。如果expires到期需要重新请求。
Cache-control
: 假设你的站点有引用一个脚本文件,你非常确认这个脚本文件内容十年不变。那么自然希望浏览器把这个脚本缓存起来,不用每一次都请求服务器,然后服务器再返回相同的内容。这样能够节省带宽开销并且提升性能。
设置文件返回的HTTP头中的Cache-Control设置为:Cache-Control: max-age=31536000
(标准中规定max-age值最大不能超过一年,以秒为单位,值为31536000)
Cache-Control:
public
:表示缓存的版本可以被代理服务器或者其他中间服务器识别。
private
:意味着这个文件对不同的用户是不同的。只有用户自己的浏览器能够进行缓存,公共的代理服务器不允许缓存。
no-cache
:意味着文件的内容不应当被缓存。这在搜索或者翻页结果中非常有用,因为同样的URL,对应的内容会发生变化。
相关字段:
max-age
:指定缓存的有效时间(以秒为单位),max-ag=0或者是负值,浏览器会在对应的缓存中把Expires设置为1970-01-01 08:00:00。
s-maxage
:类似于max-age,只能指定Public类型的共享缓存,优先级高于max-age,比如proxy。
private
:私有缓存,如果个人计算机上的缓存
public
:公开缓存,如cdn服务器上的资源缓存,因为共很多用户使用所以是public。通常情况下需要http身份验证的情况,响应是不可cache的,加上public可以使它被cache。
no-cache
: 强制浏览器在使用cache拷贝之前先提交一个http请求到源服务器进行确认。请求获取当前服务器端该文件的Last-Modified对比判断是否使用继续使用本地缓存文件
no-store
:告诉浏览器在任何情况下都不要进行cache,不在本地保留拷贝。
must-revalidate
: 强制浏览器严格遵守你设置的cache规则。
proxy-revalidate
: 强制proxy严格遵守你设置的cache规则。
cache
:使用本地缓存,不发生请求。
Last-Modified & if-modified-since
: Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
ETag & If-None-Match
: Etag(全称Entity Tag.):web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定,具体下文中介绍)。
If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。
Etag和304类似,但是级别比 Last-Modified 高一些
过程:
Etag 主要为了解决 Last-Modified 无法解决的一些问题:
某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存,这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是秒级的,它将不能准确标注文件的修改时间。(或者说UNIX记录MTIME只能精确到秒);
缓存 | cookie | localStorage | sessionStorage |
---|---|---|---|
大小数量 | 不能超过4K | 5M或更大 | 5M或更大 |
有效性 | 可以设置path路径,限制只属于某个路径。在设置的过期时间之前一直有效,即使窗口或浏览器关闭 | 始终有效,存储持久数据,浏览器关闭后数据不丢失,除非主动删除数据 | 数据在当前浏览器窗口关闭后自动删除(会话级) |
操作方法 | 修改读取方法需自己实现 | 提供了get,set方法 | 提供了get,set方法 |
作用域 | 所有同源窗口中都共享 | 所有同源窗口中都共享 | 不在不同的浏览器窗口中共享,即使是同一个页面 |
数据与服务器之间的交互方式 | 每次请求都会自动发送到服务器,然后回传给浏览器,服务器端也可以写cookie到客户端 | 不会自动把数据发给服务器,仅在本地保存 | 不会自动把数据发给服务器,仅在本地保存 |
Web Storage带来的好处:
1、减少网络流量:一旦数据保存在本地之后,就可以避免再向服务器请求数据,因此减少不必要的数据请求,减少数据在浏览器和服务器间不必要的来回传递
2、快速显示数据:性能好,从本地读数据比通过网络从服务器上获得数据快得多,本地数据可以及时获得,再加上网页本身也可以有缓存,因此整个页面和数据都在本地的话,可以立即显示
3、临时存储:很多时候数据只需要在用户浏览一组页面期间使用,关闭窗口后数据就可以丢弃了,这种情况使用sessionStorage非常方便
补:web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
web Storage的api接口使用更方便
待更新ing
// 创建link标签
const myCSS = document.createElement( "link" );
myCSS.rel = "stylesheet";
myCSS.href = "mystyles.css";
// 插入到header的最后位置
document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );
将link元素的media属性设置为用户浏览器不匹配的媒体类型(或媒体查询),如media=“print”,甚至可以是完全不存在的类型media=“noexist”。对浏览器来说,如果样式表不适用于当前媒体类型,其优先级会被放低,会在不阻塞页面渲染的情况下再进行下载。文件加载完成之后,将media的值设为screen或all,从而让浏览器开始解析CSS。
通过rel属性将link元素标记为alternate可选样式表,也能实现浏览器异步加载。加载完成之后,将rel改回去。
可以使用 loadCSS和 Preload
// 对于一些不是首屏加载的css,可以如下写法:
<link rel="preload" href="path/to/haorooms.css" as="style" onload="this.rel='stylesheet'">
// 防止浏览器禁止js,保险起见,也可以如下:
<link rel="preload" href="path/to/haorooms.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/haorooms.css"></noscript>
// 为了避免有些浏览器会重新调用处理程序rel='stylesheet'这个属性,我们一般推荐如下写法:
<link rel="preload" href="path/to/haorooms.css" as="style" onload="this.οnlοad=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/haorooms.css"></noscript>
// 不考虑兼容问题再次优化
<head>
<link rel="stylesheet" href="/首屏加载css.css">
<link rel="preload" href="/不是首屏加载的css.css" as="style" onload="this.οnlοad=null;this.rel='stylesheet'">
</head>
<body>
<header>…</header>
<main>…</main>
<section class="comments">…</section>
<section class="about-me">…</section>
<footer>…</footer>
</body>
有选择的使用选择器
保持简单,不要使用嵌套过多过于复杂的选择器。
通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用。
不要使用类选择器和ID选择器修饰元素标签,如h3#markdown-content,这样多此一举,还会降低效率。
不要为了追求速度而放弃可读性与可维护性。
减少使用昂贵的属性
如box-shadow/border-radius/filter/透明度/:nth-child等。
减少重排,避免不必要的重绘
不使用CSS @import —— CSS的@import会造成额外的请求
避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。
标签设置defer属性,将脚本文件设置为延迟加载,当浏览器遇到带有defer属性的
标签时,会再开启一个线程去下载js文件,同时继续解析HTML文档,等等HTML全部解析完毕DOM加载完成之后,再去执行加载好的js文件
标签,可以保证多个js文件的执行顺序就是它们在页面中出现的顺序,但是要注意,添加defer属性的js文件不应该使用document.write方法document.write
方法,但是对多个带有async的js文件,它不能像defer那样保证按顺序执行,它是哪个js文件先下载完就先执行哪个。
标签
标签来实现异步加载js文件 如果将不可见区域的内容延迟加载,那么页面就会更快地展现在用户面前,这个区域叫做below the fold
,为了减少页面加载后需要重新访问的内容,可以将图片替换为正确的高宽所标记的标签
实现原理
实现
HTML5包括了一些新的结构元素,例如header,nav,article和footer,使用这些 语义化标签 比传统的使用div和span标签能使得页面更简单和更容易解析。
HTML5的一些表单元素提供了许多新属性来完成原本需要javascript来完成的功能,placeholder,autofocus等;也有一些新的输入框元素能不用依靠Javascript就可以完成一些通用的需求,包括像e-mail,url,tel,time等。
使用CSS 3.0中的圆角,阴影,动画,过渡和其他的图片效果,轻便简易,加速渲染
在触摸屏设备上,当一个用户触碰屏幕的时候,
onclick
事件并没有立即触发,设备会使用大约半秒(大多数设备差不多都是300毫秒)来让用户确定是手势操作还是点击操作,这个延迟会很明显地影响用户期望的响应性能,要使用touchend
事件来替换才能解决,当用户触碰屏幕的时候,这个事件会立即触发。
为了要确保不会产生用户不期望的行为,应该使用touchstart
和touchmove
事件。也可以在touchstart
事件之后使用touchmove
事件来避免将touchend
事件误判为点击,当然前提是需要假设拖拽的手势并不是预期产生点击行为。
我们需要去处理onclick
事件来让浏览器改变button
的外观从而标识为已点击的状态,同时也需要处理那些不支持touch
事件的浏览器,为了避免代码在touchend
和onclick
代码中重复执行,需要在确保用户触碰事件已经在touchend
执行了之后,在click事件中调用preventDefault
和stopPropagation
方法。
重定向用于将用户从一个URL重新路由到另一个URL。
重定向会增加http请求的次数,会影响到整个网站的性能,但是必要的重定向又可以提高用户体验,所以我们需要在性能和用户体验之间去权衡
如何避免重定向
从性能的角度来说,如果一个资源没有很高的被缓存的几率的话,最好把它嵌入到页面的HTML中(叫inlining),而不是使用链接外部,脚本和样式是支持内嵌到HTML中的,但是图片和其他的二进制资源其实也是可以通过内嵌包含base64编码的文本来嵌入到HTML中的。
内嵌的缺点是页面的大小会变得非常大,所以对于Web应用来说,关键的是能够跟踪分析这个资源什么时候需要从服务端获取,什么时候已经缓存到客户端了。
Tools: yuicompresser, closure complie, jsmin, packer…
混淆:主要针对Javascript代码,它的目的是减低代码的可读性,防止被追踪出程序逻辑。会从javascript代码中移除注释和空白,另外也会改写代码。作为改写的一部分,函数和变量的名字将被转换为更短的字符串,所以进一步减少了javascript文件的大小。(混淆更能减少js代码的大小)。
压缩
编译
HTML5的EventSource对象和Server-Sent事件能通过浏览器端的JavaScript代码打开一个服务端连接客户端的单向通道,服务端可以使用这个写通道来发送数据,这样能节省了HTTP创建多个轮询请求的消耗。
这种方式比HTML的WebSocket更高效,WebSocket的使用场景是,当有许多客户端和服务端的交互的时候(比如消息或者游戏),在全双工连接上建立一个双向通道。
HTML5中的Web Worker是使用多个线程并发执行Javascript程序
Web Worker 使用教程
由于使用更多带宽会使用更多移动网络的费用,所以只有能检测网络的类型才能使用针对特定网络的优化技术。
例如,预加载未来使用到的请求是非常聪明的做法,但是如果用户的带宽很稀有,并且加载的有些资源是永远不会用到的话,则duck不必。
if(navigator.onLine){
...
}else{
...
}
非常简单,但是并不准确——根据MDN的描述:
navigator.onLine只会在机器未连接到局域网或路由器时返回false,其他情况下均返回true。
也就是说,机器连接上路由器后,即使这个路由器没联通网络,navigator.onLine仍然返回true。
$.ajax({
url: 'x.html',
success: function(result){
...
},
error: function(result){
...
}
});
<script src="./jquery-3.1.1.min.js"></script>
<script>
function getImgError(){
alert("Network disconnect!");
}
$().ready(function(){
$("#btn-test").click(function(){
var imgPath = "https://www.baidu.com/img/bd_logo1.png";
var timeStamp = Date.parse(new Date());
$("#img-test").attr("src", imgPath + "?timestamp=" + timeStamp);
});
});
</script>
<body>
<img id="img-test" style="display:none;" onerror="getImgError()"/>
<button id="btn-test">check status</button>
</body>
每次点击button时,更新该图片的src。若获取图片失败,则认为网络连接失败
这种判断网络状态的准确完全取决于图片资源是否稳定
var netStatue = true;
$(window).bind('online', function(){
netStatue = true;
});
$(window).bind('offline', function(){
netStatue = false;
});
...
if(netStatue){
...
}else{
...
}
减少DNS查找 ↩︎
使用CDN托管资源 ↩︎
减少http请求 ↩︎
*浏览器发送Http请求过程 ↩︎
压缩文本和图像Gzip
↩︎
善用缓存添加Expires头,配置Etag...
↩︎
*Cookie、localStorage、以及sessionStorage之间的区别 ↩︎
使用Ajax来增强进程 ↩︎
CSS优先加载,JS延迟加载(css,js加载优化) ↩︎
延迟渲染Below the fold内容(懒加载) ↩︎
使用HTML5和CSS 3.0来简化页面 ↩︎
一些编写代码的优化 ↩︎
将Click事件替换成Touch事件 ↩︎
减少重定向 ↩︎
首次使用的时候在HTML中嵌入资源 ↩︎
代码精简和混淆 ↩︎
使用HTML5服务端发送事件 ↩︎
对多线程来说尽量使用HTML5的Web Worker特性 ↩︎
根据网络状况进行适配处理(使用JS在浏览器中判断当前网络连接状态的几种方法) ↩︎