前端工程化 – webpack
webpack
是一个用于JavaScript应用程序的静态打包工具当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图,将项目所需的每一个模块组合成一个或多个bundles(均为静态资源)用于展示内容。
静态模块:指开发阶段,可以被webpack
直接引用的资源(可以直接被获取打包进bundle.js的资源)
当 webpack
处理应用程序时,它会在内部构建一个依赖图,此依赖图对应映射到项目所需的每个模块(不再局限 js文件
),并生成一个或多个 bundle
核心概念
- 入口(entry)
入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。默认值是
./src/index.js
,在webpack.config.js中配置entry
属性来指定入口起点。module.exports = { entry: './path/to/my/entry/file.js', };
- 输出(output)
output属性告诉webpack在哪里输出所创建的bundle,以及如何命名文件。主要输出文件的默认值是
./dist/main.js
,其他生成文件默认放置在./dist
文件夹中。const path = require('path'); module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js', }, };
- loader
webpack只能理解javascript和JSON文件,而
loader
让webpack能处理其他类型文件,并转换为有效模块,供程序使用以及被添加到依赖图中。loader的两个属性:
test
:识别哪些文件会被转换use
:定义在进行转换时,应该使用哪个loaderconst path = require('path'); module.exports = { output: { filename: 'my-first-webpack.bundle.js', }, module: { rules: [{ test: /\.txt$/, use: 'raw-loader' }], }, };
- 插件(plugin)
用于执行范围更广的任务。包括:打包优化、资源管理、注入环境变量
想要使用一个插件,你只需要
require()
它,然后把它添加到plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用new
操作符来创建一个插件实例const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); // 用于访问内置插件 module.exports = { module: { rules: [{ test: /\.txt$/, use: 'raw-loader' }], }, plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })], };
- 模式(mode)
通过选择
development
,production
或none
之中的一个,来设置mode
参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为production
。选项说明
选项 描述 development 开发模式,打包更加快速,省去代码优化步骤 production 生产模式,打包较慢,会开启tree-shaking和压缩代码 none 不适用任何默认优化选项 module.exports = { mode: 'production', };
- 浏览器兼容性(browser compatibility)
Webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的
import()
和require.ensure()
需要Promise
。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill。
- 环境(envrironment)
Webpack 5 运行于 Node.js v10.13.0+ 的版本
webpack的能力
- 代码编译能力,提高效率,解决浏览器兼容问题,将es6+的语法、typescript的脚本编译为es5低版本代码
- 模块整合能力,提高性能,可维护性,解决浏览器频繁请求文件的问题(将多个模块文件打包成一个 bundle)
- 万物皆可模块化,项目维护性增强,支持不同种类的前端模块类型,统一的模块化方案,所有资源文件的加载都可以通过代码控制
webpack的构建流程
初始化流程
:从配置文件和 Shell 语句中读取与合并参数,并初始化需要使用的插件和配置插件等执行环境所需要的参数编译构建流程
:从Entry
出发,针对每个Module
串行调用对应的Loader
去翻译文件内容,再找到该Module
依赖的Module
,递归地进行编译处理输出流程
:对编译后的Module
组合成Chunk
,把Chunk
转换成文件,输出到文件系统
webpack基础(配置)
简单配置:
- Webpack常规配置项有哪些?
- 常用Loader有哪些?如何配置?
- 常用插件(Plugin)有哪些?如何配置?
- Babel如何配置?Babel插件如何使用?
安装依赖
npm install webpack webpack-cli -D
运行npx webpack
,启动打包
配置文件 webpack.config.js
const path = require('path')
module.exports = {
mode: 'development', // 模式
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist') // 输出文件目录
}
}
Loader
webpack默认支持处理JS与JSON文件,其他类型都处理不了,因此必须借助Loader来对不同类型的文件进行处理。
例如:安装css-loader
来处理CSS
npm install css-loader -D
配置资源加载模块:(通过选项test和use来匹配)
const path = require('path')
module.exports = {
mode: 'development', // 模式
entry: './src/main.css', // 打包入口地址
output: {
filename: 'bundle.css', // 输出文件名
path: path.join(__dirname, 'dist') // 输出文件目录
},
module: {
rules: [ // 转换规则
{
test: /\.css$/, //匹配所有的 css 文件
use: 'css-loader' // use: 对应的 Loader 名称
}
]
}
}
插件
插件贯穿Webpack打包的生命周期,执行不同的任务
如果我想打包后的资源文件,例如:js 或者 css 文件可以自动引入到 Html 中,就需要使用插件 html-webpack-plugin来帮助你完成这个操作
(自动清空打包目录插件:clean-webpack-plugin)
安装插件:
npm install html-webpack-plugin -D
配置插件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ITEM</title>
<script defer src="bundle.js"></script></head>
<body>
</body>
</html>
区分环境
根据本地开发和部署线上,有不同的需求
本地安装croos-env
npm install cross-env -D
在webpack配置文件中获取环境变量:
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
console.log('process.env.NODE_ENV=', process.env.NODE_ENV) // 打印环境变量
const config = {
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist') // 输出文件目录
},
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: 'css-loader' // use: 对应的 Loader 名称
}
]
},
plugins:[ // 配置插件
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
}
module.exports = (env, argv) => {
console.log('argv.mode=',argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config;
}
启动devServer
安装 webpack-dev-server
npm install webpack-dev-server@3.11.2 -D
配置本地服务:
// webpack.config.js
const config = {
// ...
devServer: {
contentBase: path.resolve(__dirname, 'public'), // 静态文件目录
compress: true, //是否启动压缩 gzip
port: 8080, // 端口号
// open:true // 是否自动打开浏览器
},
// ...
}
module.exports = (env, argv) => {
console.log('argv.mode=',argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config;
}
引入css
需要再安装一个 style-loader 来完成这个功能 npm install style-loader -D
配置loader:
const config = {
// ...
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: ['style-loader','css-loader']
}
]
},
// ...
}
引用样式文件:在入口文件 ./src/index.js
引入样式文件 ./src/main.css
CSS兼容性,引入postcss-loader,自动添加CSS3部分属性的浏览器前缀
引入less和sass
文件类型 | loader |
---|---|
less | less-loader |
sass | sass-loader node-sass 或 dart-sass |
JS兼容性(Babel)
对JS做兼容处理,让有些不支持新特性的浏览器也能正常运行,常见的方式是将ES6语法转化为ES5。
安装Babel依赖:$ npm install babel-loader @babel/core @babel/preset-env -D
babel-loader
使用 Babel 加载 ES2015+ 代码并将其转换为 ES5@babel/core
Babel 编译的核心包@babel/preset-env
Babel 编译的预设,可以理解为 Babel 插件的超集
配置Babel预设:
// webpack.config.js
// ...
const config = {
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist'), // 输出文件目录
},
module: {
rules: [
{
test: /\.js$/i,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
}
}
]
},
// ...
]
},
//...
}
// ...
SourceMap配置选择
SourceMap 是一种映射关系,当项目运行后,如果出现错误,我们可以利用 SourceMap 反向定位到源码位置
devtool配置
const config = {
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist'), // 输出文件目录
},
devtool: 'source-map',
module: {
// ...
}
// ...
执行打包后,dist 目录下会生成以 .map
结尾的 SourceMap 文件
模式总结:
devtool | build | rebuild | 显示代码 | SourceMap 文件 | 描述 |
---|---|---|---|---|---|
(none) | 很快 | 很快 | 无 | 无 | 无法定位错误 |
eval | 快 | 很快(cache) | 编译后 | 无 | 定位到文件 |
source-map | 很慢 | 很慢 | 源代码 | 有 | 定位到行列 |
eval-source-map | 很慢 | 一般(cache) | 编译后 | 有(dataUrl) | 定位到行列 |
eval-cheap-source-map | 一般 | 快(cache) | 编译后 | 有(dataUrl) | 定位到行 |
eval-cheap-module-source-map | 慢 | 快(cache) | 源代码 | 有(dataUrl) | 定位到行 |
inline-source-map | 很慢 | 很慢 | 源代码 | 有(dataUrl) | 定位到行列 |
hidden-source-map | 很慢 | 很慢 | 源代码 | 有 | 无法定位错误 |
nosource-source-map | 很慢 | 很慢 | 源代码 | 无 |