模块化打包工具,一切皆模块
-
- 3.1. 配置示例
- 3.2. loader
- 3.3. plugins
- 3.4. source-map
- 3.5. HMR(Hot Module Replacement)
- 3.6. babel
- 3.7. polyfill
-
- [dev 与 pro 的区别](#dev 与 pro 的区别)
- 6.1. dev环境
- 6.2. pro环境
- 6.3. 共同点
- 6.4. 配置方案
- 6.5. 配置示例
- 6.5.1. 目录结构
- 6.5.2. webpack.prod.js
- 6.5.3. webpack.dev.js
- 6.5.4. webpack.base.js
- 6.5.5. package.json
-
- 7.1. 入口配置
- 7.1.1. entry多入口
- 7.2. 抽取公共代码
- 7.2.1. splitchunks
- 7.3. 动态加载
- 7.4. Css文件代码分割
- 7.4.1. mini-css-extract-plugin
- 7.4.2. optimize-css-assets-webpack-plugin
- 7.1. 入口配置
-
- 8.1. webpack-bundle-analyzer
- 8.1.1. webpack.prod.js
- 8.1. webpack-bundle-analyzer
-
- 9.1. yargs
- 9.1.1. package.json
- 9.1.2. webpack.base.js
- 9.1. yargs
- loader、plugins、Entry、Output、SourceMap、DevServer、Hmr、[--watch]、Babel
- TreeShaking、环境区分、CodeSpliting、打包分析、代码分割、环境变量使用
- webpack
- webpack-cli
自定义webpack配置,会覆盖默认配置
const path = require('path');
module.exports = {
// 默认production,打包文件压缩,开发:development,不被压缩
mode: 'production',
// 入口文件
// entry: './src/index.js', // 简化写法
entry: {
main: './src/index.js',
},
// 输出
output: {
// 自定义打包文件名
filename: 'index.js',
// 尽量写绝对路径,避免不同环境下路径解析的方式不一致
path: path.resolve(__dirname, 'dist'),
},
};
通过 npx webpack --config webpack.config.js
执行打包。
文件预处理器,对文件的特定的处理方案,需要安装相关的loader依赖
// ...
module.exports = {
// ...
module: {
rules: [
{
test: /\.(jpg|png|gif)$/, // 指定检测图片相关类型的文件
use: {
loader: 'file-loader',
},
},
{
test: /\.ttf$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[hash:5].[ext]',
outputPath: 'assets/fonts/',
},
},
},
{
test: /\.(less|css)$/, // 指定检测less相关类型的文件
use: {
loader: ['style-loader', 'css-lodaer', 'less-loader'], // compiles Less to Css
},
},
],
},
}
// ...
const HtmlWebpackPlugin = require('html-webpack-plguin');
// ...
module.exports = {
// ...
plguins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
],
}
// ...
用于定位打包后的源码
// ...
module.exports = {
// ...
// eval-cheap-module-source-map 开发模式推荐
// cheap-module-source-map 生产模式推荐
devtool: 'eval-cheap-module-source-map',
},
开启步骤:
- 使用
webpack-dev-server
作为服务器启动 - devServer中配置
hot: true
- plugins中配置
hotModuleReplacementPlugin
- js模块中增加
module.hot.acceput
增加 HMR 代码
webpack.config.js
// ...
module.exports = {
// ...
devServer: {
// 定位服务访问目录
contenBase: path.join(__dirname, 'dist'),
port: 8081,
},
}
package.json
{
// ...
"scripts": {
// ...
"dev": "webpack-dev-server"
}
}
js的编译器
npm install @babel/core @babel/preset-env babel-loader -D
// ...
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
// 转换ES5+语法
presets: ['@babel/preset-env'],
},
exclude: /node_modules/,
},
],
},
}
// ...
标准引入的语法:箭头函数、let、const等可以被转换
标准引入的全局变量,部分原生对象新增的原型链上的方法:Promise、Symbol、set等无法被转换,此时需要用到 polyfill
进行兼容处理。
npm install @babel/polyfill -D
index.js
// 在页面中引入,如果在webpack中配置了 useBuiltIn 则不需要在页面中手动引入
import '@babel/polyfill';
webpack.config.js
// ...
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
// 转换ES5+语法
presets: ['@babel/preset-env', {
// 必须同时设置corejs,默认使用corejs@2,我们需要用的是corejs@3
useBuiltIn: 'usage', // options: 'useage', 'entry', false
corejs: 3,
}],
},
exclude: /node_modules/,
},
],
},
}
// ...
全局变量形式将方法注入,在开发类库、UI组件时会造成全局变量的污染,这时候就需要用到 @babel/plugin-transform-runtime
,以闭包的形式注入,保证全局环境不被污染。
npm install @babel/plugin-transform-runtime @babel/runtime-corejs3 -D
webpack.config.js
// ...
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
plugins: [
'@babel/plugin-transform-runtime', {
corejs: 3,
},
],
},
exclude: /node_modules/,
},
],
},
}
// ...
隔离babel配置与其他webpack配置
.babelrc
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3
}
]
]
}
摇树优化
webpack4中将 mode
设置成 production
会自动进行摇树优化。
- devServer
- sourceMap
- 接口代理 proxy
- treeShaking
- 代码压缩
- 提供公共代码
- 同样的入口
- 部分相同的代码处理
- webpack.prod.js
- webpack.dev.js
- webpack.base.js 开发环境与生产环境公用的代码
- 使用 webpack-merge 进行配置合并
|-- project
|-- config
|-- webpack.base.js
|-- webpack.dev.js
|-- webpack.prod.js
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
const prodConfig = {
mode: 'production',
};
module.exports = merge(baseConfig, prodConfig);
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
const devConfig = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: 8081,
},
plugins: [
new webpack.HotModuleReplacementPlguin(),
],
};
module.exports = merge(baseConfig, devConfig);
const path = require('path');
const HtmlWebpackPlguin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: {
index: './src/index.js',
demo: './src/demo.js',
},
output: {
filename: 'index.js',
path: path.resolve(__dirname, '../dist'),
},
module: {
rules: [
{
test: /\.(jpb|png|gif)$/, // 指定检测图片相关类型的文件
use: {
loader: 'file-loader',
},
},
{
test: /\.ttf$/, // 指定检测字体相关类型的文件
use: {
loader: 'file-loader',
name: '[name].[hash5].[ext]',
outputpath: 'assets/',
},
},
{
test: /\.(less|css)$/, // 指定检测less相关类型的文件
use: {
loader: ['css-lodaer', 'less-loader'], // compiles Less to Css
},
},
],
},
plugins: [
new HtmlWebpackPlguin({
template: './src/index.html',
}),
new CleanWebpackPlugin(),
],
};
// ...
"scripts": {
"build:dev": "webpack --coinfig ./config/webpack.dev.js",
"build:prod": "webpack --coinfig ./config/webpack.prod.js",
"dev:server": "webpack-dev-server --config ./config/webpack.dev.js"
},
// ...
以引入JQuery为例,如果在每个页面中引入非常麻烦,我们可以在webpack配置中统一注入。
webpack.base.js
// ...
entry: {
// ...
jquery: 'jquery',
},
// ...
plugins: [
// ...
nwe webpack.ProvidePlguin({
$: 'jquery',
}),
],
splitchunksPlugins
webpack.base.js
// ...
module.exports = {
// ...
output: {
chunkFilename: (pathData) => {
return pathData.chunk.name === 'main' ? '[name].js' : '[name]/[name].js';
},
path: path.join(__dirname, '../dist'),
},
// ...
optimization: {
splitChunks: {
chunks: 'all', // 从哪些chunks中抽取代码
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/, // 匹配node_modules目录下的问题
priority: -10, // 优先级
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
@babel/plguin-syntax-dynamic-import
.babelrc
{
"plugins": [
"@babel/plugin-syntax-dynamic-import",
// ...
]
}
index.js
import (/*webpackChunkName: 'jquery'*/ 'jquery').then(({default: $}) => {
console.log($.length);
});
配置代码分割压缩CSS代码的时候,会覆盖默认配置,所以需要手动配置压缩JS代码
一般用于生产环境
配置示例
webpack.prod.js
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const prodConfig = {
// ...
module: {
rules: [
{
test: /\.(less)$/, // 指定检测less相关类型的文件
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
plugins: [
// ...
new MiniCssExtractPlugin({
filename: '[name].[hash:5].css',
}),
]
};
一般在生产环境下配置
配置示例
webpack.prod.js
// ...
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const prodConfig = {
// ...
optimization: {
minimizer: [
new OptimizeCssAssetsWebpackPlugin(),
new TerserWebpackPlugin(), // 由于CSS压缩优化配置导致原来默认压缩JS的配置被覆盖了,所以需要用TerserWebpackPlugin插件进行JS压缩
],
}
};
// ...
const WebpackBundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const prodConfig = {
plugins: [
// ...
new WebpackBundleAnalyzer(),
]
};
// ...
"scripts": {
"build:dev": "webpack --coinfig ./config/webpack.dev.js",
"build:prod": "webpack --coinfig ./config/webpack.prod.js --env production --myenv me",
"dev:server": "webpack-dev-server --config ./config/webpack.dev.js"
},
// ...
// ...
const argv = require('yargs').argv;
console.log('环境参数: ', argv.env, argv.myenv); // production me
// 使用: argv.env === 'production' ? '' : '';
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
- 确定入口:根据配置中的 entry 找出所有的入口文件;
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
- 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。