一、通用版

const webpack = require('webpack')
const TerserPlugin = require('terser-webpack-plugin')
const CompressionPlugin = require('compression-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')

const proName = '/gdyb-h5/'
const envTime = new Date().getTime()
const baseUrl = process.env.VUE_APP_BASE_API
const envName = process.env.VUE_APP_MODE || 'sit'
const proEnv = envName === 'pro'

const cdnList = {
  css: ['cdnList/css/vant_2.12.37_index_min.css'],
  js: [
    'cdnList/js/vue_2.6.11_min.js',
    'cdnList/js/vue-vuex_3.4.0_min.js',
    'cdnList/js/vue-router_3.6.5_min.js',
    'cdnList/js/lib-flexible_0.3.2_min.js',
    'cdnList/js/axios_0.24.0_min.js',
    'cdnList/js/crypto-js_4.1.1_min.js',
    'cdnList/js/better-scroll_2.4.2_min.js',
    'cdnList/js/moment_2.29.1_min.js',
    // 'cdnList/js/echars_5.2.2_min.js',
    'cdnList/js/html2-canvas_1.0.0-rc.4_min.js',
    'cdnList/js/signature_pad_3.0.0-beta4_min.js'
    // 'cdnList/js/tinymce_5.10.2_min.js',
    // 'cdnList/js/vue-tinymce_1.1.2_min.js'
  ]
}

module.exports = {
  publicPath: proName,
  assetsDir: 'static',
  filenameHashing: false,
  productionSourceMap: !proEnv,
  css: {
    extract: true,
    sourceMap: !proEnv,
    loaderOptions: {
      sass: {
        additionalData: `
          @import "@/assets/scss/variable.scss";
          @import "@/assets/scss/mixin.scss";
        `
      }
    }
  },
  pages: {
    index: {
      title: 'gdyb-h5',
      cdnList: cdnList,
      filename: 'index.html',
      entry: './src/main.js',
      template: './public/index.html',
      chunks: ['chunk-vendors', 'chunk-common', 'index']
    }
  },
  chainWebpack: config => {
    // 避免加载多余无用资源
    config.plugins.delete('prefetch')
    config.plugins.delete('preload')

    const minimizer = proEnv ? [new TerserPlugin()] : []
    config.optimization = Object.assign(config.optimization, {
      minimize: true,
      minimizer: minimizer,
      splitChunks: {
        chunks: 'all'
      }
    })

    // CSS压缩
    const miniCssExtractPlugin = new MiniCssExtractPlugin({
      filename: 'static/css/[name].[hash:8].css',
      chunkFilename: 'static/css/[name].[hash:8].css'
    })
    config.plugin('mini-css-extract-plugin').use(miniCssExtractPlugin)
    // config.plugin('extract-css').use(miniCssExtractPlugin)

    // image按需加载
    config.module.rule('images')
      .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        disable: !!proEnv,
        bypassOnDebug: true
      })
      .end()
  },
  configureWebpack: (config) => {
    if (proEnv) {
      config.devtool = false

      // 打包分析
      config.plugins.push(new BundleAnalyzerPlugin())
    }

    // CND去除打包依赖项
    config.externals = {
      vue: 'Vue',
      vuex: 'Vuex',
      'vue-router': 'VueRouter',
      'lib-flexible': 'lib',
      axios: 'axios',
      'crypto-js': 'CryptoJS',
      'better-scroll': 'BetterScroll',
      moment: 'moment',
      // echarts: 'echarts',
      html2canvas: 'html2canvas',
      // tinymce: 'tinymce',
      // 'vue-tinymce': 'VueTinymce',
      signature_pad: 'SignaturePad'
    }

    // 输出文件名
    config.output = Object.assign(config.output, {
      publicPath: proName,
      filename: `static/js/[name]-${envTime}.js`,
      chunkFilename: `static/js/[name]-${envTime}.js`
    })

    // 兼容依赖
    config.entry.app = ['babel-polyfill', './src/main.js']

    // 公共代码抽离
    // config.optimization = {
    //   splitChunks: {
    //     cacheGroups: {
    //       common: {
    //         name: 'chunk-common',
    //         chunks: 'initial',
    //         minSize: 0,
    //         minChunks: 2
    //       },
    //       vendor: {
    //         name: 'chunk-vendors',
    //         priority: 1,
    //         test: /node_modules/,
    //         chunks: 'initial',
    //         minSize: 0,
    //         minChunks: 2
    //       }
    //     }
    //   }
    // }

    config.optimization.splitChunks = {
      chunks: 'async',
      minChunks: 2,
      minSize: 20 * 1024,
      maxSize: 200 * 1024
      // minSizeReduction: 30 * 1024 // 至少缩小bundle体积限制:如果生成chunk不会减小总bundle体积,则不生成, [webpack5+]
    }

    // 开启Gzip压缩
    const compression = new CompressionPlugin({
      algorithm: 'gzip',
      filename: '[path].gz[query]',
      test: /\.js$|\.html$|\.css$|\.scss$/,
      minRatio: 1,
      threshold: 10240,
      deleteOriginalAssets: false
    })

    const limitChuck = new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 60,
      minChunkSize: 1000
    })

    config.plugins.push(compression, limitChuck)
    // config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
  },
  devServer: {
    proxy: baseUrl
  }
}

二、改良版

const webpack = require('webpack')
const CompressionPlugin = require('compression-webpack-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')

const proName = '/gdyb-h5/'
const envTime = new Date().getTime()
const baseUrl = process.env.VUE_APP_BASE_API
const envName = process.env.VUE_APP_MODE || 'sit'
const proEnv = envName === 'pro'

const cdnList = {
  css: ['cdnList/css/vant_2.12.37_index_min.css'],
  js: [
    'cdnList/js/vue_2.6.11_min.js',
    'cdnList/js/vue-vuex_3.4.0_min.js',
    'cdnList/js/vue-router_3.6.5_min.js',
    'cdnList/js/lib-flexible_0.3.2_min.js',
    'cdnList/js/axios_0.24.0_min.js',
    'cdnList/js/crypto-js_4.1.1_min.js',
    'cdnList/js/better-scroll_2.4.2_min.js',
    'cdnList/js/moment_2.29.1_min.js',
    'cdnList/js/html2-canvas_1.0.0-rc.4_min.js',
    'cdnList/js/signature_pad_3.0.0-beta4_min.js'
    // 'cdnList/js/echars_5.2.2_min.js',
    // 'cdnList/js/tinymce_5.10.2_min.js',
    // 'cdnList/js/vue-tinymce_1.1.2_min.js'
  ]
}

module.exports = {
  publicPath: proName,
  assetsDir: 'static',
  filenameHashing: false,
  productionSourceMap: !proEnv,
  css: {
    extract: true,
    sourceMap: !proEnv,
    loaderOptions: {
      sass: {
        additionalData: `
          @import "@/assets/scss/variable.scss";
          @import "@/assets/scss/mixin.scss";
        `
      }
    }
  },
  pages: {
    index: {
      title: 'gdyb-h5',
      cdnList: cdnList,
      filename: 'index.html',
      entry: './src/main.js',
      template: './public/index.html',
      chunks: ['index', 'chunk-common', 'chunk-vendors', 'chunk-tinymce', 'chunk-echarts', 'chunk-pdfh5']
    }
  },
  configureWebpack: (config) => {
    // 生产开启配置
    if (proEnv) {
      config.devtool = false

      // 模块拆分
      config.optimization.splitChunks = {
        cacheGroups: {
          common: {
            name: 'chunk-common',
            chunks: 'async',
            minChunks: 2,
            minSize: 20 * 1024,
            maxSize: 200 * 1024
          },
          vendor: {
            name: 'chunk-vendors',
            chunks: 'async',
            priority: 1,
            test: /[\\/]node_modules[\\/]/,
            minSize: 0,
            minChunks: 2
          },
          vue: {
            name: 'chunk-vuejs',
            test: /[\\/]node_modules[\\/]_?vue(.*)/,
            priority: 20
          },
          echarts: {
            name: 'chunk-echarts',
            test: /[\\/]node_modules[\\/]_?echarts(.*)/,
            priority: 50,
            chunks: 'async'
          },
          pdfh5: {
            name: 'chunk-pdfh5',
            test: /[\\/]node_modules[\\/]_?pdfh5(.*)/,
            priority: 50,
            chunks: 'async'
          },
          tinymce: {
            name: 'chunk-tinymce',
            test: /[\\/]node_modules[\\/]_?tinymce(.*)/,
            priority: 50,
            chunks: 'async'
          },
        }
      }

      // 打包分析
      config.plugins.push(new BundleAnalyzerPlugin())
    }

    // CND去除打包依赖项
    config.externals = {
      vue: 'Vue',
      vuex: 'Vuex',
      'vue-router': 'VueRouter',
      'lib-flexible': 'lib',
      axios: 'axios',
      'crypto-js': 'CryptoJS',
      'better-scroll': 'BetterScroll',
      moment: 'moment',
      html2canvas: 'html2canvas',
      signature_pad: 'SignaturePad'
      // echarts: 'echarts',
      // tinymce: 'tinymce',
      // 'vue-tinymce': 'VueTinymce',
    }

    // 输出文件名
    config.output = Object.assign(config.output, {
      publicPath: proName,
      filename: `static/js/[name]-${envTime}.js`,
      chunkFilename: `static/js/[name]-${envTime}.js`
    })

    // 兼容依赖
    config.entry.app = ['babel-polyfill', './src/main.js']

    // 开启Gzip压缩
    const compression = new CompressionPlugin({
      algorithm: 'gzip',
      filename: '[path].gz[query]',
      test: /\.js$|\.html$|\.css$|\.scss$/,
      minRatio: 1,
      threshold: 10240,
      deleteOriginalAssets: false
    })

    const limitChuck = new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 60,
      minChunkSize: 1000
    })

    config.plugins.push(compression, limitChuck)
  },
  devServer: {
    proxy: baseUrl
  }
}
文章目录