- 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/#usage
entry: {
// 注意⚠️:我这里是通过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: {
// 去除 console
drop_console: true,
// 去除部分影响性能代码,如:1/0
keep_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 kb
removeComments: 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: {
// 去除 console
drop_console: true,
// 去除部分影响性能代码,如:1/0
keep_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 8100
如非特别注明,文章皆为原创。
转载请注明出处: 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
👍