网站性能优化

网站性能优化是必须的技能,并且须要长期积累,如下是我本身总结的一些性能优化的策略,主要分为几个方面:javascript

  1. 网络请求优化
  2. 页面渲染优化
  3. JS阻塞性能与内存泄漏
  4. 负载均衡

1 网络请求优化

1.1 浏览器缓存

浏览器在向服务器发起请求前,会先查询本地是否有相同的文件,若是有,就会直接拉取本地缓存,这和咱们在后台部署的Redis、Memcache相似,都是起到了中间缓冲的做用,咱们先看看浏览器处理缓存的策略:php

浏览器默认的缓存是放在内存内的,内存里的缓存会由于进程的结束或者说浏览器的关闭而被清除,而存在硬盘里的缓存才可以被长期保留下去。不少时候,咱们在network面板中各请求的size项里,会看到两种不一样的状态:from memory cache 和 from disk cache,前者指缓存来自内存,后者指缓存来自硬盘。而控制缓存存放位置的,不是别人,就是咱们在服务器上设置的Etag字段。在浏览器接收到服务器响应后,会检测响应头部(Header),若是有Etag字段,那么浏览器就会将本次缓存写入硬盘中。css

以Nginx为例,设置Etaghtml

etag on;   //开启etag验证
expires 14d;    //设置缓存过时时间为14天
复制代码

打开咱们的网站,在chrome devtools的network面板中观察咱们的请求资源,若是在响应头部看见Etag和Expires字段,就说明咱们的缓存配置成功了。java

在咱们配置缓存时必定要切记,浏览器在处理用户请求时,若是命中强缓存,浏览器会直接拉取本地缓存,不会与服务器发生任何通讯,也就是说,若是咱们在服务器端更新了文件,并不会被浏览器得知,就没法替换失效的缓存。因此咱们在构建阶段,须要为咱们的静态资源添加md5 hash后缀,避免资源更新而引发的先后端文件没法同步的问题。webpack

1.2 资源打包压缩

咱们以前所做的浏览器缓存工做,只有在用户第二次访问咱们的页面才能起到效果,若是要在用户首次打开页面就实现优良的性能,必须对资源进行优化。咱们常将网络性能优化措施归结为三大方面:减小请求数、减少请求资源体积、提高网络传输速率。如今,让咱们逐个击破:nginx

以Webpack为例web

  • 压缩JS
new webpack.optimize.UglifyJsPlugin()
复制代码
  • 压缩Html
new HtmlWebpackPlugin({
            template: __dirname + '/views/index.html', // new 一个这个插件的实例,并传入相关的参数
            filename: '../index.html',
            minify: {
                removeComments: true,
                collapseWhitespace: true,
                removeRedundantAttributes: true,
                useShortDoctype: true,
                removeEmptyAttributes: true,
                removeStyleLinkTypeAttributes: true,
                keepClosingSlash: true,
                minifyJS: true,
                minifyCSS: true,
                minifyURLs: true,
            },
            chunksSortMode: 'dependency'
        })
复制代码

咱们在使用html-webpack-plugin 自动化注入JS、CSS打包HTML文件时,不多会为其添加配置项,这里我给出样例,你们直接复制就行。chrome

PS:这里有一个技巧,在咱们书写HTML元素的src 或 href 属性时,能够省略协议部分,这样也能简单起到节省资源的目的。后端

  • 压缩CSS

在使用webpack的过程当中,咱们一般会以模块的形式引入css文件(webpack的思想不就是万物皆模块嘛),可是在上线的时候,咱们还须要将这些css提取出来,而且压缩,这些看似复杂的过程只须要简单的几行配置就行

const ExtractTextPlugin = require('extract-text-webpack-plugin')
module: {
        rules: [..., {
            test: /\.css$/,
            use: ExtractTextPlugin.extract({
                fallback: 'style-loader',
                use: {
                    loader: 'css-loader',
                    options: {
                        minimize: true
                    }
                }
            })
        }]
    }
复制代码
  • 使用webpack3的新特性:ModuleConcatenationPlugin
new webpack.optimize.ModuleConcatenationPlugin()
复制代码
  • 把prod环境的shouldUseSourceMap设置为false,去掉build生成的map文件
devtool: shouldUseSourceMap ? 'source-map' : false,
复制代码

最后,咱们还应该在服务器上开启Gzip传输压缩,它能将咱们的文本类文件体积压缩至原先的四分之一,效果立竿见影,仍是切换到咱们的nginx配置文档,添加以下两项配置项目:

gzip on;
gzip_types text/plain application/javascriptapplication/x-javascripttext/css application/xml text/javascriptapplication/x-httpd-php application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
复制代码

若是你在网站请求的响应头里看到这样的字段,那么就说明我们的Gzip压缩配置成功啦:

【!!!特别注意!!!】不要对图片文件进行Gzip压缩!不要对图片文件进行Gzip压缩!不要对图片文件进行Gzip压缩!我只会告诉你效果拔苗助长,至于具体缘由,还得考虑服务器压缩过程当中的CPU占用还有压缩率等指标,对图片进行压缩不但会占用后台大量资源,压缩效果其实并不可观,能够说是“弊大于利”,因此请在gzip_types 把图片的相关项去掉。针对图片的相关处理,咱们接下来会更加具体地介绍。

1.3 图片资源优化

  • 不要在HTML里缩放图像
  • 使用雪碧图(CSS Sprite)
  • 使用字体图标(iconfont)

1.4 使用CDN

使用CDN存放静态资源,避免带宽爆炸以及加快资源下载.

2 页面渲染性能优化

2.1 减小重绘和回流

  • CSS属性读写分离:浏览器每次对元素样式进行读操做时,都必须进行一次从新渲染(回流 + 重绘),因此咱们在使用JS对元素样式进行读写操做时,最好将二者分离开,先读后写,避免出现二者交叉使用的状况。最最最客观的解决方案,就是不用JS去操做元素样式,这也是我最推荐的。
  • 经过切换class或者使用元素的style.csstext属性去批量操做元素样式。
  • DOM元素离线更新:当对DOM进行相关操做时,例、appendChild等均可以使用Document Fragment对象进行离线操做,带元素“组装”完成后再一次插入页面,或者使用display:none 对元素隐藏,在元素“消失”后进行相关操做。
  • 将没用的元素设为不可见:visibility: hidden,这样能够减少重绘的压力,必要的时候再将元素显示。
  • 压缩DOM的深度,一个渲染层内不要有过深的子元素,少用DOM完成页面样式,多使用伪元素或者box-shadow取代。
  • 图片在渲染前指定大小:由于img元素是内联元素,因此在加载图片后会改变宽高,严重的状况会致使整个页面重排,因此最好在渲染前就指定其大小,或者让其脱离文档流。
  • 对页面中可能发生大量重排重绘的元素单独触发渲染层,使用GPU分担CPU压力。(这项策略须要慎用,得着重考量以牺牲GPU占用率为代价可否换来可期的性能优化,毕竟页面中存在太多的渲染层对于GPU而言也是一种没必要要的压力,一般状况下,咱们会对动画元素采起硬件加速。)

2.2 减小页面从新渲染以及Dom嵌套

  • 以React为例,若是会引发页面State变化的,最好在shouldComponentUpdate进行处理,避免每次props或者state更新的时候都从新渲染,若是页面主要用来显示的话,可使用PureComponent代替Component,注意PureComponent对于引用类型的变化,不会从新渲染。巧用Fragment代替Component,减小Dom嵌套。

3 JS阻塞性能与内存泄漏

3.1 巧用JS的防抖与节流

函数防抖:将几回操做合并为一此操做进行。原理是维护一个计时器,规定在delay时间后触发函数,可是在delay时间内再次触发的话,就会取消以前的计时器而从新设置。这样一来,只有最后一次操做能被触发。

function debounce(fn, wait) {
    var timeout = null;
    return function() {
        if(timeout !== null) 
                clearTimeout(timeout);
        timeout = setTimeout(fn, wait);
    }
}
复制代码

函数节流:使得必定时间内只触发一次函数。原理是经过判断是否到达必定时间来触发函数。

var throttle = function(func, delay) {
            var prev = Date.now();
            return function() {
                var context = this;
                var args = arguments;
                var now = Date.now();
                if (now - prev >= delay) {
                    func.apply(context, args);
                    prev = Date.now();
                }
            }
        }
复制代码

区别: 函数节流无论事件触发有多频繁,都会保证在规定时间内必定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 好比在页面的无限加载场景下,咱们须要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操做时才去请求数据。这样的场景,就适合用节流技术来实现。

3.2 内存泄漏

  • 闭包内存泄漏Pattern
  • 在某个页面(SPA)WillUnMount的时候,记得关闭一些资源,例如WebSocket的断开,eChart对象置空等。

4 负载均衡

  1. 使用PM2管理多进程
  2. Nginx作反向代理
  3. Docker管理多个容器
相关文章
相关标签/搜索