随着vue-cli、CreateReactApp等脚手架工具的愈加完善,其带给广大开发者方便、高效的开发体验使其越来越受追捧。其实,这类脚手架的底层都是通过webpack来实现的,其中vue-cli对webpack更是进行了很好的封装,使开发者即使不了解webpack也能很好的使用其各种功能。这样做的好处是使得开发者能够更轻松、更快的上手vue-cli,但是使用起来也就不是很灵活了,所以了解webpack本身的一些配置和功能对我们的开发或是理解脚手架底层实现很有帮助。
这里我整理了一些webpack的常用配置,适用于稍有webpack基础的同学阅读,也给自己以后的工作提供一些便利。有些地方没做详细说明,如有不明,请查阅官网手册或各种大牛博客。
阅读前大家最好先浏览一下webpack官网,了解一下webpack是什么样的一个工具,同时对怎么利用webpack搭建一个工程也要有所了解。(中文网址:https://www.webpackjs.com/concepts/、英文网址:https://webpack.js.org/concepts/)
工程目录:
公共配置项:webpack.common.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); //自动生成html插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); //打包前清空打包文件夹(dist)
const merge = require("webpack-merge"); //配置文件合并插件
const path = require('path');
const webpack = require("webpack");
const prodConfig = require("./webpack.prod");
const devConfig = require("./webpack.dev");
const commonConfig = {
entry: {
main: './src/index.js',
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/, //降低loader作用范围,减少loader执行次数,优化打包速度
loader: 'babel-loader', //webpack和babel的通信
// options: { 最好配置在.babelrc文件中
// presets: [['@babel/preset-env', {
// useBuiltIns: 'usage' //@babel/polyfill填充时去掉多余部分
// }]] //es6翻译成es5
// }
}, {
test: /\.(jpeg|png|jpg|gif)$/,
use: {
loader: 'url-loader', //可替代file-loader,file-loader只是实现文件转移
options: {
name: '[name].[ext]',
outputPath: 'img/', //打包到该文件夹
limit: '2048' //大于2048生成单独文件,否则在bundle.js里(以base64形式)
},
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html', //模板
}), //打包结束自动生成html,自动引入bundle.js
new CleanWebpackPlugin(), //打包前清空dist
new webpack.ProvidePlugin({
$: 'jquery' //用$ 就自动引入jquery,无需手动
})
],
optimization: {
usedExports: true, //配置Tree shaking 1.还需在package.json 配置sideEffects 2.production模式下不用写
splitChunks: {
chunks: 'all', //配置代码分割Code splitting all:同步异步都起作用 async:异步引入才起作用
// minSize: 30000, //30kb 引用文件大小大于该值做代码分割
//默 // maxSize: 0, //大于该值再次进行分割,直到不大于该值,否则不起作用,一般不设置
// minChunks: 1, //引用次数大于该值才做代码分割
// maxAsyncRequests: 5, //分割最多5个
// maxInitialRequests: 3, //入口文件最多分三个
// automaticNameDelimiter: '~', //组和文件之间连接符
//认 // name: true, //使自定义名字有效
// cacheGroups: { //同步进入
// vendors: {
// test: /[\\/]node_modules[\\/]/,
// priority: -10,
// filename: 'vendors.js'
//值 // },
// default: {} //不属于vendors进入,
// }
}
},
performance: false,
output: {
// publicpath: 'http://xxx.com', //打包后 http://xxx.com + [name].js
// filename: '[name].js', //占位符[name] //作用入口文件
// chunkFilename: '[name].chunk.js', //作用间接引入文件
path: path.resolve(__dirname, '../dist')
}
}
module.exports = (env) => {
if (env && env.production) {
//生产环境
return merge(commonConfig, prodConfig);
} else {
//开发环境
return merge(commonConfig, devConfig);
}
}
生产环境配置项:webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //css代码分割用插件
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); //css压缩插件
const WorkboxPlugin = require('workbox-webpack-plugin');
const prodConfig = {
mode: 'production',
devtool: 'cheap-module-source-map',
optimization: {
minimizer: [new OptimizeCSSAssetsPlugin({})], //css压缩
},
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //css代码分割用
{
loader: 'css-loader',
options: {
importLoaders: 2, //保证当前引入的scss文件加载全部loader
modules: true //模块加载css
}
},
'sass-loader',
'postcss-loader'
] //有顺序,从下到上,从右到左
}, {
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
] //有顺序,从下到上,从右到左
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].chunk.css'
}),
new WorkboxPlugin.GenerateSW({ //pwa,使页面缓存住,服务器突然崩溃也会有画面
clientsClaim: true,
skipWaiting: true
})
],
output: {
filename: '[name].[contenthash].js', //占位符[name] //入口文件入此 hash解决浏览器缓存问题
chunkFilename: '[name].[contenthash].js', //间接文件入此
}
}
module.exports = prodConfig;
开发环境配置项:webpack.dev.js
const webpack = require("webpack");
const devConfig = {
mode: 'development', //production 会压缩
// devtool: 'cheap-source-map', //报错信息可追溯 +'cheap-'优化打包速度,信息只精确到行,"eval"最快
devtool: 'cheap-module-eval-source-map', //适用于development
// devtool: 'cheap-module-source-map', //适用于production
devServer: {
contentBase: './dist', //配置服务器
open: true, //自动打开浏览器
port: 8080, //配置端口号
hot: true, //配置HMR
hotOnly: true //即使hmr没有生效,也会阻止浏览器自动刷新
},
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2, //保证当前引入的scss文件加载全部loader
modules: true //模块加载css
}
},
'sass-loader',
'postcss-loader'
] //loader加载有顺序,从下到上,从右到左
}, {
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin() //启用HMR
],
output: {
// publicpath: 'http://xxx.com', //打包后 http://xxx.com + [name].js
filename: '[name].js', //占位符[name] //入口文件入此
chunkFilename: '[name].js', //间接文件入此
}
}
module.exports = devConfig;
注意:
一、Tree shaking 只支持 ES Module模块引入方式(import)。
二、代码分割和webpack无关
1、同步代码: 在webpack.common.js配置optimization
2、异步代码无需操作,会自动分割打包。
三、webpack中,多写异步代码,有助于提升性能,如:
//如此方式(import)引入lodash懒加载,使用时才加载
async function getComponent() {
const { default: _ } = await import(/* webpackChunkName:"lodash" */ 'lodash');
const element = document.createElement('div');
element.innerHTML = _.join(["a,b,c", "d"], "-");
return element;
}
//tips:
/* webpackPrefetch: true */ //魔法注释,主文件加载完,空闲加载所需模块,提升性能
import(/* webpackPrefetch: true */'./click.js').then(({default: _})=> {
_();
})
提高打包速度:
1、合理使用插件,尽量删除多余插件
2、及时升级版本
3、降低loader作用范围,合理使用exclude
4、第三方插件只需打包一次,借助DllPlugin、DllReferencePlugin