webpack.prod.conf.js 5.38 KB
Newer Older
Pan's avatar
Pan committed
1 2 3 4 5 6 7 8 9
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
10 11 12
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
Pan's avatar
Pan committed
13
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
Pan's avatar
Pan committed
14

15
function resolve(dir) {
Pan's avatar
Pan committed
16
  return path.join(__dirname, '..', dir)
Pan's avatar
Pan committed
17 18
}

Pan's avatar
Pan committed
19 20
const env = require('../config/prod.env')

21 22 23 24
// For NamedChunksPlugin
const seen = new Set()
const nameLength = 4

Pan's avatar
Pan committed
25
const webpackConfig = merge(baseWebpackConfig, {
26
  mode: 'production',
Pan's avatar
Pan committed
27 28 29
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
Pan's avatar
Pan committed
30 31
      extract: true,
      usePostCSS: true
Pan's avatar
Pan committed
32 33
    })
  },
Pan's avatar
Pan committed
34
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
Pan's avatar
Pan committed
35 36
  output: {
    path: config.build.assetsRoot,
37 38
    filename: utils.assetsPath('js/[name].[chunkhash:8].js'),
    chunkFilename: utils.assetsPath('js/[name].[chunkhash:8].js')
Pan's avatar
Pan committed
39 40 41 42 43 44 45
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // extract css into its own file
46 47 48
    new MiniCssExtractPlugin({
      filename: utils.assetsPath('css/[name].[contenthash:8].css'),
      chunkFilename: utils.assetsPath('css/[name].[contenthash:8].css')
Pan's avatar
Pan committed
49 50 51 52 53 54 55 56
    }),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: config.build.index,
      template: 'index.html',
      inject: true,
Pan's avatar
Pan committed
57
      favicon: resolve('favicon.ico'),
Pan's avatar
Pan committed
58
      title: 'vue-admin-template',
Pan's avatar
Pan committed
59 60 61 62 63 64 65
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      }
66 67 68
      // default sort mode uses toposort which cannot handle cyclic deps
      // in certain cases, and in webpack 4, chunk order in HTML doesn't
      // matter anyway
Pan's avatar
Pan committed
69
    }),
70 71 72
    new ScriptExtHtmlWebpackPlugin({
      //`runtime` must same as runtimeChunk name. default is `runtime`
      inline: /runtime\..*\.js$/
Pan's avatar
Pan committed
73
    }),
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    // keep chunk.id stable when chunk has no name
    new webpack.NamedChunksPlugin(chunk => {
      if (chunk.name) {
        return chunk.name
      }
      const modules = Array.from(chunk.modulesIterable)
      if (modules.length > 1) {
        const hash = require('hash-sum')
        const joinedHash = hash(modules.map(m => m.id).join('_'))
        let len = nameLength
        while (seen.has(joinedHash.substr(0, len))) len++
        seen.add(joinedHash.substr(0, len))
        return `chunk-${joinedHash.substr(0, len)}`
      } else {
        return modules[0].id
      }
Pan's avatar
Pan committed
90
    }),
91 92
    // keep module.id stable when vender modules does not change
    new webpack.HashedModuleIdsPlugin(),
Pan's avatar
Pan committed
93 94 95 96 97 98 99 100
    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        libs: {
          name: 'chunk-libs',
          test: /[\\/]node_modules[\\/]/,
          priority: 10,
          chunks: 'initial' // 只打包初始时依赖的第三方
        },
        elementUI: {
          name: 'chunk-elementUI', // 单独将 elementUI 拆包
          priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
          test: /[\\/]node_modules[\\/]element-ui[\\/]/
        }
      }
    },
    runtimeChunk: 'single',
    minimizer: [
      new UglifyJsPlugin({
        uglifyOptions: {
          mangle: {
            safari10: true
          }
        },
        sourceMap: config.build.productionSourceMap,
        cache: true,
        parallel: true
      }),
      // Compress extracted CSS. We are using this plugin so that possible
      // duplicated CSS from different components can be deduped.
      new OptimizeCSSAssetsPlugin()
    ]
  }
Pan's avatar
Pan committed
136 137 138
})

if (config.build.productionGzip) {
Pan's avatar
Pan committed
139
  const CompressionWebpackPlugin = require('compression-webpack-plugin')
Pan's avatar
Pan committed
140 141 142 143 144 145

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
146
        '\\.(' + config.build.productionGzipExtensions.join('|') + ')$'
Pan's avatar
Pan committed
147 148 149 150 151 152 153
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
if (config.build.generateAnalyzerReport || config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
    .BundleAnalyzerPlugin

  if (config.build.bundleAnalyzerReport) {
    webpackConfig.plugins.push(
      new BundleAnalyzerPlugin({
        analyzerPort: 8080,
        generateStatsFile: false
      })
    )
  }

  if (config.build.generateAnalyzerReport) {
    webpackConfig.plugins.push(
      new BundleAnalyzerPlugin({
        analyzerMode: 'static',
        reportFilename: 'bundle-report.html',
        openAnalyzer: false
      })
    )
  }
Pan's avatar
Pan committed
176 177 178
}

module.exports = webpackConfig