前端性能优化(记录一次页面加载从8s到1s的优化过程)

面临情况

在没有缓存的情况下,浏览器第一次加载要8~9秒

打出来的包比较大,全文件夹下来18.7M

1、先去掉map文件,设置了不要map文件还是这样会打出来,好奇怪
productionSourceMap: false, // 是否要map文件

发现是这个配置影响了

module.exports = {
    configureWebpack: () => ({
        devtool: 'source-map', 
  }), 
}

1、使用 SourceMapDevToolPlugin 进行更细粒度的配置。查看 source-map-loader 来处理已有的 source map。

2、选择一种 source map 格式来增强调试过程。不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。

注释devtool: 'source-map’后打出来的包反而变大了,全文件夹下来22.2M

改为eval,打包后全文件下来11M

module.exports = {
    configureWebpack: () => ({
        devtool: 'eval', 
  }), 
}

包实在是太大了,估计是引入某个包太大了,猜测要么是拼音,要么是语音输入,找了一圈,最后发现是echart太大了

2、引入echart的CDN,参考了博客vue-cli4项目配置cdn引入静态资源

按照配置修改后我的页面报错了

3、搞不定,还是看官网吧vue-cli官网

configureWebpack

  • 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中。

  • 如果这个值是一个函数,则会接收被解析的配置作为参数。该函数既可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本。

1、我这里是以对象的方式配置的,官方文档的意思是,如果你要判断是生产环境还是开发环境,你就使用函数的方式,不区分就使用对象的方式

2、外部扩展externals
防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。例如,从 CDN 引入 jQuery,而不是把它打包

// 配置webpack
  configureWebpack: {
    devtool: 'eval',
    resolve: {
      // 配置项目别名
      alias: {
        'assets': path.resolve('./src/assets'),
        'api': path.resolve('./src/api'),
        'components': path.resolve('./src/components'),
        'utils': path.resolve('./src/utils'),
        'views': path.resolve('./src/views'),
        'styles': path.resolve('./src/styles'),
        '@': resolve('src')
      }
    },
    externals: {
      vue: 'Vue',
      axios: 'axios',
      'vant': 'VANT',
      'echarts': 'echarts',
      'vue-router': 'VueRouter',
      vuex: 'Vuex',
      'element-ui': 'ELEMENT'
    },
  },
4、vue-cli的pages

在 multi-page 模式下构建应用。每个“page”应该有一个对应的 JavaScript 入口文件。其值应该是一个对象,对象的 key 是入口的名字,value 是:

一个指定了 entry, template, filename, title 和 chunks 的对象 (除了 entry 之外都是可选的);
或一个指定其 entry 的字符串。

pages: {
    index: {
      entry: 'src/main.js',
      template: 'public/index.html',
      filename: 'index.html',
      chunks: ['chunk-vendors', 'chunk-common', 'index'],
      cdn: {
        css: [
          'https://cdn.jsdelivr.net/npm/element-ui@2.13.2/lib/theme-chalk/index.css'
        ],
        js: [
          "https://cdn.staticfile.org/vue/2.5.22/vue.min.js",
          "https://cdn.staticfile.org/vue-router/3.0.2/vue-router.min.js",
          "https://cdn.staticfile.org/axios/0.19.0-beta.1/axios.min.js",
          'https://cdn.jsdelivr.net/npm/vant@1.6/lib/vant.min.js',
          "https://cdn.staticfile.org/vuex/3.1.1/vuex.min.js",
          "https://cdn.jsdelivr.net/npm/element-ui@2.13.2/lib/index.js",
          "https://cdn.bootcss.com/echarts/4.8.0/echarts.simple.min.js",
        ]
      },
    }
  },

配置完pages后要配置index.html,用asp语句动态动态生产secript标签和link标签

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <!-- 适配iPhone X viewport-fit=cover -->
  <meta name="viewport"
    content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover" />
  <meta name="format-detection" content="telephone=yes" />
  <link rel="icon" href="<%= BASE_URL %>app_logo.svg" />
  <% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
  <link href="<%=js%>" rel="preload" as="script" />
  <script src="<%=js%>"></script>
  <% } %>
  <meta http-equiv="pragma" content="no-cache" />
  <meta http-equiv="cache-control" content="no-cache" />
  <meta http-equiv="expires" content="0" />
  <title></title>
</head>

<body>
  <noscript>
    <strong>We're sorry but vue-cli3-project doesn't work properly without
      JavaScript enabled. Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>
</body>

</html>

配置cdn后,打包后的文件显著减小
bootcnd

5、遇到问题,从cdn引入axios,在main.js中把axios挂在原型中,在别的文件中以this.$axios配置axios会不生效
// import axios from "axios"
Vue.prototype.$axios = axios

在Staff-login.js就算按下面的设置了,请求还是么得token,搞不定,axios还是不导入cdn了

this.$axios.defaults.headers.common['token'] = this.queryData.token
6、包还是很大,查看分析
  • 先装上分析插件 webpack-bundle-analyzer

1、 具体使用 这个可以复制

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin // 导入
module.exports = {
    configureWebpack:{
        // 配置
        plugins:[
          new BundleAnalyzerPlugin({ analyzerPort: 8919 })
        ]
    }
}

打包后会自动出现一个端口为8919的站点,站点内容如下:

chainWebpack 是一个函数,会接收一个基于 webpack-chain 的 ChainableConfig 实例。允许对内部的 webpack 配置进行更细粒度的修改。

7、配置优化,拆包

module.exports = {
    configureWebpack:{
        // 优化
        //优化
    optimization: {
      //告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer 定义的插件压缩 bundle。
      minimize: true,
      // 对于动态导入模块,默认使用 webpack v4+ 提供的全新的通用分块策略
      splitChunks: {
        chunks: 'async',
        minSize: 30000,
        // minRemainingSize: 0,//这个打开会报错
        maxSize: 0,
        minChunks: 1,
        maxAsyncRequests: 6,
        maxInitialRequests: 4,
        automaticNameDelimiter: '~',
        cacheGroups: {
          defaultVendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
          },
          default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true
          }
        }
      }
    }
    }
}

加上这个后又小了一些,总共10.3

8、剩下比较大的svg和vconsole,按理说vconsole是不需要在生产用的

  • vsonsole忘了注释掉了,注释掉就不会打包进来了,或者可以配置vconsole只在生产展示
// import Vconsole from "vconsole"
// const vConsole = new Vconsole()
// Vue.use(vConsole)

  • svg好解决,删掉不要的就行

结果,从最开始的18.7M到最后的3.36M,感觉包的大小还是优化了蛮多的,加载时间也从8s多到现在1s左右

最后贴上vue.config.js,

const CompressionPlugin = require('compression-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
// 自动增加浏览器前缀,提高兼容性
const autoprefixer = require('autoprefixer')
const path = require('path')
const resolve = dir => path.join(__dirname, dir)
const IS_PROD = process.env.NODE_ENV === 'production'
const cdnDomain = '/'
module.exports = {
  publicPath: IS_PROD ? cdnDomain : '/',
  productionSourceMap: false, // 是否要map文件
  lintOnSave: false, //设置是否在开发环境下每次保存代码时都启用 eslint验证。
  devServer: {
    hot: true,
    // 设置端口
    port: 9527,
    https: false,
    hotOnly: false,
    overlay: {
      warnings: true,
      errors: true
    },
    // 配置多个代理
    proxy: {
      [process.env.VUE_APP_BASE_API]: {
        target: process.env.BASE_URL,
        changeOrigin: true,
        pathRewrite: {
          ['^' + process.env.VUE_APP_BASE_API]: ''
        }
      }
    }
  },
  chainWebpack: config => {
    const svgRule = config.module.rule('svg') // 找到svg-loader
    svgRule.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
    svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
    svgRule // 添加 svg新的 loader 处理
      .test(/\.svg$/)
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })

    // 修改images loader 添加svg处理
    const imagesRule = config.module.rule('images')
    imagesRule.exclude.add(path.resolve('src/assets/icons'))
    // 图片压缩
    config.module
      .rule('images')
      .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
      .use('img-loader')
      .loader('img-loader').options({
        plugins: [
          require('imagemin-jpegtran')(),
          require('imagemin-pngquant')({
            quality: [0.75, 0.85]
          })
        ]
      })

    // 启用GZip压缩
    config
      .plugin('compression')
      .use(CompressionPlugin, {
        asset: '[path].gz[query]',
        algorithm: 'gzip',
        test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
        threshold: 10240,
        minRatio: 0.8,
        cache: true
      })
      .tap(args => { })
  },
  css: {
    loaderOptions: {
      postcss: {
        plugins: [
          autoprefixer(),
          // 使用px-to-viewport做屏幕适配
          require('postcss-px-to-viewport')({
            unitToConvert: 'px',
            viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750
            viewportHeight: 1334, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
            unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数
            viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw
            selectorBlackList: [], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
            minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
            mediaQuery: false // 允许在媒体查询中转换`px`
          })
        ]
      },
      less: {
        modifyVars: {
          black: '#313131',
          red: '#F44',
          blue: '#666666',
          orange: '#F89516',
          green: '#268AED',
          'text-color': '#313131',
          'background-color': '#F5F5F5',
          // 按钮重置
          'button-default-height': '38px',
          'button-default-line-height': '36px',
          'button-default-font-size': '14px',
          'button-default-color': '#34394A',
          'button-default-border-color': '#DDD',
          'button-large-height': '47px',
          'button-large-line-height': '45px',
          'button-border-radius': '4px',
          'button-small-font-size': '14px',
          'button-small-min-width': '64px',
          // nav重置
          'nav-bar-arrow-size': '18px',
          'nav-bar-title-font-size': '17px'
        }
      }
    }
  },
  // 配置webpack
  configureWebpack: {
    // devtool: 'eval',
    devtool: 'source-map',
    resolve: {
      // 配置项目别名
      alias: {
        'assets': path.resolve('./src/assets'),
        'api': path.resolve('./src/api'),
        'components': path.resolve('./src/components'),
        'utils': path.resolve('./src/utils'),
        'views': path.resolve('./src/views'),
        'styles': path.resolve('./src/styles'),
        '@': resolve('src')
      }
    },
    externals: {
      vue: 'Vue',
      // axios: 'axios',// 不能用cdn,不然配置没法用,暂未找到原因
      'vant': 'VANT',
      'echarts': 'echarts',
      'vue-router': 'VueRouter',
      vuex: 'Vuex',
      // swiper: 'swiper', // cdn加载太慢
      // "vue-ls": "vue-ls", // cdn加载太慢
      'lottie-web': 'lottie-web',
      'element-ui': 'ELEMENT'
    },
    plugins: [
      // 分析打包体积
      new BundleAnalyzerPlugin({ analyzerPort: 8919 })
    ],
    //优化
    optimization: {
      //告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer 定义的插件压缩 bundle。
      minimize: true,
      // 对于动态导入模块,默认使用 webpack v4+ 提供的全新的通用分块策略
      splitChunks: {
        chunks: 'async',
        minSize: 30000,
        // minRemainingSize: 0,//webpack官网搂过来的,但是有这句会报错
        maxSize: 0,
        minChunks: 1,
        maxAsyncRequests: 6,
        maxInitialRequests: 4,
        automaticNameDelimiter: '~',
        cacheGroups: {
          defaultVendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
          },
          default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true
          }
        }
      }
    }
  },
  pages: {
    index: {
      entry: 'src/main.js',
      template: 'public/index.html',
      filename: 'index.html',
      chunks: ['chunk-vendors', 'chunk-common', 'index'],
      // 配置在index.html动态生成的标签
      cdn: {
        css: [
          'https://cdn.jsdelivr.net/npm/vant@2.2/lib/index.css'
        ],
        js: [
          "https://cdn.staticfile.org/vue/2.5.22/vue.min.js",
          "https://cdn.staticfile.org/vue-router/3.0.2/vue-router.min.js",
          // "https://cdn.staticfile.org/axios/0.19.0-beta.1/axios.min.js",// 不能用cdn,不然配置没法用,暂未找到原因
          'https://cdn.jsdelivr.net/npm/vant@1.6/lib/vant.min.js',
          "https://cdn.staticfile.org/vuex/3.1.1/vuex.min.js",
          "https://cdn.jsdelivr.net/npm/element-ui@2.13.2/lib/index.js",
          "https://cdn.bootcss.com/echarts/4.8.0/echarts.simple.min.js",
          "https://cdn.staticfile.org/lottie-web/5.7.1/lottie.min.js",
          // "https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.0.2/js/swiper.min.js",// cdn加载太慢
          // "https://cdn.bootcdn.net/ajax/libs/vue-ls/3.2.1/vue-ls.js",// cdn加载太慢
        ]
      },
    }
  }
}

本文主要是记录下自己优化的过程,配置还不完善,很多地方写的太乱,条理性也不好,有空慢慢改…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值