一份比较完整的webpack配置文件

2018年2月24日 Web 582

  • 2018/02/06更新: 解决CSS的HMR问题

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

webpack.base.js

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

  1. const webpack = require('webpack');
  2. const path = require('path');
  3. const ExtractTextPlugin = require('extract-text-webpack-plugin');
  4. const HappyPack = require('happypack');
  5. const os = require('os');
  6. const ProgressBarPlugin = require('progress-bar-webpack-plugin');
  7. const chalk = require('chalk');
  8. const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
  9. const sourcePath = path.join(__dirname, '../web');
  10. const nodeModules = path.resolve(__dirname, '../node_modules');
  11. const isDev = !!(process.env.NODE_ENV != 'production');
  12. // const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
  13. function createHappyPlugin(id, loaders) {
  14. return new HappyPack({
  15. id: id,
  16. loaders: loaders,
  17. // threadPool: happyThreadPool,
  18. });
  19. }
  20. const cssLoader = ExtractTextPlugin.extract({
  21. fallback: "style-loader",
  22. use: [
  23. 'happypack/loader?id=happy-css'
  24. ]
  25. });
  26. const lessLoader = ExtractTextPlugin.extract({
  27. fallback: "style-loader",
  28. use: [
  29. 'happypack/loader?id=happy-less'
  30. ]
  31. })
  32. module.exports = {
  33. context: sourcePath,
  34. module: {
  35. rules: [{
  36. test: /\.(js|jsx)$/,
  37. exclude: nodeModules,
  38. include: sourcePath,
  39. use: ['happypack/loader?id=happy-babel-js'],
  40. // use: [{
  41. // loader: 'babel-loader',
  42. // options: {
  43. // cacheDirectory: true,
  44. // presets: ['react-hmre']
  45. // }
  46. // }],
  47. }, {
  48. test: /\.css$/,
  49. exclude: nodeModules,
  50. use: isDev ? ['style-loader', 'happypack/loader?id=happy-css'] : cssLoader,
  51. // use: ExtractTextPlugin.extract({
  52. // fallback: "style-loader",
  53. // use: [{
  54. // loader: 'css-loader',
  55. // options: {
  56. // minimize: true
  57. // }
  58. // }, {
  59. // loader: 'postcss-loader',
  60. // options: {
  61. // config: {
  62. // path: path.join(__dirname, './postcss.config.js')
  63. // }
  64. // }
  65. // }]
  66. // }),
  67. }, {
  68. test: /\.less$/,
  69. use: isDev ? ['style-loader', 'happypack/loader?id=happy-less'] : lessLoader,
  70. // use: ExtractTextPlugin.extract({
  71. // fallback: "style-loader",
  72. // use: [{
  73. // loader: 'css-loader',
  74. // options: {
  75. // minimize: true
  76. // }
  77. // }, {
  78. // loader: 'postcss-loader',
  79. // options: {
  80. // config: {
  81. // path: path.join(__dirname, './postcss.config.js')
  82. // }
  83. // }
  84. // }, 'less-loader']
  85. // })
  86. }, {
  87. test: /.(gif|jpg|png)$/,
  88. use: [{
  89. loader: 'url-loader',
  90. options: {
  91. limit: 8192,
  92. name: 'images/[name].[hash:8].[ext]'
  93. }
  94. }]
  95. }, {
  96. test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
  97. // use: ['happypack/loader?id=happy-font']
  98. use: [{
  99. loader: 'file-loader',
  100. options: {
  101. limit: 8192,
  102. name: 'font/[name].[hash:8].[ext]'
  103. }
  104. }
  105. ]
  106. }, {
  107. // test: require.resolve('jquery'),
  108. // use: [{
  109. // loader: 'expose-loader',
  110. // options: '$'
  111. // }, {
  112. // loader: 'expose-loader',
  113. // options: 'Zepto'
  114. // }]
  115. }],
  116. noParse: /node_modules\/(jquey|js\-cookie\.js)/
  117. },
  118. resolve: {
  119. extensions: ['.js', '.jsx'],
  120. modules: [
  121. sourcePath,
  122. nodeModules
  123. ],
  124. alias: {
  125. Components: path.join(__dirname, '../web/components/')
  126. },
  127. },
  128. externals: {
  129. jquery: "$"
  130. },
  131. plugins: [
  132. new ExtractTextPlugin('css/[name].[contenthash:8].css'),
  133. new webpack.DllReferencePlugin({
  134. context: path.resolve(__dirname, "../"),
  135. manifest: require('./vendor-manifest.json'),
  136. }),
  137. createHappyPlugin('happy-babel-js', [{
  138. loader: 'babel-loader',
  139. query: {
  140. cacheDirectory: true,
  141. presets: ['react-hmre']
  142. }
  143. }]),
  144. createHappyPlugin('happy-css', [{
  145. loader: 'css-loader',
  146. query: {
  147. minimize: true,
  148. }
  149. }, {
  150. loader: 'postcss-loader',
  151. query: {
  152. config: {
  153. path: path.join(__dirname, './postcss.config.js')
  154. },
  155. }
  156. }]),
  157. createHappyPlugin('happy-less', [{
  158. loader: 'css-loader',
  159. query: {
  160. minimize: true,
  161. }
  162. }, {
  163. loader: 'postcss-loader',
  164. query: {
  165. config: {
  166. path: path.join(__dirname, './postcss.config.js')
  167. },
  168. }
  169. }, {
  170. loader: 'less-loader',
  171. query: {
  172. }
  173. }]),
  174. // createHappyPlugin('happy-font', [{
  175. // loader: "file-loader",
  176. // query: {
  177. // limit: 8192,
  178. // name: 'font/[name].[hash:8].[ext]'
  179. // }
  180. // }]),
  181. new ProgressBarPlugin({
  182. format: chalk.blue.bold("build ") + chalk.cyan("[:bar]") + chalk.green.bold(':percent') + ' (' + chalk.magenta(":elapsed") + ' seconds) ',
  183. clear: false
  184. }),
  185. new webpack.NamedModulesPlugin(),
  186. new LodashModuleReplacementPlugin(),
  187. // new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'js/[name].js' })
  188. ]
  189. };

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. module.exports = merge(baseWebpackConfig, {
  10. devtool: 'source-map',
  11. entry: {
  12. admin: [
  13. 'eventsource-polyfill',
  14. 'webpack-hot-middleware/client',
  15. '../web/page/admin/index.js',
  16. ],
  17. blog: [
  18. 'eventsource-polyfill',
  19. 'webpack-hot-middleware/client',
  20. '../web/page/blog/index.js',
  21. ]
  22. },
  23. output: {
  24. path: outputPath,
  25. publicPath: '/',
  26. filename: 'js/[name].js',
  27. chunkFilename: "js/[name].[chunkhash:8].js"
  28. },
  29. plugins: [
  30. new webpack.DefinePlugin({
  31. 'process.env': {
  32. NODE_ENV: JSON.stringify('development')
  33. }
  34. }),
  35. new Html({
  36. filename: 'admin.html',
  37. alwaysWriteToDisk: true,
  38. template: path.join(templateSrc, '/admin/index.html'),
  39. title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',
  40. chunks: ["admin"],
  41. }),
  42. new Html({
  43. filename: 'blog.html',
  44. alwaysWriteToDisk: true,
  45. template: path.join(templateSrc, '/blog/index.html'),
  46. html: '<%- html %>',
  47. script: '<%- JSON.stringify(ServerData) %>',
  48. title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',
  49. chunks: ["blog"],
  50. }),
  51. new webpack.HotModuleReplacementPlugin(),
  52. new HtmlWebpackHarddiskPlugin()
  53. ]
  54. })

webpack.product.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 OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
  6. const Html = require('html-webpack-plugin');
  7. const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  8. const templateSrc = path.join(__dirname, '../web/page/');
  9. const outputPath = path.join(__dirname, '../dist/client/');
  10. module.exports = merge(baseWebpackConfig, {
  11. devtool: false,
  12. entry: {
  13. admin: '../web/page/admin/index.js',
  14. blog: '../web/page/blog/index.js'
  15. },
  16. output: {
  17. path: outputPath,
  18. publicPath: '//static.liayal.com/',
  19. filename: 'js/[name].[chunkhash:8].js',
  20. chunkFilename: "js/[name].[chunkhash:8].js"
  21. },
  22. plugins: [
  23. new webpack.DefinePlugin({
  24. 'process.env': {
  25. NODE_ENV: JSON.stringify('production'),
  26. DEBUG: false
  27. }
  28. }),
  29. new Html({
  30. filename: 'admin.html',
  31. template: path.join(templateSrc, '/admin/index.html'),
  32. chunks: ["admin"],
  33. title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',
  34. }),
  35. new Html({
  36. filename: 'blog.html',
  37. template: path.join(templateSrc, '/blog/index.html'),
  38. chunks: ["blog"],
  39. html: '<%- html %>',
  40. script: '<%- JSON.stringify(ServerData) %>',
  41. title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',
  42. }),
  43. new webpack.optimize.UglifyJsPlugin({
  44. // 最紧凑的输出
  45. beautify: false,
  46. // 删除所有的注释
  47. comments: false,
  48. compress: {
  49. // 在UglifyJs删除没有用到的代码时不输出警告
  50. warnings: false,
  51. // 删除所有的 `console` 语句
  52. // 还可以兼容ie浏览器
  53. drop_console: true,
  54. // 内嵌定义了但是只用到一次的变量
  55. collapse_vars: true,
  56. // 提取出出现多次但是没有定义成变量去引用的静态值
  57. reduce_vars: true,
  58. }
  59. }),
  60. new webpack.optimize.OccurrenceOrderPlugin(),
  61. new OptimizeCSSPlugin(),
  62. new webpack.optimize.ModuleConcatenationPlugin(),
  63. new BundleAnalyzerPlugin()
  64. ]
  65. });

webpack.dll.config.js

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

  1. const path = require('path');
  2. const webpack = require('webpack');
  3. const outputPath = path.join(__dirname, '../dist/client/');
  4. module.exports = {
  5. entry: {
  6. vendor: ['react', 'react-dom', 'axios', 'classnames', "moment", "react-router-dom"]
  7. },
  8. output: {
  9. path: outputPath,
  10. filename: 'lib/[name].dll.js',
  11. library: '[name]'
  12. },
  13. plugins: [
  14. new webpack.optimize.ModuleConcatenationPlugin(),
  15. new webpack.DefinePlugin({
  16. 'process.env': {
  17. NODE_ENV: JSON.stringify('production'),
  18. DEBUG: false
  19. }
  20. }),
  21. new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn|en-gb/),
  22. new webpack.DllPlugin({
  23. context: path.resolve(__dirname, "../"),
  24. path: path.resolve(__dirname, './[name]-manifest.json'),
  25. name: '[name]'
  26. }),
  27. new webpack.optimize.UglifyJsPlugin({
  28. // 最紧凑的输出
  29. beautify: false,
  30. // 删除所有的注释
  31. comments: false,
  32. compress: {
  33. // 在UglifyJs删除没有用到的代码时不输出警告
  34. warnings: false,
  35. // 删除所有的 `console` 语句
  36. // 还可以兼容ie浏览器
  37. drop_console: true,
  38. // 内嵌定义了但是只用到一次的变量
  39. collapse_vars: true,
  40. // 提取出出现多次但是没有定义成变量去引用的静态值
  41. reduce_vars: true,
  42. }
  43. })
  44. ]
  45. }

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

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

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. }

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

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

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

😀😃😄😁😆😅😂🤣☺️😊😇🙂🙃😉😌😍😘😗😙😚😋😜😝😛🤑🤗🤓😎🤡🤠😏😒😞😔😟😕🙁☹️😣😖😫😩😤😠😡😶😐😑😯😦😧😮😲😵😳😱😨😰😢😥🤤😭😓😪😴🙄🤔🤥😬🤐🤢🤧😷🤒🤕😈👿👹👺💩👻💀☠️👽👾🤖🎃😺😸😹😻😼😽🙀😿😾👐👐🏻👐🏼👐🏽👐🏾👐🏿🙌🙌🏻🙌🏼🙌🏽🙌🏾🙌🏿👏👏🏻👏🏼👏🏽👏🏾👏🏿🙏🙏🏻🙏🏼🙏🏽🙏🏾🙏🏿🤝👍👍🏻👍🏼👍🏽👍🏾👍🏿👎👎🏻👎🏼👎🏽👎🏾👎🏿👊👊🏻👊🏼👊🏽👊🏾👊🏿✊🏻✊🏼✊🏽✊🏾✊🏿
记小栈02-06 16:25
@佐伯楽: 博主可以帮忙讲解一下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 16:23

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

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

感谢🙏🙏,已修复

佐伯楽02-01 10:27

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

记小栈01-28 00:13
@andywen: 👍

😎😎😎

andywen01-27 14:37

👍