Typescript 配置

  • 依赖安装实现 pnpm install -D typescript @types/node

  • 核心的配置文件是 tsconfig.json 对应的是 jsconfig.json

Typescript CompilerOptions 字段

是 tsconfig.json 的最为核心的一个字段配置吧

核心的目的是为开发者提供一套工具出来,用来实现控制 Typescript 编译器的行为特点

  • 极致的类型安全:通过静态类型分析,在代码运行前捕获尽可能多的错误

  • 足够的灵活性:适应不同的项目规模、目标环境和开发流程吧

类型检查严格性

  • 核心的字段有 strict noTmplicitAny strictNullChecks

  • 这是 ts 配置的核心吧,直接决定的是我们的项目整体的类型检查的严格性吧

  • strict 严格模式的开启,有很多的默认的配置就是开启了的,尽可能用上吧

    • 空值安全相关的判断

      • strictNullChecks 强制性的控制检查吧,核心是为了方式运行时的空值引用错误吧,针对于 null 空安全的防患吧

      • noUnCheckedIndexedAccess 检查索引的连通性吧

      • exactOptionalPropertyTypes 不允许的类型检查吧,属性检查吧

    • 函数类型的检查

      • noImplicitAny 函数类型的严格性检查吧

      • strictFunctionTypes 严格性的函数类型检查

      • strictBindCallApply this 指针绑定的严格检查吧

    • 类严格检查吧

      • strictPropertyInitialization 严格类型的属性实例化检查

      • noImplicitOverride 集成重写的严格的 override 的书写吧

    • 控制流分析的严格性

      • noImplicitReturns 方式返回不符合预期的类型吧

      • noFallthroughCasesInSwitch 必须把所有的类型全部考虑的类型把

    • 影响开发的类型限制把

      • noPropertyAccessFromIndexSignature 不知道的类型只能通过索引来访问把

      • useUnknownInCatchVariables unknown 类型的严格检查把

    • 调试运行命令是npx tsc --strict --noEmit

模块解析策略

  • module 指定的是模块代码的执行方案把

    • commonjs 就是使用的nodejs的模块解析规范把 require module.exports

    • ES5/ES2020 就是使用的是esmodule的模块解析规范把

    • AMD:RequireJS,浏览器异步模块

    • UMD:通用模块定义,兼容 CommonJS 和 AMD

    • System:SystemJS 加载器

    • None:不使用模块系统

  • moduleResolution 模块解析策略

    • node 或者 classic 一般使用的是 node

    • bundler

  • moduleDetection 模块检测策略把

    • auto (默认):有 import/export 即为模块

    • force:所有文件都视为模块

    • legacy:TypeScript 4.6 前的行为

  • baseUrlpaths

    • 核心是进行的是指定路径别名的,和构建工具一起配置讷,否则出问题哈

  • 模块解析行为控制

    • resolveJsonModule JSON 模块解析支持

    • allowUmdGlobalAccess UMD模块解析支持把

    • resolvePackageJsonExports 包到处支持把

  • 输出目标配置

    • target 指定编译目标把

    • lib 类型声明库

      • Node.js["ES2020"]

      • 浏览器["ES2020", "DOM", "DOM.Iterable"]

      • React["ES2020", "DOM", "DOM.Iterable", "ESNext.Promise"]

    • outDir 输出目录指定吧

Typescript 其他配置

属性

核心作用

适用场景

include/exclude

控制编译文件范围

所有项目

references

Monorepo 项目依赖管理

多包 / 多项目架构

extends

配置继承与复用

团队共享配置、框架预设

composite

标记可被引用的复合项目

Monorepo 子项目

declarationDir

类型声明文件输出目录

库项目、需要导出类型的场景

Typescript Monorepo 下的设计思考

pnpm + workspace 下的深入思考

  • 该方案的话只是简单的依赖我们的 pnpm 的也行实现吧,核心的基础架构

monorepo-root/
├── apps/                      # 应用目录(可独立部署)
│   ├── web/                   # 前端 Web 应用
│   │   ├── src/
│   │   ├── package.json
│   │   └── tsconfig.json
│   └── api/                   # 后端 API 服务
│       ├── src/
│       ├── package.json
│       └── tsconfig.json
├── packages/                  # 共享库目录(可复用代码)
│   ├── shared-utils/          # 共享工具函数库
│   │   ├── src/
│   │   ├── package.json
│   │   └── tsconfig.json
│   └── shared-types/          # 共享类型定义库
│       ├── src/
│       ├── package.json
│       └── tsconfig.json
├── package.json               # 根项目 package.json
└── pnpm-workspace.yaml        # pnpm workspace 配置文件
  • 核心的依赖项 pnpm-workspace.yaml 的配置实现吧,配置的是当前的当仓仓库的含有的工具目录和其他的工作目录吧

packages:
  - 'apps/*'
  - 'packages/*'
  - '!**/test/**'
  • package.json 的核心配置,主要是用于定义规范我们的所有的仓库下的命令 scripts 的命令规范吧

    • pnpm --filter <package-name> <command>

{
  "name": "my-monorepo",
  "private": true, // 根项目通常是 private 的,不发布
  "scripts": {
    "dev:web": "pnpm --filter web dev", // 运行 web 应用的 dev 脚本
    "dev:api": "pnpm --filter api dev", // 运行 api 服务的 dev 脚本
    "build:web": "pnpm --filter web build",
    "build:api": "pnpm --filter api build",
    "build:all": "pnpm run build:web && pnpm run build:api", // 串行执行
    "test": "pnpm --filter \"./packages/**\" test" // 运行所有 packages 的 test 脚本
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "eslint": "^8.0.0",
    "prettier": "^3.0.0"
    ...
  }
}
  • 子包间的依赖关系总结,使用我们的 workspace:* 来进行指定,以及子包使用scope命名实现吧: "@my-monorepo/shared-utils

  • 整体的架构总结

    • 每个配置是相互继承和相互依赖的讷

monorepo/
├── package.json
├── pnpm-workspace.yaml
├── tsconfig.json              # 根配置(仅工具)
├── tsconfig.base.json         # 基础共享配置
├── tsconfig.frontend.json     # 前端项目配置
├── tsconfig.backend.json      # 后端项目配置
├── tsconfig.library.json      # 库项目配置
├── packages/
│   ├── shared/                # 共享工具库
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   └── src/
│   ├── ui/                    # UI 组件库
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   └── src/
│   └── utils/                 # 工具函数库
│       ├── package.json
│       ├── tsconfig.json
│       └── src/
├── apps/
│   ├── web/                   # Web 应用
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   └── src/
│   ├── admin/                 # 管理后台
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   └── src/
│   └── api/                   # API 服务
│       ├── package.json
│       ├── tsconfig.json
│       └── src/
└── tools/
    ├── scripts/               # 构建脚本
    ├── configs/               # 共享配置
    └── cli/                   # 命令行工具

turbopack + pnpm 下的深入探究

  • 在上面的基础上就是多了一个 turbo.json 的配置文件吧

  • 此时实现使用的运行命令就是通过 turbo 来进行运行实现,就不用使用我们的 pnpm 了吧

  • 执行流程机制

    • 首次运行运行 pnpm run build 执行 turbo run build ,此时 turbopack 会根据 turbo.json 进行依赖的深入分析,分析包之间的依赖关系,然后按照顺序的执行对应的 build 命令

    • 二次运行的时候,因为 turbopack 具备对应的构建缓存的功能实现,只有子包源码发生了变化,才会重新构建,否则就是使用的是上次的构建缓存吧,实现一个增量构建的能力吧

    • 远程缓存实现(remote caching)

      • turbopack支持将缓存的构建依赖上传到 cdn 服务器,vercel,实现团队开发成员之间、ci/cd的共享缓存的功能,提高开发效率吧

nx + pnpm 深入探究

  • 自己调研,还是很好用的,生态成熟的遭不住,哈哈

特性

pnpm + workspace

Turborepo + pnpm

NX + pnpm

核心定位

包管理与工作区

高性能构建系统

全功能开发工具包

易用性

性能

功能广度

窄 (仅包管理)

中 (构建与缓存)

宽 (全生命周期)

灵活性

生态

庞大 (pnpm 生态)

成长中

成熟 (Angular 背景)

适用团队

小 / 中型,希望灵活控制

中 / 大型,追求极致速度

中 / 大型,重视规范与工具链

代码质量检查和格式化

核心原则

  1. 一致性 (Consistency):确保团队内所有成员编写的代码风格统一,降低阅读和维护成本。

  2. 可读性 (Readability):通过清晰的代码风格和结构,提升代码的可读性。

  3. 可维护性 (Maintainability):一致的代码更容易被理解、修改和扩展。

  4. 提前发现问题 (Early Detection):在代码提交前或运行前,自动发现潜在的 bug、错误或不最佳实践。

  5. 提升效率 (Productivity):减少因代码风格争论所浪费的时间,让开发者专注于业务逻辑。

  6. 遵循最佳实践 (Best Practices):引导团队成员遵循行业公认的最佳实践和设计模式。

  • 核心的工具继承:eslint prettier stylelint

  • 在我们的monorepo 架构下的话就是做好对应的依赖共享实现即可吧,如何在 “共享” 与 “隔离” 之间取得平衡

代码质量保障体系
├── 代码格式化 (Formatting)
│   ├── Prettier (主流)
│   └── Biome (新兴)
├── 代码检查 (Linting)
│   ├── ESLint (JavaScript/TS)
│   ├── Stylelint (CSS)
│   ├── Markdownlint (Markdown)
│   └── Commitlint (Git Commit)
├── 类型检查 (Type Checking)
│   └── TypeScript
├── 提交规范 (Commit Convention)
│   └── Husky + lint-staged
└── 安全扫描 (Security)
    └── SonarQube / Snyk

Prettier - 代码格式化器

核心原理

  • 基于 AST 解析代码,完全忽略原始格式

  • 重新输出符合统一规则的代码

  • 支持多种语言,配置简单

Biome - 新兴一体化工具

核心优势

  • 单一工具替代 Prettier + ESLint

  • 性能极快(Rust 编写)

  • 零配置开箱即用

ESLint - JavaScript/TypeScript 检查

源码 → 解析器 (Parser) → AST → 规则 (Rules) → 报告 (Reports)
  • Possible Errors:可能的错误

  • Best Practices:最佳实践

  • Variables:变量相关

  • Stylistic Issues:代码风格(通常交给 Prettier)

  • ES6+:ES6+ 特性使用

Stylelint - CSS/预处理器检查

TypeScript 严格配置

运行时类型检查 - Zod

Husky + lint-staged

Commitizen + Commitlint

兼容性工具适配

Babel (JavaScript 转译器)

  • 核心原理:Babel 是一个 JavaScript 编译器。它的工作流程分为三个阶段:

    • 解析 (Parse):将最新的 ES6+ 代码解析成一个抽象语法树 (AST)。

    • 转换 (Transform):遍历 AST,并应用一系列插件(Plugins)来修改它。这些插件负责将新语法(如箭头函数、const/let、解构赋值)转换为旧浏览器能够理解的等价语法(如 varfunction)。

    • 生成 (Generate):将转换后的 AST 重新生成普通的 JavaScript 代码。

  • 实践引入

    • 安装核心库和预设:npm install --save-dev @babel/core @babel/cli @babel/preset-env

    • 创建配置文件 babel.config.json,在其中指定 preset-env 和目标浏览器。

    • @babel/preset-env 是一个智能预设,它会根据你指定的目标环境(如 > 0.25%, not dead)自动确定需要哪些插件来转换代码。

    • 在构建工具(如 Webpack, Vite, Rollup)中集成 Babel 插件。

  • 思考

    • Babel 只负责语法转换,不处理 API 层面的兼容性(如 Promise, Array.prototype.includes 等)。

    • 它是现代前端工程化中不可或缺的一环,允许开发者享受最新语言特性带来的效率提升。

Core-js (JavaScript API 补丁库)

  • 核心原理:Core-js 是一个模块化的 JavaScript 标准库,它提供了 ES5、ES6+ 等标准中定义但旧浏览器缺失的 API 的实现。当你的代码使用了这些 API 时,Core-js 会在运行时为目标环境提供一个 “补丁”(Polyfill)。

  • 实践引入

    • 安装:npm install core-js

    • 在 Babel 配置中通过 useBuiltIns: 'usage'corejs 选项自动引入所需的 Polyfill,无需手动在代码中 import

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": "3.20"
      }
    ]
  ]
}
  • 思考

    • Core-js 完美弥补了 Babel 的不足,负责API 层面的兼容性。

    • useBuiltIns: 'usage' 是推荐用法,它会分析你的代码,只引入实际用到的 Polyfill,从而减小最终 bundle 的体积。

PostCSS (CSS 转译器)

  • 核心原理:PostCSS 的工作模式与 Babel 非常相似,但它处理的是 CSS。它将 CSS 解析成 AST,然后通过插件对其进行转换,最后再生成 CSS。

    • Autoprefixer:PostCSS 最著名的插件。它会根据 Can I Use 的数据,自动为 CSS 属性添加浏览器前缀(如 -webkit-, -moz-)。

    • CSSnext (现已融入 postcss-preset-env):允许你使用未来的 CSS 特性(如 CSS 变量、@custom-media),并将其转换为当前浏览器支持的语法。

  • 实践引入

    • 安装:npm install --save-dev postcss autoprefixer postcss-preset-env

    • 创建配置文件 postcss.config.js

    • 在构建工具中集成 PostCSS loader。

  • 思考

    • PostCSS 生态非常强大,除了兼容性,还可以用于代码压缩(cssnano)、linting(stylelint)等。

    • 它是处理 CSS 兼容性和自动化的瑞士军刀。

SWC 的核心原理

SWC 的工作流程与 Babel 类似,但由于其底层语言和架构的差异,实现了性能的巨大飞跃。

  1. 解析 (Parsing)

    • 将输入的 JavaScript 或 TypeScript 源代码字符串解析成一个抽象语法树 (AST)

    • SWC 的解析器是从零开始用 Rust 编写的,专门优化了解析速度和内存使用。

  2. 转换 (Transformation)

    • 这是 SWC 最核心的部分。它会遍历 AST,并根据预设的规则或插件对其进行修改。

    • 内置转换能力:SWC 内置了大量用于将 ES6+ 语法转换为 ES5 的规则,例如将箭头函数转换为 functionconst/let 转换为 var、处理解构赋值等。

    • TypeScript 支持:它能将 TypeScript 代码转换为 JavaScript,并可以选择性地移除类型注解。

    • 插件系统:虽然 SWC 本身已经很强大,但它也支持插件,允许社区扩展其转换能力。不过,与 Babel 庞大的插件生态相比,SWC 的插件生态还在成长中。

  3. 代码生成 (Code Generation)

    • 将转换后的 AST 重新生成为目标 JavaScript 代码字符串。

    • 同样,代码生成器也经过了高度优化,以确保输出代码的质量和紧凑性。

CSS方案总结

纯 Web 端解决方案

这类方案主要针对浏览器环境,利用 CSS 预处理器、后处理器和模块化技术。

1. CSS 预处理器 (CSS Preprocessors)

  • 代表工具:Sass/SCSS, Less, Stylus

  • 核心原理

    扩展了 CSS 的语法,引入了变量、嵌套、混合(Mixin)、继承、函数等编程特性。

    代码编写完成后,通过编译器将其转换为标准的 CSS 文件。

  • 主要特性

    • 变量$color-primary: #1890ff; 方便统一管理颜色、尺寸等。

    • 嵌套nav { ul { margin: 0; } } 清晰地反映 DOM 结构。

    • Mixin/Extend:复用样式片段,减少冗余。

    • 函数 / 运算width: 100px + 50px; 实现动态计算。

  • 适用场景

    • 中大型 Web 项目,需要结构化、可维护性高的样式代码。

    • 团队成员较多,需要统一代码风格和设计规范。

  • 生态与工具

    • 与 Webpack、Vite、Rollup 等构建工具深度集成。

    • 大量 UI 框架基于此构建,如 Bootstrap (Less), Element UI (SCSS)。

2. CSS Modules

  • 核心原理

    将 CSS 文件视为一个模块,通过构建工具(如 Webpack 的 css-loader)对类名进行哈希处理(如 button -> _button_123abc),从而实现样式的局部作用域。

  • 主要特性

    • 局部作用域:默认所有样式都是局部的,避免了全局样式污染。

    • 全局变量:通过 :global(.className) 可以声明全局样式。

    • 组合样式composes: className from './other.css'; 实现样式复用。

  • 适用场景

    • 组件化开发的项目,尤其是 React、Vue 项目。

    • 对样式隔离有严格要求的场景。

  • 生态与工具

    • Webpack css-loader、Vite 内置支持。

    • 在 React 项目中应用非常广泛。

3. CSS-in-JS

  • 代表库:Styled Components, Emotion, JSS, Aphrodite

  • 核心原理

    将 CSS 样式直接写在 JavaScript/TypeScript 代码中,以组件的形式存在。在运行时或构建时,这些样式会被注入到页面的 <style> 标签中。

  • 主要特性

    • 组件化:样式是组件的一部分,具有更强的内聚性。

    • 动态样式:可以轻松地根据组件的 props 或 state 生成动态样式。

    • 自动前缀:大多数库内置了 Autoprefixer 功能。

    • 无类名冲突:自动生成唯一的类名。

  • 适用场景

    • React 项目(生态最成熟)。

    • 样式高度动态、依赖组件状态的场景。

    • 追求极致组件化和 JSX 内一站式开发体验的团队。

  • 优缺点

    • 优点:动态性强、组件化程度高、无样式污染。

    • 缺点:学习曲线、可能导致 JS bundle 体积增大、运行时开销(部分库)、调试体验不如纯 CSS。

4. Utility-First CSS

  • 代表工具:Tailwind CSS, Windi CSS, UnoCSS

  • 核心原理

    预先定义大量原子化的 utility 类(如 flex, text-center, p-4),开发时直接在 HTML 元素的 class 属性中组合这些类来构建页面。

  • 主要特性

    • 高度可定制:可以通过配置文件自定义颜色、间距、字体等所有 utility。

    • 快速开发:无需编写大量自定义 CSS,极大提高开发速度。

    • 响应式设计:内置响应式前缀(如 sm:, md:),轻松实现响应式布局。

    • PurgeCSS 集成:生产环境下自动移除未使用的 CSS,保证最终体积小巧。

  • 适用场景

    • 快速原型开发。

    • 对 UI 一致性要求高、设计系统明确的项目。

    • 不希望编写大量 CSS 的团队。

  • 优缺点

    • 优点:开发效率极高、样式统一、可维护性强(通过配置)。

    • 缺点:HTML class 属性会变得很长、有一定的学习成本(记住 utility 类名)。


B. React Native (RN) 专用方案

RN 不直接支持 CSS,而是通过 JavaScript 对象来描述样式。

1. StyleSheet API (官方)

  • 核心原理

    使用 StyleSheet.create() 方法创建一个样式对象,其语法是 CSS 的一个子集,属性名采用驼峰式(如 fontSize, backgroundColor)。

  • 主要特性

    • 性能优化StyleSheet 会将样式对象进行优化,比如将其转换为一个 ID,而不是每次渲染都传递整个对象。

    • 类型安全:与 TypeScript 结合良好,提供类型检查。

  • 适用场景

    • 所有 RN 项目的基础样式方案。

  • 局限性

    • 功能有限,不支持嵌套、变量(原生不支持,需结合其他方案)、:hover 等伪类。

2. CSS-in-JS 库 (适配 RN)

  • 代表库:Styled Components (RN 适配版), Emotion (RN 适配版), Styled System, NativeBase

  • 核心原理

    与 Web 端的 CSS-in-JS 理念相同,但底层会将样式转换为 RN 可以理解的 StyleSheet 对象。

  • 主要特性

    • 提供了嵌套、动态样式、主题支持等高级功能。

    • Styled SystemNativeBase 更进一步,提供了一套基于主题的设计系统和预设组件。

  • 适用场景

    • 希望在 RN 中获得与 Web 端一致的 CSS-in-JS 开发体验。

    • 需要构建复杂、主题化 UI 的 RN 应用。

3. 预处理器桥接方案

  • 代表工具:React Native CSS Modules, RN-Sass-Loader

  • 核心原理

    允许开发者使用 Sass/SCSS 等预处理器编写样式文件,然后通过构建工具将其转换为 RN 可用的 JavaScript 样式对象。

  • 适用场景

    • 团队成员熟悉 Sass/SCSS,希望在 RN 项目中继续使用。

    • 希望复用部分 Web 端的 Sass/SCSS 代码。


C. 跨端框架 (Taro, uni-app) 方案

这类框架的目标是 “一套代码,多端运行”,因此它们提供的样式方案需要能同时编译到 Web、小程序、App 等平台。

1. 框架内置样式方案

  • Taro

    • 核心原理:Taro 支持多种样式编写方式,并在编译时根据目标平台进行转换。

    • 支持方式

      • CSS Modules:推荐方式,文件命名为 xxx.module.css

      • 普通 CSS/SCSS/Less:会被编译为全局样式或组件样式(通过特殊的 scope 注释)。

      • CSS-in-JS:需配合 @tarojs/plugin-csso 等插件。

    • 特色:支持 :root 选择器定义全局变量,编译时会处理成各平台支持的形式。

  • uni-app

    • 核心原理:uni-app 对 CSS 进行了增强,使其能更好地适配多端。

    • 支持方式

      • Scoped CSS:在 <style> 标签上添加 scoped 属性,实现组件样式隔离(类似 Vue)。

      • App.vue 中的全局样式

      • 支持 Sass/SCSS/Less/Stylus:需在 HBuilderX 中安装相应插件或在 CLI 项目中配置 loader。

    • 特色

      • rpx 单位:自动根据屏幕宽度进行适配,是 uni-app 推荐的尺寸单位。

      • CSS 变量:支持 --var-name: value; 定义变量,并在各端生效。

2. 跨端 UI 框架

  • 代表框架:Taro UI, NutUI (Taro/uni-app), Vant Weapp (可在 Taro/uni-app 中使用), uView UI (uni-app)

  • 核心原理

    这些 UI 框架基于底层跨端框架(Taro/uni-app)构建,提供了一套统一的、经过多端适配的组件库。它们内部已经解决了样式在不同平台上的兼容性问题。

  • 适用场景

    • 快速开发,不想过多关注底层样式兼容性的项目。

    • 需要统一 UI 风格的项目。


三、方案选择策略与总结

技术栈 / 平台

推荐方案

推荐理由

现代 Web (React/Vue)

Tailwind CSS

开发效率极高,可维护性强,生态完善。

现代 Web (追求极致组件化)

CSS-in-JS (如 Styled Components/Emotion)

样式与组件紧密结合,动态样式能力强。

传统 Web 或需要兼容旧项目

Sass/SCSS + CSS Modules

生态成熟,开发者熟悉,样式隔离效果好。

React Native

CSS-in-JS (如 Styled Components/NativeBase)

提供丰富的样式能力和组件化体验,是社区主流。

Taro

CSS Modules

Taro 官方推荐,样式隔离,编译性能好。

uni-app

Scoped CSS + Sass/SCSS

uni-app 原生支持,配合 rpx 单位,多端适配体验佳。

需要最大化代码复用的跨端项目 (Web + RN)

CSS-in-JS (如 Styled Components)

在 Web 和 RN 中语法和理念一致,可复用部分样式逻辑(需注意平台差异)。

网络请求思考

前端网络架构设计全景:从协议到可配置化引擎

一、核心通信协议深度解析与业务适配

协议 / 技术

核心设计原理

通信模式

关键技术点

业务适配性分析

优缺点

HTTP/HTTPS

基于 请求 - 响应 模型的应用层协议。客户端发送请求(Request),服务器处理后返回响应(Response)。HTTPS 通过 TLS/SSL 加密,保证传输安全。

半双工短连接(HTTP/1.1 Keep-Alive 是长连接,但本质仍是请求 - 响应循环)。

- 方法(GET, POST, PUT, DELETE...)

- 状态码(200, 404, 500...)

- 头部(Headers)

- 缓存机制(Cache-Control, ETag)

- 认证(Basic, Bearer Token, OAuth)

通用型业务

- 所有 CRUD 操作(查询用户、提交表单)。

- 数据量不大、实时性要求不高的场景。

- 第三方 API 集成。

- 对安全性要求高的交易、登录业务(HTTPS)。

优点

- 简单、成熟、标准化。

- 缓存机制完善,减少服务器压力。

- 易于调试(浏览器 Network 面板)。

- 跨域解决方案成熟(CORS)。

缺点

- 实时性差,无法主动推送。

- 频繁建立连接带来开销。

- 服务器被动响应,无法主动向客户端推送数据。

WebSocket

基于 TCP 的全双工通信协议。通过 HTTP 握手(Upgrade: websocket)将连接升级为 WebSocket 连接,之后双方可随时发送数据。

全双工长连接

- 握手协议

- 数据帧(Frame)格式

- 心跳检测(Keep-Alive)

- 关闭握手

实时交互业务

- 即时通讯(IM)、聊天应用。

- 实时协作工具(在线文档、白板)。

- 实时通知、公告。

- 股票行情、体育赛事直播。

- 多人在线游戏。

优点

- 真正的实时双向通信,低延迟。

- 连接建立后,数据传输效率高。

- 支持二进制和文本数据。

缺点

- 实现复杂,需处理连接管理、断线重连。

- 服务器需维护长连接,资源消耗大。

- 无原生缓存,需自行设计。

- 跨域配置需服务器支持。

SSE (Server-Sent Events)

基于 HTTP 的单向服务器推送技术。客户端发起一个普通的 HTTP GET 请求,服务器保持连接打开,持续向客户端发送格式化的事件流(MIME type: text/event-stream)。

单向(服务器→客户端),长连接

- 事件流格式(data:, event:, id:)

- 自动重连机制

- Last-Event-ID 头部

服务器推送业务

- 实时数据展示(如监控仪表盘、日志流)。

- 新闻推送、Feed 更新。

- 不需要客户端向服务器实时反馈的场景。

优点

- 实现简单,基于 HTTP,兼容性好。

- 浏览器原生支持(EventSource API)。

- 自带断线重连和事件 ID 机制。

缺点

- 单向通信,无法满足客户端实时上传需求。

- 数据格式限制为文本(可封装 JSON)。

- 部分浏览器对最大连接数有限制。

WebRTC

一套支持浏览器之间 点对点(P2P) 实时通信的 API。通过信令服务器交换连接信息(SDP、ICE Candidates),然后直接建立 UDP 连接,传输音视频流和数据。

全双工P2P 直连

- 信令(Signaling)

- 媒体协商(SDP)

- NAT 穿透(ICE, STUN, TURN)

- 数据通道(DataChannel)

实时音视频与文件传输业务

- 视频会议、在线教育。

- 实时语音通话。

- 点对点文件共享。

- 对延迟要求极高的实时游戏。

优点

- 极低延迟,P2P 直连,不经过服务器中转(音视频数据)。

- 节省服务器带宽和资源。

- 支持高保真音视频和任意二进制数据。

缺点

- 实现非常复杂,涉及多个协议。

- NAT 穿透挑战大,需 TURN 服务器 fallback。

- 浏览器兼容性和版本差异问题。

- 安全性和隐私保护复杂。

二、前端网络请求层设计与 URL 操作

前端网络请求层是业务与底层协议之间的桥梁,负责请求的发起、拦截、处理和错误统一管理。

1. URL 操作与管理

URL 是网络资源的唯一标识,其构造和解析是请求层的基础能力。

  • 核心能力:

    • 基础 URL 与路径拼接: https://api.example.com + /users = https://api.example.com/users

    • 动态路径参数替换: /users/:id -> /users/123

    • 查询参数(Query String)构建: { page: 1, limit: 10 } -> ?page=1&limit=10

    • URL 编码 / 解码: 处理特殊字符(如中文、空格)。

    • URL 解析: 从一个完整 URL 中提取出协议、主机、路径、查询参数等。

  • 实现方式与工具:

    • 原生 API: URLURLSearchParams 提供了现代、标准的方式。

    • 库函数: qs 库是处理查询参数的利器,支持复杂对象的序列化。许多请求库(如 Axios)内置了强大的 URL 处理能力。

2. 网络请求库的集成与拓展

直接使用 fetchXMLHttpRequest 过于底层,项目中通常会选择成熟的请求库,并进行二次封装。

  • 主流请求库对比:

    • Axios: 生态最成熟,API 友好,支持拦截器、请求取消、超时设置、自动转换 JSON 等,是目前的首选。

    • fetch API: 浏览器原生,基于 Promise,语法简洁,但功能相对基础(无拦截器、无超时等),需要自行封装。

    • SuperAgent: API 设计优雅,支持链式调用,功能全面。

    • @angular/common/http: Angular 框架内置,与 RxJS 深度集成,适合 Angular 项目。

  • 请求层封装核心(以 Axios 为例):

    • 创建实例: 配置基础 URL、超时时间、默认请求头。

    • 请求 / 响应拦截器:

      • 请求拦截: 统一添加认证 Token、日志记录、数据转换、加密。

      • 响应拦截: 统一处理响应数据(如剥离外层包装)、错误处理(网络错误、401、403、500 等状态码)、日志记录。

    • API 方法封装: 将后端接口按业务模块封装成函数,统一管理,便于调用和维护。

    • 错误处理机制: 定义统一的错误类型和错误码,配合全局错误提示组件。

    • 请求取消: 支持取消单个或多个请求(如页面切换时取消未完成的请求)。

三、数据模型层定义与 JSON Schema

网络请求返回的数据需要被结构化地处理,才能安全、高效地在应用中流转。

1. 数据模型(Model)的重要性

  • 类型安全: 在 TypeScript 项目中,为接口返回数据定义 interfacetype,可以获得类型检查和 IDE 智能提示。

  • 数据验证: 确保从后端获取的数据符合预期的格式和约束(如必填字段、数据类型、长度等)。

  • 数据转换: 将后端返回的 “蛇形命名”(snake_case)转换为前端偏好的 “驼峰命名”(camelCase)。

  • 业务逻辑分离: 将数据处理逻辑(格式化、计算衍生数据)封装在模型中。

2. JSON Schema:可配置化的数据契约

JSON Schema 是一种基于 JSON 的格式,用于描述 JSON 数据的结构和约束。它可以作为前端和后端之间的数据契约。

  • 核心价值:

    • 可配置化验证: 不再需要手写大量的 if/else 验证逻辑。只需定义一份 Schema,即可使用 AjvZod 等库进行验证。

    • 自动生成类型: 可以从 JSON Schema 自动生成 TypeScript 类型定义,确保类型和验证规则同步。

    • 文档化: Schema 本身就是一份清晰的接口文档,便于前后端协作。

    • 可扩展性: 支持复杂的验证规则,如枚举、正则表达式、条件验证等。

  • 实践流程:

    1. 定义 Schema: 为每个 API 接口的响应数据定义 JSON Schema。

    2. 生成 Type: 使用工具(如 json-schema-to-typescript)从 Schema 生成 TypeScript 类型。

    3. 请求后验证: 在响应拦截器中,使用 Schema 验证返回的数据。

      • 验证通过: 将数据转换为生成的 Type,并返回给业务代码。

      • 验证失败: 抛出错误,记录日志,并可能触发全局错误处理(如显示 “数据格式错误”)。

四、网络请求的可配置化与 DSL 设计

为了实现更高程度的灵活性和可维护性,可以将网络请求的行为(URL、方法、参数、 headers、数据处理等)通过配置文件来定义,甚至设计一套简单的领域特定语言(DSL)。

1. 配置化设计思路

  • 目标: 将网络请求的 “如何做”(How)与业务逻辑的 “做什么”(What)分离。业务代码只需调用一个标识符,具体的请求细节由配置决定。

  • 配置文件示例 (JSON/YAML):

// api.config.json
{
  "apis": {
    "getUserList": {
      "method": "GET",
      "url": "/users",
      "params": {
        "page": "{{page}}",
        "limit": "{{limit}}"
      },
      "responseSchema": "userListSchema" // 关联的 JSON Schema ID
    },
    "createUser": {
      "method": "POST",
      "url": "/users",
      "headers": {
        "Content-Type": "application/json"
      },
      "data": "{{userData}}", // 占位符,由调用方传入
      "responseSchema": "userSchema"
    }
  },
  "schemas": {
    // 内嵌或引用外部 JSON Schema 文件
    "userListSchema": {
      "type": "object",
      "properties": {
        "code": { "type": "number", "const": 200 },
        "data": {
          "type": "array",
          "items": { "$ref": "#/schemas/userSchema" }
        }
      },
      "required": ["code", "data"]
    },
    "userSchema": { /* ... */ }
  }
}
  • 实现流程:

    1. 解析 DSL: 编写一个解析器(可以用 PEG.js, ANTLR 等工具),将 DSL 文本解析成一个抽象语法树(AST)或一个结构化的配置对象。

    2. 生成代码 / 配置: 根据 AST,自动生成 TypeScript 类型定义、API 调用函数、JSON Schema 等。

    3. 执行引擎: 构建一个执行引擎,它接收 API 名称和参数,根据生成的配置发起网络请求,并进行数据验证和转换。

  • 核心优势:

    • 更高的抽象层次: 专注于业务意图,而非技术实现。

    • 更强的表达力: 可以定义复杂的逻辑,如条件请求、并发请求、数据依赖等。

    • 自动化: 从一份 DSL 定义自动生成多种 artifacts,减少重复工作,保证一致性。

    • 可维护性: 所有 API 契约和数据模型都集中管理,易于查找和修改。

决策维度

考量因素

推荐方案

通信协议

业务实时性要求、数据流向、是否 P2P

- 通用场景: HTTP/HTTPS

- 实时双向: WebSocket

- 服务器推送: SSE

- 音视频 / P2P: WebRTC

请求库

框架生态、功能需求、团队熟悉度

- 首选: Axios (功能全面,生态好)

- 轻量 / 原生: fetch API (需自行封装)

- Angular 项目: @angular/common/http

数据验证与建模

类型安全需求、验证复杂度、可配置性

- TypeScript 项目: Zod /io-ts (类型和验证一体)

- 通用 / 可配置: JSON Schema + Ajv

可配置化程度

项目规模、接口数量、变更频率、团队协作模式

- 中小型项目: 封装请求库 + TypeScript 类型

- 大型 / 频繁变更项目: JSON Schema + 配置文件驱动

- 超大规模 / 复杂逻辑: 自定义 DSL + 代码生成

状态管理工具思考

工具

核心原理

适用生态

核心特性

优缺点

典型应用场景

Zustand

基于 React Hooks发布 - 订阅 模式。通过 create 函数创建一个独立的 store,组件通过 useStore hook 订阅和修改状态。

React, React Native

- 极简 API,学习成本极低。

- 无需 Provider 包裹整个应用。

- 支持 中间件(日志、持久化 persist、不可变 immer 等)。

- 状态可以是任意类型(对象、数组、原始类型)。

- 支持 选择性订阅,只更新使用到的状态切片,性能优秀。

优点:简洁、灵活、高性能、易于集成。

缺点:生态相对 Redux 较小,对于超大型应用的规范约束较弱。

- 中小型 React/RN 项目。

- 追求开发效率和简洁代码的团队。

- 可以作为 Redux 的轻量级替代品。

Redux Toolkit (RTK)

基于 Redux,但进行了封装和简化。核心是 createSlice(自动生成 action types 和 creators)、createAsyncThunk(处理异步逻辑)和内置的 immer(允许直接 “修改” 状态)。

React, React Native (通过 react-redux)

- 官方推荐的 Redux 写法,解决了传统 Redux 的样板代码问题。

- 内置 不可变性 支持(Immer)。

- 简化了 异步逻辑 处理。

- 提供 configureStore,自动配置中间件(如 redux-thunk)。

- 强大的 开发者工具 支持。

优点:生态最成熟、社区庞大、工具链完善、规范统一、适合大型项目。

缺点:相对于 Zustand 仍有一定的学习曲线和模板代码。

- 大型、复杂的 React/RN 应用。

- 团队成员较多,需要强规范来保证代码一致性。

- 对状态变化的可追溯性要求高的场景。

Pinia

Vue 官方推荐的状态管理库,基于 Vue 3 Composition API。核心概念是 defineStore,每个 store 是一个独立的模块。

Vue 3, Nuxt 3

- TypeScript 优先,提供极佳的类型推断。

- 支持 响应式 API,与 Vue 3 的 ref/reactive 完全兼容。

- 支持 Getters(计算属性)、Actions(同步 / 异步方法)。

- 无需手动注册,在组件中直接导入使用。

- 支持 热模块替换 (HMR)

优点:Vue 生态原生支持、类型安全、API 简洁直观、与 Vue Devtools 深度集成。

缺点:仅限 Vue 3 生态。

- 所有 Vue 3 和 Nuxt 3 项目。

- 是 Vuex 的官方替代品。

Jotai/Recoil

基于 原子化状态 (Atomic State) 理念。将状态拆分为最小的、独立的 “原子”(atom)。组件可以订阅任意原子,当原子更新时,只有依赖它的组件会重新渲染。

React, React Native

- 细粒度更新,性能极高。

- 支持 派生状态selector),类似于计算属性。

- Jotai API 更简洁,Recoil 功能更丰富(如 RecoilRootuseRecoilTransaction)。

- 非常适合 状态依赖关系复杂 的场景。

优点:极致的性能优化、状态依赖清晰。

缺点:对于简单状态,写法可能显得繁琐。生态不如 Redux/Zustand 成熟。

- 对渲染性能要求极高的复杂 React 应用。

- 状态之间存在复杂派生关系的场景。

现代框架对状态管理有其特殊考量,尤其是在 服务端渲染 (SSR)静态站点生成 (SSG)增量静态再生 (ISR) 等场景下。

1. Next.js (React)

  • 客户端状态:

    • Zustand/RTK/Jotai: 可以直接使用,与在普通 React 应用中无异。

    • 推荐实践: 在 app 目录下,创建一个 stores 文件夹,将 store 定义为可复用的函数,在组件中直接导入 useStore

  • 服务端状态 (Server State):

    • 内置方案: Next.js 13+ 的 App Router 推荐使用 React Server Components (RSC)fetch API。fetch 在 RSC 中会被自动缓存和去重,非常适合获取服务端数据。

    • 数据获取库:

      • SWR/React Query: 仍然是非常好的选择,用于客户端的数据获取、缓存、重新验证和状态管理。它们可以与客户端状态管理库配合使用(SWR 管理服务器数据,Zustand 管理客户端 UI 状态)。

      • RTK Query: 如果整个项目深度使用 Redux,可以用它来统一管理客户端和服务端状态。

2. Nuxt.js (Vue)

  • 客户端状态:

    • Pinia: Nuxt 3 官方推荐,与框架深度集成。只需在 stores 目录下创建 store 文件,Nuxt 会自动注册。

  • 服务端状态:

    • 内置 useAsyncDatauseFetch: Nuxt 提供了强大的组合式函数来获取数据,支持 SSR/SSG/ISR,并自动处理数据在服务端和客户端之间的传递。

    • Pinia + SSR: Pinia 对 SSR 有良好的支持。可以在 nuxt.config.ts 中配置 pinia 模块,并在 asyncDatafetch 钩子中填充初始状态。

3. Remix (React)

  • 客户端状态:

    • Zustand/RTK: 可以直接使用。

  • 服务端状态:

    • Loader 和 Action: Remix 的核心是通过 loader 函数在服务端加载数据,并通过 action 函数处理表单提交等 mutations。这些数据会自动传递给组件,组件通过 useLoaderDatauseActionData hooks 获取。

    • 嵌套路由数据: Remix 自动并行加载所有嵌套路由的 loader 数据,避免了瀑布流请求。

    • 与客户端状态配合: Remix 鼓励将服务器数据和客户端 UI 状态分离。服务器数据通过 Remix 的 loader/action 管理,客户端状态(如表单输入、UI 开关)可以用 Zustand 等轻量级库管理。

三、后端考量:NestJS 中的状态管理思想

NestJS 是一个后端框架,虽然它不直接处理 “前端状态”,但其设计思想与前端状态管理有共通之处,特别是在 模块化依赖注入 (DI) 方面。

  • 模块化 (Modules): NestJS 应用由多个模块组成,每个模块负责一个特定的业务领域。这类似于前端将状态按业务域拆分为多个 store(如 Pinia 的 defineStore 或 Zustand 的多 store 模式)。

  • 服务 (Services): 业务逻辑和数据处理被封装在服务中。服务通过 依赖注入 机制被注入到控制器或其他服务中。这类似于前端 store 中的 actionsthunks,负责处理核心逻辑。

  • 数据层:

    • Repository Pattern: 通过 TypeORM 或 Prisma 等 ORM 库,NestJS 鼓励使用 Repository 模式来抽象数据访问。这可以看作是后端的 “数据 store”,负责与数据库交互。

    • 缓存: NestJS 提供了 CacheModule,可以轻松地为频繁访问的数据(如用户信息、产品列表)添加缓存(内存、Redis 等)。这与前端的 SWRReact Query 的缓存机制目标一致 ——减少重复计算 / 请求,提高性能

  • 与前端状态的关系:

    • API 契约: 后端通过 RESTful API 或 GraphQL 提供数据,这是前后端状态同步的 “契约”。

    • 状态同步: 前端通过调用后端 API 来 “获取” 或 “修改” 数据,从而同步前后端的状态。后端的状态(数据库中的数据)是单一事实来源 (Single Source of Truth)

四、微前端架构下的状态管理

微前端架构将一个大型应用拆分为多个独立的、可独立开发和部署的小型应用(微应用)。状态管理在这种架构下变得更加复杂。

核心挑战:

  • 应用间通信: 不同微应用之间如何共享状态或传递消息?

  • 全局状态 vs 应用内状态: 哪些状态是全局共享的(如用户信息、主题),哪些是某个微应用独有的?

解决方案:

  1. 基于 URL 的状态共享:

    • 原理: 将需要共享的状态(如当前选中的标签、分页信息)编码到 URL 的查询参数中。

    • 适用场景: 跨应用的简单状态同步,如从一个应用跳转到另一个应用时传递上下文。

  2. 基于 CustomEvent 的事件总线:

    • 原理: 利用浏览器的 CustomEvent API,在主应用中创建一个全局事件总线。微应用可以通过 dispatchEvent 发布事件,或通过 addEventListener 订阅事件。

    • 适用场景: 微应用之间的松散耦合通信,如通知、状态变更广播。

    • 缺点: 缺乏类型安全,事件过多时难以追踪和调试。

  3. 基于 Web Components 的共享组件:

    • 原理: 创建一个包含共享状态逻辑的 Web Component(如 <user-info>),并将其挂载到主应用的全局 DOM 中。微应用通过调用该组件的方法或监听其自定义事件来交互。

    • 适用场景: 共享一些通用的、有 UI 表现的功能模块,如用户登录组件、全局通知组件。

  4. 共享库 (Shared Library):

    • 原理: 将状态管理库(如 Zustand)或一个自定义的状态共享库打包成 UMD 格式,通过 Webpack 的 shared 配置或直接在 index.html 中引入,使其在所有微应用中可用。

    • 适用场景: 需要共享复杂状态逻辑(如一个全局的购物车),且所有微应用技术栈一致(如都是 React)。

    • 缺点: 增加了微应用与共享库的耦合度,版本管理复杂。

  5. 中心化状态服务:

    • 原理: 单独部署一个负责管理全局状态的服务(可以是一个简单的 Node.js 服务或 Redis)。微应用通过调用该服务的 API 来读写全局状态。

    • 适用场景: 对全局状态有强一致性要求,或微应用技术栈异构(React, Vue, Angular 混合)的场景。

    • 缺点: 引入了网络延迟和单点故障风险,架构复杂度最高。

五、与 JSON Schema 的集成思考

JSON Schema 用于描述数据结构,可以与状态管理结合,提升数据处理的健壮性和可维护性。

  • 状态验证:

    • 在状态更新前(如通过 API 获取数据后,或用户提交表单前),使用 JSON Schema 和验证库(如 Ajv)对数据进行验证,确保状态符合预期的结构和约束。

    • 示例: 在 RTK 的 createAsyncThunk 或 Zustand 的 action 中,接收到 API 响应后,先用 Schema 验证 response.data,验证通过再更新状态。

  • 类型生成:

    • 从 JSON Schema 自动生成 TypeScript 类型定义(如使用 json-schema-to-typescript)。这确保了状态的类型安全,并使 Schema 和 Type 保持同步。

    • 示例: 为 API 的请求体和响应体定义 JSON Schema,生成对应的 UserRequestUserResponse 类型,在状态管理中使用这些类型。

  • 表单状态管理:

    • JSON Schema 可以作为表单库的配置源。库(如 react-hook-form + @hookform/resolvers 配合 zodajv)可以根据 Schema 自动生成表单字段、进行表单验证,并管理表单的 defaultValues

    • 优势: 表单的结构、验证规则和数据类型都由一份 Schema 驱动,实现了 “一次定义,多处使用”,极大提升了可维护性。

总结与选型策略

架构 / 场景

推荐状态管理方案

理由

React 单体应用

ZustandRedux Toolkit

Zustand 简洁高效;RTK 适合大型、复杂应用。

Vue 3 单体应用

Pinia

官方推荐,与 Vue 3 深度集成,TypeScript 友好。

Next.js/Nuxt/Remix

框架内置数据获取 + 轻量客户端状态库

Next.js (RSC/fetch)、Nuxt (useAsyncData)、Remix (loader/action) 擅长管理服务端状态; Zustand/Pinia 管理客户端 UI 状态。

React Native 应用

ZustandRedux Toolkit

Zustand 轻量,性能好;RTK 生态完善,适合复杂业务。

微前端架构

事件总线 (CustomEvent) + 共享库 (Zustand)

事件总线实现松耦合通信;共享库管理少量核心全局状态。

追求极致可维护性

JSON Schema + TypeScript + 表单库

Schema 作为 “单一事实来源”,驱动数据验证、类型生成和表单渲染。

拓展了解