对于webpack视频学习的简单记录
1、基础用法
1.1 entry
单入口-字符串,多入口-对象
当设置 entry: './src/index.js' 时,你可以省略它,此为默认值
可以赋值一个空对象(允许使用插件来修改入口)
module.exports = {
entry: './path/file.js'
}
module.exports = {
entry: {
app: './path/file.js',
admin: './path/admin.js'
}
}
// 入口文件输出文件名
module.exports = {
entry: {
about: { import: './about.js', filename: 'pages/[name][ext]' },
},
};
// 入口文件依赖,使用 dependOn-选项, 你可以将模块从一个入口文件代码块共享到另一个,app 代码块 将不包含 react-vendors 所拥有的模块
module.exports = {
entry: {
app: { import: './app.js', dependOn: 'react-vendors' },
'react-vendors': ['react', 'react-dom', 'prop-types'],
},
};
// 入口文件类库,为每个入口文件传递不同的 library 选项
module.exports = {
entry: {
commonjs: {
import: './lib.js',
library: {
type: 'commonjs-module',
},
},
amd: {
import: './lib.js',
library: {
type: 'amd',
},
},
},
};
// 入口文件运行时,为每个入口文件指定一个 运行时代码。 当指定时,将创建一个以该名称命名的代码块,其中仅包含该条目的运行时代码
module.exports = {
entry: {
app: {
import: './app.js',
runtime: 'app-runtime',
},
},
};
// 入口文件代码块加载,这个入口文件的运行时代码将使用这个来加载代码块
module.exports = {
entry: {
app: {
import: './app.js',
},
worker: {
import: './worker.js',
chunkLoading: 'importScripts',
},
},
};
1.2 output
多入口通过占位符确保文件名唯一
output.filename 可以设置为函数
设置 output.path: path.resolve(__dirname, 'dist') 时:你可以省略它,此为默认值
设置 output.filename: '[name].js' 时:你可以省略它,此为默认值
在webpack 4 中,多个 webpack 运行时可能会在同一个 HTML 页面上发生冲突,因为它们使用同一个全局变量进行代码块加载。为了解决这个问题,需要为 output.jsonpFunction 配置提供一个自定义的名称。Webpack 5 确实会从 package.json name 中自动推断出一个唯一的构建名称,并将其作为 output.uniqueName 的默认值。
module.exports = {
entry:'./path/file.js',
output:{
filename: 'bundle.js',
path: _dirname + '/dist'
}
}
module.exports = {
entry:{
app: './path/file.js',
admin: './path/admin.js'
},
output:{
filename: '[name].js',
path: _dirname + '/dist'
}
}
1.3 loaders
webpack只支持JS和JSON两种文件类型,通过Loaders去支持其他文件类型并且把它们转换成有效的模块
本身是一个函数,接受源文件作为参数,返回转换的结果
module.exports = {
entry:'./path/file.js',
output:{
filename: 'bundle.js',
path: _dirname + '/dist'
},
module:{
rules:[
{test:/.\txt$/,use:'raw-loader'}
]
}
}
1.4 plugins
用于bundle文件的优化,资源管理和环境变量注入,作用于整个构建过程
module.exports = {
entry:'./path/file.js',
output:{
filename: 'bundle.js',
path: _dirname + '/dist'
},
plugins:[
new HtmlWebpackPlugin({template:'./src/index.html'})
]
}
1.5 mode
指定当前构建环境,默认值为production
1.6 解析 ES6 和 react ——babel-loader
npm i @babel/core @babel/preset-env babel-loader -D
// babel-loader依赖babel,创建 .babelrc 文件
{
"presets": [
"@babel/preset-env"
]
}
// 修改 webpack.config.js
module.exports = {
module: {
rules: [
{
test: /.js$/,
use: 'babel-loader'
}
]
}
}
npm i react react-dom @babel-preset-react
// .babelrc
{
"presets": [
"@babel/preset-react"
]
}
1.7 解析CSS——css-loader
css-loader 用于加载 .css 文件,并且转换成 commonjs 对象
style-loader 将样式通过 <style> 标签插入到 head 中
loader链式调用,执行顺序从右到左
npm i css-loader style-loader -D
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: [
'style-loader',
'css-loader'style-loader
] // 先用css-loader解析css,然后传递到style-loader
},
]
}
}
npm i less less-loader -D
module.exports = {
module: {
rules: [
{
test: /.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
},
]
}
}
1.8 解析文件——file-loader
如果你定义了 rules,以使用 raw-loader,url-loader 或 file-loader 来加载资源,请使用 资源模块 替代,因为它们可能在不久的将来被淘汰。
在 webpack 5 之前,通常使用:
raw-loader 将文件导入为字符串
url-loader 将文件作为 data URI 内联到 bundle 中
file-loader 将文件发送到输出目录
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。
1.9 文件监听
发现源代码发生变化时,自动冲洗你构建出新的输出文件
- 启动
webpack命令时,带上--watch参数,需要手动刷新浏览器 - 在配置
webpack.config.js中设置watch: true
原理
1.10 热更新
模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除 模块,而无需重新加载整个页面。
webpack-dev-server 为你提供了一个基本的 web server,并且具有 live reloading(实时重新加载) 功能。webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中
// package.json
{
"scripts": {
"dev": "webpack-dev-server --open"
},
}
// webpack.config.js
const webpack = require('webpack');
module.exports = {
mode: 'development',
plugins: [
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: './dist',
hot: true
}
}
v5
// npm install --save-dev webpack-dev-server
// 将 dist 目录下的文件 serve 到 localhost:8080 下
module.exports = {
devServer: {
static: './dist',
},
optimization: {
runtimeChunk: 'single', // 单个 HTML 页面有多个入口
},
};
// package.json
{
"scripts": {
"start": "webpack serve --open",
},
}
原理
1.11 文件指纹
打包后输出的文件名后缀,用作版本管理
Chunkhash:和webpack打包的chunk有关,不同的entry会生成不同的chunckhash值Contenthash:根据文件内容来定义hash,文件内容不变,则contenthash不变
module.exports = {
entry:{
app: './path/file.js',
admin: './path/admin.js'
},
// JS chunckhash
output:{
filename: '[name][chunkhash:8].js',
path: _dirname + '/dist'
},
// css Contenthash
plugis:[
new MiniCssExtractPlugin({ // 把style-loader中的css提取出来
filename:`[name][contenthash:8].css`
})
],
// 图片 Contenthash
module: {
rules: [
{
test: /.(png|jpg|gif|jpeg|svg)$/,
use: [{
loader: 'file-loader',
option: {
name:'img/[name][hash:8].[ext]'
}
}],
},
]
}
}
1.12 代码压缩
JS
内置uglifyjs-webpack-plugin
CSS
使用 optimize-css-assets-webpack-plugin ,同时使用 cssnano
npm i optimize-css-assets-webpack-plugin cssnano -D
const OptimizeCSSAssetsPlungin = require('optimizecss-assets-webpack-plugin')
module.exports = {
plugins:[
// css压缩
new OptimizeCSSAssetsPlungin({
assetNameRwgExp: /.css$/g,
cssProcessor: require('cssnano')
})
],
}
For webpack v5 or above please use css-minimizer-webpack-plugin instead.
// npm install css-minimizer-webpack-plugin
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
optimization: {
minimize: true,
minimizer: [
// For webpack@5 you can use the `...` syntax to extend existing minimizers (i.e. `terser-webpack-plugin`), uncomment the next line `...`,
new CssMinimizerPlugin({
test: /.css$/g,
}),
],
},
}
HTML
修改html-webpack-plugin,设置压缩参数
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [new HtmlWebpackPlugin()],
};
这将会生成一个包含以下内容的 dist/index.html 文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>webpack App</title>
</head>
<body>
<script src="index_bundle.js"></script>
</body>
</html>
2、进阶用法
2.1 自动清理构建目录产物
-
用
output.clean配置项 -
通过
npm scriptsrm -rf ./dist && webpack rimraf ./dist && webpack -
clean-webpack-plugin// npm i clean-webpack-plugin -D const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { plugins: [ // 自动清理构建目录产物 new CleanWebpackPlugin(), ], } clean-webpack-pluginconst path = require('path');
2.2 补齐CSS前缀
// npm i postcss-loader autoprefixer -D
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ()=>{
require('autoprefixer')({
browsers:['last 2 version','>1%']
})
}
}
}
}
]
},
]
}
}
// 方式2
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
]
}
}
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')({browsers:['last 6 version', '>1%', 'ios 7']})
],
};
2.3 px自动转成rem
// npm i px2rem-loader -D
// npm i lib-flexible -S
2.4 静态资源内联
避免页面闪动
assets module (资源模块) 替换raw-loader:juejin.cn/post/702957…
2.5 多页面应用(MPA)打包
多入口,每次页面跳转,后台服务器返回新的html文件
动态配置入口,利用glob.sync
// npm i `glob -D
const glob = require('glob');
const setMPA = () => {
const entry = {};
const htmlWebpackPlugin = [];
const entryFiles = glob.sync('./src/*/index.js');
Object.keys(entryFiles).map((index) => {
const entryFile = entryFiles[index]; // ./src/index/index.js
const match = entryFile.match(/src/(.*)/index.js/);
const pageName = match && match[1];
entry[pageName] = entryFile;
htmlWebpackPlugin.push(
new HtmlWebpackPlugin({
template: path.join(__dirname,`src/${pageName}/index.html`),
filename: `${pageName}.html`,
chunks: [pageName],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
}));
})
return {
entry,
htmlWebpackPlugin
}
}
const {entry, htmlWebpackPlugin} = setMPA()
module.exports = {
entry: entry,
plugins:[
//...
].concat(htmlWebpackPlugin)
}
2.6 使用sourcemap
定位到源代码,开发环境开启,线上环境关闭,线上排查问题可以将sourcemap上传到错误监控系统
module.exports = {
devtool: 'source-map', // 推荐使用 source-map或者cheap-module-source-map
}
2.7 提取页面公共资源
基础库,如react,通过cdn引入,不打入bundle
splitchunksplugin
2.8 代码分割和动态引入
部分代码在某些特殊时候才用到,将代码库分割成chunks(语块),当代码运行时再进行加载
懒加载JS脚本
- CommonJS:require.ensure
- 动态import
// npm i @babel/plugin-syntax-dynamic-import --save-dev
// .babellrc
{
"plugins":[
"@babel/plugin-syntax-dynamic-import"
]
}
// index.js
import('./text.js').then() // 返回promise对象
2.9 ESlint
帮助发现代码错误,统一代码风格
优秀实践:eslint-config-airbnb
落地
- CI/CD
// npm i husky --save--dev
// package.json
{
"script":{
"precommit":"lint-staged"
},
"lint-staged":{
"linters":{
"*.{js,scss}":["eslint--fix","git add"]
}
}
}
- webpack
// npm i eslint eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y eslint-loader eslint-config-airbnb -D
module.exports = {
module: {
rules: [
{
test: /.js$/,
use: ['babel-loader','eslint-loader']
},
}
}
// .eslintrc.js
module.exports = {
"parser":"babel-eslint",
"extends":"airbnb",
"env":{
"browser":true
},
// "rules" 自定义配置规则
}
// package.json
{
"script": "eslint ./lib fix" // eslint检查并简单修复
}
2.10 打包组件和基础库
实现大整数加法库打包
-
需要打包压缩版本(开发阶段 large-number.min.js)和非压缩版本(线上 large-number.js)
-
支持 AMD/CJS/ESM 模块引入
// ES module import * as largeNumber from 'large-number' // ... largeNumber.add('999','1') // CJS const largeNumber = require('largr-number') // ... largeNumber.add('999','1') // AMD require(['large-number'], function(large-number){ // ... largeNumber.add('999','1') }) // 发布到cdn后,script引入 <!doctype html> <html> <script src="http://..."></script> <script> // ... // Global var largeNumber.add('999','1') // property in the window object window.largeNumber.add('999','1') </script> </html>
// webpack.config.js
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
entry:{
"large-number":"./src/index.js",
"large-number.min":"./src/index.js"
},
output:{
filename:"[name].js",
library:"largeNumber", // 指定库的全局变量
libraryExport:"default",
libraryTarget:"umd", // 支持库引入方式
clean: true
},
// 只对.min压缩
mode:"none",
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
include: /.min.js$/,
})
]
}
}
// package.json
{
"name": "large-number",
"version": "1.0.0",
"description": "大整数加法打包",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"build": "webpack",
"prepublish": "webpack" // 有npm账号,执行这个命令就可以把包发布到npm
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"terser-webpack-plugin": "^5.3.6",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
}
}
// index.js
if (process.env.NODE_ENV = 'production') {
module.exports = require('./dist/large-number.min.js')
} else {
module.exports = require('./dist/large-number.js')
}
2.11 SSR
服务端将渲染好的页面返回,缩短首屏加载时间
2.12 命令行显示日志
stats:输出统计信息
stats:"errors-only"
// 如果有devServer
devServer: {
stats:"errors-only"
}