Web 优化——资源加载优化

阅前悉知:

  • 其一、本文是《Web 优化》系列的第三篇。该系列是我查阅了大量资料总结而来的。其中可能存在不足之处。希望大家在阅读时,抱着质疑的态度去阅读

  • 其二、因为本文内容是承接本系列的第一篇文章所讲内容。所以阅读本文前,希望各位优先看下第一篇:《Web 优化——简述访问某个网址浏览器背后所做的工作》。有第一篇的基础,可以比较好的进行理解。

  • 其三、本文将从影响资源加载速度的三个因素以及浏览器缓存出发,讲述如何优化资源加载,提高页面加载效率。


本系列其他文章:
Web 优化——浏览器访问某个网址背后所做的工作
Web 优化——正确放置CSS和Javascript,减少页面渲染时间

前言

在《Web 优化——简述访问某个网址浏览器背后所做的工作》一文中,我们知道浏览器访问一个网站最先做的事情就是资源加载。只有资源加载成功后,浏览器才能进行资源解析、渲染及执行。所以,资源加载的快慢是优化网页加载速度的重要一环。

资源加载计算公式

从《Web 优化——浏览器访问某个网址背后所做的工作》一文我们了解到资源加载的流程如下:

  1. 浏览器根据DNS服务器得到域名的IP地址
  2. 向这个IP的机器发送http(s)请求
  3. 服务器收到,处理并返回http请求,比如返回图片或html代码等
  4. 浏览器得到返回内容
  5. 浏览器自上而下的读取返回内容。期间如果遇到引用外部的CSS、JS或图片等静态资源。浏览器会开设新的线程去获取这些资源。但需要注意的是,如果遇到的是获取JS,浏览器会暂停HTML文档的渲染线程。直到JS加载、解析完毕

从上面的资源加载流程来看,大致可以概括为三个阶段:

  1. 请求响应:浏览器发出请求到收到Webserver的反馈信息这一阶段。
  2. 资源传输:浏览器接收Webserver源源不断传输过来的资源。
  3. 资源读取:浏览器接收完Webserver传输过来的资源后,开始读取接收到的资源。

因此,影响浏览器资源加载时间的主要看三个阶段的所花的时间。公式如下:

单个资源加载时间 = 请求响应时间+资源传输时间+资源读取时间

资源传输时间 = 资源大小 / 单位时间内传送的资源多少

资源读取时间 = 资源大小 / 单位时间内资源读取的多少

上面三个公式结合,得:

单个资源加载时间 = 请求响应时间+资源大小 / 单位时间内传送的资源大小 + 资源大小 / 单位时间内资源读取的大小

因为单位时间内传送的资源多少主要受带宽等网络因素的影响,而单位时间内资源读取的多少主要受用户计算机以及用户所使用的浏览器的影响。所以,这两个因素排除优化范围。

则,上面公式可简化为:

单个资源加载时间=请求响应时间+资源大小

注意,这条公式并不是说请求响应时间加上资源大小就等于单个资源加载时间。而是,单个资源加载时间主要受到请求响应时间和资源大小的影响

需要注意的是,这里讲到是单个资源加载的时间。如果要计算整个页面全部加载的时间,则不能单纯的将每个资源加载的时间进行相加。因为,资源加载是并发的、多线程的,也就是可能存在多个资源加载同时进行。但是如果从优化的角度来讲,我们可以仅从单个资源加载时间来研究。因为当所以的资源加载时间都得以缩短了,整体的页面资源加载时间必然也能得到减少

所以,对资源加载的优化可以从下面三个方面进行:资源大小资源数量资源响应时间。接下来将基于这三点逐步展开:

减少请求资源的大小

上面说到,

资源传输时间 = 资源大小 / 单位时间内传送的资源大小

单位时间内传送的资源大小主要受带宽等网络因素的影响。所以,我们主要从减小资源大小这一方面着手优化。压缩是计算机领域减少资源大小的常用方法。在Web开发方面同样如此。在Web优化上,压缩资源常用的做法有两种:

  1. 压缩资源——在不影响效果的情况下,去除多余的数据
  2. 压缩传输——在资源传输过程中先压缩再传输,接收到资源后再解压缩
压缩资源——压缩HTML、CSS、JS以及图片

对这三种的压缩基本上就是,去除代码中的空格、回车及注释。压缩后,整个文件的代码会变成一行,然后变量命以及函数名等都会自动重名为简单的英文简写,极大可能的减少了文件大小。

压缩JS

压缩JS文件常用webpack插件UglifyJS Webpack Plugin来实现。

module.exports = {
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        uglifyOptions: {
          output: {
            comments: false,	// 删除所有注释
          },
        },
      }),
    ],
  },
};
压缩CSS

使用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

使用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压缩

gzip(GNU- ZIP)是一种压缩技术,用于webServer压缩请求的资源。启用gzip压缩可达到很好的压缩效果的效果,基本上可以减少资源大小的30%-40%。
gzip的压缩页面需要浏览器和服务器双方都支持,服务器端在向客户端传输资源时会把资源进行gzip压缩,传到浏览器后浏览器解压并解析。目前,较新版本的浏览器已经都支持gzip,当浏览器接收到gzip资源时会自动进行解压缩操作。而server端则需要进行手动配置才能启用gzip服务。具体可查看:《web性能优化–用gzip压缩资源文件》《如何通过GZIP来优化你的网站》

减少请求资源的次数

除了上面提及的压缩请求资源大小,请求次数也深刻影响着浏览器打开网页的速度。如果要请求的资源非常多,即使我们将当个资源加载的时间减少到最小,也抵不住请求次数累加起来产生的时间。所以,减少请求次数也是重要的一环。目前来说,在这方面的优化主要着眼于较小图片(比如:小图标)上

合并小资源——雪碧图

雪碧图也叫CSS sprite, 是一种CSS图像合成技术;用于将小图标合并成一个大图片,通过配合background-position来进行显示。

通过使用雪碧图,可以把许多的小图标请求合成一次请求。从而达到页面减少请求次数的效果。

不过呢!随着Web技术的不断发展与优化,以及雪碧图使用过程中出现的种种弊端。对于小图标的处理,很少使用雪碧图了,更多的转为使用iconfont(字体图标)SVG(可缩放矢量图形)来对小图标进行优化了。

将小图标转为base64编码

如果图标够小(小于10kb或者更小),我们就可以将图片转为base64编码。

什么是图片的 base64 编码?

图片的 base64 编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址,图片随着 HTML\CSS 的下载同时下载到本地,不再单独消耗一个http来请求图片。

需要注意一点,转为base64编码的图片不能太大。如果图片太大,必然会造成转化后的base64编码字符串非常长。这样就会导致HTML\CSS随着变得很大。反而没有实现页面资源加载的优化了。得不偿失!

使用iconfont(字体图标)替代图片

iconfont是一种特殊的字体,通过使用这种字体,可以给用户显示一个个图标。换而言之,就是把各种图标转存到字体库中。

使用iconfont时,我们只需要加载iconfont库,就可以直接使用库中的全部图标。不用再去向Webserver请求图标资源。这样就可以实现减少资源请求次数。

关于字体图标的用法可以参考:

加快资源响应时间——CDN加速

经过上面的一系列优化操作,我们已经极大的提高页面资源的加载速度了。但是呢!我们还有一点可以做优化,那就是请求响应时间。

想要优化请求响应时间,就要知道通讯网络的工作原理(这部分我在本系列首篇《Web 优化——浏览器访问某个网址背后所做的工作》做了粗略的讲解)。因为优化资源响应速度涉及到的通讯网络以及服务器部署、负载均衡等知识了。这些已经远超前端开发的工作范围。所以,这部分感兴趣可以看,不感兴趣可跳过。下面也仅仅是简单介绍一下CDN。

CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。

——来自《科普中国》

简单来说,CDN是一个缓存机制,将资源存储在离用户最近的服务器上,使得用户能够以最快的速度获取资源。

推荐阅读:
想要进一步的理解CDN,可查看《网络的拓扑结构》《CDN加速原理》

善用浏览器缓存

浏览器缓存原理简述

缓存是针对非第一次请求来说的,当浏览器访问过一个网站后,会将一些静态资源缓存起来。下次访问时就会对缓存进行判断使用。以浏览器获取具体一个资源(如:index.html)为例:

第一次访问

浏览器:向服务器发送HTTP请求获取index.html

服务器:服务器接收到浏览器发送的HTTP请求,返回 Code 200 以及 index.html并再响应报头里设置缓存过期时间(cache-control:max-ageExpires)、文件修改时间(Last-Modified)、根据资源内容计算出来的实体标记Entity Tag(Etag);


第二次访问

浏览器:

首先判断根据cache-control:max-ageExpires判断缓存是否过期:

  1. 若没过期,直接使用缓存的资源
  2. 若过期,则携带Last-ModifiedEtag发送给服务器

服务器:

服务器接收到浏览器发送过来的Last-ModifiedEtag。根据这两个字段判断请求的资源(index.html)是否被更新了。

  1. 请求的资源没有更新。则不携带请求资源,返回 304 Not Modified。告诉浏览器直接使用缓存中的资源就行了,同时延长缓存过期时间。
  2. 如果请求资源进行过更新,则服务器会进行第一次访问的操作。

浏览器更加详细的解释请查看:浏览器 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数据分析

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值