一、代码层面的优化

 1、v-if和v-show的使用场景  (1)v-if适用于在运行时很少改变条件,不需要频繁切换条件的场景,属于真正的条件渲染,会确保在切换过程中,元素适当的被销毁和重建,也是惰性的;  (2)v-show适用于频繁切换条件的场景,v-show不管初始的渲染条件是什么,元素总是会被渲染,然后根据css的display属性的值去切换显示与隐藏。

2、computed和watch的使用场景  (1)当需要进行数值计算,并且依赖于其他数据时,应该使用computed,可以利用computed的缓存特性,避免每次获取值的时候都重新计算;  (2)当需要在数据变化时执行异步或开销较大的操作时,应该是用watch,使用watch允许我们进行异步操作,限制我们执行该操作的频率,并且在我们得到最终结果前,设置中间值,能拿到每一次的当前值和前一次的值。

 3、v-for循环遍历必须为item添加key,key不建议设置为索引,且避免在同一个元素中同时使用v-if

4、长列表性能优化  vue会通过Object.defineProperty()对数据进行劫持,来实现响应数据的变化,但是有时候我们的组件只需要实现对数据的展示就可以了,不会有其他修改删除的操作,所以就不需要vue来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,可以通过Object.freeze()方法来冻结一个对象,一旦被冻结的对象就不可以被修改

5、图片资源懒加载:使用vue-lazyload插件 安装:npm install vue-lazyload --save-dev main.js引入:import VueLazyload from 'vue-lazyload' 使用:Vue.use(VueLazyload) 页面中使用,直接将img的src属性改成v-lazy:前端 vue.js Vue项目性能优化方案  第1张

6、路由懒加载:const comp = () => import('./Comp.vue')

7、第三方插件的按需引入

8、vue优化无限列表性能:使用vue-virtual-scroll-list插件 安装:npm install vue-virtual-scroll-list --save-dev 使用: (itemData:数据,itemConponent:真正展示内容的组件)

9、keep-alive的使用 利用keep-alive将不活动的组件缓存起来,在组价切换过程中,将组件的状态保存在内存中,防止重复渲染dom,减少加载时间及性能消耗,提高用户体验。

二、webpack层面的优化

1、对图片进行压缩 在 vue 项目中除了可以在 webpack.base.conf.js 中 url-loader 中设置 limit 大小来对图片处理,对小于 limit 的图片转化为 base64 格式,其余的不做操作。所以对有些较大的图片资源,在请求资源的时候,加载会很慢,我们可以用 image-webpack-loader来压缩图片; 安装:npm install image-webpack-loader --save-dev webpack.js中配置:

{

  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,

      }

    }

  ]

}

2、减少es6转为es5的冗余代码 Babel插件会在es6代码转换成es5代码的时候注入一些辅助函数,例如:class HelloWebpack extends Component{...},这段代码再被转换成能正常运行的es5代码时需要以下两个辅助函数:

babel-runtime/helpers/createClass  // 用于实现 class 语法

babel-runtime/helpers/inherits  // 用于实现 extends 语法

在默认情况下,Babel会在每个输出文件中内嵌这些依赖的辅助函数代码,如果多个源代码文件都依赖于这些辅助函数,那么这些辅助函数的代码将会出现很多次,造成代码冗余,为了不然他们重复出现,可以在依赖他们时通过 require('babel-runtime/helpers/createClass')的方式导入,这样就能做到只出现一次了,babel-plugin-transform-runtime这个插件就是用来实现这个作用的,将相关的辅助函数进行替换成导入语句,从而减小babel编译出来的代码的大小; 安装:npm install babel-plugin-transform-runtime --save-dev 修改.babelrc配置文件:

"plugins": [

    "transform-runtime"

]

3、提取公共代码,业务代码和第三方库代码分开:webpack3中使用CommonsChunkPlugin插件、webpack4中使用optimization.splitChunks插件

CommonsChunkPlugin插件详细参数: name: 给这个包含公共代码的chunk命名 filename: 命名打包后生成的js文件 minChunks: 公共代码的判断标准,某个js模块被多少个文件引入才算公共代码,默认为1,我们可以为2, 也就是说如果 一个文件被其他页面引入了超过了2次及以上,就可以认为该文件就是公用代码。 chunks: 表示需要在哪些chunk(配置中entry的每一项)里寻找公共代码进行打包,默认不设置,那么它的寻找范围为所有的chunk;

如果项目中没有把每个页面的第三方库和公共模块提取出来,则项目会存在以下问题: a、相同的资源被重复加载,浪费客户的流量和服务器的成本 b、每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验 所以我们需要将多个页面的公共代码抽离成单独的文件,来优化以上问题,webpack专门内置了用于提取多个chunk中的公共部分的插件CommonsChunkPlugin,我们在项目中CommonChunkPlugin的配置如下:

// 所有在 package.json 里面依赖的包,都会被打包进 vendor.js 这个文件中。

new webpack.optimize.CommonsChunkPlugin({

  name: 'vendor',

  minChunks: function(module, count) {

    return (

      module.resource &&

      /\.js$/.test(module.resource) &&

      module.resource.indexOf(

        path.join(__dirname, '../node_modules')

      ) === 0

    );

  }

}),

// 抽取出代码模块的映射关系

new webpack.optimize.CommonsChunkPlugin({

  name: 'manifest',

  chunks: ['vendor']

})

optimization.splitChunks参数详解:

(1)、cacheGroups:splitChunks配置的核心,对代码的拆分规则全在cacheGroups缓存组里配置,缓存组里的每一个属性都是一个配置规则,可以自己定义属性名称,属性值是一个对象,放的是对一个代码拆分规则的描述,有两个属性:name和chunks; name:提取出来的公共模块将会以这个名称命名,可以不配置,如果不配置的话,就会生成默认的文件名 chunks:指定哪些类型的chunks参与拆分,值可以是string也可以是函数,如果是string的话,可以有三个值:all、async、initial,all代表所有模块,async代表异步加载的,initial代表初始化时就能获取的模块,如果是函数,则可以通过chunks的name属性等进行更详细的筛选

(2)、miniChunks:splitChunks是自带默认配置的,而缓存组默认会继承这些配置,其中有个miniChunks属性,它是控制每个模块什么时候被抽离出去:当模块被不同的entry引用的次数大于等于这个配置的值时,才会被抽离出去,默认值是1,也就是任何模块都会被抽离出去,(入口模块也会被webpack引入一次)

(3)、miniSize:提取的chunk的最小大小

(4)、priority:值为数值型,可以是负数,作用是当拆分组中设置多个拆分规则,且某个模块符合多个规则时,则通过优先级属性priority来决定使用哪个拆分规则,优先级高者执行

(5)、test:用于进一步控制缓存组选择的模块,可以是一个正则表达式,也可以是一个函数,可以匹配模块的绝对路径或chunk名称,匹配chunk名称时将选择chunk中所有的模块

例子:

optimization: {

    splitChunks: {

        minSize: 30,  //提取出的chunk的最小大小

        cacheGroups: {

            default: {

                name: 'common',

                chunks: 'initial',

                minChunks: 2,  //模块被引用2次以上的才抽离

                priority: -20

            },

            vendors: {  //拆分第三方库(通过npm|yarn安装的库)

                test: /[\\/]node_modules[\\/]/,

                name: 'vendor',

                chunks: 'initial',

                priority: -10

            }

        }

    }

}

4、构建结果输出分析:webpack-bundle-analyzer webpack输出的代码可读性很差,而且文件非常大,为了简单、直观的分析输出结果,可以使用分析工具,以图形的形式将结果更直观的展示出来 安装:npm install --save-dev webpack-bundle-analyzer webpack.config.js配置:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

plugins: [

    new BundleAnalyzerPlugin()

    ]

package.json的"scripts"中添加:

"dev": "webpack --config webpack.config.js --progress"

执行语句生成分析报告:npm run dev

5、打包去掉console.log等语句:uglifyjs-webpack-plugin 安装:npm install uglifyjs-webpack-plugin webpack.config.js中配置:

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {

    configureWebpack: (config) => {

        if (process.env.NODE_ENV === 'production') {

            return {

                plugins: [

                    //打包环境去掉console.log

                    new UglifyJsPlugin({

                        uglifyOptions: {

                            compress: {

                                warnings: false,

                                drop_console: true,  //注释console

                                drop_debugger: true, //注释debugger

                                pure_funcs: ['console.log'], //移除console.log

                            },

                        },

                    }),

                ],

            }

        }

    }

}

三、基础的web技术优化

1、开启gzip压缩:compression-webpack-plugin 安装://新版本不太兼容,推荐这个版本 npm install compression-webpack-plugin@6.1.1 --save-dev 在webpack.config.js中使用:

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

new CompressionWebpackPlugin({

      filename: '[path][base].gz',

      algorithm: 'gzip',  // 压缩算法,官方默认压缩算法是gzip

      test: /\.js$|\.css$|\.html$|\.eot$|\.woff$/, // 使用gzip压缩的文件类型

      threshold: 10240,  // 以字节为单位压缩超过此大小的文件,默认是10240

      minRatio: 0.8,  // 最小压缩比率,默认是0.8

    })

后端配置: (1)Nginx:主要是下方的gizp配置哦,直接复制粘贴就可以使用啦,亲测有效哦

server{

    //开启和关闭gzip模式

    gzip on;

    //gizp压缩起点,文件大于2k才进行压缩;设置允许压缩的页面最小字节数,页面字节数从header头得content-length中进行获取。 默认值是0,不管页面多大都压缩。建议设置成大于2k的字节数,小于2k可能会越压越大。

    gzip_min_length 2k;

    // 设置压缩所需要的缓冲区大小,以4k为单位,如果文件为7k则申请2*4k的缓冲区 

    gzip_buffers 4 16k;

    // 设置gzip压缩针对的HTTP协议版本

    gzip_http_version 1.0;

    // gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间

    gzip_comp_level 2;

    //进行压缩的文件类型

    gzip_types text/plain application/javascript text/css application/xml;

    // 是否在http header中添加Vary: Accept-Encoding,建议开启

    gzip_vary on;

}

(2)express的中间件:compression,主要用于进行gzip压缩,通常用于web性能优化,能对文本内容进行压缩,一般用于html文件; 安装:npm install compression --save 使用:

var compression = require('compression');

const app = express()

// 启用gzip

app.use(compression());

2、浏览器缓存 对静态资源进行缓存,根据是否需要重新向服务器发起请求来分类,将http请求分为强制缓存和对比缓存

3、CDN的使用 浏览器从服务器上下载css、js和图片等文件时都要和服务器连接,而大部分的服务器的带宽都有限,如果超过限制,网页就半天反应不过来,而CDN可以通过不同的域名来加载文件,从而使下载文件的并发连接数大大增加,且CDN具有更好的可用性,更低的网络延迟和丢包率

4、使用Chrome Performance查找性能瓶颈 Chrome的Performance面板可以录制一段时间内的js执行细节及时间,步骤: a、打开Chrome开发者工具,点进Performance面板 b、点击Record开始录制 c、刷新页面或展开某个节点 d、点击stop停止录制,就可以查看了

四、网络层面的优化

1、重复请求的取消 第一次请求还没回来之前就进行了第二次同样的请求时,就应该把第二次请求取消掉,防止重复请求; 在axios请求中进行封装,采用拦截器进行处理,首先实现三个方法,当请求方式、请求URL和携带参数都一致时,就可以认为是同样的请求,因此在发起请求时,可以根据当前的请求方式、请求地址和携带的参数来生成一个唯一的key,同时为每个请求创建一个专属的cancelToken,然后将key和cancelToken函数以键值对的形式保存到map对象中,使用map对象的好处就是可以快速判断是否有重复的请求:

// 用于根据当前的请求信息生成请求的key

    generateReqKey(config) {

      const { url, method, params, data } = config;

      return [url, method, qs.stringify(params), qs.stringify(data)].join("&");

    },

    // 用于将当前请求信息添加到pendingRequest对象中

    PendingRequest = new Map(),

    addPendingRequest(config) {

      const requestKey = this.generateReqKey(config)

      config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {

        if(!this.PendingRequest.has(requestKey)) {

          this.PendingRequest.set(requestKey, cancel)

        }

      })

    },

    // 检查是否有重复的请求,若存在则需要取消已经发出的请求

    removeRequest(config) {

      const requestKey = this.generateReqKey(config)

      if (this.PendingRequest.has(requestKey)) {

        const CancelToken = this.PendingRequest.get(requestKey)

        CancelToken(requestKey)

        this.PendingRequest.delete(requestKey)

      }

    }

因为需要对所有的请求都进行处理,所以直接在拦截器来实现取消重复请求的操作, (1)请求拦截器:作用是在请求发送前统一执行某些操作,比如在请求头中添加token字段 (2)响应拦截器:作用是在接收到服务器响应后统一执行某些操作,比如根据不同的状态码进行不同的响应操作

axios.interceptors.request.use(config => {

      // 检查是否有重复的请求

      this.removeRequest(config)

      // 把当前请求添加到PendingRequest对象中

      this.addPendingRequest(config)

      const token = localStorage.getItem('token')

      if(token) {

        config.headers.Authorization = `Bearer ${token}`

      }

      return config

    }, error => {

      return Promise.reject(error)

    })

    axios.interceptors.response.use(res => {

      // 从PendingRequest对象中移除请求

      this.removeRequest(res.config)

      const {data} = config;

      return data

    }, error => {

      // 从PendingRequest对象中移除请求

      this.removeRequest(error.config)

      return Promise.reject(error)

    })

2、限制失败请求重试的次数,避免无效请求过多,导致大量的资源消耗

//在main.js设置全局的请求次数,请求的间隙

axios.defaults.retry = 4;  // 请求次数

axios.defaults.retryDelay = 1000;  // 请求间隙

//响应拦截

axios.interceptors.response.use(undefined, function axiosRetryInterceptor(err) {

    var config = err.config;

    // 如果配置不存在或未设置重试选项,则返回错误信息

    if(!config || !config.retry) return Promise.reject(err);

 

    // 设置变量即跟踪重试次数

    config.__retryCount = config.__retryCount || 0;

 

    // 检查我们是否已经超过了总重试次数

    if(config.__retryCount >= config.retry) {

        // 返回错误信息

        return Promise.reject(err);

    }

 

    // 重试次数加1

    config.__retryCount += 1;

 

    // 创建延时器等待发送重试请求

    var backoff = new Promise(function(resolve) {

        setTimeout(function() {

            resolve();

        }, config.retryDelay || 1);

    });

 

    // 返回调用AXIOS来重试请求

    return backoff.then(function() {

        return axios(config);

    });

});

3、善用缓存,减小网络请求的次数 4、断网处理: 在vuex中存放network的状态,根据network的状态判断是否需要加载断网组件,断网情况就加载断网组件,不加载对应的页面组件,点击刷新,就跳转到refresh页面然后立即返回来实现重新获取数据,所以新建refresh.vue,在beforeRouteEnter钩子中返回当前页面  

// 断网页面

在refresh.vue页面中:

// refresh.vue

beforeRouteEnter (to, from, next) {

    next(vm => {            

        vm.$router.replace(from.fullPath)        

    })    

}

精彩内容

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: