webpack 的基本认识和使用
什么是 webpack ?
- webpack is static module bundler for modern Javascript applications
- webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具
词法解释:
- 打包工具(bundler): webpack 可以对前端源代码进行打包,所以它是一个打包工具
- 静态的(static): 将代码打包成最终的静态资源(部署到静态服务器)
- 模块化(module): webpack默认支持各种模块化开发 ESModule,CommonJS,AMD等
- 现代的(modern): 前面说过,正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和法展
这张图很形象,把一些浏览器不认识的文件通过 webpack 的转换成浏览器认识的文件。
webpack 安装
需要安装 webpack
和 webpack-cli
webpack: 核心代码
webpack-cli: 用来解析命令行的参数,然后运行 webpack(比如:webpack-cli 就是对终端中的指令进行读取 比如:webpack --entry ./src/index.ts
等等指令)。
# 全局安装
npm install webpack webpack-cli -g
# 局部安装
npm install webpack webpack-cli -D
webpack 的测试文件搭建
创建几个测试文件
// src/utils/math.js
function sum(num1, num2) {
return num1 + num2;
}
export default sum;
// src/utils/format.js
function upper(str) {
return str.toLocaleUpperCase();
}
module.exports = {
upper,
};
// src/index.js
import sum from "./utils/math";
const { upper } = require("./utils/format");
console.log(sum(10, 20));
console.log(upper("copyer"));
在这里可以顺手测试一下,创建一个 html 文件来测试下,这个 src/ index.js
,是否可以在浏览器上编译成功?
答案显然易见: 肯定是不可能的。因为为浏览器不识别 ES Module
和 CommonJS
这个模块的导入导出。
所以这里就开始借助 webpack 来进行转化了。
webpack 打包指令
方式一:借助 vscode 终端,直接运行。
# 打开终端
webpack # 查找全局中的 webpack
npx webpack # 查找当前项目中的 webpack
方式二:使用 package.json 中命令执行(本质都是一样的)
"scripts": {
"build": "webpack", //这里会自动的去node_module/.bin文件下面去找
"watch": "webpack --watch", // 文件被修改,自动生成新的打包,但是不会刷新浏览器
"server": "webpack server", // 文件被修改,自动生成新的打包,也会刷新浏览器,需要借助(webpack-dev-server)
},
针对 server 命令,自动打包,打包的文件存在放 服务器 中,当通过服务器地址打开,读取的是服务器中的 dist 文件,在前端代码中是没有生成 dist 文件夹,所以当需要发布的时候,还是需要手动打包,生成dist文件夹,发布部署。
针对 watch 命令,也可以在 webapck.config.js 配置中配置 watch:true
,效果与此类似。
执行完打包之后,就在当前路径下,生成一个 dist 文件夹,该文件夹下创建 main.js 文件。这里是走的 webpack 中的默认配置。如果需要修改其默认配置的话,就是创建一个 webpack.config.js 文件来覆盖默认配置。
// 当然不一定是 webpack.config.js 文件名,还可以是其他的,只是没有必要修改
"scripts": {
"build": "webpack --config copyer.config.js", // 就是去读取 copyer.config.js 配置文件了
},
webpack 的构建依赖图
在学习 webpack.config.js 配置之前,先来理解一下 webpack 的构建依赖图,有助于后面配置项的理解。
简单来说,webpack 从入口文件解析,会拿到入口文件中的所有模块,在各自模块内部又有对应的依赖,webpack也会进行解析,这样不停的依赖,就会形成一个图。只要一个文件没有在这个依赖图里面,就不会对其进行解析。
就比如这样:
-- src
---- utils
------ math.js
------ format.js
------ b.js
---- index.js
在 入口文件 index.js 中引入了 math.js 和 format.js 两个文件,但是就是没有引用 b.js 文件,那就就会形成类似依赖图:
b.js 文件就没有出现在 webpack 的依赖图中。
总结:就是从入口文件开始查找,如果存在模块依赖,就会添加到依赖图中,模块又会依赖其他的模块,依次循环下去,直到构建成一个完成的依赖图。(这也是 webpack 性能慢的地方之一)
webpack.config.js 基本配置
webpack.config.js 就是用来覆盖 webpack 中的默认配置,那么常用的配置有哪些呢?
entry
入口文件路径配置,也就是让 webpack 知道从哪里开始打包(默认是 ./src/index.js
),从入口文件开始构建 webpack 依赖图。
// webpack.config.js
module.exports = {
entry: './src/index.js',
}
output
告诉 webpack 在哪里输出它所创建的静态文件(bundle),以及如何命名这些文件。默认值为 ./dist/main.js
// webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "./build"), //打包的路径
filename: "build.js", //打包过后的文件名
},
};
loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。如果想要支持别的格式文件,就需要使用 loader 来进行转化,转换成有效识别的文件(模块)。
注意:webpack 配置 loader 的属性名为 module,module 对象下有个 rules 属性用来进行配置。
module.rules
中允许我们配置多个 loader- 这种方式可以更好的配置 loader,也可以清晰的看出使用哪些 loader
module.rules 的配置规则:
-
rules 属性对应的值是一个数组: [ Rule ]
-
数组中存放的是一个一个的
Rule
,Rule 是一个对象,对象中可以设置多个属性-
test
属性: 对资源进行匹配,一般是用正则的方式。 -
use
属性: 对应的值是一个数组:[ useEntry ]
useEntry
是一个对象,可以通过对象来设置其他的属性.loader
必选参数,对应的是一个字符串options
可选参数,值是一个字符串或者对象,会被传入到 loader 中
-
接下来就来看看有哪些常见的 loader,当然使用的 loader,需要自行安装。
css-loader
pnpm install css-loader -D
可以将 css 文件看成是一个模块。是需要通过 import 来加载这个模块的
module.exports = {
module: {
rules: [{
test: /.css$/,
//loader: 'css-loader' //写法一
//use: ['css-loader'] //写法二 (推荐)
use: [
//完整写法
{ loader: "css-loader" },
],
}]
}
};
style-loader
pnpm install style-loader -D
在上面的我们已经配置了css-loader
,但是在实际的效果中是没有生效的
原因:
- css-loader 只是
负责将 css 文件进行解析
,并不会将解析之后的css 插入到页面
中 - 如果希望再完成
插入 style 的操作
,那么我们还需要另外一个 loader,就是style-loader
module: {
rules: [
{
test: /.css$/,
use: ['style-loader', 'css-loader'] // 从右至左的加载
}
]
}
less-loader
npm install less -D
npm install less-loader -D
module: {
rules: [
{
test: /.less$/,
//先转化成css, 使用css-loader加载,最后用style-loader来添加到页面中
use: ['style-loader', 'css-loader', 'less-loader']
}
]
}
postcss-loader
理解 postcss,可以去看我的另外一篇文章,可能会加深你对 postcss 的认识和基本使用
pnpm install postcss-loader -D
# 安装 postcss 插件,预设
pnpm install postcss-preset-env -D
const postcssPresetEnv = require("postcss-preset-env");
module: {
rules: [
{
test: /.less$/,
use: ['style-loader', 'css-loader', 'less-loader', {
loader: 'postcss-loader',
options: {
//可以提出一个单独的文件
postcssOptions: {
plugins: [postcssPresetEnv()]
}
}
}]
}
]
}
当使用 postcss-loader 的时候,会先查找有不有自己 options 选项,如果没有就去查找 postcss.config.js
这个配置文件。所以可以把options 选项里面的内容提出来。
// postcss.config.js
module.exports = {
plugins: [postcssPresetEnv()]
}
// webpack.config.js
module: {
rules: [
{
test: /.less$/,
use: ['style-loader', 'css-loader', 'less-loader', 'postcss-loader']
}
]
}
file-loader
file-loader 的作用就是帮助我们处理 import / require() 方式引入的一个文件资源(比如说:图片),并且会将它放到我们输出的文件夹中。
pnpm install file-loader -D
module: {
rules: [
{
test: /.(jpe?g|png|gif|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
//在打包的文件夹中创建一个images文件夹,用来保存图片的
outputPath: 'images',
//给图片取名字
name: "[name]_[hash:8].[ext]"
}
}
]
}
]
}
url-loader
url-loader 跟 file-loader 的工作原理是一样的
url-loader的区别: 就是对图片有限制,对图片较小的进行 base64 编码
默认情况下: url-loader对所有图片都会进行base64编码
- 小的图片转换base64之后可以和页面一起被请求,减少不必要的请求过程
- 而大的图片也进行转换,反而会影响页面的请求速度
pnpm install url-loader -D
module: {
rules: [
{
test: /.(jpe?g|png|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: {
//在打包的文件夹中创建一个images文件夹,用来保存图片的
outputPath: 'images',
//给图片取名字
name: "[name]_[hash:8].[ext]",
//配置limit
limit: 100 * 1024
}
}
]
}
]
}
限制小于 100kb 的,才会转化成 base64。
asset module type
- 在webpack5之前,加载这些资源是使用的
url-loader
file-loader
- 在webpack5开始,可以直接使用资源模块类型 (
asset module type
),来代替上面的类型
资源模块类型,通过添加模块类型,来代替 loader
asset/resource
发送一个单独的文件并导入URL。代替 file-loader
asset/inline
导出一个资源的data URl。代替使用 url-loader
asset
在导出一个data URI 和 发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现
module: {
rules: [
{
test: /.(jpe?g|png|gif|svg)$/,
type: 'asset',
// 添加images,存放图片
generator: {
filename: 'images/[name]_[hash:8][ext]'
},
//限制图片的大小,小的进行转化
parser: {
dataUrlCondition: {
maxSize: 100 * 1024
}
}
}
]
}
babel-loader
在前端中有着很多的新语法 , 比如: ES6, TS,JSX 等,他们都需要 babel
的转化 js
pnpm install @babel/core babel-loader @babel/preset-env -D
# @babel/core babel的核心依赖
# @babel/preset-env 内置了很多预设
module: {
rules: [
{
test: /.js$/,
use: [
{
loader: "babel-loader",
options: {
//预设
presets: ["@babel/preset-env"],
},
},
],
}
]
}
plugin
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
就是在 webpack 执行的生命周期(hook)过程中做一些额外的操作。
CleanWebpackPlugin
每次打包之后,需要手动删除,再次进行打包,这个手动删除的过程,我们就可以借助该插件帮助我们来完成。
pnpm install clean-webpack-plugin -D
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
plugins: [
new CleanWebpackPlugin()
]
}
HtmlWebpackPlugin
插件将为你生成一个 HTML5 文件, 在 body 中使用 script
标签引入你所有 webpack 生成的 bundle
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
插件很多,用着很香,用的时候再去查找。
mode
通过选择 development
, production
或 none
之中的一个,来设置 mode
参数,你可以启用 webpack 内置在相应环境下的优化。
其默认值为 production
。
module.exports = {
mode: 'production',
};
选项 | 描述 |
---|---|
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development . 为模块和 chunk 启用有效的名。 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production 。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePlugin ,FlagIncludedChunksPlugin ,ModuleConcatenationPlugin ,NoEmitOnErrorsPlugin 和 TerserPlugin 。 |
none | 不使用任何默认优化选项(也就是 webpack 不会自动添加优化项) |
devServer
webpack 内部集成了 webpack-dev-server,一个本地服务器(express),用于开发阶段使用,可以通过 webpack serve
命令启动本地服务器。
该服务器实现了什么功能呢?
- 解决了每次修改源代码,需要重新打包才能看效果的问题。服务器内部会进行打包,但是会把打包的内容放在内存中,没有输出文件(没有涉及到文件的生成,性能就比较高),然后通过地址打开,就是读取内存中的打包内容,当修改源代码时,就会修改内存中的内容,看到实时的效果。
- 也可以解决跨域问题,利用服务器与服务器之间没有同源策略问题。
- ...
static
contentBase 被 static 代替了,更好的见名知意。
webpack-dev-server 只会对 webpack 的依赖图中的文件进行处理,但是有些文件没有在依赖图里面,那么该如何做呢?
那就手动告诉它:
module.exports ={
devServer: {
// 就是服务器中处理静态资源,如果不配置,默认就是 ['public']
static: ['public', 'css']
}
}
错误写法:<script src='./public/a.js'></script>
正确写法:<script src='./a.js'></script>
就是当文件没有在依赖图里面,想要加载的话,就从public
文件夹里面去找
hot
开启热更新
module.exports ={
devServer: {
hot: true
}
}
proxy
代理服务器配置
module.exports = {
devServer: {
proxy: {
target: 'http://localhost:9999' // 代理的目标
pathRewrite: { // 对路径的重写
"^/api": ''
},
secure: false, // 针对代理,针对 https 协议是需要证书,设置false,就不需要证书了
changeOrigin: true // 是否改变源(用于服务端对源的验证)
}
}
}
port
端口设置(1024 以上的端口)
open
是否打开浏览器。默认为false,true,就会打开浏览器
compress
是否为静态文件开启 gzip。 默认为 false,true 就会压缩
historyApiFallback
单页面应用(SPA),当进行路由跳转时,手动刷新页面会找不到内容,从而404。
地址
/
匹配的是 index.html如果地址改变,
/xxxx
,刷新之后就找不到对应的 html 文件,就会 404
把 historyApiFallback 设置为 true
, 就能解决这个问题。如果地址为 http://localhost:3000/xxx
, 刷新之后,还是直接会去读取 http://localhost:3000
, 从而得到静态资源。
结语
webpack 的配置很多,上面只是列举了在学习过程中的一些基础且常见的配置,后续用到继续学习。
转载自:https://juejin.cn/post/7231594699093344317