- 2018/06/03更新: 更新到webpack 4.0, 添加部分注释
- 2018/02/06更新: 解决CSS的HMR问题
这里放上一份记目前在项目中使用的webpack的配置,基本上集成了目前大家能搜索的到优化技巧~~~大家有什么其他的黑科技的欢迎砸砖,默认大家都知道webpack 的相关配置,因此基础的东西不在本文内
webpack.base.js
基本配置文件,给线上线下配置继承;
const webpack = require('webpack');const path = require('path');// webpack 4.0 中用来抽离css 的插件const MiniCssExtractPlugin = require("mini-css-extract-plugin");const HappyPack = require('happypack');const os = require('os');const ProgressBarPlugin = require('progress-bar-webpack-plugin');const chalk = require('chalk');// 针对 Lodash 按需打包const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');const sourcePath = path.join(__dirname, '../web');const nodeModules = path.resolve(__dirname, '../node_modules');const isDev = !!(process.env.NODE_ENV != 'production');// const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });function createHappyPlugin(id, loaders) {return new HappyPack({id: id,loaders: loaders,// threadPool: happyThreadPool,});}module.exports = {context: sourcePath,module: {rules: [{test: /\.(js|jsx)$/,exclude: nodeModules,include: sourcePath,use: ['happypack/loader?id=happy-babel-js'],// use: [{// loader: 'babel-loader',// options: {// cacheDirectory: true,// presets: ['react-hmre']// }// }],}, {test: /\.css$/,exclude: nodeModules,use: isDev ? ['style-loader', 'happypack/loader?id=happy-css'] : ["style-loader", MiniCssExtractPlugin.loader, 'happypack/loader?id=happy-css']}, {test: /\.less$/,use: isDev ? ['style-loader', 'happypack/loader?id=happy-less'] : ["style-loader", MiniCssExtractPlugin.loader, 'happypack/loader?id=happy-less']}, {test: /.(gif|jpg|png)$/,use: [{loader: 'url-loader',options: {limit: 8192,name: 'images/[name].[hash:8].[ext]'}}]}, {test: /\.(woff|woff2|eot|ttf|otf|svg)$/,// use: ['happypack/loader?id=happy-font']use: [{loader: 'file-loader',options: {limit: 8192,name: 'font/[name].[hash:8].[ext]'}}]}],noParse: /node_modules\/(jquey|js\-cookie\.js)/},resolve: {extensions: ['.js', '.jsx'],modules: [sourcePath,nodeModules],alias: {Components: path.join(__dirname, '../web/components/')},},externals: {jquery: "$"},plugins: [new MiniCssExtractPlugin({filename: "css/[name].[contenthash:8].css",chunkFilename: "[name].css"}),new webpack.DllReferencePlugin({context: path.resolve(__dirname, "../"),manifest: require('./react-manifest.json'),}),new webpack.DllReferencePlugin({context: path.resolve(__dirname, "../"),manifest: require('./common-manifest.json'),}),createHappyPlugin('happy-babel-js', [{loader: 'cache-loader',options: {cacheDirectory: path.resolve(__dirname, '.cache--happypack')}}, {loader: 'babel-loader',query: {// cacheDirectory: isDev,presets: isDev ? ['react-hmre'] : []}}]),createHappyPlugin('happy-css', [{loader: 'css-loader',query: {minimize: false, // 压缩css功能在postcss中启用importLoaders: 1}}, {loader: 'postcss-loader',query: {config: {path: path.join(__dirname, './postcss.config.js')},}}]),createHappyPlugin('happy-less', [{loader: 'css-loader',query: {minimize: false,importLoaders: 2}}, {loader: 'postcss-loader',query: {config: {path: path.join(__dirname, './postcss.config.js')},}}, {loader: 'less-loader',query: {}}]),// createHappyPlugin('happy-font', [{// loader: "file-loader",// query: {// limit: 8192,// name: 'font/[name].[hash:8].[ext]'// }// }]),new ProgressBarPlugin({format: chalk.blue.bold("build ") + chalk.cyan("[:bar]") + chalk.green.bold(':percent') + ' (' + chalk.magenta(":elapsed") + ' seconds) ',clear: false}),new LodashModuleReplacementPlugin(),]};
webpack.dev.config.js
开发环境的配置文件
const path = require('path');const merge = require('webpack-merge');const webpack = require('webpack');const baseWebpackConfig = require('./webpack.base.config');const Html = require('html-webpack-plugin');const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');const outputPath = path.join(__dirname, '../dist/client/');const templateSrc = path.join(__dirname, '../web/page/');const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');// process.traceDeprecation = true;module.exports = merge(baseWebpackConfig, {devtool: 'source-map',mode: "development", // webpack 中加入了 mode 参数,具体看官方文档 https://webpack.js.org/concepts/mode/#usageentry: {// 注意⚠️:我这里是通过koa服务端启动webpack, 如果你是webpack-dev-server, 得这么写// app: [// 'webpack-dev-server/client?http://localhost:8080',// 'webpack/hot/only-dev-server',// "./src/app/app.js"//]admin: ['eventsource-polyfill','webpack-hot-middleware/client','../web/page/admin/index.js',],blog: ['eventsource-polyfill','webpack-hot-middleware/client','../web/page/blog/index.js',]},output: {path: outputPath,publicPath: '/',filename: 'js/[name].js',chunkFilename: "js/[name].[chunkhash:8].js"},plugins: [new Html({filename: 'admin.html',alwaysWriteToDisk: true,template: path.join(templateSrc, '/admin/index.html'),title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',chunks: ["admin"],}),new Html({filename: 'blog.html',alwaysWriteToDisk: true,template: path.join(templateSrc, '/blog/index.html'),html: '<%- html %>',script: '<%- JSON.stringify(ServerData) %>',title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',chunks: ["blog"],}),// 在html中插入dll库new HtmlWebpackIncludeAssetsPlugin({files: '*.html',assets: [{ path: 'lib', glob: '*.dll.js', globPath: 'dist/client/lib/' }],append: false}),new webpack.HotModuleReplacementPlugin(),new HtmlWebpackHarddiskPlugin()]})
webpack.product.config.js
线上打包的配置文件
const path = require('path');const merge = require('webpack-merge');const webpack = require('webpack');const UglifyJsPlugin = require('uglifyjs-webpack-plugin');const baseWebpackConfig = require('./webpack.base.config');const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');const Html = require('html-webpack-plugin');const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;const templateSrc = path.join(__dirname, '../web/page/');const outputPath = path.join(__dirname, '../dist/client/');module.exports = merge(baseWebpackConfig, {devtool: false,mode: "production",entry: {admin: '../web/page/admin/index.js',blog: '../web/page/blog/index.js'},output: {path: outputPath,publicPath: '//static.liayal.com/',filename: 'js/[name].[chunkhash:8].js',chunkFilename: "js/[name].[chunkhash:8].js"},optimization: {minimizer: [new UglifyJsPlugin({// 开启多线程parallel: true,uglifyOptions: {compress: {// 去除 consoledrop_console: true,// 去除部分影响性能代码,如:1/0keep_infinity: true,},output: {// 去除注释comments: false,// 紧凑输出beautify: false}}})]},plugins: [new Html({filename: 'admin.html',template: path.join(templateSrc, '/admin/index.html'),chunks: ["admin"],title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',minify: { // 对生成的html进行压缩,节省每 1 kbremoveComments: true,collapseWhitespace: true}}),new Html({filename: 'blog.html',template: path.join(templateSrc, '/blog/index.html'),chunks: ["blog"],html: '<%- html %>',script: '<%- JSON.stringify(ServerData) %>',title: '<%= title || "游走在技术与艺术边缘地带的前端攻城狮" %>',minify: {removeComments: true,collapseWhitespace: true}}),new HtmlWebpackIncludeAssetsPlugin({files: '*.html',assets: [{ path: 'lib', glob: '*.dll.js', globPath: 'dist/client/lib/' }],append: false}),new webpack.optimize.ModuleConcatenationPlugin(),// new BundleAnalyzerPlugin()]});
webpack.dll.config.js
DllPlugin 配置文件,DllPlugin 是什么? 戳这里
const path = require('path');const webpack = require('webpack');const HappyPack = require('happypack');const outputPath = path.join(__dirname, '../dist/client/');const UglifyJsPlugin = require('uglifyjs-webpack-plugin');function createHappyPlugin(id, loaders) {return new HappyPack({id: id,loaders: loaders,// threadPool: happyThreadPool,});}module.exports = {mode: "production",entry: {react: ['react', 'react-dom', "react-router-dom", "react-router", "prop-types"],common: ['axios', 'classnames', "moment", 'core-js/es6/promise', 'core-js/es6/map', 'core-js/es6/set']},output: {path: outputPath,filename: 'lib/[name]_[hash:8].dll.js',library: '[name]_[hash:8]'},optimization: {minimizer: [new UglifyJsPlugin({// 开启多线程parallel: true,uglifyOptions: {compress: {// 去除 consoledrop_console: true,// 去除部分影响性能代码,如:1/0keep_infinity: true,},output: {// 去除注释comments: false,// 紧凑输出beautify: false}}})]},plugins: [new webpack.optimize.ModuleConcatenationPlugin(),new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn|en-gb/),new webpack.DllPlugin({context: path.resolve(__dirname, "../"),path: path.resolve(__dirname, './[name]-manifest.json'),name: '[name]_[hash:8]'}),createHappyPlugin('happy-babel-js', [{loader: 'cache-loader',options: {cacheDirectory: path.resolve(__dirname, '.cache--happypack')}}, {loader: 'babel-loader',query: {}}]),]}
~~~简单说一下这里面用到的几个插件吧~~~
Happypack
一般我们本地构建都是单进程的,而Happypack则希望通过多进程模型,来加速代码构建。你可以根据你的cpu内核来开启多个进程来构建,对构建速度的提升会有不少的提高(不过我自己在本地测了下效果不是特别明显

)。
如果对其原理感兴趣可以看一下:happypack 原理解析
progress-bar-webpack-plugin
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文件。这个还是很实用的东西,用法看这里
HTML Webpack Plugin用法
webpack-bundle-analyzer
强烈推荐!!! 这个插件可以将webpack打包后的内容束展示为方便交互的直观树状图,让你明白你所构建包中真正引入的内容;我们可以借助她,发现它大体有哪些模块组成,找到不合时宜的存在,然后优化它。

看,是不是很屌~
使用很简单~
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;module.exports = {plugins: [new BundleAnalyzerPlugin()]}
具体的项目实践可以参考本站源代码
写于 2018年01月16日Web webpack 8443
如非特别注明,文章皆为原创。
转载请注明出处: https://www.liayal.com/article/5a5d770924f2803679a960e5
记小栈小程序上线啦~搜索【记小栈】或【点击扫码】体验



lanting07-11 2018
大赞👍👍👍👍👍👍顺便问下,下次能把package.json也贴一下吗?
记小栈02-06 2018
是用来实时生成 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
感谢🙏🙏,已修复
佐伯楽02-01 2018
😳 ……这个网站有BUG,点击右下角的TOP返回顶部,再使用滚轮向下浏览页面,会一直回滚顶部。然后滚轮向上一下就可以恢复了。浏览器:Safari 11.0.2 (13604.4.7.1.3)
记小栈01-28 2018
😎😎😎
andywen01-27 2018
👍