tomoya06 / web-developer-guidance

Actually it's just a notebook for keeping down some working experience.
4 stars 0 forks source link

Webpack - Basic #30

Open tomoya06 opened 3 years ago

tomoya06 commented 3 years ago

Webpack

概述

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图会映射项目所需的每个模块,并生成一个或多个 bundle。

核心概念

webpack 的核心有四个

入口(entry)

入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

输出(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。

loader

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。

在更高层面,在 webpack 的配置中,loader 有两个属性:

插件(plugin)

loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

loader vs plugin:loader,是一个转换器,将A文件进行编译成B文件,比如:将A.less转换为A.css,单纯的文件转换过程;plugin,是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务

构建流程

webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

  1. 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
  2. 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
  3. 确定入口:根据配置中的 entry 找出所有的入口文件
  4. 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
  5. 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
  7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

在以上过程中,webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 webpack 提供的 API 改变 webpack 的运行结果。

tomoya06 commented 3 years ago

热更新原理

热更新过程概述:

  1. 启动devServer时,服务端和浏览器会建立一个WebSocket长连接,以便通知更新
  2. webpack监听文件变化(watch),有变化时重新编译打包;依赖于memory-fs库,打包后的代码存放在内存中,而不是output.path配置的目录
  3. 通过websocket,打包完成后devServer将新模块的hash值发送到浏览器,之后发送type=ok的消息触发执行reload操作
  4. reload操作具体有两种实现,一是模块热更新,二是直接刷新页面。HMR runtime会检查更新,先是发送AJAX向服务器请求更新文件列表、然后通过jsonp请求最新的模块代码,HMR runtime会根据新的模块代码来选择具体如何更新。

完整过程参考博客

更新过程的网络请求

首次加载时:

image

热更新时:

image

image

image

tomoya06 commented 3 years ago

tree-shaking

概述:tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。webpack的tree-shaking依赖于 ES2015 模块语法的 静态结构 特性,例如 import 和 export。

原理

Dead Code 一般具有以下几个特征

ES6 module特点:

ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这就是tree-shaking的基础。

进一步分析

side effect

"side effect(副作用)" 的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。

具有副作用的文件不应该做TS。默认webpack认为所有的代码都有副作用,这可以保护你免于删除必要的文件,但这意味着 Webpack 的默认行为实际上是不进行 tree-shaking。不过可以通过配置告诉webpack当前项目是没有副作用的,可以进行TS。

配置方法

代码参考掘金博客

package.json 有一个特殊的属性 sideEffects。它有三个可能的值:

另外,也可以在loader的配置中设置sideEffects属性,告诉webpack这类文件都会有副作用。

写法

在编写支持 tree-shaking 的代码时,导入方式非常重要。你应该避免将整个库导入到单个 JavaScript 对象中。当你这样做时,你是在告诉 Webpack 你需要整个库, Webpack 就不会摇它。

以流行的库 Lodash 为例。一次导入整个库是一个很大的错误,但是导入单个的模块要好得多。当然,Lodash 还需要其他的步骤来做 tree-shaking,但这是个很好的起点:

// 全部导入 (不支持 tree-shaking)
import _ from 'lodash';
// 具名导入(支持 tree-shaking)
import { debounce } from 'lodash';
// 直接导入具体的模块 (支持 tree-shaking)
import debounce from 'lodash/lib/debounce';