阅前悉知:
其一、本文是《Web 优化》系列的第三篇。该系列是我查阅了大量资料总结而来的。其中可能存在不足之处。
希望大家在阅读时,抱着质疑的态度去阅读
。其二、因为本文内容是承接本系列的第一篇文章所讲内容。所以阅读本文前,希望各位优先看下第一篇:《Web 优化——简述访问某个网址浏览器背后所做的工作》。有第一篇的基础,可以比较好的进行理解。
其三、本文将从影响资源加载速度的三个因素以及浏览器缓存出发,讲述如何优化资源加载,提高页面加载效率。
本系列其他文章:
Web 优化——浏览器访问某个网址背后所做的工作
Web 优化——正确放置CSS和Javascript,减少页面渲染时间
在《Web 优化——简述访问某个网址浏览器背后所做的工作》一文中,我们知道浏览器访问一个网站最先做的事情就是资源加载。只有资源加载成功后,浏览器才能进行资源解析、渲染及执行。所以,资源加载的快慢是优化网页加载速度的重要一环。
从《Web 优化——浏览器访问某个网址背后所做的工作》一文我们了解到资源加载的流程如下:
- 浏览器根据DNS服务器得到域名的IP地址
- 向这个IP的机器发送http(s)请求
- 服务器收到,处理并返回http请求,比如返回图片或html代码等
- 浏览器得到返回内容
- 浏览器自上而下的读取返回内容。期间如果遇到引用外部的CSS、JS或图片等静态资源。浏览器会开设新的线程去获取这些资源。但需要注意的是,如果遇到的是获取JS,浏览器会暂停HTML文档的渲染线程。直到JS加载、解析完毕。
从上面的资源加载流程来看,大致可以概括为三个阶段:
因此,影响浏览器资源加载时间的主要看三个阶段的所花的时间。公式如下:
单个资源加载时间 = 请求响应时间+资源传输时间+资源读取时间
资源传输时间 = 资源大小 / 单位时间内传送的资源多少
资源读取时间 = 资源大小 / 单位时间内资源读取的多少
上面三个公式结合,得:
单个资源加载时间 = 请求响应时间+资源大小 / 单位时间内传送的资源大小 + 资源大小 / 单位时间内资源读取的大小
因为单位时间内传送的资源多少
主要受带宽等网络因素的影响,而单位时间内资源读取的多少
主要受用户计算机以及用户所使用的浏览器的影响。所以,这两个因素排除优化范围。
则,上面公式可简化为:
单个资源加载时间=请求响应时间+资源大小
注意,这条公式并不是说请求响应时间加上资源大小就等于单个资源加载时间。而是,单个资源加载时间主要受到请求响应时间和资源大小的影响
需要注意的是,这里讲到是单个资源加载
的时间。如果要计算整个页面全部加载的时间,则不能单纯的将每个资源加载的时间进行相加。因为,资源加载是并发的、多线程的,也就是可能存在多个资源加载同时进行。但是如果从优化的角度来讲,我们可以仅从单个资源加载时间来研究。因为当所以的资源加载时间都得以缩短了,整体的页面资源加载时间必然也能得到减少。
所以,对资源加载的优化可以从下面三个方面进行:资源大小
、资源数量
、资源响应时间
。接下来将基于这三点逐步展开:
上面说到,
资源传输时间 = 资源大小 / 单位时间内传送的资源大小
在单位时间内传送的资源大小
主要受带宽等网络因素的影响。所以,我们主要从减小资源大小
这一方面着手优化。压缩
是计算机领域减少资源大小的常用方法。在Web开发方面同样如此。在Web优化上,压缩资源常用的做法有两种:
对这三种的压缩基本上就是,去除代码中的空格、回车及注释。压缩后,整个文件的代码会变成一行,然后变量命以及函数名等都会自动重名为简单的英文简写,极大可能的减少了文件大小。
压缩JS文件常用webpack
插件UglifyJS Webpack Plugin
来实现。
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false, // 删除所有注释
},
},
}),
],
},
};
使用css-loader.minimize
来压缩CSS
{
test: /\.css$/,
use: [
{
loader: ‘style-loader‘
},
{
loader: ‘css-loader‘,
options: {
root: ‘/‘, //修改css中url指向的根目录, 默认值为/, 对于绝对路径, css-loader默认是不会对它进行处理的
modules: false, //开启css-modules模式, 默认值为flase
localIdentName: ‘[name]-[local]-[hash:base64:5]‘, //设置css-modules模式下local类名的命名
minimize: false, //压缩css代码, 默认false
camelCase: false, //导出以驼峰化命名的类名, 默认false
import: true, //禁止或启用@import, 默认true
url: true, //禁止或启用url, 默认true
sourceMap: false, //禁止或启用sourceMap, 默认false
importLoaders: 0, //在css-loader前应用的loader的数目, 默认为0
alias: {} //起别名, 默认{}
}
}
]
}
使用html-webpack-plugin
压缩HTML
// 在webpack.config.js里使用
const HtmlWebpackPlugin = require('html-webpack-plugin')
//code
plugins:[
new HtmlWebpackPlugin({
minify:{
//是否对大小写敏感,默认false
caseSensitive: true,
//是否简写boolean格式的属性如:disabled="disabled" 简写为disabled 默认false
collapseBooleanAttributes: true,
//是否去除空格,默认false
collapseWhitespace: true,
//是否压缩html里的css(使用clean-css进行的压缩) 默认值false;
minifyCSS: true,
//是否压缩html里的js(使用uglify-js进行的压缩)
minifyJS: true,
//Prevents the escaping of the values of attributes
preventAttributesEscaping: true,
//是否移除属性的引号 默认false
removeAttributeQuotes: true,
//是否移除注释 默认false
removeComments: true,
//从脚本和样式删除的注释 默认false
removeCommentsFromCDATA: true,
//是否删除空属性,默认false
removeEmptyAttributes: true,
// 若开启此项,生成的html中没有 body 和 head,html也未闭合
removeOptionalTags: false,
//删除多余的属性
removeRedundantAttributes: true,
//删除script的类型属性,在h5下面script的type默认值:text/javascript 默认值false
removeScriptTypeAttributes: true,
//删除style的类型属性, type="text/css" 同上
removeStyleLinkTypeAttributes: true,
//使用短的文档类型,默认false
useShortDoctype: true
},
hash:true, // 加入哈希来禁止缓存
template:'./src/index.html', // 源模板
filename:'assets/admin.html' // 编译后的文件及路径
})
]
使用image-webpack-loader
压缩图片
// 需先安装image-webpack-loader
// npm install image-webpack-loader --save-dev
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use:[
{
loader: 'url-loader',
options: {
limit: 10000, // 图片小于10k的转为base64格式
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
loader: 'image-webpack-loader',// 压缩图片
options: {
bypassOnDebug: true,
}
}
]
}
需要注意一点,上面说到如果图片小于10k将转为base64格式。这里判断的图片大小是经过
image-webpack-loader
压缩后的图片大小。
gzip(GNU- ZIP)是一种压缩技术,用于webServer压缩请求的资源。启用gzip压缩可达到很好的压缩效果的效果,基本上可以减少资源大小的30%-40%。
gzip的压缩页面需要浏览器和服务器双方都支持,服务器端在向客户端传输资源时会把资源进行gzip压缩,传到浏览器后浏览器解压并解析。目前,较新版本的浏览器已经都支持gzip,当浏览器接收到gzip资源时会自动进行解压缩操作。而server端则需要进行手动配置才能启用gzip服务。具体可查看:《web性能优化–用gzip压缩资源文件》、《如何通过GZIP来优化你的网站》
除了上面提及的压缩请求资源大小,请求次数也深刻影响着浏览器打开网页的速度。如果要请求的资源非常多,即使我们将当个资源加载的时间减少到最小,也抵不住请求次数累加起来产生的时间。所以,减少请求次数也是重要的一环。目前来说,在这方面的优化主要着眼于较小图片(比如:小图标)上。
雪碧图也叫CSS sprite, 是一种CSS图像合成技术;用于将小图标合并成一个大图片,通过配合
background-position
来进行显示。
通过使用雪碧图,可以把许多的小图标请求合成一次请求。从而达到页面减少请求次数的效果。
不过呢!随着Web技术的不断发展与优化,以及雪碧图使用过程中出现的种种弊端。对于小图标的处理,很少使用雪碧图了,更多的转为使用iconfont(字体图标)
、SVG(可缩放矢量图形)
来对小图标进行优化了。
如果图标够小(小于10kb或者更小),我们就可以将图片转为base64
编码。
什么是图片的 base64 编码?
图片的 base64 编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址,图片随着 HTML\CSS 的下载同时下载到本地,不再单独消耗一个http来请求图片。
需要注意一点,转为base64
编码的图片不能太大。如果图片太大,必然会造成转化后的base64
编码字符串非常长。这样就会导致HTML\CSS随着变得很大。反而没有实现页面资源加载的优化了。得不偿失!
iconfont
是一种特殊的字体,通过使用这种字体,可以给用户显示一个个图标。换而言之,就是把各种图标转存到字体库中。
使用iconfont
时,我们只需要加载iconfont
库,就可以直接使用库中的全部图标。不用再去向Webserver请求图标资源。这样就可以实现减少资源请求次数。
关于字体图标的用法可以参考:
经过上面的一系列优化操作,我们已经极大的提高页面资源的加载速度了。但是呢!我们还有一点可以做优化,那就是请求响应时间。
想要优化请求响应时间,就要知道通讯网络的工作原理(这部分我在本系列首篇《Web 优化——浏览器访问某个网址背后所做的工作》做了粗略的讲解)。因为优化资源响应速度涉及到的通讯网络以及服务器部署、负载均衡等知识了。这些已经远超前端开发的工作范围。所以,这部分感兴趣可以看,不感兴趣可跳过。下面也仅仅是简单介绍一下CDN。
CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。
——来自《科普中国》
简单来说,CDN是一个缓存机制,将资源存储在离用户最近的服务器上,使得用户能够以最快的速度获取资源。
推荐阅读:
想要进一步的理解CDN,可查看《网络的拓扑结构》、《CDN加速原理》
缓存是针对非第一次请求来说的,当浏览器访问过一个网站后,会将一些静态资源缓存起来。下次访问时就会对缓存进行判断使用。以浏览器获取具体一个资源(如:index.html
)为例:
第一次访问:
浏览器:向服务器发送HTTP请求获取
index.html
。服务器:服务器接收到浏览器发送的HTTP请求,返回 Code 200 以及
index.html
并再响应报头里设置缓存过期时间(cache-control:max-age
、Expires
)、文件修改时间(Last-Modified
)、根据资源内容计算出来的实体标记Entity Tag(Etag
);
第二次访问:
浏览器:
首先判断根据
cache-control:max-age
、Expires
判断缓存是否过期:
- 若没过期,直接使用缓存的资源
- 若过期,则携带
Last-Modified
及Etag
发送给服务器服务器:
服务器接收到浏览器发送过来的
Last-Modified
及Etag
。根据这两个字段判断请求的资源(index.html
)是否被更新了。
- 请求的资源没有更新。则不携带请求资源,返回 304 Not Modified。告诉浏览器直接使用缓存中的资源就行了,同时延长缓存过期时间。
- 如果请求资源进行过更新,则服务器会进行第一次访问的操作。
浏览器更加详细的解释请查看:浏览器 HTTP 缓存原理分析
扩展:
Cache-Control:max-age 与 Expires 的关系:
- 同:
- 两者都表示缓存的存储时长,允许客户端在这个时间之前发请求时,不用去检查缓存。
- 异
- max-age 指定的是从文档被访问后的存活时间,这个时间是个相对值(比如:3600s),相对的是文档第一次被请求时服务器记录的请求时间(Atime);
- Expires指定一个绝对的过期时间(GMT格式),这个时间相对于文件的最后访问时间(Atime)或者修改时间(MTime);
- 如果同时存在,则Expires被Cache-Control的max-age覆盖。
通过上面了解了浏览器的缓存原理也就明白浏览器缓存的好处了:使用浏览器缓存,可以极大的减少网页非第一次访问的加载时间。因为从本地读取文件总是比从服务器获取同样的资源快上许多。所以,最大化的使用浏览器缓存功能是提升网页访问速度很重要的一环。
了解浏览器缓存机制后,我们知道了对于静态资源,只有当缓存的时间过期了,浏览器才会主动向后台发送HTTP请求获取对应的资源。
那这样就会存在一个问题。
在缓存没过期前,开发人员改动了服务器上的静态资源。因为缓存时间没过期,浏览器就没法及时获取到更新的资源
面对这种情况有两种解决方法:
其一,用户在浏览器进行强制刷新。强制刷新可以让浏览器强制从服务器获取全部的资源。但这种方法治标不治本。最好就是使用第二种方法。
其二,保证每次改动过后的资源命名跟改动前的资源命名不一样。这样就会因为请求资源的名字变了,浏览器就不会从缓存中找到对应的资源了。
资源加载的快慢决定的页面打开的速度。本文从资源大小
、资源数量
、资源响应时间
以及浏览器缓存
这四个方面介绍了一些常用的优化技巧。通过这些优化,可以很好的提高网站的用户体验度。希望本文能对您有所帮助。
恰饭时间,嘻嘻!!!
推荐的是齐伟老师的两门课程:《8小时Python零基础轻松入门》《迈向数据科学家:带你玩转Python数据分析》
8小时Python零基础轻松入门
迈向数据科学家:带你玩转Python数据分析