前端工程化复习

59 阅读7分钟

如何使用 axios

  1. 创建实例
import axios from 'axios'

const service = axios.create({
  baseURL: '/api',
  timeout: 10000
})
  1. 加请求/响应拦截器:请求拦截器一般加 token,响应拦截器进行统一错误处理、获取数据
// 请求拦截器
service.interceptors.request.use(config => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

// 响应拦截器
service.interceptors.response.use(
  res => {
    // 后端一般会有统一格式 { code, data, message }
    if (res.data.code !== 0) {
      return Promise.reject(res.data.message || 'Error')
    }
    return res.data.data
  },
  error => {
    if (error.response?.status === 401) {
      // token 过期,跳转登录
    }
    return Promise.reject(error)
  }
)
  1. 导出通用方法
export const get = (url, params) => service.get(url, { params })

export default service

vite 与 webpack 区别

比较项vitewebpack
构建开发时利用 ESM 让浏览器解析 import,请求某个文件时才进行转换并提供源码开发时先解析依赖、打包构建再启动服务器
HMR只对这个文件进行替换修改了 bundle 的一个子模块,需要重新构建整个 bundle
配置简单复杂
生态正在快速发展较成熟
打包生产环境基于 rollup 打包(不过最新的是 rust 写的 rolldown了),开发时基于 esbuild(go 写的,也要被 rolldown 干掉了)有自己的打包流程

webpack loader 与 plugin

loader

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

它有两个属性:

  1. test 属性,识别出哪些文件会被转换。
  2. use 属性,定义出在进行转换时,应该使用哪个 loader。多个 loader,则从右到左执行。

当解析到与 test 属性相匹配的路径时,会使用相应的 user 转换,再进行打包。

  • loader 可以是同步的,也可以是异步的。
  • loader 运行在 Node.js 中,并且能够执行任何操作。
  • loader 支持链式调用。链中的每个 loader 会将转换应用在已处理过的资源上。

plugin

插件目的在于解决 loader 无法实现的其他事

webpack 插件是一个具有 apply 方法的 JavaScript 对象

Tree-shaking

它的核心原理是:

  1. 静态分析源码的 import/export。
  2. 找出哪些导出的东西实际没有被用到。
  3. 在打包时去掉这些未引用的代码。

vite 原理

简介

vite 是一个构建工具,主要由两部分构成:

  • 一个开发服务器,它基于 原生 ES 模块 提供了丰富的内建功能,如 预构建、HMR、依赖强缓存、重写导入语句、转译 TS。
  • 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。

依赖预购建(开发模式)

首次启动 vite,将使用 ESBuild 进行依赖预购建。这个过程主要干两件事:

  • 模块转换:在开发阶段中,Vite 的开发服务器将所有代码视为原生 ES 模块。因此,Vite 必须先将以 CommonJS 或 UMD 形式提供的依赖项转换为 ES 模块
  • 合并模块:具有许多内部模块的 ESM 依赖项转换为单个模块。比如 lodash-es 有 600 多个内置模块,处理后只需要发送一个 HTTP 请求。

对于 monorepo 链接的本地包,Vite 不会把它当作依赖去打包,而是把它当作源码来看待。

预购建缓存

Vite 将预构建的依赖项缓存到 node_modules/.vite 中。在以下某项发生更改时,才需要重新运行预构建:

  • 包管理器的锁文件内容,例如 package-lock.jsonyarn.lockpnpm-lock.yaml,或者 bun.lock
  • 补丁文件夹的修改时间;
  • vite.config.js 中的相关字段;
  • NODE_ENV 的值。

如果没有找到现有的缓存,Vite 会扫描代码里的 bare import(一般都指向 node_modules 里的依赖),把这些依赖当作入口文件,先用 esbuild 预打包成单文件,避免浏览器直接去解析 node_modules 里成百上千的小模块。

已预构建的依赖请求使用 HTTP 头 max-age=31536000, immutable 进行强缓存,以提高开发期间页面重新加载的性能。一旦被缓存,这些请求将永远不会再次访问开发服务器。不同版本的依赖项会通过附加版本查询自动失效。

HMR

  1. 创建一个websocket服务端和client文件,启动服务

  2. 通过chokidar监听文件变更

  3. 当代码变更后,服务端进行判断并推送到客户端

  4. 客户端根据推送的信息执行不同操作的更新

只用对发生变更的模块重新加载,只需要精确的使相关模块与其临近的 HMR边界连接失效即可(不过通常只有自己)

Webpack 原理

核心概念

概念作用
Entry打包入口,Webpack 从这里开始分析依赖,一个 chunk 组的入口。可以有多个
Module项目中每个 JS/CSS/图片等资源都被当作模块
Loader模块转换器,把非 JS 资源转为 JS
Plugin扩展功能,修改打包流程或增强能力
Chunk打包输出的代码块,一个 chunk 可能包含多个模块。有两种,initial(包含为入口起点指定的所有模块及其依赖项) 和 non-initial(动态加载导致的),每个 chunk 都有一个输出文件
Dependency Graph模块依赖关系图,Webpack 的核心
Runtime模块加载器,保证模块按需执行和缓存。功能:模块注册、加载、缓存、处理异步加载,使打包后的模块能在浏览器里按依赖顺序正确执行

流程

初始化:读取 webpack.config.js,根据 entry 确定打包起点

构建模块依赖图

  1. 先解析入口文件:用 Loader 处理非 JS 资源
  2. 递归解析依赖:import / require / import() 等都会触发解析,每个模块会生成一个 module 对象,记录模块内容(转换后的代码)、模块依赖、模块 id
  3. 构建依赖图:最终生成一个 完整的依赖图(Dependency Graph) ,表示所有模块之间的关系

编译:Webpack 将依赖图中每个模块进行 转换和封装,Loader 转换文件(如 Sass → CSS、TS → JS), Plugin 扩展功能(如打包优化、压缩、注入 HTML)

生成:根据依赖图生成最终 bundle,输出最终静态资源到 dist 或指定目录

优化

  1. Tree-shaking
  2. 代码分割
  3. 使用 contenthash 优化缓存
  4. 压缩:Terser 压缩 JS、CSS 压缩

PostCSS 原理

Tokenizer → parser → processer → stringifier

  1. tokenizer 负责将 CSS 字符转换为 Token,用来记录语法的某些组成,比如类名属于 word,左括号属于左括号,也可以包含位置信息。
  2. parser 负责根据 Token 生成抽象语法树。它与 Tokenizer 是流式的关系,出于性能考虑,postCSS 不会存储所有 Token,而是存储当前需要的部分 Token,当 parser 告诉 Tokenizer 它需要新的 token 时,token 的生成才会继续。
  3. processer 负责初始化插件和执行语法转换。
  4. stringifier 负责将修改后的 AST 转换为纯 CSS 字符串。

Babel 原理

解析(生成 Token 和 AST)、转换(插件进行工作)、生成(修改后的 AST 转字符串),和 postCSS 差不多。其他概念:

  • 插件:是Babel转换功能的最小单位。一个插件通常只负责处理一种或一类语法(如转换箭头函数、解构赋值)。
  • 预设:是一组插件的集合,目的是为了方便使用。
  • Polyfill:Babel 默认只转换语法(Syntax)(如 letconst, 箭头函数、类),但无法转换新的 API(如 PromiseArray.prototype.flatObject.assign),需要引入 Polyfill 来模拟这些新的API,让它们在旧浏览器中可用。

PostCSS 与 Babel

  • Babel 主要专注于 JavaScript 的语法转换和兼容性处理,确保新JS代码能在旧环境中运行。
  • PostCSS 主要专注于 CSS 的处理、增强和优化,比如自动加前缀、转换新CSS语法、压缩。