webpack5的知识点整理
一、 关于打包的相关知识
1.1、首先我们为什么需要打包工具?
开发时,我们会使用框架(React、Vue),ES6 模块化语法,Less/Sass 等 css 预处理器等语法进行开发。
这样的代码要想在浏览器运行必须经过编译成浏览器能识别的才能运行(浏览器只认识html,css,js)。
所以我们需要打包工具帮我们做完这些事。
除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等
1.2、常见的打包工具都有哪些?
Grunt:最老牌的打包工具,它运用配置的思想来写打包脚本,一切皆配置、
Gulp:用代码方式来写打包脚本,并且代码采用流式的写法、
Parcel:一款速度极快、零配置的web应用程序打包、
Webpack:是模块化管理工具和打包工具,他的宗旨是一切静态资源皆可打包、
Rollup:下一代 ES6 模块化工具,最大的亮点是利用 ES6 模块设计,利用 tree-shaking生成更简洁、更简单的代码、
Vite:Vite 是 vue 的作者尤雨溪在开发 vue3.0 的时候开发的一个 基于原生 ES-Module 的前端构建工具,是基于浏览器原生 ES module,利用浏览器解析 imports,服务器端按需编译返回,把压力给到了服务器和浏览器、
Turbopack:号称比vite还要快10倍,由 Webpack 的创建者 Tobias Koppers 和 Next.js 团队使用 Rust 编写,是针对 JavaScript 和 TypeScript 优化的增量打包工具
关于vite和webpack的比较分析:blog.csdn.net/gao_xu_520/…
二、首先webpack的概念官网如是所说:
本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
-
在模块化编程中,开发者将程序分解为功能离散的 chunk,并称之为 模块。
每个模块都拥有小于完整程序的体积,使得验证、调试及测试变得轻而易举。 精心编写的 模块 提供了可靠的抽象和封装界限,使得应用程序中每个模块都具备了条理清晰的设计和明确的目的
-
何为 webpack 模块
与 Node.js 模块相比,webpack 模块 能以各种方式表达它们的依赖关系。下面是一些示例:
- ES2015
import语句 - CommonJS
require()语句 - AMD
define和require语句 - css/sass/less 文件中的
@import语句。 - stylesheet
url(...)或者 HTML<img src=...>文件中的图片链接。
支持的模块类型
Webpack 天生支持如下模块类型:
- ECMAScript 模块
- CommonJS 模块
- AMD 模块
- Assets
- WebAssembly 模块
- ES2015
通过 loader 可以使 webpack 支持多种语言和预处理器语法编写的模块。loader 向 webpack 描述了如何处理非原生模块,并将相关依赖引入到你的 bundles中。 webpack 社区已经为各种流行的语言和预处理器创建了 loader
- 静态:项目中的静态资源如css、图片等文件
- 打包:打包:主要帮你压缩代码,然后还会加密、混淆代码或者过个文件压缩成一个文件
二、本质上webpack可识别的js或者json类的文件,所以需要扩展一些功能帮助webpack对其他类型的文件进行处理
- loader:帮助 webpack 将不同类型的文件转换为 webpack 可识别的模块。下文在详细讲解loader
- plugin:本质上是处理一些loader不能完成的功能和任务。通过插件我们可以扩展 webpack,加入自定义的构建行为,使 webpack 可以执行更广泛的任务,拥有更强的构建能力 plugin的工作原理:webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。 插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack 通过 Tapable 来组织这条复杂的生产线。 webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。 webpack 的事件流机制保证了插件的有序性,使得整个系统扩展性很好。 ——「深入浅出 Webpack」 类似于vue组件生命周期的概念,站在代码逻辑的角度就是:webpack 在编译代码过程中,会触发一系列
Tapable钩子事件,插件所做的,就是找到相应的钩子,往上面挂上自己的任务,也就是注册事件,这样,当 webpack 构建的时候,插件注册的事件就会随着钩子的触发而执行了。
三、基础的使用方法
扩展知识:npm run xxx 之后发生了什么
基础的打包工具:npx webpack --config webpack.dev.js
在package中的打包命令是:dev:webpack --config webpack.dev.js
注意:在script中不再需要添加npx。它会自动在node_modules/.bin中去找命令
npm run xxx 发生了什么?
第一步
第二步:在node_modules/.bin中找到文件
第三步:执行脚本:#!/bin/sh:表示这是一个脚本 相当于执行了,./node_modules/.bin/vue-cli-serve serve(最后的serve作为参数传入)
.bin目录下的文件表示软连接, 那这个bin文件是哪里来的?
在npm i 的时候在pack-lock.json 中npm i 的时候读到这个的时候就npm 将bin/vue-cli-serve.js作为了bin声明了,就是类似 npm i 的时候npm就把这种软连接给配置好了,其实就是一种映射,根据命令一步步找到映射文件,然后在找到相应的js文件进行脚本的执行。
总结:1、运行npm run xxx 的时候 npm 会在node_modules/.bin文件中找执行的脚本,找到则运行
2、没有找到则从全局的node_modules/.bin中查找,npm i -g就是安装到全局的目录中的
3、如果全局中还没有找到,就从path变量中查找有没有其他同名的可执行的程序。
基本的使用方法
打开终端,来到项目根目录。运行以下指令:
- 初始化
package.json
npm init -y
此时会生成一个基础的 package.json 文件。
需要注意的是 package.json 中 name 字段不能叫做 webpack, 否则下一步会报错
- 下载依赖
npm i webpack webpack-cli -D
- 新建文件count.js和sum.js和main.js
- 开发模式
npx webpack ./src/main.js --mode=development
- 生产模式
npx webpack ./src/main.js --mode=production
默认 Webpack 会将文件打包输出到 dist 目录下,我们查看 dist 目录下文件情况就好了
Webpack 本身功能比较少,只能处理 js 资源,一旦遇到 css 等其他资源就会报错。
所以我们学习 Webpack,就是主要学习如何处理其他资源。
万恶的eval():blog.csdn.net/qq_50892584…
四、基本配置
五大核心功能:
- entry(入口)
指示 Webpack 从哪个文件开始打包
- output(输出)
指示 Webpack 打包完的文件输出到哪里去,如何命名等
- loader(加载器)
webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析
- plugins(插件)
扩展 Webpack 的功能
- mode(模式)
主要由两种模式:
- 开发模式:development
- 生产模式:production
以上是生产环境基础配置和打包dist/main.js文件
开发和生产模式
开发模式:顾名思义就是开发的时候的代码模式,
1、编译代码(使浏览器能够识别),开发时我们有的样式资源、字体图标、图片资源,html文件资源等因为webpack默认是不识别这些资源的(只识别js和json文件)
2、代码质量检查、树立代码规范,提前检查代码的一些规范,让代码更加健壮,提前检查代规范和统一团队编码风格。让代码更加优雅和美观。
生产模式
1、开发完成的代码打包部署上线
2、优化代码,让代码性能更加好,优化代码的运行速度,和优化代码的打包速度。
五、我们要学习的webpack基础常见配置下篇文章会将
-
处理样式资源,css资源、less资源、sass(scss)资源、styl资源
-
处理图片资源:过去在 Webpack4 时,我们处理图片资源通过
file-loader和url-loader进行处理现在 Webpack5 已经将两个 Loader 功能内置到 Webpack 里了,我们只需要简单配置即可处理图片资源
-
修改输出资源的名称和路径
-
自动清空上次打包资源
-
处理字体图标资源
-
处理其他资源(例如音视频)
-
处理js资源:因为webpack只能编译 js 中 ES 模块化语法,不能编译其他语法,导致 js 不能在 IE 等浏览器运行,所以我们希望做一些兼容性处理。
其次开发中,团队对代码格式是有严格要求的,我们不能由肉眼去检测代码格式,需要使用专业的工具来检测。
- 针对 js 兼容性处理,我们使用 Babel 来完成
- 针对代码格式,我们使用 Eslint 来完成
我们先完成 Eslint,检测代码格式无误后,在由 Babel 做代码兼容性处理
-
处理html资源
-
开发服务器和自动化
-
css在处理,提取成单独文件和兼容性处理以及代码压缩
-
html代码压缩
六、webpack高级优化配置下下篇文章会将
-
提升开发体验:sourcemap(源代码映射)是一个用来生成源代码与构建后代码一一映射的文件的方案
-
提升打包构建速度
- HotModuleReplacement(HMR/热模块替换):在程序运行中,替换、添加或删除模块,而无需重新加载整个页面。
- oneOf:顾名思义就是只能匹配上一个 loader, 剩下的就不匹配了,打包时每个文件都会经过所有 loader 处理,虽然因为
test正则原因实际没有处理上,但是都要过一遍。比较慢 - Include/Exclude:开发时我们需要使用第三方的库或插件,所有文件都下载到 node_modules 中了。而这些文件是不需要编译可以直接使用的。所以我们在对 js 文件处理时,要排除 node_modules 下面的文件。
- Cache:对 Eslint 检查 和 Babel 编译结果进行缓存。
- Thead:多进程打包:开启电脑的多个进程同时干一件事,速度更快。需要注意:请仅在特别耗时的操作中使用,因为每个进程启动就有大约为 600ms 左右开销
-
减少代码体积
-
Tree Shaking:通常用于描述移除 JavaScript 中的没有使用上的代码
-
Babel:Babel 为编译的每个文件都插入了辅助代码,使代码体积过大!Babel 对一些公共方法使用了非常小的辅助代码,比如
_extend。默认情况下会被添加到每一个需要它的文件中。你可以将这些辅助代码作为一个独立模块,来避免重复引入 -
Image Minimizer:开发如果项目中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢。
我们可以对图片进行压缩,减少图片体积。
注意:如果项目中图片都是在线链接,那么就不需要了。本地项目静态图片才需要进行压缩
-
-
优化代码性能
-
Code Split:代码分割(Code Split)主要做了两件事:
- 分割文件:将打包生成的文件进行分割,生成多个 js 文件。
- 按需加载:需要哪个文件就加载哪个文件。
-
Preload / Prefetch:
Preload:告诉浏览器立即加载资源。Prefetch`:告诉浏览器在空闲时才开始加载资源。
-
它们共同点:
都只会加载资源,并不执行。
都有缓存。
-
NetWork Cache:通过更新文件名字来进行对文件的缓存,根据更新前后的文件名不一样,这样就可以做缓存了。
- chunkhash:根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值,我们js和css是同一个引入,会共享一个哈希值。
- contenthash:根据文件内容生成hash值,只有文件内容变化了,hash值才会改变,所有文件的hash值是共享的。
-
core-js:
core-js是专门用来做 ES6 以及以上 API 的polyfill。polyfill翻译过来叫做垫片/补丁。就是用社区上提供的一段代码,让我们在不兼容某些新特性的浏览器上,使用该新特性 -
PWA:渐进式网络应用程序(progressive web application - PWA):是一种可以提供类似于 native app(原生应用程序) 体验的 Web App 的技术。
其中最重要的是,在 离线(offline) 时应用程序能够继续运行功能。
内部通过 Service Workers 技术实现的。
七、项目react脚手架和vue脚手架
- 如何搭建
React-Cli和Vue-Cli。 - 如何对脚手架进行优化。
- 未来随着项目越来越大,还可以在优化的方案。
八、loader原理分析以及开发简单的demo
loader:是什么,怎么使用,如何自定义
1、是什么:帮助webpack将其他模块转换为webpack可以识别模块
2、分类
- pre: 前置 loader
- normal: 普通 loader(默认情况下是normal)
- inline: 内联 loader
- post: 后置 loader
3、执行顺序
- 4 类 loader 的执行优级为:
pre > normal > inline > post。 - 相同优先级的 loader 执行顺序为:从右到左,从下到上。
4、开发一个简单的loader:本质上就是一个函数
5、loader的分类
- 同步loader
- 异步loader
- raw loader
- pitching loader
6、loader 的API
| 方法名 | 含义 | 用法 |
|---|---|---|
| this.async | 异步回调 loader。返回 this.callback | const callback = this.async() |
| this.callback | 可以同步或者异步调用的并返回多个结果的函数 | this.callback(err, content, sourceMap?, meta?) |
| this.getOptions(schema) | 获取 loader 的 options | this.getOptions(schema) |
| this.emitFile | 产生一个文件 | this.emitFile(name, content, sourceMap) |
| this.utils.contextify | 返回一个相对路径 | this.utils.contextify(context, request) |
| this.utils.absolutify | 返回一个绝对路径 | this.utils.absolutify(context, request) |
例如:手写 clean-log-loader
作用:用来清理 js 代码中的console.log
// loaders/clean-log-loader.js
module.exports = function cleanLogLoader(content) {
// 将console.log替换为空
return content.replace(/console.log(.*);?/g, "");
};
之后文章会在写
-
手写 banner-loader 作用:给 js 代码添加文本注释
-
手写 babel-loader 作用:编译js代码,将ES6+语法编译成ES5-语法
- 手写file-loader 作用:将文件原封不动的输出出去
- 手写style-loader 作用:动态创建style标签,插入js中的样式代码,使样式生效。
九、plugin原理分析以及开发简单的demo之后文章会将
1、作用:扩展loader不能做到的功能,可以扩展webpack的功能,加入自定义的构建行为。
2、工作原理
webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。 插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack 通过 Tapable 来组织这条复杂的生产线。 webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。 webpack 的事件流机制保证了插件的有序性,使得整个系统扩展性很好。
——「深入浅出 Webpack」
站在代码逻辑的角度就是:webpack 在编译代码过程中,会触发一系列 Tapable 钩子事件,插件所做的,就是找到相应的钩子,往上面挂上自己的任务,也就是注册事件,这样,当 webpack 构建的时候,插件注册的事件就会随着钩子的触发而执行 了。
3、什么是钩子
钩子的本质就是:事件。为了方便我们直接介入和控制编译过程,webpack 把编译过程中触发的各类关键事件封装成事件接口暴露了出来。这些接口被很形象地称做:hooks(钩子)。开发插件,离不开这些钩子
4、plugin本质
1)Plugin 的本质是一个 node 模块,这个模块导出一个 JavaScript 类
2)它的原型上需要定义一个apply 的方法
3)通过compiler获取 webpack 内部的钩子,获取 webpack 打包过程中的各个阶段
钩子分为同步和异步的钩子,异步钩子必须执行对应的回调
4)通过compilation操作 webpack 内部实例特定数据
5)功能完成后,执行 webpack 提供的 cb 回调
十、mini版webpack
1、 npm init -y 创建package.json文件
2、创建创建minipack.js
3、创建测试文件夹和文件
4、下载依赖npm i babylon babel-traverse babel-core babel-preset-env -D
5、启动项目:node minipack.js
babel-preset-env:这种现象是由于在 .babelrc 文件中设置了env 选项,需要插件 babel-preset-env 处理
miniWebpack的打包流程
总结一下webpack完整的打包流程
1)webpack从项目的entry入口文件开始递归分析,调用所有配置的 loader对模块进行编译
因为webpack默认只能识别js代码,所以如css文件、.vue结尾的文件,必须要通过对应的loader解析成js代码后,webpack才能识别
2)利用babel(babylon)将js代码转化为ast抽象语法树,然后通过babel-traverse对ast进行遍历
3)遍历的目的找到文件的import引用节点
因为现在我们引入文件都是通过import的方式引入,所以找到了import节点,就找到了文件的依赖关系
4)同时每个模块生成一个唯一的id,并将解析过的模块缓存起来,如果其他地方也引入该模块,就无需重新解析,最后根据依赖关系生成依赖图谱
5)递归遍历所有依赖图谱的模块,组装成一个个包含多个模块的 Chunk(块)
6)最后将生成的文件输出到 output 的目录中
热更新原理
什么是webpack热更新?
开发过程中,代码发生变动后,webpack会重新编译,编译后浏览器替换修改的模块,局部更新,无需刷新整个页面
好处:节省开发时间、提升开发体验
热更新原理
主要是通过websocket实现,建立本地服务和浏览器的双向通信。当代码变化,重新编译后,通知浏览器请求更新的模块,替换原有的模块
1) 通过webpack-dev-server开启server服务,本地server启动之后,再去启动websocket服务,建立本地服务和浏览器的双向通信
2) webpack每次编译后,会生成一个Hash值,Hash代表每一次编译的标识。本次输出的Hash值会编译新生成的文件标识,被作为下次热更新的标识
3)webpack监听文件变化(主要是通过文件的生成时间判断是否有变化),当文件变化后,重新编译
4)编译结束后,通知浏览器请求变化的资源,同时将新生成的hash值传给浏览器,用于下次热更新使用
5)浏览器拿到更新后的模块后,用新模块替换掉旧的模块,从而实现了局部刷新