当浏览器从第三方服务器请求资源时,必须先将该跨域域名解析为IP地址,然后浏览器才能发出请求,此过程称为DNS解析。
DNS作为互联网的基础协议,其解析的速度似乎很容易被网站优化人员忽略,现在大多数新流量乃全已经针对DNS解析进行了优化,比如DNS缓存。
典型的一次DNS解析需要耗费20-120毫秒,所花费的时间几乎可以忽略不计,但是当网站中使用的资源依赖多个不同域的时候,时间就会成倍增加,从而增加了网站的加载时间。
比如某些图片较多的页面中,在发起图片加载请求之前预先把域名解析好将会有至少5%的图片加载速度提成。
一般来说前端与华中与DNS有关的有两点,一是减少DNS的请求次数(缓存DNS地址),二是进行DNS的预获取,DNS Prefetch。
dns缓存可以在服务器设置DNS缓存的时间,不经常变更的ip建议设置的时间长一些。尽可能使用A或者AAAA代替CNAME,使用CND加速域名。还可以自己搭建DNS服务。
DNS与解析可以在页面中通过link标签来实现。
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
DNS与解析只能解析不同域,同域是不能解析的,因为已经解析完了。dns-prefetch要慎用,不要每个页面都添加,会造成资源浪费。默认情况下浏览器会对当前页面中所有出现的域名进行预解析,及时没有写link标签,这是隐式解析。
经过DNS解析获取到IP之后就要进行TCP的链接进行数据传输。
HTTP协议的初始版本中,每进行一次HTTP通信就要断开一次TCP链接,也就是短连接。
以早期的通信情况来说,因为都是些容量很小的文本传输,所以即使这样也没有多大问题,但是随着HTTP的大量普及,文旦中包含大量富文本的情况多了起来。每次的请求都会造成无谓的TCP链接建立和断开,增加通信录的开销。
为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段。这个字段要求服务器不要关闭TCP链接,以便其他请求复用,服务器同样回应这个字段。
Connection: keep-alive
一个可以复用的TCP链接就建立了,直到客户端或服务器主动关闭链接,但是这并非标准字段,不同实现的行为可能不一致,还可能造成混乱。
Content-Length: 3000
上面代码告诉浏览器,本次回应的长度是3000个字节,后面的字节就属于下一个回应了。在1.0版本中,Content-Length字段不是必须的,因为浏览器发现服务器关闭了TCP链接,就表明收到的数据包已经完成了。
Transfer-Encoding: chunked
每个非空数据块之前会有一个16进制的数值,表示这个块的的长度,最后是一个大小为0的块,表示本次回应的数据发送完了。
HTTP/1.1 200 OK
...
25
This is the data in the first chunk
...
2
...
4
...
0
...
虽然HTTP1.1允许复用TCP链接,但是同一个TCP链接里面,所有的数据通信是按次序进行的,服务器只有处理完一个回应才会进行下一个回应。如果前面的请求慢,后面就会有需要请求排队,称为对头阻塞。为了避免这种问题,可以减少请求数或者同事多开持续请求。这就出现了很多的优化技巧,比如说。合并脚本和样式表,将图片嵌入css代码,域名分片等等。其实如果HTTP协议设计的更好一些,这些额外的工作都是可以避免的。
为了解决响应阻塞问题2015年推出了HTTP2。
HTTP2主要用于解决HTTP1.1效率不高的问题,他不叫HTTP2.0是因为不打算发布子版本了,下一个版本直接就叫HTTP3。
通过压缩传输数据资源提升性能体验。默认HTTP进行数据传输数据是没有进行压缩的,原始数据多大传输的数据就多大。
我们都知道文件压缩之后数据体积减少是很客观的。
const zlib = require('zlib');
const fs = require('fs');
const rs = fs.cerateReadStream('jquery.js');
const ws = fs.cerateWriteStream('jquery.js.gz');
const gz = zlib.createGzip();
rs.pipe(gz).pipe(ws);
ws.on('error', (err) => {
console.log('失败');
})
ws.on('finish', () => {
console.log('完成')
})
正常工作中gzip一般可以在nginx服务器中开启,不需要自己编写。还是比较简单的。
gzip一般是针对文本文件,比如js,css,对于图片来说一般是在开发阶段压缩。
const rawBody = 'content=test';
const rawLen = rawBody.length;
const bufBody = new Unit8Array(rawLen);
for (let i = 0; i < rawLen; i++) {
bufBody[i] = rawBody.charCodeAt(i);
}
const format = 'gzip';
let buf;
switch (format) {
case gzip': buf = window.pako.gzip(bufBody); break;
}
const xhr = new XMLHttpRequest();
xhr.open('POST', '/service/');
xhr.setRequestHeader('Content-Encoding', format);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
xhr.send(buf);
服务器端进行解压
const http = require('http');
const zlib = require('zlib');
http.createServer((req, res) => {
let zlibStream;
const encoding = req.headers['content-encoding']
switch (encoding) {
case 'gzip' : zlibStream = zlib.createGunzip(); break;
}
res.writeHead(200, { 'Content-Type': 'text/plain' });
req.pipe(zlibStream).pipe(res);
}).listen(3000)
这种压缩一半也只适用于文本,如果数据量太大压缩过程也是比较耗时的。
缓存的原理是在客户端首次请求后保存一份请求资源的响应副本存储在客户端中,当用户再次发起相同的请求后,如果判断缓存命中则拦截请求,将之前缓存的响应副本返回给用户,从而避免重新向服务器发起资源请求。
缓存的技术种类有很多,比如代理缓存,浏览器缓存,网关缓存,负载均衡器及内容分发网络等,大致可以分为两类,共享缓存和私有缓存。
共享缓存指的是缓存内容可以被多个用户使用,如公司内部架设的Web代理,私有缓存是只能单独被用户使用的缓存,如浏览器缓存。
HTTP缓存是前端开发中最常接触的缓存机制之一,他又可细分为强制缓存与协商缓存,二者最大的区别在于判断缓存命中时浏览器是否需要向服务器进行询问。强制缓存不会去询问,协商缓存则仍旧需要询问服务器。
res.writeHEAD(200, {
Expires: new Date('2021-6-18 12: 51: 00').toUTCString(),
})
expires存在一个很大的漏洞就是对本地时间戳过分依赖,如果客户端本地的时间与服务器时间不同步,或者客户端时间被修改,那么缓存过期的判断可能就无法和预期相符。
为了解决这个问题,HTTP1.1新增了cache-control字段来对expires的功能进行扩展和完善。
cache-control的值为maxage=xxx来控制响应资源的有效期,xxx是一个以秒为单位的时间长度,表示该资源在被请求到的一段时间内有效,以此便可避免服务端和客户端时间戳不同步而造成的问题。
res.writeHEAD(200, {
'Cache-Control': 'maxage=1000',
})
除了maxage还可以设置其他参数,比如no-cache和no-store。
no-cache表示强制进行协商缓存,对于每次发起的请求不会再去判断强制缓存是否过期,而是直接进行协商缓存。协商缓存后面会说到。
no-store表示禁止使用任何缓存策略,客户端的每次请求都直接从服务器获取。也就是无缓存。
no-cache和no-store是互斥的,不能同时设置。
res.writeHEAD(200, {
'Cache-Control': 'no-cache',
})
还有private和public,他们也是cache-control的一组互斥属性值,他们用来明确响应资源是否可被代理服务器进行缓存。
publicb表示响应资源即可被浏览器缓存又可以被代理服务器缓存。private限制了响应资源只能被浏览器缓存。
对于应用程序中不会改变的文件比如图片,js,css, 字体库等通常可以使用公共缓存。
res.writeHEAD(200, {
'Cache-Control': 'public, max-age=31600',
})
除了max-age还有s-maxage,max-age表示服务器告知浏览器的响应资源的过期时长。一般使用它就足够了了。但如果是大型项目架构通常会涉及代理服务器缓存,这就需要考虑缓存在代理服务器上的有效性问题,这便是s-maxage存在的意义。他表示缓存在代理服务器上的过期时长,需要配合public来使用。
cache-control能作为expires的完全替代方案,目前expires只作为兼容使用。
const http = require('http');
const fs = require('fs');
const url = require(''url');
http.creatServer((req, res) => {
const { pathname } = url.parse(req.url);
// 获取文件日期
fs.stat(`www/${pathname}`, (err, stat) => {
if (err) {
res.writeHeader(404);
res.write('Not Found');
res.end();
} else {
if (req.headers['if-modified-since']) {
const oDate = new Date(req.headers['if-modified-since']);
const time_client = Math.floor(oDate.getTime() / 1000);
const time_server = Math.floor(stat.mtime.getTime() / 1000);
if (time_server > time_client) { // 服务器的文件时间大于客户端
sendFileToClient();
} else {
res.writeHeader(304);
res.write('Not Modified');
res.end();
}
} else {
sendFileToClient();
}
function sendFileToClient() {
let rs = fs.createReadStream(`www/${pathname}`);
res.setHeader('Last-Modifyed', state.mtime.toGMTString());
rs.pipe(res);
rs.on('error', err => {
res.writeHeader(404);
res.write('Not Found');
res.end();
})
}
}
})
}).listen(8080);
上面的这种缓存方式存在两个问题,首先他只是根据资源最后的修改时间戳进行判断,如果文件没有变更只是保存了一下修改时间也会变化。其次标识时间是秒,如果修改特别快在毫秒内完成(程序修改会有这样的速度),那么就无法识别缓存过期。
主要原因就是服务器无法仅依据资源修改的时间戳识别出真正的更新,进而导致缓存不准确。
为了解决这个问题从HTTP1.1规范开始新增了一个ETag的头信息, 实体标签。其内容主要是服务器为不同资源进行哈希运算生成的一个字符串,该字符串类似于文件指纹,只要文件内容编码存在差异,对应的ETag标签值就会不同,因此可以使用ETag对文件资源进行更精准的变化感知。
const etag = require('etag')
res.setHeader('etag', etag(data));
基于ETag发送的请求会在请求头中以If-None-Match传递给服务器。
在协商缓存中ETag并非last-modified的替代方案而是一种补充方案,因为他也存在一些问题。
首先服务器对生成文件资源的ETag需要付出额外的计算开销,如果资源体积较大,数量较多且修改较频繁,那么生成ETag的过程会影响服务器的性能。其次ETag的值分为强验证和弱验证,强验证根据资源内容进行生成,能够保证每个字节都相同。弱验证则根据资源的部分属性值来生成,生成速度快但无法确保每次字节都相同。并且在服务器集群场景下,也会因为不够准确而降低协商缓存的有效性和校验的成功性。
恰当的方式是根据具体的资源使用场景选择恰当的缓存校验方式。
cache-control: no-cache
图片文件的修改基本都是替换,同时考虑图片文件的数量及大小可能对客户端缓存空间造成不小的开销,所以可以采用强制缓存且过期时间不宜过长。
cache-control: max-age=86400
css样式表属于文本文件,可能存在的内容不定期修改,还想使用强制缓存来提高重用效率,故可以考虑在样式表文件的命名中增加指纹或版本号(一般为hash值),这样发生修改后不同的文件便会有不同的文件指纹,也就是请求的url不同。所以css的缓存时间可以设置长一些, 一年。
cache-control: max-age=31536000
js脚本文件可以类似样式表的设置,采用指纹和较长的过期时间,如果js中包含了用户的私人信息而不想让中间代理缓存,可添加private属性。
cache-control: private, max-age=31536000
缓存策略就是为不同的资源进行组合使用强制缓存,协商缓存及文件指纹或版本号,这样可以做到一举多得,及时修改更新,较长缓存过期时间及控制所能进行缓存的位置。
缓存设置需要注意不存在适用于所有场景下的最佳缓存策略,凡是恰当的缓存策略都需要根据具体的场景考虑制定。缓存决策要考虑下面几种情况。
来自:http://www.zhiqianduan.com/javascript/资源请求速度优化.md
在程序开发中,经常会使用到for循环的,但是很多人写的for循环效率都是比较低的,下面就举例说明,并总结优化for循环的方法,来提高我们程序的执行效率。
网站的加载速度不仅影响着用户体验,也会影响搜索引擎的排名,在百度推出“闪电算法”以来,将网站首屏打开速度被列入优化排名行列,作为前端开发的我们需要如果来优化网站的打开速度呢?下面就整理挖掘出很多细节上可以提升性能的东西分享给大家
DocumentFragments是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。因为文档片段存在于内存中,并不在DOM树中
对于代码裡面的 if else,我们可以使用逻辑判断式,或更好的三元判断式来优化代码。除了可以降低维护项目的成本之外,还可以提升代码可读性。就让我们从最简单的 if else 例子开始吧。
小程序从发布到现在也已经有将近两年的时间,越来越来多的公司开始重视小程序生态带来的流量,今年也由于小程序平台对外能力的越来越多的开放以及小程序平台的自身优化,越来越多的开发者也自主的投入到小程序的开发当中
无论你正在将 GIF 动图转换为 MP4 视频,还是手头已经有一大堆 MP4 视频,你都可以优化文件结构,以使得这些视频更快地加载和播放。通过重组 atoms 将 moov 放到文件开头,浏览器可以避免发送额外的 HTTP range request 请求来搜寻和定位 moovatom
要优化 Web 服务器的性能,我们先来看看 Web 服务器在 web 页面处理上的步骤:Web 浏览器向一个特定的服务器发出 Web 页面请求; Web 服务器接收到 web 页面请求后,寻找所请求的 web 页面,并将所请求的 Web 页面传送给 Web 浏览器; 显示出来
浏览器下载完页面所有的资源后,就要开始构建DOM树,于此同时还会构建渲染树(Render Tree)。(其实在构建渲染树之前,和DOM树同期会构建Style Tree。DOM树与Style Tree合并为渲染树)
写篇文章的目的,是以开放小程序代码的层面的优化。包括:条件判断将wx:if换成了hidden 、页面跳转请销毁之前使用的资源、列表的局部更新、小程序中多张图片懒加载方案、Input状态下隐藏input,应预留出键盘收起的时间
生活在信息爆炸的今天,我们每天不得不面对和过滤海量的信息--无疑是焦躁和浮动的,这就意味着用户对你站点投入的时间可能是及其吝啬的(当然有一些刚需站点除外)。如何给用户提供迅速的响应就显得十分重要了
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!