了解webpack5原理之前可以先了解一下tapable。https://github.com/webpack/tapable/tree/tapable-1
1. webpack安装
- 创建package.json 项目依赖管理文件
npm init
2. 安装局部|全局webpack
npm install webpack webpack-cli -D //局部
npm install webpack webpack-cli -g //全局
3. 根目录创建webpack.config.js 配置文件
4. 在根目录package.json 文件下创建*scripts*脚本
"scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production"
},
2. loader
webpack中loader的作用就是将特定的模块类型转换
2.1 样式打包
2.1.1 css-loader
作用: 解析.css文件
配置:
1. 安装css-loader
npm install style-loader -D
2. 在webpack.config.js文件中 配置css-loader
const path = require("path")
module.exports = {
entry: './src/js/index.js', //输入文件路径
output: {
path: path.resolve(__dirname, './dist'), //输出文件路径
filename: 'main.js' //输出文件名称
},
module:{ //module.rules中允许我们配置多个loader
rules:[{
test:/\.css$/, //通过正则表达式对 resource(资源)进行匹配
use:[ //用于对 resource(资源)进行匹配
{ loader:"css-loader"}, // 参数1 loader:必须有一个 loader属性。 参数2 options,{}||[],值会被传递到loader中
]
},]
}
}
2.1.2 style-loader
**作用:**将css渲染到页面中,css-loader只负责文件的解析不负责渲染,所以需要style-loader。(style-loader必须在css-loader前use)
配置:
1. 安装style-loader
npm install style-loader -D
2. 在webpack.config.js文件中 配置style-loader
module:{
rules:[{
test:/\.css$/,
use:[
"style-loader",//简写形式
"css-loader"
]
}]
}
2.2.3 less-loader
作用: 将.less文件编译为css文件。
配置:
1.安装less-loader
npm install less-loader -D
2. 在webpack.config.js文件中 配置style-loader
scss-loader同理
module:{
rules:[{
test:/\.less$/,
use:[
"style-loader",//简写形式
"css-loader"
"less-loader"
]
}]
}
2.2.4 PostCss工具
作用: 对css进行优化,例如自动添加浏览器前缀。
postcss-preset-env是PostCSS工具的插件,它可以帮助我们将一些现代的CSS特性,转成大多数浏览器认识的CSS,并且会根据目标浏览器或者运行时环境 添加所需的polyfill,添加私有前缀等
配置:
1. 安装 PostCss工具
npm install postcss postcss-cli -D
2. 安装postcss-preset-env插件
npm install postcss-preset-env -D
2. 在webpack.config.js文件中rules下的 use中配置。例:
module:{
rules:[{
test:/\.css$/,
use:[
{ loader:"style-loader"},
{ loader:"css-loader"},
{
loader:"postcss-loader",
otpions:{
postcssOptions:{
plugins:[
require("postcss-preset-env") //当使用PostCss工具时 调用postcss-preset-env插件
]
}
}
}
]
}]
}
2.2 图片打包
2.2.1 file-loader
目前webpack5已经不推荐使用file-loader和url-loader,但是目前部分cli仍在使用。
作用: file-loader的作用就是帮助我们处理import/require()方式引入的一个文件资源,并且会将它放到我们输出的文件夹中。
配置:
1.安装 file-loader
npm install file-loader -D
2.在webpack.config.js文件中配置file-loader
{
test:/\.(png|jpg|jpeg|gif|svg) /,
use:[{
loader:"file-loader"
}]
}
2.2.2 url-loader
作用: url-loader作用与file的工作方式相同,但可以将较小的文件转为base64的url。在开发过程中可以将小的图片转换成base64格式和页面一同被请求,这样可以减小不必要的请求过程。
配置:
1.安装url-loader
npm install url-loader -D
2.在webpack.config.js文件中配置
{
test:/\.(png|jpg|jpeg|gif|svg) /,
use:[{
loader:"url-loader",
options:{
limit:100 *1024 //limit字段可以设置转换设置 只有76kb以下的才会被转换为base64
}
}]
}
2.2.3 知识补充-PlaceHolders占位符
作用: 让打包的文件名称按我们固定的形式显示
官网: https://webpack.js.org/loaders/file-loader/#placeholders
常用:
1. [ext]:文件打包前的扩展名;
2. [name]:文件打包前的名称;
3. [hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制);
4. [hash:<length>]:截取的hash长度,因为hash默认32字符;
5. [path]:文件相对于webpack.config.js文件的路径
例:
use:{
loader:"file-loader",
options:{
name:"img/[name][hash:8][ext]" //打包后的文件名为
}
}
3.1 asset module type
资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。
在Webpack5之前,通常使用raw-loader将文件导入字符串;url-loader将文件data URI内联到bundle中;file-loader将文件发送到输出目录。
在Webpack5中,我们使用asset module 的四种新的模块类型,来替换这些loader:
1.asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现;
{
test: /\.(jpe?g|png|gif|svg)$/,
type: "asset/resource",
generator: { //genergtor字段下的filename 配置自定义输出文件名
filename: "img/[name]_[hash:6][ext]"
},
}
//asset自定义输出文件名也可以通过在 webpack 配置中设置 output.assetModuleFilename 来修改此模板字符串:
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'images/[hash][ext][query]'
},
2.asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现;
同上
3.asset/source 导出资源的源代码。之前通过使用 raw-loader 实现;
同上
4.asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体 积限制实现;
{
test: /\.(jpe?g|png|gif|svg)$/,
type: "asset",
generator: {
filename: "img/[name]_[hash:6][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 100 * 1024 //体积限制
}
}
}
3.Plugin
Loader和Plugin的区别:
1.Loader用于特定模块的转换,如JS,TS,CSS等等。
2.Plugin可以用于执行更广泛的任务,比如打包优化,资源管理,环境注入等等,plugins中的插件都需要require。webpack Plugin是一个具有 apply
方法的 JavaScript 对象。apply
方法会被 webpack compiler 调用,并且在 整个 编译生命周期都可以访问 compiler 对象。
以下是开发常用插件:
3.1 CleanWebpackPlugin
作用: 打包前,先清理上一次打包。
1.首先安装
npm install clean-webpack-plugin -D
2.webpack.config.js配置
const {CleanWebpackPlugin}=require('clean-webpack-plugin');
module.exports = {
plugins:[
new CleanWebpackPlugin()
],
}
3.2 HtmlWebpackPlugin
作用: 打包并生成对应的index.html文件。
1. 安装
npm install html-webpack-plugin -D
2.webpack.config.js配置
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
plugins:[
new HtmlWebpackPlugin(
{
title: "我的主页",//webpack会在htmlWebpackPlugin.options.title中找到该属性
template: "./pubilc/index.html" //生成index自定义模板的路径
}
),
]
}
这个文件是如何生成的呢?
默认情况下是根据ejs的一个模板来生成的;
在html-webpack-plugin的源码中,有一个default_index.ejs模块;
3.3 DefinePlugin
作用: DefinePlugin是webpack内置的一个插件,在编译时会创建我们配置的常量。
在日常的开发中,我们不会使用默认打包的index.html文件,一般都会自定义一个HTML模板。
例如 :VueCLI的index.html模板:
1.webpack.config.js配置
const {DefinePlugin} = require("webpack")
module.exports = {
plugins: [
new DefinePlugin({
BASE_URL: "'./'" // './'表示当前路径下
})
]
}
3.4 CopyWebpackPlugin
作用:配置在打包过程中,需要copy到打包目录下的文件。
1. 安装 CopyWebpackPlugin
npm install copy-webpack-plugin -D
2.webpack.config.js配置
//导入插件
const CopyWebpackPlugin = require("copy-webpack-plugin")
//配置
module.exports = {
plugins: [
new CopyWebpackPlugin({
patterns: [{
from: "pubilc", //复制的路径
to: "./", //复制到那里去
globOptions: { //配置规则
ignore: [ //需要忽略的文件
'**/index.html'
]
}
}]
})
]
}
3.5 扩展:mode和devtool
1.mode: 设置模式
module.exports = {
mode:""
//参数1:development 开发模式 自动生成开发配置
//参数2:production 生产模式 自动生成发布配置,启用名称混淆,代码压缩等等
}
文档:
2.devtool
参数众多,介绍其作用域在https://webpack.docschina.org/configuration/devtool/#root
在开发过程中我们主要设置
module.exports = {
// 设置source-map, 建立js映射文件, 方便调试代码和错误,报错后我们可以精准到某一行
devtool: "source-map",
}
4.Babel
4.1Babel 说明
Babel从本质上来说可以算是一个编译器,作用:将浏览器无法识别源代码转化为浏览器可以直接识别的代码,比如TypeScript。Babel本身是’独立’的,可以单独使用。
Babel工作流程:解析阶段–>转换阶段–>生成阶段
具体流程:
1.词法分析:将源代码进行拆分,如let demo = ‘案例’ 拆为let 、demo 、=、‘案例’
2.tokens数组:将词法分析的拆分结果存入tokens数组
3.语法分析:存放tokens数组后进入语法分析阶段,标记关键字,如上面的let (这就是为什么我们命名不能使用关键字的原因,因为在词法分析的过程中会混淆)
4.AST抽象语法树:词法分析后生成AST抽象语法树。AST语法树理解https://blog.csdn.net/huangpb123/article/details/84799198。
5.对AST抽象树进行遍历,查找到关键字
6.访问关键字通过我们所配置的插件进行转换,如把上诉的let转换为var
7.生成新的AST抽象语法树
4.2 Babel 命令行使用
1. 安装@babel/core:babel的核心代码,必须安装;
npm install @babel/core -D
2. 安装@babel/cli:babel的核心代码,必须安装;
npm install @babel/cli -D
运行babel打包指定文件:
//src 源文件的目录 或根目录的文件
// --out-dir:指定要输出的文件夹
npx babel src --out-dir dist
根据使用场景安装
1. 如果需要转化为箭头函数 plugin-transform-arrow-functions
npm install @babel/plugin-transform-arrow-functions -D
//打包文件
npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions
2.转换变量名等 转var plugin-transform-arrow-functions
npm install @babel/plugin-transform-block-scoping -D
//打包
npx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping
,@babel/plugin-transform-arrow-functions
如果转换的内容过多,我们可以使用预设preset,会帮助我们生成一些默认配置
1.安装preset预设
npm install @babel/preset-env -D
2. 打包
npx babel src --out-dir dist --presets=@babel/preset-env
4.3 Babel—loader
在webpack中配置Babel
1.安装babel-loader
npm install babel-loader @babel/core
2.webpack.config.js配置规则
module:{
rules:[
{
test:/\.m?js$/,
use:{
//按需导入配置
options:{
plugins:[
"@babel/plugin-transform-arrow-functions",
" @babel/plugin-transform-block-scoping"
]
}
}
}
]
}
按需导入太麻烦我们也可以预设插件,插件预设有
1.env
2.react
3.TypeScript
1.安装预设
npm install @babel/preset-env
2.配置规则
module:{
rules:[
{
test:/\.m?js$/,
use:{
//按需导入配置
options:{
presets:[
["@babel/preset-env"]
]
}
}
}
]
}
也有一种情况就是将Bebal配置信息提取到一个文件中,一般名为babel.config.(js|json|cjs|mjs)
1. babel.config.js
module.exports={
presets:[
["@babel/preset-env"]
]
}
2.webpack.config.js配置
{
test:/\.m?js$/,
loader:"babel-loader"
}
5.webpack-dev-serve
作用: 热更新。当文件发生变化时,可以自动的完成 编译 和 展示。
1.安装
npm install webpack-dev-server -D
2. 在package.json中设置
"scripts": {
"serve":"webpack serve --open"
},
5.1 static设置
1.static: 当在打包的资源中找不到引用资源时,去这个目录下查找。
webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中。 这是因为webpack-dev-server使用的库为memory-fs ,会在内存中存放打包的资源,并开启express服务器。
通常情况下,我们把webpack需要打包的资源都放在一个目录下,如src。而我们使用的index模板,通常存放在src同级目录下的public中,而我们的入口设置为“./src",所以public下index.html的引用不会被打包。在上线阶段前我们会使用CopyWebpackPlugin插件进行拷贝,而开发阶段我们会设置static。
1.在webpack.config.js设置
module.exports = {
static: ['pubilc','assets']
},
}
还有很多参数,详见官网https://webpack.docschina.org/configuration/dev-server/#devserverstatic
5.2 HMR
HMR称为热模块替换。在应用程序运行过程中,添加、替换和删除某一模块,而不需要刷新整个页面。
webpack-dev-server已经支持HMR,我们只需要开启即可。默认不开启,则使用live reloading,整个页面自动刷新。
1.webpack.config.js配置
module.exports = {
target: "web",//我们最好声明下作用场景
devServer: {
static: ['assets'],
hot: true //开启
},
}
2. 入口文件下指定模块更新
if(module.hot){
module.hot.accept("./main.js",()=>console.log("该模块更新了"))
}
在vue和React中不需要这种操作,因为他们已经有loader做了相关的处理,Vue使用vue-loader,React使用react-refresh。
HMR原理
webpack-dev-server会创建两个服务:提供静态资源的服务(express)和Socket服务(net.Socket)。
一.express server
负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析)
二. Socket Server
1.开启长连接通信通道,使服务器可以发送文件给客户端。
2.当服务器监听到相应模块发送变化时,生成manifest.json(描述文件 指定哪些模块发生改变)和update chunk.js(模块改变的内容),将两个文件发送到客户端
3.浏览器执行HMR runtime机制,加载两模块,并针对性渲染两模块的内容。