vue 性能优化

本文具体介绍 vue.config.js 配置,以优化打包后 chunk-vendors.js 的大小,不具体介绍代码优化及各类api封装等。

1. 老生常谈的代码层面的优化
  1. 按需引入各类组件库
    详情请见element 官网
    1)element,echarts等
  // element
  import { Button, Input } from 'element-ui';
  Vue.use(Button).use(Input);
  
  // echarts
  //引入基本模板
  let echarts = require('echarts/lib/echarts')

  // 引入折线图等组件
  require('echarts/lib/chart/gauge')
  require('echarts/lib/chart/radar')
  Vue.prototype.$echarts = echarts

.babel.config.js

module.exports = {
  presets: [["@vue/app", { useBuiltIns: "entry" }]],
  plugins: [
    // element官方教程
    [
      "component",
      {
        libraryName: "element-ui",
        styleLibraryName: "theme-chalk"
      }
    ]
  ]
}
  1. v-if 和 v-show选择调用
    1)少使用v-if
    2)绑定key

  2. 细分vuejs组件
    1)组件细分,比如一个组件,可以把整个组件细分成轮播组件、列表组件、分页组件等。

  3. 减少watch的数据
    1)数据大时,系统会出现卡顿,所以减少watch的数据。

  4. 路由懒加载
    1)分割路由,当路由被访问的时候才加载对应的组件。

  const router = new VueRouter({
    routes: [
      { path: '/foo', component:  () => import('./Foo.vue')}
    ]
  })
  1. 内容类系统的图片资源按需加载
    1)图片加载比较多,使用v-lazy之类的懒加载。
  npm install vue-lazyload --save-dev
  import VueLazyload from 'vue-lazyload'
  Vue.use(VueLazyload)

  
  1. 手动清除各类事件监听器,定时器及闭包等

  2. SSR(服务端渲染)
    如果项目比较大,首屏无论怎么做优化,都出现闪屏或者一阵黑屏的情况。可以考虑使用SSR(服务端渲染)

  3. vue functional 优化

  4. Object.freeze 优化

  5. 减少http请求

  6. 减少缓存及dom操作

2. webpack 打包的优化
  • 查看引入的各类包占比
  1. 在package.json 的scripts里加入 "report": "vue-cli-service build --report"
  2. 运行npm run report
  3. 在 dist 文件里找到report.html, 在浏览器打开即可
  1. 开启gzip压缩
// 下载
npm install compression-webpack-plugin --save-dev
const isProduction = ["production", "prod"].includes(process.env.NODE_ENV);
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;

configureWebpack: config => {
    const plugins = [];
    if (isProduction) {
       plugins.push(
              new CompressionWebpackPlugin({
              filename: "[path].gz[query]",
              algorithm: "gzip",
              test: productionGzipExtensions,
              threshold: 10240,
              minRatio: 0.8
           })
       );
    }
  config.plugins = [...config.plugins, ...plugins];
}
  1. splitChunks 单独打包第三方模块
  // webpack配置
  chainWebpack: (config) => {
    if (isProduction) {
      config.optimization.delete("splitChunks");
    }
    return config;
  },
  configureWebpack: config => {
    if (isProduction) {
      // 利用 splitChunks 单独打包第三方模块
      config.optimization = {
        splitChunks: {
          cacheGroups: {
            common: {
              name: "chunk-common",
              chunks: "initial",
              minChunks: 2,
              maxInitialRequests: 5,
              minSize: 0,
              priority: 1,
              reuseExistingChunk: true,
              enforce: true
            },
            vendors: {
              name: "chunk-vendors",
              test: /[\\/]node_modules[\\/]/,
              chunks: "initial",
              priority: 2,
              reuseExistingChunk: true,
              enforce: true
            },
            elementUI: {
              name: "chunk-elementui",
              test: /[\\/]node_modules[\\/]element-ui[\\/]/,
              chunks: "all",
              priority: 3,
              reuseExistingChunk: true,
              enforce: true
            },
            echarts: {
              name: "chunk-echarts",
              test: /[\\/]node_modules[\\/](vue-)?echarts[\\/]/,
              chunks: "all",
              priority: 4,
              reuseExistingChunk: true,
              enforce: true
            }
          }
        }
      };
    }
    config.plugins = [...config.plugins, ...plugins];
  }
  1. 去除consollog
  configureWebpack: config => {
    const plugins = [];
    if (isProduction) {
      // 去除consollog
      plugins.push(
        new UglifyJsPlugin({
          uglifyOptions: {
            compress: {
              warnings: false,   // 打包报错可注掉
              drop_console: true,
              drop_debugger: false,
              pure_funcs: ["console.log"] //移除console
            },
          },
          sourceMap: false,
          parallel: true,
        })
      );
    }
    config.plugins = [...config.plugins, ...plugins];
  }
  1. 压缩图片
   config.module
      .rule('images')
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        bypassOnDebug: true
      })
    .end()

设置完后,打包后可以看到 .gz 结尾的文件,在http请求的Request Headers 中能看到 Accept-Encoding:gzip。
要使服务器返回.gz文件,还需要对服务器进行配置,根据Request Headers的Accept-Encoding标签进行鉴别,如果支持gzip就返回.gz文件。

完整vue.config.js

const path = require("path");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
const isProduction = ["production", "prod", 'test'].includes(process.env.NODE_ENV);
const resolve = dir => path.join(__dirname, dir);

module.exports = {
  publicPath: "/", // 公共路径
  runtimeCompiler: true, // 是否使用包含运行时编译器的 Vue 构建版本
  outputDir: process.env.outputDir, // 不同的环境打不同包名
  pluginOptions: {
    "style-resources-loader": {
      preProcessor: "less",
      patterns: [
        // 这个是加上自己的路径,
        // 注意:试过不能使用别名路径
        path.resolve(__dirname, "./src/assets/css/variable.less")
      ]
    }
  },
  lintOnSave: false, // 关闭eslint
  productionSourceMap: false, // 生产环境下css 分离文件
  // css相关配置
  devServer: {
    // 配置服务器
    port: 8112,
    open: true,
    https: false,
    // disableHostCheck: true,
    // overlay: {
    //   warnings: false,
    //   errors: true
    // },
    proxy: {
      "/api/*": {
        target: process.env.VUE_APP_URL, // 目标 API 地址
        ws: true, // 是否代理 websockets
        changOrigin: true, // 跨域配置
        pathRewrite: {
          "^/api": "/"
        }
      },
      // "/common/*": {
      //   target: process.env.VUE_APP_MIPA_URL, // 目标 API 地址
      //   ws: true, // 是否代理 websockets
      //   changOrigin: true, // 跨域配置
      //   pathRewrite: {
      //     "^/common": "/"
      //   }
      // }
    }
  },
  // 兼容ie10
  // webpack配置
  chainWebpack: config => {
    // 移除 prefetch 插件
    config.plugins.delete('prefetch')
    // 移除 preload 插件
    config.plugins.delete('preload');
    // ie 兼容,与main.js引入,存在一个就可
    // config.entry("main").add("babel-polyfill");
    // 别名
    config.resolve.alias
      .set("@", resolve("src"))
      .set("@i", resolve("src/assets/image"));
    if (isProduction) {
      config.optimization.delete("splitChunks");
    }
    // 压缩图片
    // config.module
    //   .rule('images')
    //   .use('image-webpack-loader')
    //   .loader('image-webpack-loader')
    //   .options({
    //     bypassOnDebug: true
    //   })
    // .end()
    return config;
  },
  // 配置webpack
  configureWebpack: config => {
    const plugins = [];
    if (isProduction) {
      // 开启gzip压缩
      plugins.push(
        new CompressionWebpackPlugin({
          filename: "[path].gz[query]",
          algorithm: "gzip",
          test: productionGzipExtensions,
          threshold: 10240,
          minRatio: 0.8,
          deleteOriginalAssets: false // 删除原文件
        })
      );
      // 利用 splitChunks 单独打包第三方模块
      config.optimization = {
        splitChunks: {
          cacheGroups: {
            common: {
              name: "chunk-common",
              chunks: "all",
              minChunks: 2,
              maxInitialRequests: 5,
              minSize: 0,
              priority: 1,
              reuseExistingChunk: true,
              enforce: true
            },
            vendors: {
              name: "chunk-vendors",
              test: /[\\/]node_modules[\\/]/,
              chunks: "all",
              priority: 2,
              reuseExistingChunk: true,
              enforce: true
            },
            styles: {
              name: 'chunk-styles',
              test: /\.(sa|sc|le|c)ss$/,
              chunks: 'all',
              enforce: true,
            },
            elementUI: {
              name: "chunk-element-ui",
              test: /[\\/]node_modules[\\/]element-ui[\\/]/,
              chunks: "all",
              priority: 3,
              reuseExistingChunk: true,
              enforce: true
            },
            echarts: {
              name: "chunk-echarts",
              test: /[\\/]node_modules[\\/]echarts[\\/]/,
              chunks: "all",
              priority: 4,
              reuseExistingChunk: true,
              enforce: true
            },
            vue: {
              name: "chunk-vue",
              test: /[\\/]node_modules[\\/]vue[\\/]/,
              chunks: "all",
              priority: 5,
              reuseExistingChunk: true,
              enforce: true
            },
            xlsx: {
              name: "chunk-xlsx",
              test: /[\\/]node_modules[\\/]xlsx[\\/]/,
              chunks: 'all',
              priority: 6,
              reuseExistingChunk: true,
              enforce: true
            },
            runtimeChunk: {
              name: 'manifest'
            }
          }
        }
      };
      // 去除consollog
      plugins.push(
        new UglifyJsPlugin({
          uglifyOptions: {
            // 删除注释
            output: {
              comments: false
            },
            compress: {
              // warnings: false,
              drop_console: true,
              drop_debugger: false,
              pure_funcs: ["console.log"] //移除console
            }
          },
          sourceMap: false,
          parallel: true
        })
      );
    }
    config.plugins = [...config.plugins, ...plugins];
  }
};

你可能感兴趣的:(vue 性能优化)