什么是WebPack
前端项目由大量的资源构成,在前端发展初期,大量的资源依赖手工管理,存在大量缺点:
- 操作过程繁琐,对开发效率影响较大,限制了页面规模
- 当代码文件之间存在依赖时,需要严格按照依赖顺序书写
- 开发与生产环境一致,难以引入TS或JS的新特性
- 难以接入Less、Sass等工具
- JS、图片、CSS资源管理模型不一致
类似WebPack等工具的出现,使得前端逐渐工程化。
WebPack本质上是一种前端资源编译、打包工具:
WebPack应用示例
- 安装webpack
- 准备好两个需要打包的js文件
- bar.js
export default 'bar'; - index.js
import bar from './bar' console.log(`hello ${bar}`)
- bar.js
- 编辑配置文件
const path = require("path"); module.exports = { entry: './src/index', // 定义项目入口 mode: "development", devtool: false, output: { // 定义项目打包完成后的存放路径 filename: "[name].js", path: path.join(__dirname, "./dist"), } } - 执行编译命令:
npx webpack,可以将两个js文件打包为一个js文件
WebPack核心流程
- 入口处理:从
entry文件开始,启动编译流程 - 依赖解析:从
entry文件开始,根据require和import等语句找到依赖资源 - 资源解析:根据
module配置,调用资源转移器,将png、css等非标准js资源转译为js内容 - 递归调用第2、3步,直到所有资源处理完毕
- 资源合并打包:将转译后的资源内容合并打包为可直接在浏览器运行的js文件
WebPack主要工作
模块化 + 一致性:
- 多个资源文件合并为一个,减少http请求数
- 支持模块化开发
- 支持高级JS特性
- 只支持TypeScript、CofferScript方言
- 同一图片、CSS、字体等其他资源的处理模型
WebPack使用
WebPack的使用核心是书写配置文件,配置文件中的配置项可大致分为两类:
- 流程类:作用于流程中某个或若干个环节,直接影响打包效果的配置项
- 常见流程类配置按流程步骤划分:
流程阶段|常用配置
--|--
入口处理|
entry,定义项目入口;context,定义WebPack寻找资源的路径 依赖解析|resolve、externals资源解析|module资源合并打包|optimization、mode、target
- 常见流程类配置按流程步骤划分:
流程阶段|常用配置
--|--
入口处理|
- 工具类:主流程之外,提供更多工程化能力的配置项
-
常见的工具类配置按作用类别划分:
类别 常用配置 开发效率类 watch、devtool、devServer性能优化类 cache、performance日志类 stats、infrastructureLogging其他 amd、bail
-
其中最常用的配置有:
entry/outputmodel/pluginsmodewatch/devServer/devtool
WebPack使用实例
使用WebPack至少需要有entry及output。
const path = require("path");
module.exports = {
entry: './src/index', // 定义项目入口
output: { // 定义项目打包完成后的存放路径
filename: "[name].js",
path: path.join(__dirname, "./dist"),
}
}
非js文件处理
当需要处理非js文件,如css文件时的处理步骤:
- 安装Loader:
npm add -D css-loader style-loader - 准备好需要打包的js文件和css文件
- 在webpack配置中添加
module处理css文件module: { rules: [{ test: /\.css$/, // 过滤文件:用正则表达式得到以css结尾的文件 use: ['style-loader', 'css-loader'] // 指定处理所用的loader }] }
接入Babel
Babel本质上是一种代码转译工具,将Bable接入WebPack的方法:
- 安装依赖:
npm i -D @babel/core @babel/preset-env babel-loader - 准备使用ES6语法的js文件
- 在webpack配置中添加
module处理js文件module: { rules: [{ test: /\.js$/, // 过滤出js文件 use: [{ loader: 'babel-loader', // 使用babel-loader处理js文件 options: { // options会传入babel-loader中运行 presets: [ ['@babel/preset-env'] ] } }] }] }
生成HTML
在WebPack中,可以不写HTML文件,直接通过插件自动生成HTML:
- 安装依赖(插件):
npm i -D html-webpack-plugin - 准备需要插入HTML的js文件
- 在webpack配置中添加
plugins使用html-webpack-plugin插件const path = require('path') const HTMLWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/index', mode: 'development', devtool: false, output: { filename: '[name].js', path: path.join(__dirname, './dist') }, plugins: [new HTMLWebpackPlugin()] } - 使用
npx webpack命令后,会生成js和html文件两个产物
WebPack工具线
HMR
Hot Module Replacement(HMR,热模块替换),用户修改的代码可以立刻更新到浏览器当中,且浏览器不需要进行刷新操作。
开启HMR的方法:
- 安装依赖:
npm i -D webpack-dev-server - 在webpack配置中添加
devServer配置,设置hot: true;添加watch: true配置const path = require('path') const HTMLWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/index', mode: 'development', devtool: false, watch: true, devServer: { hot: true, open: true }, module: { rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'] }] }, output: { filename: '[name].js', path: path.join(__dirname, './dist') }, plugins: [new HTMLWebpackPlugin()] } - 使用
npx webpack serve打包命令进行打包(配置中设置watch: true时不用加serve关键字,添加serve关键字时不会生成打包文件)
Tree-Shaking
Tree-Shaking插件可以用于删除Dead Code,如:
- 代码未被用到,不可达到
- 代码的执行结果未被使用
- 代码只读不写等情况
开启Tree-Shaking的方法:
- 在webpack配置中设置
mode: production - 添加optimization配置:
const path = require('path') module.exports = { entry: './src/index', devtool: false, mode: 'production', optimization: { usedExports: true }, output: { filename: '[name].js', path: path.join(__dirname, './dist') } }
WebPack Loader
WebPack插件的功能主要是用于内容的转化,将非js资源转译为标准js。 使用loader的方法:
- 使用npm安装loader包
- 在webpack配置的
module中添加rules配置,包含test和use两部分
Loader的运行流程
以打包js文件和less文件为例,分析loader的运行流程:
打包js和less文件,使用了3个loader,其功能分别为:
- less-loader:实现less => css的转换
- css-loader:将CSS包装成类似module.exports = "${css}" 的内容,实现css => js的转换
- style-loader:将css模块包进require语句,并在运行时调用injectStyle等函数将内容注入到页面的style标签
Loader的运行特性
- 链式执行
- 支持异步执行
- 分normal和pitch两种模式
- Loader的调用顺序与声明顺序相反,在声明要使用的Loader时要注意顺序
自定义Loader
可以根据需要自定义loader,loader的输入为源代码或上一个loader输出的内容,输出为自定义处理后的结果,调用的最后一个loader的输出结果会直接插入到webpack打包产物中。
自定义loader的基础写法如下:
module.exports = function (source){
console.log(source);
return source;
}
WebPack 插件
插件功能可以极大地提高应用的可扩展性和成长性,插件的常规使用流程:
- 使用npm安装需要的插件
- 在webpack配置文件中import要用的插件
- 在配置中实例化插件类:
plugins: [new DashboardPlugin()];
理解插件
插件运行主要依赖于webpack中的钩子,钩子的核心信息:
- 触发时机:编译过程的特定节点,Webpack会以钩子形式通知插件此刻正在发生什么事情;
- 上下文(参数):通过tapable提供的回调机制,以参数方式传递上下文信息;
- 交互:在上下文参数对象中附带了很多存在side effect的交互接口,插件可以通过这些接口改变