您现在的位置是:首页 >技术教程 >vue中vue.config.js配置优化首屏加载速度,以及打包分析网站首页技术教程

vue中vue.config.js配置优化首屏加载速度,以及打包分析

寒墨茗殇 2026-03-25 00:01:04
简介vue中vue.config.js配置优化首屏加载速度,以及打包分析

1. 优化 CDN 引入

vue.config.js:

// import { swiper, swiperSlide } from 'vue-awesome-swiper'
// import 'swiper/swiper-bundle.css'

const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)

const externals = {
  'vue-awesome-swiper': 'VueAwesomeSwiper',
  Swiper: 'Swiper',
}
const cdn = {
  // 开发环境
  dev: {
    css: [],
    js: [],
  },
  // 生产环境
  build: {
    css: ['https://cdn.jsdelivr.net/npm/swiper@6.1.1/swiper-bundle.css'],
    js: [
      'https://cdn.jsdelivr.net/npm/swiper@6.1.1/swiper-bundle.js',
      'https://cdn.jsdelivr.net/npm/vue-awesome-swiper@3.1.3/dist/vue-awesome-swiper.js',
    ],
  },
}

module.exports = {
  configureWebpack: (config) => {
    config.name = name
    if (IS_PROD) {
      Object.assign(config, {
        name: name,
        externals: externals,
      })
    }
  },

  chainWebpack: (config) => {
    /**
     * 添加CDN参数到htmlWebpackPlugin配置中
     */
    config.plugin('html').tap((args) => {
      if (IS_PROD) {
        args[0].cdn = cdn.build
      } else {
        args[0].cdn = cdn.dev
      }
      return args
    })
  },
}

index.html:

<head>
  <!-- 动态css-->
  <% for (var i in
  htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
  <link
    href="<%= htmlWebpackPlugin.options.cdn.css[i] %>"
    rel="preload"
    as="style"
  />
  <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
  <% } %>

  <!-- 动态js-->
  <% for (var i in
  htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
  <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
  <% } %>
</head>

2.压缩和 Gzip

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

vue.config.js:

const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = /.(js|css|json|txt|html|ico|svg)(?.*)?$/i
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
module.exports = {
  // 其他配置...
  configureWebpack: (config) => {
    config.name = name
    const plugins = []
    if (IS_PROD) {
      // gzip
      plugins.push(
        new CompressionWebpackPlugin({
          filename: '[path].gz[query]',
          algorithm: 'gzip',
          test: productionGzipExtensions,
          threshold: 10240,
          minRatio: 0.8,
        })
      )
    }
    config.plugins = [...config.plugins, ...plugins]
  },
  // 其他配置...
}

3. 代码分割

vue.config.js:

const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
module.exports = {
  // 其他配置...
  chainWebpack: (config) => {
    // 其他配置...

    // 判断是否是生产环境
    config.when(IS_PROD, (config) => {
      config.optimization.splitChunks({
        chunks: 'all',
        cacheGroups: {
          // cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块
          commons: {
            name: 'chunk-commons',
            test: resolve('src/components'),
            minChunks: 3, //  被至少用三次以上打包分离
            priority: 5, // 优先级
            reuseExistingChunk: true, // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
          },
          node_vendors: {
            name: 'chunk-libs',
            chunks: 'initial', // 只打包初始时依赖的第三方
            test: /[\/]node_modules[\/]/,
            priority: 10,
          },
          vueawesomeswiper: {
            name: 'chunk-vueawesomeswiper', // 单独将 vueawesomeswiper 拆包
            test: /[\/]node_modules[\/]vue-awesome-swiper[\/]/,
            priority: 20, // 数字大权重到,满足多个 cacheGroups 的条件时候分到权重高的
          },
        },
      })
      config.optimization.runtimeChunk('single')
    })
  },
}

4.使用 Service Worker

npm install workbox-webpack-plugin --save-dev

vue.config.js:

const WorkboxPlugin = require('workbox-webpack-plugin')
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
module.exports = {
  // 其他配置...
  configureWebpack: (config) => {
    config.name = name

    if (IS_PROD) {
      config.plugins.push(
        new WorkboxPlugin.GenerateSW({
          clientsClaim: true,
          skipWaiting: true,
        })
      )
    }
  },
  // 其他配置...
}

5. 使用预渲染

npm install prerender-spa-plugin --save-dev

vue.config.js:

const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
module.exports = {
  // 其他配置...
  configureWebpack: (config) => {
    config.name = name

    if (IS_PROD) {
      config.plugins.push(
        new PrerenderSPAPlugin({
          staticDir: path.join(__dirname, 'dist'),
          routes: [
            '/',
            '/#/custom', // hash路由时
          ],
          renderer: new Renderer({
            renderAfterTime: 5000, // 等待时间(确保页面加载完成)
            headless: true,
            injectProperty: '__PRERENDER_INJECTED',
            waitForElement: '#app', // 等待特定元素出现
          }),
        })
      )
    }
  },
  // 其他配置...
}

6. 其他东西 webpack-report,打包分析

npm install --save-dev webpack-bundle-analyzer

vue.config.js:

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
module.exports = {
  // 配置1
  chainWebpack: (config) => {
    /**
     * 打包分析
     */
    if (IS_PROD) {
      config.plugin('webpack-report').use(BundleAnalyzerPlugin, [
        {
          analyzerMode: 'static',
        },
      ])
    }
  },

  //配置2
  configureWebpack: (config) => {
    config.name = name
    const plugins = []
    if (IS_PROD) {
      plugins.push(
        new BundleAnalyzerPlugin({
          analyzerMode: 'static', // 生成静态 HTML 报告
          generateStatsFile: true, // 生成 stats 文件
          statsFilename: 'stats.json', // stats 文件的名称
          reportFilename: 'report.html', // 报告文件的名称
          openAnalyzer: false, // 不自动打开报告
        })
      )
    }
    config.plugins = [...config.plugins, ...plugins]
  },
}

7.整体结果

vue.config.js:

'use strict'
const path = require('path')

// 压缩和 Gzip
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = /.(js|css|json|txt|html|ico|svg)(?.*)?$/i

// 预渲染
const PrerenderSPAPlugin = require('prerender-spa-plugin').PrerenderSPAPlugin
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer

// 打包分析
const BundleAnalyzerPlugin =
  require('webpack-bundle-analyzer').BundleAnalyzerPlugin

const resolve = (dir) => path.join(__dirname, dir)
const name = 'vue mobile template'
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)

const externals = {
  'vue-awesome-swiper': 'VueAwesomeSwiper',
  Swiper: 'Swiper',
}

const cdn = {
  dev: {
    css: [],
    js: [],
  },
  build: {
    css: [
      'https://image-v2.youpaizhaohuo.cn/insideH5/node_modules/swiper-bundle-6.1.1.css',
    ],
    js: [
      'https://image-v2.youpaizhaohuo.cn/insideH5/node_modules/swiper-bundle-6.1.1.js',
      'https://image-v2.youpaizhaohuo.cn/insideH5/node_modules/vue-awesome-swiper-3.1.3.js',
    ],
  },
}

module.exports = {
  publicPath: './',
  outputDir: 'dist',
  assetsDir: 'static',
  lintOnSave: !IS_PROD,
  productionSourceMap: false,
  devServer: {
    //port: 8000, // 端口
    open: false,
    overlay: {
      warnings: false,
      errors: true,
    },
    //代理
    proxy: {
      '/api': {
        target: ''//代理的域名,
        changeOrigin: true,
        pathRewrite: {
          '^/api': '/',
        },
      },
    },
  },
  css: {
    extract: IS_PROD, // 是否将组件中的 CSS 提取至一个独立的 CSS 文件中 (而不是动态注入到 JavaScript 中的 inline 代码)。
    sourceMap: false,
    loaderOptions: {
      // 向全局sass样式传入共享的全局变量, $src可以配置图片cdn前缀
      // 详情: https://cli.vuejs.org/guide/css.html#passing-options-to-pre-processor-loaders
      scss: {
        prependData: `
          @import "assets/css/mixin.scss";
          @import "assets/css/variables.scss";
           $cdn: "";
        `,
      },
    },
  },
  configureWebpack: (config) => {
    config.name = name
    const plugins = []
    if (IS_PROD) {
      Object.assign(config, {
        name: name,
        externals: externals,
      })

      // prerender-spa-plugin
      plugins.push(
        new PrerenderSPAPlugin({
          staticDir: path.join(__dirname, 'dist'),
          routes: ['/', '/#/customBack/commonIndex'],
          renderer: new Renderer({
            renderAfterTime: 5000,
            headless: true,
            injectProperty: '__PRERENDER_INJECTED',
            waitForElement: '#app',
          }),
        })
      )
      // gzip
      plugins.push(
        new CompressionWebpackPlugin({
          filename: '[path].gz[query]',
          algorithm: 'gzip',
          test: productionGzipExtensions,
          threshold: 10240,
          minRatio: 0.8,
        })
      )

      // Service Worker
      plugins.push(
        new WorkboxPlugin.GenerateSW({
          clientsClaim: true,
          skipWaiting: true,
        })
      )
    }
    config.plugins = [...config.plugins, ...plugins]
  },

  chainWebpack: (config) => {
    config.plugins.delete('preload')
    config.plugins.delete('prefetch')

    // 别名 alias
    config.resolve.alias
      .set('@', resolve('src'))
      .set('assets', resolve('src/assets'))
      .set('api', resolve('src/api'))
      .set('views', resolve('src/views'))
      .set('components', resolve('src/components'))

    /**
     * 添加CDN参数到htmlWebpackPlugin配置中
     */
    config.plugin('html').tap((args) => {
      if (IS_PROD) {
        args[0].cdn = cdn.build
      } else {
        args[0].cdn = cdn.dev
      }
      return args
    })

    /**
     * 设置保留空格
     */
    config.module
      .rule('vue')
      .use('vue-loader')
      .loader('vue-loader')
      .tap((options) => {
        options.compilerOptions.preserveWhitespace = true
        return options
      })
      .end()

    /**
     * 打包分析
     */
    if (IS_PROD) {
      config.plugin('webpack-report').use(BundleAnalyzerPlugin, [
        {
          analyzerMode: 'static',
        },
      ])
    }

    config.when(!IS_PROD, (config) => config.devtool('cheap-source-map'))

    config.when(IS_PROD, (config) => {
      config
        .plugin('ScriptExtHtmlWebpackPlugin')
        .after('html')
        .use('script-ext-html-webpack-plugin', [
          {
            inline: /runtime..*.js$/,
          },
        ])
        .end()

      config.optimization.splitChunks({
        chunks: 'all',
        cacheGroups: {
          // cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块
          commons: {
            name: 'chunk-commons',
            test: resolve('src/components'),
            minChunks: 3, //  被至少用三次以上打包分离
            priority: 5, // 优先级
            reuseExistingChunk: true, // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
          },
          node_vendors: {
            name: 'chunk-libs',
            chunks: 'initial', // 只打包初始时依赖的第三方
            test: /[\/]node_modules[\/]/,
            priority: 10,
          },
          vueawesomeswiper: {
            name: 'chunk-vueawesomeswiper', // 单独将 vueawesomeswiper 拆包
            test: /[\/]node_modules[\/]vue-awesome-swiper[\/]/,
            priority: 20, // 数字大权重到,满足多个 cacheGroups 的条件时候分到权重高的
          },
        },
      })

      config.optimization.runtimeChunk('single')
    })
  },
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。