webpack、vite
webpack、vite
type
status
date
slug
summary
tags
category
icon
password
Blocking
Blocked by
top
URL
Sub-item
Parent item

构建流程

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
  • 初始化参数:读取webpack的配置参数。
  • 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
  • 确定入口:根据配置中的 entry 找出所有的入口文件
  • 解析模块:从入口文件(entry)开始解析,并且找到其导入的依赖模块,递归遍历分析,形成依赖关系树;
  • 编译模块:对不同文件类型的依赖模块文件使用对应的Loader进行编译,最终转为Javascript文件;
  • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
  • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
简单说
  • 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
  • 编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
  • 输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中
 

Loader

编写思路

Loader 支持链式调用,所以开发上需要严格遵循“单一职责”,每个 Loader 只负责自己需要负责的事情。
  • Loader 运行在 Node.js 中,我们可以调用任意 Node.js 自带的 API 或者安装第三方模块进行调用
  • Webpack 传给 Loader 的原内容都是 UTF-8 格式编码的字符串,当某些场景下 Loader 处理二进制文件时,需要通过 exports.raw = true 告诉 Webpack 该 Loader 是否需要二进制数据
  • 尽可能的异步化 Loader,如果计算量很小,同步也可以
  • Loader 是无状态的,我们不应该在 Loader 中保留状态
  • 使用 loader-utils 和 schema-utils 为我们提供的实用工具
  • 加载本地 Loader 方法
    • Npm link
    • ResolveLoader

常用的Loader

  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试
  • babel-loader:把 ES6 转换成 ES5
  • ts-loader: 将 TypeScript 转换成 JavaScript
  • awesome-typescript-loader:将 TypeScript 转换成 JavaScript,性能优于 ts-loader
  • sass-loader:将SCSS/SASS代码转换成CSS
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
  • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS
  • postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀
  • eslint-loader:通过 ESLint 检查 JavaScript 代码
  • tslint-loader:通过 TSLint检查 TypeScript 代码
  • mocha-loader:加载 Mocha 测试用例的代码
  • vue-loader:加载 Vue.js 单文件组件
  • cache-loader: 可以在一些性能开销较大的 Loader 之前添加,目的是将结果缓存到磁盘里
 

Plugin

原理

webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的阶段执行自己的插件任务,从而实现自己想要的功能。Webpack 的 Tapable 事件流机制保证了插件的有序性,使得整个系统扩展性良好。

常用的Plugin

  • otModuleReplacementPlugin :模块热替换
  • define-plugin:定义环境变量 (Webpack4 之后指定 mode 会自动配置)
  • ignore-plugin:忽略部分文件
  • html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)
  • web-webpack-plugin:可方便地为单页应用输出 HTML,比 html-webpack-plugin 好用
  • terser-webpack-plugin: 支持压缩 ES6 (Webpack4)
  • webpack-parallel-uglify-plugin: 多进程执行代码压缩,提升构建速度
  • mini-css-extract-plugin: 分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin)
  • serviceworker-webpack-plugin:为网页应用增加离线缓存功能
  • clean-webpack-plugin: 目录清理
  • ModuleConcatenationPlugin: 开启 Scope Hoisting
  • speed-measure-webpack-plugin: 可以看到每个 Loader 和 Plugin 执行耗时 (整个打包耗时、每个 Plugin 和 Loader 耗时)
  • webpack-bundle-analyzer: 可视化 Webpack 输出文件的体积 (业务组件、依赖第三方模块)

Loader和Plugin的区别

Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。 因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。
Plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。
Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。

source map是什么?生产环境怎么用?

source map 是将编译、打包、压缩后的代码映射回源代码的过程。打包压缩后的代码不具备良好的可读性,想要调试源码就需要 soucre map。
map文件只要不打开开发者工具,浏览器是不会加载的。
线上环境一般有三种处理方案:
  • hidden-source-map:借助第三方错误监控平台 Sentry 使用
  • nosources-source-map:只会显示具体行数以及查看源代码的错误栈。安全性比 sourcemap 高
  • sourcemap:通过 nginx 设置将 .map 文件只对白名单开放(公司内网)

webpack 性能优化

  1. 升级 webpack 版本,3升4,实测是提升了几十秒的打包速度
  1. 多进程/多实例构建:thread-loader
  1. 压缩代码:通过对应的plugin来压缩css、js代码。
  1. 图片压缩:通过配置 image-webpack-loader。
  1. 缩小打包作用域
      • exclude/include (确定 loader 规则范围)
      • resolve.modules 指明第三方模块的绝对路径 (减少不必要的查找)
      • resolve.mainFields 只采用 main 字段作为入口文件描述字段 (减少搜索步骤,需要考虑到所有运行时依赖的第三方模块的入口文件描述字段)
      • resolve.extensions 尽可能减少后缀尝试的可能性
      • noParse 对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)
      • IgnorePlugin (完全排除模块)
  1. 充分利用缓存提升二次构建速度
      • babel-loader 开启缓存
      • terser-webpack-plugin 开启缓存
      • 使用 cache-loader 或者 hard-source-webpack-plugin
  1. Tree shaking :ESM项目开启 useExport标记,使用 tree-shaking。
  1. gzip压缩:打包过程可以进行gzip压缩,优化静态资源文件的体积。
 

代码分割

代码分割的本质其实就是在源代码直接上线打包成唯一脚本main.bundle.js这两种极端方案之间的一种更适合实际场景的中间状态。
「用可接受的服务器性能压力增加来换取更好的用户体验。」
源代码直接上线:虽然过程可控,但是http请求多,性能开销大。
打包成唯一脚本:一把梭完自己爽,服务器压力小,但是页面空白期长,用户体验不好。

tree-sharking

Tree shaking(摇树)是一种用于优化 JavaScript代码的技术,旨在通过静态分析的方式去除未使用的代码(dead code)从而减小最终的代码包大小。
Tree shaking的实现原理如下:
  1. 静态分析:Tree shaking利用es6的静态分析技术,分析模块之间的依赖关系,从入口模块开始递归地遍历整个模块依赖图。
  1. 标记依赖:在遍历过程中,对于每个模块,通过识别模块中的导入(import)和导出(export)语句,标记出模块与模块之间的依赖关系。
  1. 标记使用:在遍历过程中,对于每个模块,通过识别模块中的变量引用和函数调用,标记出实际被使用的代码。
  1. 剔除未使用的代码:根据标记的依赖和使用信息,从每个模块中剔除未被使用的代码。这样,最终的打包结果就不会包含未使用的代码,从而减小了代码的体积。
Tree shaking的关键在于静态分析和标记依赖/使用的过程。通过这些分析和标记的信息,工具可以确定哪些代码是被使用的,哪些代码是未使用的,然后进行相应的优化。
需要注意的是,Tree shaking只能消除那些静态可分析的未使用代码。对于动态导入(dynamic import)或通过字符串拼接等方式引入的模块,无法在构建时进行静态分析,因此无法进行有效的优化。在这种情况下,需要使用代码分割等技术来实现动态加载和按需加载的效果。

Babel

大多数JavaScript Parser遵循 estree 规范,Babel 最初基于 acorn 项目(轻量级现代 JavaScript 解析器) Babel大概分为三大部分:
  • 解析:将代码转换成 AST
    • 词法分析:将代码(字符串)分割为token流,即语法单元成的数组
    • 语法分析:分析token流(上面生成的数组)并生成 AST
  • 转换:访问 AST 的节点进行变换操作生产新的 AST
    • Taro就是利用 babel 完成的小程序语法转换
  • 生成:以新的 AST 为基础生成代码
 
 

vite 为什么快

  • 按需动态编译。
初始化时
  1. webpack启动时,需要打包,先把所有文件build一遍,从入口开始遍历所有依赖文件,然后编译成打包后的多个js文件,最后打包到bundler里面。
  1. vite启动时不需要打包,当浏览器需要哪个文件时,再对模块内容进行编译。利用现代浏览器本身支持esm,会自动的向依赖的module发送请求。
热更新
  1. webpack每次都需要把所有模块重新编译一遍,再把改动的模块发送给浏览器。
  1. vite当改动某个模块时,仅需要让浏览器重新请求模块即可。
  • esbuild构建
esbuild使用go语言编写,编译速度比用js编写的webpack 快10-100倍。

webpack与vite区别

构建速度

Webpack: Webpack的构建速度相对较慢,尤其在大型项目中,因为它需要分析整个依赖图,进行多次文件扫描和转译。
Vite: Vite以开发模式下的极速构建著称。它利用ES模块的特性,只构建正在编辑的文件,而不是整个项目。这使得它在开发环境下几乎是即时的。

开发模式

Webpack: Webpack通常使用热模块替换(HMR)来实现快速开发模式,但配置相对复杂。
Vite: Vite的开发模式非常轻量且快速,支持HMR,但无需额外配置,因为它默认支持。

配置复杂度

Webpack: Webpack的配置相对复杂,特别是在处理不同类型的资源和加载器时。
Vite: Vite鼓励零配置,使得项目起步非常简单,但同时也支持自定义配置,使其适用于复杂项目。

插件生态

Webpack: Webpack拥有庞大的插件生态系统,适用于各种不同的需求。
Vite: Vite也有相当数量的插件,但相对较小,因为它的开发模式和构建方式减少了对一些传统插件的需求。

编译方式

Webpack: Webpack使用了多种加载器和插件来处理不同类型的资源,如JavaScript、CSS、图片等。
Vite: Vite利用ES模块原生支持,使用原生浏览器导入来处理模块,不需要大规模的编译和打包。

应用场景

Webpack: 适用于复杂的大型项目,特别是需要大量自定义配置和复杂构建管道的项目。
Vite: 更适用于小到中型项目,或者需要快速开发原型和小型应用的场景。

打包原理

Webpack: Webpack的打包原理是将所有资源打包成一个或多个bundle文件,通常是一个JavaScript文件。
Vite: Vite的打包原理是保持开发时的模块化结构,使用浏览器原生的导入机制,在生产环境中进行代码分割和优化
 
 
 
 
 
react hook性能优化
Loading...