webpack5配置
使用babel-loader编译ts配合fork-ts-checker-webpack-plugin完成类型检查
注意点:
fork-ts-checker-webpack-plugin默认使用vue-template-compiler解析vue文件,需换成vue3.x对应的解析器@vue/compiler-sfc
css提取、压缩
注意点:需使用
mini-css-extract-plugin的loader替换style-loader
vue-loader 编译打包.vue文件
注意点:webpack插件需要引入
vue-loader提供的的插件VueLoaderPlugin
引入代码格式校验插件 eslint-webpack-plugin、stylelint-webpack-plugin,并开启自动修复功能
注意点: 使用
eslint-webpack-plugin代替之前的eslint-loader
使用资源模块asset/resource支持图片、mp4、和其他文件得导入
注意:webpack5使用:
asset/source代替raw-loader将文件导入为字符串asset/inline代替url-loader将文件作为 data URI 内联到 bundle 中asset/resource代替file-loader将文件发送到输出目录asset在导出为dateURL(即base64)和一个单独文件之间选择,与以前得url-loader通过文件体积大小决定打包为base64还是单独文件功能一样
具体配置文件
// build/webpack.base.config.js
/*
* @Description:
* @Version: 2.0
* @Date: 2021-05-14 14:37:45
* @LastEditTime: 2022-07-01 13:43:56
*/
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");
const StylelintPlugin = require('stylelint-webpack-plugin');
const chalk = require('chalk');
const ESLintPlugin = require('eslint-webpack-plugin');
// const webpack = require('webpack')
const path = require('path')
const devMode = process.env.ENV_MODE === 'dev';
const { resolve } = require('./utils');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
// const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const compileConf = require('../config')
module.exports = {
entry: {
app: './src/main.ts',
},
performance: {
hints: false,
},
resolve: {
alias: {
'@': resolve('../src'),
'assets': resolve('../src/assets'),
'~utils': resolve('../src/utils'),
'~views': resolve('../src/views'),
'~services': resolve('../src/services'),
'~components': resolve('../src/components'),
'~composition': resolve('../src/composition'),
'~models': resolve('../src/models'),
},
extensions: ['.ts', '.js', '.tsx', '.vue', '.json','.d.ts'],
},
module: {
rules: [
{
test: /\.(js|ts|tsx|jsx)$/,
include: resolve('../src'),
use: [
'thread-loader',
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
],
},
{
test: /\.vue$/,
use: ['thread-loader', 'vue-loader'],
include: resolve('../src'),
},
{
oneOf:[
{
test: /\.css$/,
use: [devMode?"style-loader":MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.(less)$/i,
use: [
devMode? "style-loader":MiniCssExtractPlugin.loader,
'css-loader',
{
loader: "less-loader",
options: {
lessOptions:{
javascriptEnabled: true
}
}
},
'postcss-loader',
],
},
{
test: /\.(scss)$/i,
use: [
devMode? "style-loader":MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
{
loader: 'sass-resources-loader',
options: {
resources: [
path.resolve(__dirname, '../src/assets/css/variable.scss'),
]
}
},
'postcss-loader',
],
},
{
test: /\.(png|jpg|gif|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024, // 4kb
},
},
generator: {
filename: 'imgs/[name].[contenthash:6][ext]',
},
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'fonts/[name].[contenthash:6][ext]',
},
},
{
test: /\.(mp4)$/i,
type: 'asset/resource',
generator: {
filename: 'videos/[name].[contenthash:6][ext]',
},
},
],
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: resolve('../public/index.ejs'),
}),
new VueLoaderPlugin(),
new CopyPlugin({
patterns: [
{ from: "static", to: "static" },
],
}),
new ESLintPlugin({
formatter: require('eslint-friendly-formatter'),
extensions: ['js', 'ts', 'vue', 'tsx'],
fix: true,
}),
new ProgressBarPlugin({
format: `:msg ${chalk.yellow('[:bar]')} ${chalk.green.bold(':percent')} (:elapsed seconds)`,
width: 250,
clear: false,
}),
new StylelintPlugin({
fix: true,
files: ['src/**/*.vue', 'src/assets/css/*.(css|scss|less)']
}),
new ForkTsCheckerWebpackPlugin({
async: false, // webpack是否等待typescript类型检查的结果
typescript:{
extensions:{
vue:{
enabled:true,
compiler:'@vue/compiler-sfc' // 需指定vue单文件目标解析器
},
}
}
}),
new webpack.DefinePlugin({
compileConf:JSON.stringify(compileConf),
}),
],
cache: {
// 1. 将缓存类型设置为文件系统
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
};
开发环境配置:
- source-map选择
eval-cheap-module-source-map,关闭构建体积优化,提高打包速度
注意:
- 设置
target属性为web不然热更新会失效devServer->host属性设置0.0.0.0否则不能通过ip访问.- 需要通过webpack.DefinePlugin设置全局变量
__VUE_OPTIONS_API__、__VUE_PROD_DEVTOOLS__为true告诉vue当前得环境,否则vue会抛出警告
// build/webpack.dev.conf.js
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.conf');
const { resolve } = require('./utils');
const isMock = process.env.MOCK
const { addOnProxyRes, use } = require('@gc082/mock')
const proxyTable = require('@gc082/dog-cli/config/proxyTable.json')
const { assemblyProxyTable } = require('../config/utils')
const webpack = require('webpack')
const compileConf = require('../config')
module.exports = merge(baseConfig, {
mode: 'development',
target: 'web', // 不加会热更新失效
devtool: 'eval-cheap-module-source-map',
stats: 'errors-warnings',
output: {
filename: '[name].js',
path: resolve(`../${compileConf.projectName}`),
publicPath: compileConf.publicPath
},
optimization: {
runtimeChunk: true,
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
},
devServer: {
// contentBase: resolve('../dist'),
hot: true,
hotOnly: true,
// compress: true,
host: '0.0.0.0',
port: 9000,
before: function (app, server, compiler) {
if (isMock) {
use('/smartPark', app)
use('/park-system', app)
use('/smartcommunitySit', app)
}
},
proxy: assemblyProxyTable(proxyTable, addOnProxyRes)
},
plugins: [
new webpack.DefinePlugin({
'ENV_MODE': JSON.stringify(process.env.ENV_MODE),
'__VUE_OPTIONS_API__': true,
'__VUE_PROD_DEVTOOLS__':true
}),
],
});
生产环境配置
- source-map使用
source-map - 打包输出文件名称添加文件指纹
filename: 'js/[name].[contenthash].js', - 使用
terser-webpack-plugin压缩优化js代码,剔除代码中的debuger、console - 使用
css-minimizer-webpack-plugin压缩优化css - 使用
mini-css-extract-plugin将vue文件中的css代码提取到单独的文件中 - 添加打包分性能析插件
webpack-bundle-analyzer,打包时添加--report命令,即可打开打包分析页面
// build/webpack.prod.conf.js
/*
* @Description:
* @Version: 2.0
* @Date: 2021-05-14 14:38:13
* @LastEditTime: 2022-06-09 17:35:39
*/
const { merge } = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const baseConfig = require('./webpack.base.conf')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
// const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); // 不支持es6
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const compileConf = require('../config')
const { resolve } = require('./utils')
const config = merge(baseConfig, {
mode: 'production',
devtool: 'source-map',
stats: 'errors-only',
output: {
filename: 'js/[name].[contenthash].js',
path: resolve(`../${compileConf.projectName}`),
publicPath: compileConf.publicPath
},
optimization: {
emitOnErrors: true,
minimizer: [
new CssMinimizerPlugin(),
new TerserPlugin({
terserOptions:{
compress:{
drop_console: process.env.ENV_MODE==='prod',
drop_debugger: true,
pure_funcs: process.env.ENV_MODE==='prod'?['console.log']:[],
},
keep_fnames: true,
warnings:!process.env.ENV_MODE==='prod',
},
parallel: true,
}),
],
splitChunks:{
minSize: 20000,
maxSize: 500*1024,
automaticNameDelimiter:"~",
chunks: "all",
}
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css',
chunkFilename: 'css/[id].[contenthash].css',
}),
]
})
if (process.env.npm_config_report) config.plugins.push(new BundleAnalyzerPlugin())
module.exports = config
打包优化
thread-loader开启多进程- 开启webpack5自带的缓存功能,第一次会在node_models/.cache/webpack中写入缓存,后续打包速度会有显著提升
// 在buidl/webpack.base.conf.js 中添加:
...
cache: {
// 1. 将缓存类型设置为文件系统
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
...
-
使用webpack.DefinePlugin配合
add-asset-html-webpack-plugin预打包第三方包,提升打包速度- 配置
webpack.dll.js
// build/webpack.dll.js const path = require('path'); const webpack = require('webpack'); const {CleanWebpackPlugin} = require('clean-webpack-plugin') // const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') config = { mode: 'production', entry: { "common":['js-sha256', 'dayjs'], 'vendor': ['vue','vue-router','pinia','axios',] }, output: { filename: '[name].dll.js', path: path.resolve(__dirname, '../dll'), library: '[name]' }, plugins: [ new CleanWebpackPlugin(), new webpack.DllPlugin({ name: '[name]', path: path.resolve(__dirname, '../dll/[name].manifest.json') }), // new AntdDayjsWebpackPlugin({ // preset: 'antdv3' // }) ] } if (process.env.npm_config_report) config.plugins.push(new BundleAnalyzerPlugin()) module.exports = configpackage.json中添加"dll": "webpack --config ./build/webpack.dll.js",- 执行
npm run dll打包出第三方模块 - 动态读取打包出好的文件,使用
add-asset-html-webpack-plugin在index.html中引入
// build/utils.js const path = require('path') const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin') const fs = require('fs') const webpack = require('webpack') const compileConf = require('../config') const resolve = (url) => { return path.resolve(__dirname, url) } const getDllPlugins = ()=>{ const plugins = [] const files = fs.readdirSync(path.resolve(__dirname, '../dll')); files.forEach(file => { if(/.*\.dll.js/.test(file)) { plugins.push(new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, '../dll', file), outputPath:'/js', publicPath:`${compileConf.publicPath}js` })) } if(/.*\.manifest.json/.test(file)) { plugins.push(new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, '../dll', file) })) } }) return plugins } module.exports = { resolve, getDllPlugins }- 在
webpack.prod.conf添加引入
... //省略 ++ const { getDllPlugins, resolve } = require('./utils') ... //省略 const config = merge(baseConfig, { ... //省略 plugins: [ new CleanWebpackPlugin(), new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash].css', chunkFilename: 'css/[id].[contenthash].css', }), ++ ...getDllPlugins(), ] }) ... //省略bable配置
注意: 解释器使用
@babel/preset-typescript,并设置allExtensions属性为true,否则不能识别script lang=ts// babel.config.js module.exports = { presets: [ '@babel/preset-env', [ '@babel/preset-typescript', { allExtensions: true, // 不加识别不了script lang=ts }, ], ], plugins: [ [ '@babel/plugin-transform-runtime', { corejs: 2, // 指定 runtime-corejs 的版本,目前有 2 3 两个版本 }, ], ], }; - 配置