一、核心概念——entry
Entry用于指定webpack打包的入口
1、单入口(entry是一个字符串)
module.exports = {entry: './src/index.js'
}
2、多入口(entry是一个对象)
module.exports = {entry:{app: './app.js',adminApp: './src/adminApp.js'}
}
二、核心概念——output
output用来告诉webpack如何将编译后的文件输出到磁盘
1、单入口
module.exports = {entry: './src/index.js',output: {filename: 'bundle.js',path: __dirname + '/dist'}
}
2、多入口
module.exports = {entry:{app: './app.js',adminApp: './src/adminApp.js'},output: {filename: '[name].js', // 通过占位符确保文件名的唯一path: __dirname + '/dist'}
}
三、核心概念——loaders
webpack默认只支持js和json两种文件类型,通过loaders去支持其他文件类型并且把他们转化成有效的模块,并且可以添加到依赖图中。
本身是一个函数,接受源文件作为参数,返回转换的结果。
1、常见的loader
名称 | 描述 |
---|---|
babel-loader | 转换ES6、ES7等JS新特性语法 |
css-loader | 支持.css文件的加载和解析 |
less-loader | 将less转换为css |
ts-loader | 将TS转换成JS |
file-loader | 进行图片、字体的打包 |
raw-loader | 将文件以字符串的形式导入 |
thread-loader | 多进程打包JS和CSS |
2、loader的用法
module:{rules: [{test: /\.txt$/, // test:指定匹配规则use: 'raw-loader' // use:指定使用的loader名称}]
}
四、核心概念——plugins
插件用于bundle文件的优化,资源管理和环境变量注入
作用于整个构建过程
1、常见的plugins
名称 | 描述 |
---|---|
CommonsChunkPlugin | 将chunks相同的模块代码提取成公共js |
CleanWebpackPlugin | 清理构建目录 |
ExtractTextWebpackPlugin | 将css从bundle文件里提取成一个独立的css文件 |
CopyWebpackPlugin | 将文件或者文件夹拷贝到构建的输出目录 |
HtmlWebpackPlugin | 创建html文件去承载输出的bundle |
UglifyjsWebpackPlugin | 压缩JS |
ZipWebpackPlugin | 将打包的资源输出生成一个zip包 |
2、plugins用法
plugins:[new HtmlWebpackPlugin({ // 放到plugins数组里template: './src/index.html'})
]
五、核心概念——mode
mode用来指定当前的构建环境是:production、develoment还是node
设置mode可以使用webpack内置的函数,默认值为production
1、mode的内置函数功能
选项 | 描述 |
---|---|
development | 设置 process.env.NODE_ENV 的值为 development.开启 NamedChunksPlugin 和 NamedModulesPlugin . |
production | 设置 process.env.NODE_ENV 的值为 production.开启 FlagDependencyUsagePlugin , FlagInclu dedChunksPlugin ModuleConcatenationPlugin , NoEmitOnE rrorsPlugin , Occurrence0rderPlugin ,SideEffectsFl agPlugin 和 TerserPlugin . |
none | 不开启任何优化选项 |
六、使用
1、使用babel-loader解析ES6和React JSX
babel的配置文件是:.babelrc
(1)安装
npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react
npm i react react-dom
(2)增加ES6的babel preset配置 (.babelrc)
{"presets": ["@babel/preset-env","@babel/preset-react"]
}
(3)新增文件reactTest.js
'use strict';
import React from "react";
import ReactDOM from "react-dom";class ReactComponents extends React.Component {render() {return <div>Hello React</div>;}
}ReactDOM.render(<ReactComponents />,document.getElementById('root')
)
(4) 修改webpack.config.js
'use strict'const path = require('path');module.exports = {entry: {main: './src/index.js',reactTest: './src/reactTest.js'},output: {path: path.join(__dirname, 'dist'),filename: '[name].js'},mode: 'production',module: {rules: [{test: /\.js$/,use: 'babel-loader'}]}}
2、css、less、sass解析
css-loader用于加载.css文件,并且转换成commonjs对象
style-loader将样式通过<style>标签插入head中
(1)安装依赖
npm i css-loader style-loader -D
(2)修改webpack.config.js
'use strict'const path = require('path');module.exports = {entry: {main: './src/index.js',reactTest: './src/reactTest.js'},output: {path: path.join(__dirname, 'dist'),filename: '[name].js'},mode: 'production',module: {rules: [{test: /\.js$/,use: 'babel-loader'},{test: /\.css$/,// use数组中是从后往前调用的use: ['style-loader','css-loader',]}]}}
(3)新增文件reactTest.css
.react-test{font-size: 20px;color: red;
}
(4)修改reactTest.js
'use strict';
import React from "react";
import ReactDOM from "react-dom";
import './reactTest.css';class ReactComponents extends React.Component {render() {return <div className="react-test">Hello React</div>;}
}ReactDOM.render(<ReactComponents />,document.getElementById('root')
)
(5)解析less和sass
less-loader用于将less转换成css
npm i less less-loader -D
'use strict'const path = require('path');module.exports = {entry: {main: './src/index.js',reactTest: './src/reactTest.js'},output: {path: path.join(__dirname, 'dist'),filename: '[name].js'},mode: 'production',module: {rules: [{test: /\.js$/,use: 'babel-loader'},{test: /\.css$/,// use数组中是从后往前调用的use: ['style-loader','css-loader','less-loader']},{test: /\.less$/,use: ['style-loader','css-loader','less-loader']}]}}
3、解析图片和字体——file-loader
file-loader用于处理文件,也可以用来解析字体
(1)安装依赖
npm i file-loader -D
(2)修改webpack.config.js
module: {rules: [{test: /\.js$/,use: 'babel-loader'},{test: /\.css$/,// use数组中是从后往前调用的use: ['style-loader','css-loader','less-loader']},{test: /\.less$/,use: ['style-loader','css-loader','less-loader']},{test: /\.(png|jpg|gif|jpeg)$/,use: 'file-loader'},{test: /\.(woff|woff2|eot|ttf|otf)/,use: 'file-loader'}]
}
4、解析图片和字体——url-loader
url-loader也可以处理图片和字体
可以设置较小资源自动base64
(1)安装依赖
npm i url-loader -D
(2)修改webpack.config.js
'use strict'const path = require('path');
module.exports = {entry: {main: './src/index.js',reactTest: './src/reactTest.js'},output: {path: path.join(__dirname, 'dist'),filename: '[name].js'},mode: 'production',module: {rules: [{test: /\.js$/,use: 'babel-loader'},{test: /\.css$/,// use数组中是从后往前调用的use: ['style-loader','css-loader','less-loader']},{test: /\.less$/,use: ['style-loader','css-loader','less-loader']},{test: /\.(png|jpg|gif|jpeg)$/,use: [{loader: 'url-loader',options: {limit: 10240}}]},{test: /\.(woff|woff2|eot|ttf|otf)/,use: 'file-loader'},]}
}
5、webpack中的文件监听
文件监听是在发现源码发生变化时,自动重新构建出新的输出文件
webpack开启监听模式,有两种方式:
- 启动webpack命令时,带上 --watch参数
- 在配置webpack.config.js中设置watch:true
唯一缺陷:每次需要手动刷新浏览器
(1)修改package.json 后运行 npm run watch
{"name": "webpackExercise","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "webpack","watch": "webpack --watch"},"keywords": [],"author": "","license": "ISC","devDependencies": {"@babel/core": "^7.20.2","@babel/preset-env": "^7.20.2","@babel/preset-react": "^7.18.6","babel-loader": "^9.1.0","css-loader": "^6.7.2","file-loader": "^6.2.0","less": "^4.1.3","less-loader": "^11.1.0","style-loader": "^3.3.1","url-loader": "^4.1.1","webpack": "^5.75.0","webpack-cli": "^5.0.0"},"dependencies": {"react": "^18.2.0","react-dom": "^18.2.0"}
}
(2)原理
轮询判断文件的最后编辑时间是否变化
某个文件发生了变化,并不会立刻告诉监听者,而是先缓存起来,等aggregateTimeout
'use strict'const path = require('path');
module.exports = {// 默认false,不开启watch: true,watchOptions: {// 忽略监听的文件ignored: /node_modules/,// 监听到变化后会等300毫秒再去执行,默认300毫秒aggregateTimeout: 300,// 判读文件是否发生变化是通过轮询指定文件,默认每秒轮询1000次poll: 1000},entry: {main: './src/index.js',reactTest: './src/reactTest.js'},output: {path: path.join(__dirname, 'dist'),filename: '[name].js'},mode: 'production',module: {rules: [{test: /\.js$/,use: 'babel-loader'},{test: /\.css$/,// use数组中是从后往前调用的use: ['style-loader','css-loader','less-loader']},{test: /\.less$/,use: ['style-loader','css-loader','less-loader']},{test: /\.(png|jpg|gif|jpeg)$/,use: [{loader: 'url-loader',options: {limit: 10240}}]},{test: /\.(woff|woff2|eot|ttf|otf)/,use: 'file-loader'},]}
}
6、热更新:webpack-dev-server(HotModuleReplacementPlugin)
(1)安装依赖
npm i webpack-dev-server -D
webpack-dev-server不刷新浏览器
webpack-dev-server不输出文件,而是放在内存中
使用HotModuleReplacementPlugin插件
(2)修改package.json
"scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "webpack","watch": "webpack --watch","dev": "webpack-dev-server --open"},
(3)修改webpack.config.js
'use strict'const path = require('path');
const webpack = require('webpack');
module.exports = {// // 默认false,不开启// watch: true,// watchOptions: {// // 忽略监听的文件// ignored: /node_modules/,// // 监听到变化后会等300毫秒再去执行,默认300毫秒// aggregateTimeout: 300,// // 判读文件是否发生变化是通过轮询指定文件,默认每秒轮询1000次// poll: 1000// },entry: {main: './src/index.js',reactTest: './src/reactTest.js'},output: {path: path.join(__dirname, 'dist'),filename: '[name].js'},mode: 'development',module: {rules: [{test: /\.js$/,use: 'babel-loader'},{test: /\.css$/,// use数组中是从后往前调用的use: ['style-loader','css-loader','less-loader']},{test: /\.less$/,use: ['style-loader','css-loader','less-loader']},{test: /\.(png|jpg|gif|jpeg)$/,use: [{loader: 'url-loader',options: {limit: 10240}}]},{test: /\.(woff|woff2|eot|ttf|otf)/,use: 'file-loader'},]},plugins: [new webpack.HotModuleReplacementPlugin()],devServer: {static: './dist',hot: true}
}
注:在新版的webpack-dev-server中,contentBase已经被移除了,用static替代。
7、热更新:webpack-dev-middleware
webpack-dev-middleware将webpack输出的文件传输给服务器
适用于灵活的定制场景
webpack compile:将js编译成bundleHMR server:将热更新的文件输出给HMR Runtime
bundle server:提供文件在浏览器的访问
HMR runtime:会被注入到浏览器,更新文件的变化
bundle.js:构建输出的文件
8、文件指纹策略
打包后输出的文件名的后缀,通常做的是文件发布的版本管理
(1)文件指纹如何生成
- Hash:和整个项目的构建相关,只要项目文件有修改,整个项目构建的hash值就会更改
- Chunkhash:和webpack打包的chunk有关,不同的enty会生成不同的chunkhash值
- Contenthash:根据文件内容来定义hash,文件内容不变,则contenthash不变
(2)js的文件指纹设置
设置output的filename,使用 [chunkhash]
(3)css的文件指纹设置
// 提取css文件
npm i mini-css-extract-plugin -D
设置MiniCssExtractPlugin的filename,使用 [contenthash]
(4)图片或字体的文件指纹设置
设置file-loader的name,使用 [hash]
占位符名称 | 含义 |
---|---|
[ext] | 资源后缀名 |
[name] | 文件名称 |
[path] | 文件的相对路径 |
[folder] | 文件所在的文件夹 |
[contnthash] | 文件内容的hash,默认md5生成 |
[hash] | 文件内容的hash,默认md5生成 |
[emoji] | 一个随机的指代文件内容的enoj |
(5)修改package.json
"scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "webpack --config webpack.prod.js","watch": "webpack --watch","dev": "webpack-dev-server --config webpack.dev.js --open"},
(6)新建文件webpack.prod.js,并将原有的webpack.config.js改为webpack.dev.js
'use strict'const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {entry: {main: './src/index.js',reactTest: './src/reactTest.js'},output: {path: path.join(__dirname, 'dist'),filename: '[name]_[chunkhash:8].js'},mode: 'production',module: {rules: [{test: /\.js$/,use: 'babel-loader'},{test: /\.css$/,// use数组中是从后往前调用的use: [MiniCssExtractPlugin.loader,'css-loader']},{test: /\.less$/,use: [MiniCssExtractPlugin.loader,'css-loader','less-loader']},{test: /\.(png|jpg|gif|jpeg)$/,use: [{loader: 'file-loader',options: {name: '[name]_[hash:8].[ext]'}}]},{test: /\.(woff|woff2|eot|ttf|otf)/,use: [{loader: 'file-loader',options: {name: '[name]_[hash:8].[ext]'}}]},]},plugins: [new MiniCssExtractPlugin({filename: '[name]_[contenthash:8].css'})]
}
9、HTML、CSS、JS压缩
(1)JS压缩
webpack5内置了terser-webpack-plugin,如果需要额外配置需要安装terser-webpack-plugin并进行一系列调整
(2)CSS文件的压缩
webpack5中可使用css-minimizer-webpack-plugin
npm install css-minimizer-webpack-plugin --save-dev
// webpack.prod.js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");// ...plugins: [new MiniCssExtractPlugin({filename: '[name]_[contenthash:8].css',})],optimization: {minimizer: [new CssMinimizerPlugin(),],},
(3)HTML压缩
npm install --save-dev html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');plugins: [new MiniCssExtractPlugin({filename: '[name]_[contenthash:8].css',}),new HtmlWebpackPlugin({template: path.join(__dirname, 'src/index.html'),filename: 'index.html',chunks: ['index'],inject: true,minify: {html5: true,collapseWhitespace: true,preserveLineBreaks: false,minifyCSS: true,minifyJS: true,removeComments: false}}),],
10、一份prod配置
'use strict'const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {entry: {index: './src/index.js',reactTest: './src/reactTest.js'},output: {path: path.join(__dirname, 'dist'),filename: '[name]_[chunkhash:8].js'},mode: 'production',module: {rules: [{test: /\.js$/,use: 'babel-loader'},{test: /\.css$/,// use数组中是从后往前调用的use: [MiniCssExtractPlugin.loader,'css-loader']},{test: /\.less$/,use: [MiniCssExtractPlugin.loader,'css-loader','less-loader']},{test: /\.(png|jpg|gif|jpeg)$/,use: [{loader: 'file-loader',options: {name: '[name]_[hash:8].[ext]'}}]},{test: /\.(woff|woff2|eot|ttf|otf)/,use: [{loader: 'file-loader',options: {name: '[name]_[hash:8].[ext]'}}]},]},plugins: [new MiniCssExtractPlugin({filename: '[name]_[contenthash:8].css',}),new HtmlWebpackPlugin({template: path.join(__dirname, 'src/reactTest.html'),filename: 'reactTest.html',chunks: ['reactTest'],inject: true,minify: {html5: true,collapseWhitespace: true,preserveLineBreaks: false,minifyCSS: true,minifyJS: true,removeComments: false}}),new HtmlWebpackPlugin({template: path.join(__dirname, 'src/index.html'),filename: 'index.html',chunks: ['index'],inject: true,minify: {html5: true,collapseWhitespace: true,preserveLineBreaks: false,minifyCSS: true,minifyJS: true,removeComments: false}}),],optimization: {minimize: true,minimizer: [new CssMinimizerPlugin(),],},
}
11、一份dev配置
'use strict'const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {// // 默认false,不开启// watch: true,// watchOptions: {// // 忽略监听的文件// ignored: /node_modules/,// // 监听到变化后会等300毫秒再去执行,默认300毫秒// aggregateTimeout: 300,// // 判读文件是否发生变化是通过轮询指定文件,默认每秒轮询1000次// poll: 1000// },entry: {index: './src/index.js',reactTest: './src/reactTest.js'},output: {path: path.join(__dirname, 'dist'),filename: '[name][chunkhash].js'},mode: 'development',module: {rules: [{test: /\.js$/,use: 'babel-loader'},{test: /\.css$/,// use数组中是从后往前调用的use: ['style-loader','css-loader',]},{test: /\.less$/,use: ['style-loader','css-loader','less-loader']},{test: /\.(png|jpg|gif|jpeg)$/,use: [{loader: 'url-loader',options: {limit: 10240}}]},{test: /\.(woff|woff2|eot|ttf|otf)/,use: 'file-loader'},]},plugins: [new webpack.HotModuleReplacementPlugin(),new HtmlWebpackPlugin({template: path.join(__dirname, 'src/reactTest.html'),filename: 'reactTest.html',chunks: ['reactTest'],inject: true,minify: {html5: true,collapseWhitespace: true,preserveLineBreaks: false,minifyCSS: true,minifyJS: true,removeComments: false}}),new HtmlWebpackPlugin({template: path.join(__dirname, 'src/index.html'),filename: 'index.html',chunks: ['index'],inject: true,minify: {html5: true,collapseWhitespace: true,preserveLineBreaks: false,minifyCSS: true,minifyJS: true,removeComments: false}}),],devServer: {static: './dist',hot: true}
}