乐趣区

关于javascript:前端性能优化一

为什么要做性能优化?性能优化有多重要?网站的性能对于用户的留存率、转化率有很大的影响,直白的说,进步网站的性能能够间接进步网站带来的收益。

性能优化的分类

前端的性能优化次要分为两类:

  1. 加载时优化;
  2. 运行时优化;

例如压缩文件、应用 CDN 加载动态资源属于加载时优化;及时的解绑事件、缩小 DOM 的操作属于运行时优化。

吉德林法令:遇到问题,只有先弄清楚问题,能力很好地解决问题。所以在做性能优化之前,最好先看一下网站的加载性能和运行性能。

手动查看

查看加载性能

一个网站看加载性能如何次要看首屏工夫和白屏工夫。

  1. 白屏工夫:指输出网址,到页面开始显示内容的工夫;
  2. 首屏工夫:指输出网址,页面齐全加载实现之后的工夫;

能够将以下代码放在 </head> 前,就能获取白屏页面白屏工夫。

<script>
    new Date().getTime() -
performance.timing.navigationStart
</script>

window.onload 事件里执行 new Date().getTime() - performance.timing.navigationStart 能够获取首屏齐全加载所用工夫。

查看运行性能

这里咱们就用 chrome 的开发者工具查看运行时的性能。

关上网站,F12 进入浏览器控制台,抉择 performance, 点击左上角的灰色圆点,变为红色即开始记录了。这时能够模拟用户应用网站,在应用结束后,点击 stop, 而后就能看到网站运行期间的性能报告。如果有红色块,阐明有掉帧的状况;如果为绿色,阐明帧率高,网页比拟晦涩。

在 performance 选项下,按 ESC 会弹出来一个小框,点击小框右边的三个点,把 rendering 勾进去。


第一个是高亮重绘区域,另一个是显示帧渲染信息。勾上之后,再去浏览网页,便能够实时看到网页的渲染变动。

利用工具查看

chrome 工具 Lighthouse

这个工具会对网站进行性能打分。

加载时性能优化

1. 缩小 HTTP 申请

一个残缺的 http 申请要经验 DNS 查找,TCP 握手,浏览器收回 http 申请,服务器接管申请,服务器解决申请并发回相应,浏览器接管相应过程。下图为一个 http 申请实例:

名词解释:

  • Queueing: 在申请队列中的工夫;
  • Stalled: 从 TCP 连贯建设实现,到真正能够传输数据之间的时间差,此事件包含代理协商工夫。
  • Proxy negotiation:与代理服务器连贯进行协商所破费的工夫。
  • DNS Lookup:执行 DNS 查找所须要的工夫,页面上每个不同的域都须要进行 DNS 查找。
  • Initial Connection/Connecting: 建设连贯所破费的工夫,包含 TCP 握手 / 重试和协商 SSL。
  • SSL:实现 SSL 握手所破费的工夫。
  • Request sent: 收回网络申请所破费的工夫。
  • Waiting(TTFB): TTFB 是收回页面申请到承受到应答数据的第一个字节的工夫。
  • Content Download: 接管响应数据所破费的工夫。

能够看出真正的下载数据工夫为13.05 / 204.16 = 6.39%,要进步这个比例,就要将多个小文件合并为一个大文件,从而缩小 HTTP 申请次数的起因。

2. 应用 HTTP2

解析速度快

HTTP/ 2 采纳二进制格局传输数据,而非 HTTP/ 1 的文本格式,二进制协定解析起来更高效。HTTP/ 1 的申请和响应报文,都是由起始行,首部和实体注释组成,各局部之间以文本换行符分隔。HTTP/ 2 将申请和响应数据分隔为更小的帧,并且它们采纳二进制编码。
HTTP/ 2 中,同域名下所有的通信都在单个连贯上实现,该连贯能够承载任意数量的双向数据流。

多路复用

HTTP/ 1 中,如果想并发多个申请,必须应用多个 TCP 链接,且浏览器为了管制资源,还会对单个域名有 6 - 8 个的 TCP 链接申请限度。
HTTP/ 2 中,不再依赖 TCP 链接去实现多流并行了,在 HTTP/ 2 中:

  • 同域名下所有通信都在单个 TCP 连贯上实现
  • 单个连贯能够承载任意数量的双向数据流
  • 数据流以音讯的模式发送,而音讯又有一个或多个帧组成,多个帧之间能够乱序发送,因为依据帧首部的流标识能够从新组装

这一个性,使性能有了极大的晋升。

优先级

在 HTTP/ 2 中,每个申请都能够带一个 31bit 的优先值,0 示意最高优先级,数值越大优先级越低。服务器收到这样的申请后,能够优先解决。

服务器推送

HTTP2 新增的一个弱小的新性能,就是服务器能够对一个客户端申请发送多个响应。

服务端能够在发送页面 HTML 时被动推送其它资源,而不必等到浏览器解析到相应地位,发动申请再响应。例如服务端能够被动把 JS 和 CSS 文件推送给客户端,而不须要客户端解析 HTML 时再发送这些申请。

服务端能够被动推送,客户端也有权力抉择是否接管。如果服务端推送的资源曾经被浏览器缓存过,浏览器能够通过发送 RST_STREAM 帧来拒收。被动推送也恪守同源策略,服务器不会轻易推送第三方资源给客户端

3. 应用服务端渲染

客户端渲染:获取 HTML 文件,依据须要下载 javascript 文件,运行文件,生成 DOM,再渲染。

服务端渲染:服务端返回 HTML 文件,客户端只须要解析 HTML。

  • 长处:首屏渲染快,SEO 敌对;
  • 毛病:波及构建设置和部署的更多要求,更多的服务器负载。

4. 动态资源应用 CDN

内容散发网络(CDN)是一组散布在多个不同地理位置的 Web 服务器。咱们都晓得,当服务器离用户越远时,提早越高。CDN 就是为了解决这一问题,在多个地位部署服务器,让用户离服务器更近,从而缩短申请工夫。

5. 将 css 放在头部,javascript 文件放在底部

所有放在 head 标签里的 CSS 和 JS 文件都会梗塞渲染。如果这些 CSS 和 JS 须要加载和解析很久的话,那么页面就会始终显示空白。所以 JS 文件要放在底部,等 HTML 解析完了再加载 JS 文件。

CSS 放在头部是因为:如果 CSS 放在尾部,会让用户第一工夫看到没有款式的页面,比拟的‘难看’,为了防止这种状况产生,将 CSS 放在头部。

6. 应用字体图标 iconfont 代替图片图标

字体图标就是将图标制作成一个字体,应用时就跟字体一样,能够设置属性,例如 font-size、color 等等,十分不便。并且字体图标是矢量图,不会失真。还有一个长处是生成的文件特地小。

压缩字体文件

应用 fontmin-webpack 插件对字体文件进行压缩,能够更进一步的减小字体的大小。

7. 善用缓存,不反复加载雷同资源

为了防止用户每次拜访网站都得申请文件,咱们能够通过增加 Expires 或 max-age 来管制这一行为。Expires 设置了一个相对工夫,只有在这个工夫之前,浏览器都不会申请文件,而是间接应用缓存。而 max-age 是一个绝对工夫,倡议应用 max-age 代替 Expires。

  • max-age: 设置缓存存储的周期,超过这个工夫缓存被认为过期。在这个工夫前,浏览器读取文件不会收回新申请,而是间接应用缓存。
  • no-cache: 示意客户端能够缓存资源,每次应用缓存资源都必须从新验证其有效性。

8. 压缩文件

压缩文件能够缩小文件的下载工夫,晋升用户的体验。

在 webpack 能够应用如下插件进行压缩:

  • javascript: UglifyPlugin
  • CSS: MiniCssExtractPlugin
  • HTML: HtmlwebpackPlugin

还能够应用 gzip 压缩,能够通过向 HTTP 申请头中的 Accept-Encoding 增加 gzip 标识来开启这一性能。当然,服务器也得反对这一性能。

webpack 配置:

下载插件

npm install compression-webpack-plugin --save-dev

配置:

const CompressionPlugin = require('compression-webpack-plugin')

module.exports = {plugins: [new CompressionPlugin()]
}

9. 图片优化

  • 图片提早加载

在页面中,先不给图片设置门路,只有当图片呈现在可视区域内时,再去加载真正的图片,这就是提早加载。对于图片很多的网站,一次性的加载太多的图片会对用户的体验造成很大的影响。能够参考这篇文章 web 前端图片懒加载实现

  • 升高图片品质

例如 JPG 格局的图片,100% 的品质和 90% 品质的通常看不出来区别,尤其是用来当背景图的时候。

压缩形式有两种:一种是通过在线压缩网站;一种是通过 webpack 插件image-webpack-plugin;

npm i -D image-webpack-loader

webpack 配置

{test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    use: [
        {
            loader: 'url-loader',
            options: {
                limit: 10000,
                name: utils.assetsPath('img/[name].[hash:7].[ext]')
            }
        },
        {
            loader: 'image-webpack-loader',
            options: {bypassOnDebug: true}
        }
    ]
}
  • 尽可能应用 CSS3 成果代替图片

尽量应用 CSS 的成果,比方突变、暗影等,因为代码大小通常比图片小得多;

10. 按需加载代码,提取第三方库代码,缩小冗余代码

  • 依据文件内容生成文件名,联合 import 动静引入组件实现按需加载

通过 webpack 配置 outputfilename属性的值选项中有一个 [contenthash], 它将依据文件内容创立出一个 hash。当文件有变动时,[contenthash] 也会有变动。

output: {filename: '[name].[contenthash].js',
  chunkFilename: '[name].[contenthash].js',
  path: path.resolve(__dirname, '../dist'),
},
  • 提取第三方库

因为引入的第三方库个别比较稳定,不会常常扭转。所以将它们独自提取进去,作为长期缓存是一个更好的抉择。这里须要应用 webpack4 的 splitChunk 插件 cacheGroups 选项。

module.exports = {
    optimization: {
        splitChunks: {
            chunks: 'async',
            minChunks: 30000,
            cacheGroups: {
                vendors: {test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 2,
                    priority: -20,reuseExistingChunk: true
                }
            }
        }
    }
}

缓存组 因该是 SplitChunksPlugin 中最乏味的性能了。在默认设置中,会将 node_mudules 文件夹中的模块打包进一个叫 vendors 的 bundle 中,所有援用超过两次的模块调配到 default bundle 中。更能够通过 priority 来设置优先级。

reuseExistingChunk:示意是否应用已有的 chunk,如果为 true 则示意如果以后的 chunk 蕴含的模块曾经被抽取进来了,那么将不会从新生成新的。

参考文章 应用 split-chunks-plugin 代码宰割

具体配置查看官网文档 split-chunks-plugin

退出移动版