webpack配置文件(4.0+)

  • 2018/06/03更新: 更新到webpack 4.0, 添加部分注释
  • 2018/02/06更新: 解决CSS的HMR问题

这里放上一份记目前在项目中使用的webpack的配置,基本上集成了目前大家能搜索的到优化技巧~~~大家有什么其他的黑科技的欢迎砸砖,默认大家都知道webpack 的相关配置,因此基础的东西不在本文内

webpack.base.js

基本配置文件,给线上线下配置继承;

  1. const webpack = require('webpack');
  2. const path = require('path');
  3. // webpack 4.0 中用来抽离css 的插件
  4. const MiniCssExtractPlugin = require("mini-css-extract-plugin");
  5. const HappyPack = require('happypack');
  6. const os = require('os');
  7. const ProgressBarPlugin = require('progress-bar-webpack-plugin');
  8. const chalk = require('chalk');
  9. // 针对 Lodash 按需打包
  10. const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
  11. const sourcePath = path.join(__dirname, '../web');
  12. const nodeModules = path.resolve(__dirname, '../node_modules');
  13. const isDev = !!(process.env.NODE_ENV != 'production');
  14. // const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
  15. function createHappyPlugin(id, loaders) {
  16. return new HappyPack({
  17. id: id,
  18. loaders: loaders,
  19. // threadPool: happyThreadPool,
  20. });
  21. }
  22. module.exports = {
  23. context: sourcePath,
  24. module: {
  25. rules: [{
  26. test: /\.(js|jsx)$/,
  27. exclude: nodeModules,
  28. include: sourcePath,
  29. use: ['happypack/loader?id=happy-babel-js'],
  30. // use: [{
  31. // loader: 'babel-loader',
  32. // options: {
  33. // cacheDirectory: true,
  34. // presets: ['react-hmre']
  35. // }
  36. // }],
  37. }, {
  38. test: /\.css$/,
  39. exclude: nodeModules,
  40. use: isDev ? ['style-loader', 'happypack/loader?id=happy-css'] : ["style-loader", MiniCssExtractPlugin.loader, 'happypack/loader?id=happy-css']
  41. }, {
  42. test: /\.less$/,
  43. use: isDev ? ['style-loader', 'happypack/loader?id=happy-less'] : ["style-loader", MiniCssExtractPlugin.loader, 'happypack/loader?id=happy-less']
  44. }, {
  45. test: /.(gif|jpg|png)$/,
  46. use: [{
  47. loader: 'url-loader',
  48. options: {
  49. limit: 8192,
  50. name: 'images/[name].[hash:8].[ext]'
  51. }
  52. }]
  53. }, {
  54. test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
  55. // use: ['happypack/loader?id=happy-font']
  56. use: [{
  57. loader: 'file-loader',
  58. options: {
  59. limit: 8192,
  60. name: 'font/[name].[hash:8].[ext]'
  61. }
  62. }]
  63. }],
  64. noParse: /node_modules\/(jquey|js\-cookie\.js)/
  65. },
  66. resolve: {
  67. extensions: ['.js', '.jsx'],
  68. modules: [
  69. sourcePath,
  70. nodeModules
  71. ],
  72. alias: {
  73. Components: path.join(__dirname, '../web/components/')
  74. },
  75. },
  76. externals: {
  77. jquery: "$"
  78. },
  79. plugins: [
  80. new MiniCssExtractPlugin({
  81. filename: "css/[name].[contenthash:8].css",
  82. chunkFilename: "[name].css"
  83. }),
  84. new webpack.DllReferencePlugin({
  85. context: path.resolve(__dirname, "../"),
  86. manifest: require('./react-manifest.json'),
  87. }),
  88. new webpack.DllReferencePlugin({
  89. context: path.resolve(__dirname, "../"),
  90. manifest: require('./common-manifest.json'),
  91. }),
  92. createHappyPlugin('happy-babel-js', [{
  93. loader: 'cache-loader',
  94. options: {
  95. cacheDirectory: path.resolve(__dirname, '.cache--happypack')
  96. }
  97. }, {
  98. loader: 'babel-loader',
  99. query: {
  100. // cacheDirectory: isDev,
  101. presets: isDev ? ['react-hmre'] : []
  102. }
  103. }]),
  104. createHappyPlugin('happy-css', [{
  105. loader: 'css-loader',
  106. query: {
  107. minimize: false, // 压缩css功能在postcss中启用
  108. importLoaders: 1
  109. }
  110. }, {
  111. loader: 'postcss-loader',
  112. query: {
  113. config: {
  114. path: path.join(__dirname, './postcss.config.js')
  115. },
  116. }
  117. }]),
  118. createHappyPlugin('happy-less', [{
  119. loader: 'css-loader',
  120. query: {
  121. minimize: false,
  122. importLoaders: 2
  123. }
  124. }, {
  125. loader: 'postcss-loader',
  126. query: {
  127. config: {
  128. path: path.join(__dirname, './postcss.config.js')
  129. },
  130. }
  131. }, {
  132. loader: 'less-loader',
  133. query: {}
  134. }]),
  135. // createHappyPlugin('happy-font', [{
  136. // loader: "file-loader",
  137. // query: {
  138. // limit: 8192,
  139. // name: 'font/[name].[hash:8].[ext]'
  140. // }
  141. // }]),
  142. new ProgressBarPlugin({
  143. format: chalk.blue.bold("build ") + chalk.cyan("[:bar]") + chalk.green.bold(':percent') + ' (' + chalk.magenta(":elapsed") + ' seconds) ',
  144. clear: false
  145. }),
  146. new LodashModuleReplacementPlugin(),
  147. ]
  148. };

webpack.dev.config.js

开发环境的配置文件

  1. const path = require('path');
  2. const merge = require('webpack-merge');
  3. const webpack = require('webpack');
  4. const baseWebpackConfig = require('./webpack.base.config');
  5. const Html = require('html-webpack-plugin');
  6. const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');
  7. const outputPath = path.join(__dirname, '../dist/client/');
  8. const templateSrc = path.join(__dirname, '../web/page/');
  9. const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');
  10. // process.traceDeprecation = true;
  11. module.exports = merge(baseWebpackConfig, {
  12. devtool: 'source-map',
  13. mode: "development", // webpack 中加入了 mode 参数,具体看官方文档 https://webpack.js.org/concepts/mode/#usage
  14. entry: {
  15. // 注意⚠️:我这里是通过koa服务端启动webpack, 如果你是webpack-dev-server, 得这么写
  16. // app: [
  17. // 'webpack-dev-server/client?http://localhost:8080',
  18. // 'webpack/hot/only-dev-server',
  19. // "./src/app/app.js"
  20. //]
  21. admin: [
  22. 'eventsource-polyfill',
  23. 'webpack-hot-middleware/client',
  24. '../web/page/admin/index.js',
  25. ],
  26. blog: [
  27. 'eventsource-polyfill',
  28. 'webpack-hot-middleware/client',
  29. '../web/page/blog/index.js',
  30. ]
  31. },
  32. output: {
  33. path: outputPath,
  34. publicPath: '/',
  35. filename: 'js/[name].js',
  36. chunkFilename: "js/[name].[chunkhash:8].js"
  37. },
  38. plugins: [
  39. new Html({
  40. filename: 'admin.html',
  41. alwaysWriteToDisk: true,
  42. template: path.join(templateSrc, '/admin/index.html'),
  43. title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',
  44. chunks: ["admin"],
  45. }),
  46. new Html({
  47. filename: 'blog.html',
  48. alwaysWriteToDisk: true,
  49. template: path.join(templateSrc, '/blog/index.html'),
  50. html: '<%- html %>',
  51. script: '<%- JSON.stringify(ServerData) %>',
  52. title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',
  53. chunks: ["blog"],
  54. }),
  55. // 在html中插入dll库
  56. new HtmlWebpackIncludeAssetsPlugin({
  57. files: '*.html',
  58. assets: [{ path: 'lib', glob: '*.dll.js', globPath: 'dist/client/lib/' }],
  59. append: false
  60. }),
  61. new webpack.HotModuleReplacementPlugin(),
  62. new HtmlWebpackHarddiskPlugin()
  63. ]
  64. })

webpack.product.config.js

线上打包的配置文件

  1. const path = require('path');
  2. const merge = require('webpack-merge');
  3. const webpack = require('webpack');
  4. const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
  5. const baseWebpackConfig = require('./webpack.base.config');
  6. const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');
  7. const Html = require('html-webpack-plugin');
  8. const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  9. const templateSrc = path.join(__dirname, '../web/page/');
  10. const outputPath = path.join(__dirname, '../dist/client/');
  11. module.exports = merge(baseWebpackConfig, {
  12. devtool: false,
  13. mode: "production",
  14. entry: {
  15. admin: '../web/page/admin/index.js',
  16. blog: '../web/page/blog/index.js'
  17. },
  18. output: {
  19. path: outputPath,
  20. publicPath: '//static.liayal.com/',
  21. filename: 'js/[name].[chunkhash:8].js',
  22. chunkFilename: "js/[name].[chunkhash:8].js"
  23. },
  24. optimization: {
  25. minimizer: [
  26. new UglifyJsPlugin({
  27. // 开启多线程
  28. parallel: true,
  29. uglifyOptions: {
  30. compress: {
  31. // 去除 console
  32. drop_console: true,
  33. // 去除部分影响性能代码,如:1/0
  34. keep_infinity: true,
  35. },
  36. output: {
  37. // 去除注释
  38. comments: false,
  39. // 紧凑输出
  40. beautify: false
  41. }
  42. }
  43. })
  44. ]
  45. },
  46. plugins: [
  47. new Html({
  48. filename: 'admin.html',
  49. template: path.join(templateSrc, '/admin/index.html'),
  50. chunks: ["admin"],
  51. title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',
  52. minify: { // 对生成的html进行压缩,节省每 1 kb
  53. removeComments: true,
  54. collapseWhitespace: true
  55. }
  56. }),
  57. new Html({
  58. filename: 'blog.html',
  59. template: path.join(templateSrc, '/blog/index.html'),
  60. chunks: ["blog"],
  61. html: '<%- html %>',
  62. script: '<%- JSON.stringify(ServerData) %>',
  63. title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',
  64. minify: {
  65. removeComments: true,
  66. collapseWhitespace: true
  67. }
  68. }),
  69. new HtmlWebpackIncludeAssetsPlugin({
  70. files: '*.html',
  71. assets: [{ path: 'lib', glob: '*.dll.js', globPath: 'dist/client/lib/' }],
  72. append: false
  73. }),
  74. new webpack.optimize.ModuleConcatenationPlugin(),
  75. // new BundleAnalyzerPlugin()
  76. ]
  77. });

webpack.dll.config.js

DllPlugin 配置文件,DllPlugin 是什么? 戳这里

  1. const path = require('path');
  2. const webpack = require('webpack');
  3. const HappyPack = require('happypack');
  4. const outputPath = path.join(__dirname, '../dist/client/');
  5. const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
  6. function createHappyPlugin(id, loaders) {
  7. return new HappyPack({
  8. id: id,
  9. loaders: loaders,
  10. // threadPool: happyThreadPool,
  11. });
  12. }
  13. module.exports = {
  14. mode: "production",
  15. entry: {
  16. react: ['react', 'react-dom', "react-router-dom", "react-router", "prop-types"],
  17. common: ['axios', 'classnames', "moment", 'core-js/es6/promise', 'core-js/es6/map', 'core-js/es6/set']
  18. },
  19. output: {
  20. path: outputPath,
  21. filename: 'lib/[name]_[hash:8].dll.js',
  22. library: '[name]_[hash:8]'
  23. },
  24. optimization: {
  25. minimizer: [
  26. new UglifyJsPlugin({
  27. // 开启多线程
  28. parallel: true,
  29. uglifyOptions: {
  30. compress: {
  31. // 去除 console
  32. drop_console: true,
  33. // 去除部分影响性能代码,如:1/0
  34. keep_infinity: true,
  35. },
  36. output: {
  37. // 去除注释
  38. comments: false,
  39. // 紧凑输出
  40. beautify: false
  41. }
  42. }
  43. })
  44. ]
  45. },
  46. plugins: [
  47. new webpack.optimize.ModuleConcatenationPlugin(),
  48. new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn|en-gb/),
  49. new webpack.DllPlugin({
  50. context: path.resolve(__dirname, "../"),
  51. path: path.resolve(__dirname, './[name]-manifest.json'),
  52. name: '[name]_[hash:8]'
  53. }),
  54. createHappyPlugin('happy-babel-js', [{
  55. loader: 'cache-loader',
  56. options: {
  57. cacheDirectory: path.resolve(__dirname, '.cache--happypack')
  58. }
  59. }, {
  60. loader: 'babel-loader',
  61. query: {
  62. }
  63. }]),
  64. ]
  65. }

~~~简单说一下这里面用到的几个插件吧~~~

Happypack

一般我们本地构建都是单进程的,而Happypack则希望通过多进程模型,来加速代码构建。你可以根据你的cpu内核来开启多个进程来构建,对构建速度的提升会有不少的提高(不过我自己在本地测了下效果不是特别明显:see_no_evil: :see_no_evil::see_no_evil:)。
如果对其原理感兴趣可以看一下:happypack 原理解析

progress-bar-webpack-plugin

这个是一个构建进度条,看下图:point_down:

ExtractTextPlugin

这个插件不用说了,webpack css抽离处理标配。不清楚的默默的学习去 extract-text-webpack-plugin

注意⚠️: 该插件在webpack 4.0 以上有不可修复的bug,原作者已重开一个新项目mini-css-extract-plugin, 推荐使用。

html-webpack-plugin

html-webpack-plugin可以根据你设置的模板,在每次运行后生成对应的模板文件,同时所依赖的CSS/JS也都会被引入,如果CSS/JS中含有hash值,则html-webpack-plugin生成的模板文件也会引入正确版本的CSS/JS文件。这个还是很实用的东西,用法看这里:point_right:HTML Webpack Plugin用法

webpack-bundle-analyzer

强烈推荐!!! 这个插件可以将webpack打包后的内容束展示为方便交互的直观树状图,让你明白你所构建包中真正引入的内容;我们可以借助她,发现它大体有哪些模块组成,找到不合时宜的存在,然后优化它。
:point_down: :point_down::point_down: 看,是不是很屌~

使用很简单~

  1. const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  2. module.exports = {
  3. plugins: [
  4. new BundleAnalyzerPlugin()
  5. ]
  6. }

具体的项目实践可以参考本站源代码

写于 2018年01月16日Web webpack 8100

如非特别注明,文章皆为原创。

转载请注明出处: https://www.liayal.com/article/5a5d770924f2803679a960e5

记小栈小程序上线啦~搜索【记小栈】【点击扫码】体验

你不想说点啥么?
😀😃😄😁😆😅😂🤣☺️😊😇🙂🙃😉😌😍😘😗😙😚😋😜😝😛🤑🤗🤓😎🤡🤠😏😒😞😔😟😕🙁☹️😣😖😫😩😤😠😡😶😐😑😯😦😧😮😲😵😳😱😨😰😢😥🤤😭😓😪😴🙄🤔🤥😬🤐🤢🤧😷🤒🤕😈👿👹👺💩👻💀☠️👽👾🤖🎃😺😸😹😻😼😽🙀😿😾👐👐🏻👐🏼👐🏽👐🏾👐🏿🙌🙌🏻🙌🏼🙌🏽🙌🏾🙌🏿👏👏🏻👏🏼👏🏽👏🏾👏🏿🙏🙏🏻🙏🏼🙏🏽🙏🏾🙏🏿🤝👍👍🏻👍🏼👍🏽👍🏾👍🏿👎👎🏻👎🏼👎🏽👎🏾👎🏿👊👊🏻👊🏼👊🏽👊🏾👊🏿✊🏻✊🏼✊🏽✊🏾✊🏿

评论

lanting07-11 2018

大赞👍👍👍👍👍👍顺便问下,下次能把package.json也贴一下吗?

记小栈02-06 2018
@佐伯楽: 博主可以帮忙讲解一下html-webpack-harddisk-plugin的作用吗?看官方文档是为了给html-webpack-plugin做热更新用的,但是直接引入好像并没有什么用。在我的项目中,我发现保存vue单文件的时候,会触发html-webpack-plugin重新生成模板(但是实际上html模板并没有任何变化),导致页面整页刷新。我现在的做法是通过html-webpack-harddisk-plugin把html模板写入本地磁盘,然后用fs模块读入存到内存中,监听html模板文件是否发生变化,如果发生变化的话再通过事件触发整页刷新。

是用来实时生成 html 文件的,但是必须要配合 new HtmlWebpackPlugin({alwaysWriteToDisk: true})使用,你可以尝试把它去掉试试

佐伯楽02-05 2018

博主可以帮忙讲解一下html-webpack-harddisk-plugin的作用吗?看官方文档是为了给html-webpack-plugin做热更新用的,但是直接引入好像并没有什么用。在我的项目中,我发现保存vue单文件的时候,会触发html-webpack-plugin重新生成模板(但是实际上html模板并没有任何变化),导致页面整页刷新。我现在的做法是通过html-webpack-harddisk-plugin把html模板写入本地磁盘,然后用fs模块读入存到内存中,监听html模板文件是否发生变化,如果发生变化的话再通过事件触发整页刷新。

记小栈02-02 2018
@佐伯楽: 😳 ……这个网站有BUG,点击右下角的TOP返回顶部,再使用滚轮向下浏览页面,会一直回滚顶部。然后滚轮向上一下就可以恢复了。浏览器:Safari 11.0.2 (13604.4.7.1.3)

感谢🙏🙏,已修复

佐伯楽02-01 2018

😳 ……这个网站有BUG,点击右下角的TOP返回顶部,再使用滚轮向下浏览页面,会一直回滚顶部。然后滚轮向上一下就可以恢复了。浏览器:Safari 11.0.2 (13604.4.7.1.3)

记小栈01-28 2018
@andywen: 👍

😎😎😎

andywen01-27 2018

👍