文章目录
前话
- 本质上,webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle
安装
npm init -y
npm install webpack webpack-cli --save-dev
入口(entry)
- 入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的
- 默认值是
./src/index.js
,但你可以通过在webpack configuration
中配置entry
属性,来指定一个(或多个)不同的入口起点
.gitignore
node_modules
src\title.js
module.exports = 'zsy'
src\index.js
- 这个文件依赖 title.txt,但是webpack内部会处理这些文件之间的依赖,构建一个依赖图
let title = require('./title.js');
console.log(title)
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
// 入口写法一:字符串
// entry: './src/index.js',
// 入口写法二:数组形式
// entry: ['./src/index1.js', './src/index2.js'], // 其实还是单入口,即只有一个入口,只不过有两个文件而已
// 入口写法三:对象形式
entry: {
main: './src/index.js',
// main: ['./src/index1.js', './src/index2.js'], // 效果与上面的数组形式是一样的
},
};
package.json
{
// ......
"scripts": {
"build": "webpack"
},
// ......
}
输出(output)
output
属性告诉 webpack 在哪里输出它所创建的bundle
,以及如何命名这些文件- 主要输出文件的默认值是
./dist/main.js
,其他生成文件默认放置在./dist
文件夹中。
webpack.config.js
// ......
module.exports = {
// ......
output: {
// 表示要 向dist文件下写入 main.js 文件,下面代码的配置都是默认值,不写也是会默认打包到 dist/main.js
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
}
};
多入口打包
- webpack.config.js
module.exports = {
// ......
entry: {
index1: './src/index1.js',
index2: './src/index2.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js' // 这个name是对应entry 对象中的key
}
};
entry和output的路径规则
- entry和output都可以写成绝对路径
- entry还可以写成相对路径,但是output则不能
- 因为我们执行命令时,肯定是在项目的目录下执行,所以入口可以写成相对的,但是打包后的文件可以放到硬盘的任意位置,所以output不能写成相对路径的
loader
webpack
只能理解JavaScript
和JSON
文件,也就是在js文件中,webpack只认识 require(‘xxx.js’) 或 require(‘xxx.js’) 这两种文件,比如不认识 xx.txt 文件- 使用loader后,会将不认识的文件转为js文件,如 txt 文件转为 js文件
loader
让webpack
能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中
webpack.config.js
npm i raw-loader -D
// ......
module.exports = {
// ......
module: {
rules: [
{
// 当遇到以 .txt 结尾的文件时,会交给 raw-loader 去处理
test: /\.txt$/,
use: [ 'raw-loader' ] // raw-loader 意思是 '原生loader'
}
]
}
};
-
src\name.txt
‘peiqi’ -
src\index.js
let name = require('./name.txt')
console.log('我的名字', name.default) // webpack打包后,外层会包一层,所以取 default 属性才是真的值
- dist\index.html
<body>
<!-- 引入打包后的文件 -->
<script src="./main.js"></script>
</body>
- 然后直接访问该 html 页面,查看控制台
补充面试题:执行 npm run build 干了什么
-
- 找当前项目的package.json 下面的scripts 下面的 build这个key,拿到其对应的值,即 webpack,这是shell脚本
-
- 然后执行该shell脚本,即webpack命令
- 2.1 第一步先看 node_modules.bin 下是否存在 webpack.cmd,如果存在则执行它
- 2.2 如果不存在,则会执行全局目录下的 webpack.cmd,(可以通过在cmd窗口中执行 npm root -g,会输出npm的全局目录,然后去该路劲看看或者在这儿:【C:\Users\a1371\AppData\Roaming\npm】),可以查看到是否有 webpack.cmd,没有的话可以通过 执行 npm i webpack-cli -g 进行安装
- 如果还是没有则就报错了
-
- 如果找到全局的webpack.cmd那么就执行,会读取当前目录下的 webpack.config.js 进行编译
chunk和assets和file的概念
- index.js(依赖title.js) 和 title.js 都叫做一个个的模块,多个模块打包合在一起叫做 代码块,也就是 chunk
- 代码块经过编译打包后变成 资源assets,资源是由key-value组成,key就是main.js,value就是里面的内容字符串
- 资源经过输出,最后会写入硬盘,就变成了file文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-STsWmf7W-1662776328652)(./images/2.png)]
插件(plugin)
- webpack打包会经过很多流程,在过程中我们可以使用 插件 做更多的事情
- loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量
- 在前面的代码中,在 dist\index.html中我们手动引入了打包的js文件,我们使用 HtmlWebpackPlugin 插件,这样我们就不需要手动写了
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack5</title>
</head>
<body>
</body>
</html>
webpack.config.js
npm i html-webpack-plugin -D
// ......
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ......
plugins: [
// HtmlWebpackPlugin 插件会向输出目录dist下写入一个 【index.html】 文件,并且将打包后的文件,即main.js 自动引入到index.html去
new HtmlWebpackPlugin({
template: './src/index.html', // 指定使用的模板页面
filename: 'index.html' // 打包后的模板页面名称
})
]
};
- 此时我们直接把旧的 dist文件删掉,重新打包试试
模式(mode)
- 日常的前端开发工作中,一般都会有两套构建环境
- 一套开发时使用,构建结果用于本地开发调试,不进行代码压缩,打印 debug 信息,包含 sourcemap 文件
- 一套构建后的结果是直接应用于线上的,即代码都是压缩后,运行时不打印 debug 信息,静态文件不包括 sourcemap
- webpack 4.x 版本引入了 mode 的概念
- 当你指定使用 production mode 时,默认会启用各种性能优化的功能,包括构建结果优化以及 webpack 运行性能优化
- 而如果是 development mode 的话,则会开启 debug 工具,运行时打印详细的错误信息,以及更加快速的增量编译构建
选项 | 描述 |
---|---|
development | 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin |
production | 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin |
环境差异
-
开发环境
- 需要生成 sourcemap 文件
- 需要打印 debug 信息
- 需要 live reload 或者 hot reload 的功能
-
生产环境
- 可能需要分离 CSS 成单独的文件,以便多个页面共享同一个 CSS 文件
- 需要压缩 HTML/CSS/JS 代码
- 需要压缩图片
-
其默认值为 production
区分环境
--mode
用来设置模块内的process.env.NODE_ENV
--env
用来设置webpack
配置文件的函数参数cross-env
用来设置node
环境的process.env.NODE_ENV
DefinePlugin
用来设置模块内的全局变量
命令行配置1
- webpack(构建)的mode默认为
production
webpack serve
(开发服务器)的mode默认为development
- 可以在模块内通过
process.env.NODE_ENV
获取当前的环境变量,无法在webpack配置文件中获取此变量
比如在package.json:
"scripts": {
// 当不在webpack.config.js设置mode,那么当执行 npm run build,则默认都是生产环境(production)
"build": "webpack",
// webpack4 是要执行 webpack-dev-server
// webpack5直接执行 webpack serve 就ok了
// 不过还是需要安装 webpack-dev-server 的,即:npm i webpack-dev-server -D
// 当不在webpack.config.js设置mode,那么当执行 npm run start,则默认都是生产环境(production)
"start": "webpack serve"
},
src\index.js
// 查看当前是开发环境还是生产环境
console.log(process.env.NODE_ENV);// development | production
webpack.config.js
- 在该配置文件中是拿不到 process.env.NODE_ENV 的
console.log('NODE_ENV',process.env.NODE_ENV);// undefined
命令行配置2
- 在package.json 中指定生产环境或开发环境
"scripts": {
"build": "webpack --mode=production",
"start": "webpack --mode=development serve"
},
命令行配置
- 无法在模块内通过 process.env.NODE_ENV 访问
- 可以通过webpack 配置文件中中通过函数获取当前环境变量
"scripts": {
// 通过 --env=production 这样设置,那么在 webpack.cconfig.js 中不能 打印出 process.env.NODE_ENV,在 src/index.js 等模块中也是不能 打印出来的
"build": "webpack --env=production",
"start": "webpack serve --env=development",
}
index.js
// 通过 --env=production 这样设置,在模块内拿不到
console.log(process.env.NODE_ENV);// undefined
webpack.config.js
// 通过 --env=production 这样设置,在 webpack.config.js 也拿不到
console.log('NODE_ENV',process.env.NODE_ENV);// undefined
// 但是如果配置文件导出一个函数,那么 webpack.config.js 就可以拿到了
module.exports = (env,argv) => {
console.log('env',env);// development | production
return {
// 写入口 出口等配置项
// ......
}
};
mode配置
- 和命令行配置2一样
module.exports = {
/**
"scripts": {
"build": "webpack --mode=production",
"start": "webpack --mode=development serve"
},
*/
// 如下配置的优先级低于上面的配置方式:
mode: 'development'
}
DefinePlugin
- DefinePlugin 这是定义全局变量的插件
- 能设置全局变量(不是window),所有模块都能读取到该变量的值
- 可以在任意模块内通过 process.env.NODE_ENV 获取当前的环境变量
- 但无法在node环境(webpack 配置文件中)下获取当前的环境变量
// ......
const webpack = require('webpack')
module.exports = ()=>{
console.log('env', env);
let isDevelopment = env.development
let isProduction = env.production
return {
plugins:[
// ......
new webpack.DefinePlugin({
// 这里必须使用 JSON.stringify 包一层
// 否则打包后是变量,而不是字符串
'process2.env2.NODE_ENV2':JSON.stringify(isDevelopment? 'development': 'production'),
})
]
}
}
npm run start 和 npm run test 的run 都可以直接省略
index.js
console.log('process.env.NODE_ENV2',process2.env2.NODE_ENV2);// production
webpack.config.js
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// undefined
console.log('NODE_ENV',NODE_ENV);// error !!!
cross-env
- 安装:
npm i cross-env -D
- 只能设置node环境下的变量NODE_ENV
package.json
"scripts": {
"build": "cross-env NODE_ENV=production webpack", // 执行这个命令, NODE_ENV对应得值 production就会传给 process.env.NODE_ENV,那么 webpack.config.js就可以拿到,不会直接传给 js模块
"start": "cross-env NODE_ENV=development webpack serve"
}
webpack.config.js
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// 执行npm run build,输出:process.env.NODE_ENV production
module.exports = (env)=>{
return {
mode: process.env.NODE_ENV, // 也给到这儿
// ......
plugins:: [
new webpack.DefinePlugin({
// 下面的 NODE_ENV 只是一个名字,可以随便取,然后在js文件中直接取该名字就可以得到 process.env.NODE_ENV
'peiqi': JSON.stringify(process.env.NODE_ENV) // 也给到这儿
})
]
}
}
src\index.js:
console.log('mode设置的是:', process.env.NODE_ENV) // 输出:production
console.log('DefinePlugin设置的:', peiqi) // 输出:production
dotenv
安装:
# dotenv-expand: 增强版的 .env,在.env文件可以使用模板字符串的形式
npm i dotenv-expand -D
# 普通版的 .env,在.env文件只能设置 key-value
npm i dotenv -D
- 使用dotenv,只需要将程序的环境变量配置写在
.env
文件中
项目根目录 \ .env
MONGODB_HOST=localhost
MONGODB_PORT=27017
MONGODB_DB=test
MONGODB_URI=mongodb://${MONGODB_HOST}:${MONGODB_PORT}/${MONGODB_DB}
项目根目录 \ testEnv.js :
const dotenv = require('dotenv')
const dotenvExpand = require('dotenv-expand')
const dotenvFile = '.env';
let myEnv = dotenv.config({
path: dotenvFile,
})
dotenvExpand.expand(myEnv)
console.log(process.env.MONGODB_HOST); // 输出:localhost
console.log(process.env.MONGODB_PORT); // 输出:27017
console.log(process.env.MONGODB_DB); // 输出:test
console.log(process.env.MONGODB_URI); // 输出:mongodb://localhost:27017/test