Monorepo 工程化面试题:如何设计 Web + Node.js 中间层 + Shared + Scripts 的 Monorepo?

一、面试题:为什么要用 Monorepo 管理 Web、Node.js 中间层、Shared 和 Scripts?

核心思路(一句话)

Monorepo 的核心不是"代码放一个仓库",而是把多个应用和共享包放进同一个依赖图,通过统一依赖管理、类型共享、构建编排、影响分析和缓存实现全站工程化。

解决方案架构图

text 复制代码
                    Monorepo
                       │
        ┌──────────────┼──────────────┐
        │              │              │
      apps           packages        tools
        │              │              │
   ┌────┴────┐    ┌────┴─────┐       │
   │         │    │          │       │
  web      server shared    config  scripts
   │         │    │
   │         │    ├── types
   │         │    ├── utils
   │         │    └── schemas
   │         │
   │         └──────────────┐
   │                        │
   └──────────────┬─────────┘
                  │
          pnpm workspace
                  │
        ┌─────────┴─────────┐
        │                   │
     类型共享             依赖图
        │                   │
   TypeScript          affected analysis
                            │
                     增量构建 + 缓存

典型目录:

text 复制代码
repo/
├── apps/
│   ├── web/
│   │   └── package.json
│   └── server/
│       └── package.json
│
├── packages/
│   ├── shared/
│   │   ├── src/
│   │   └── package.json
│   │
│   ├── browser/
│   │   └── package.json
│   │
│   ├── node/
│   │   └── package.json
│   │
│   └── config/
│       └── package.json
│
├── scripts/
│   └── ...
│
├── pnpm-workspace.yaml
├── package.json
└── turbo.json

二、面试题:Monorepo 和"单仓库多目录"有什么区别?

核心思路(一句话)

真正的 Monorepo 是"一个仓库 + 多个独立 Package + 明确依赖图 + 统一工程能力",不是简单把多个项目放在一个 Git 仓库。

结构化理解

层次 普通单仓库 Monorepo
Git 一个仓库 一个仓库
项目 多目录 多个独立 Package
依赖 容易混乱 Package 间显式依赖
类型共享 手工复制/路径引用 Workspace Package
构建 通常全量 根据依赖图增量构建
缓存 项目自己处理 Turborepo/Nx 等统一处理
影响分析 人工判断 根据依赖图计算
CI 容易全部执行 affected + cache

主要矛盾

text 复制代码
不是:
"代码有没有放到一个仓库?"

而是:
"多个项目之间的依赖关系能不能被机器准确理解和调度?"

这才是 Monorepo 工程化的核心。


三、面试题:pnpm Workspace 在 Monorepo 中解决什么问题?

核心思路(一句话)

pnpm Workspace 解决的是"多个 Package 如何统一管理依赖以及相互引用"的问题,而不是负责完整的增量构建系统。

流程图

text 复制代码
pnpm-workspace.yaml
        ↓
识别 workspace packages
        ↓
每个目录拥有独立 package.json
        ↓
package A
   │
   │ workspace:
   ↓
package B
        ↓
pnpm 建立 workspace 依赖关系
        ↓
形成项目依赖图

例如:

yaml 复制代码
# pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"

Web:

json 复制代码
{
  "name": "@company/web",
  "dependencies": {
    "@company/shared": "workspace:*"
  }
}

Server:

json 复制代码
{
  "name": "@company/server",
  "dependencies": {
    "@company/shared": "workspace:*"
  }
}

注意

workspace:* 的作用主要是:

text 复制代码
告诉包管理器:

这个依赖必须来自当前 Workspace 中的 Package。

它不是:

text 复制代码
自动解决 ESM/CommonJS
自动进行 TypeScript 编译
自动进行浏览器兼容
自动完成增量构建

这些属于其他层次。


四、面试题:Web 和 Node.js 共享一个 Package,为什么容易出现 ESM/CommonJS 和 Node 原生模块问题?

核心思路(一句话)

真正的问题不是"共享代码",而是同一个 Package 被两个运行环境消费:浏览器和 Node.js 对模块格式、运行时 API、构建方式的要求不同。

问题模型

text 复制代码
                 @company/shared
                       │
              ┌────────┴────────┐
              │                 │
           Browser             Node.js
              │                 │
          ESM / Browser      ESM / CJS
              │                 │
        无 fs / path 等       可以使用 fs/path

假设:

ts 复制代码
// shared/src/index.ts

import fs from "node:fs";

export function readConfig() {
  return fs.readFileSync("./config.json", "utf-8");
}

然后:

ts 复制代码
// web/src/index.ts

import { readConfig } from "@company/shared";

问题:

text 复制代码
Web
 ↓
shared
 ↓
fs
 ↓
Node.js 原生模块
 ↓
浏览器无法运行

关键认知

TypeScript 类型共享 ≠ 运行时代码共享。

这是这类题非常重要的区分。


五、面试题:如何设计一个既能给浏览器又能给 Node.js 使用的 Shared Package?

核心思路(一句话)

优先从架构上隔离运行时能力,再通过 exports 条件导出让不同环境获得正确入口;不要单纯依赖构建工具"兜底"。

推荐:

text 复制代码
packages/
├── shared/
│   ├── types/
│   ├── schemas/
│   └── pure-utils/
│
├── browser/
│   └── browser-only-utils/
│
└── node/
    └── node-only-utils/

架构:

text 复制代码
                     Shared
                       │
          ┌────────────┼────────────┐
          │            │            │
        types       pure-utils    schemas
          │            │            │
          └────── Browser + Node ───┘

                 Node-only
                    │
              fs / path / process

                Browser-only
                    │
             window / document

这比:

text 复制代码
一个 shared 包里面什么都放
        ↓
靠 Webpack/Vite 排除 fs

更加稳定。


六、面试题:什么是 package.json 的 exports 条件导出?

核心思路(一句话)

exports 可以根据消费者的解析条件,把同一个 Package 的不同入口暴露给不同运行环境。

例如:

json 复制代码
{
  "name": "@company/utils",
  "exports": {
    ".": {
      "browser": "./dist/browser.js",
      "node": "./dist/node.js",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts",
      "default": "./dist/index.mjs"
    }
  }
}

逻辑:

text 复制代码
消费者
  ↓
解析 @company/utils
  ↓
exports
  ↓
判断条件
  │
  ├── browser → browser.js
  │
  ├── node → node.js
  │
  ├── import → index.mjs
  │
  ├── require → index.cjs
  │
  └── types → index.d.ts

但这里有一个非常重要的面试细节

exports 条件由具体解析器/工具决定,不能简单理解成"浏览器自动选择 browser"。

例如:

text 复制代码
Node.js
Webpack
Vite
TypeScript
Jest

它们的解析条件可能不同。因此面试时最好说:

exports 提供条件导出能力,最终选择哪个条件由 Node.js、打包器、TypeScript 等消费者的模块解析规则决定。

这比简单说"浏览器选择 browser,Node 选择 node"准确得多。


七、面试题:如果工具函数同时被前端和 Node.js 使用,应该怎么设计?

核心思路(一句话)

把"纯逻辑"和"环境能力"分离:纯逻辑共享,Node/Browser API 分层,再通过入口或条件导出连接。

推荐:

text 复制代码
@company/utils
│
├── pure
│   ├── formatDate
│   ├── debounce
│   └── validate
│
├── browser
│   └── storage
│
└── node
    ├── file
    └── process

使用:

ts 复制代码
import { formatDate } from "@company/utils";

Node:

ts 复制代码
import { readJson } from "@company/utils/node";

Browser:

ts 复制代码
import { getStorage } from "@company/utils/browser";

这样依赖边界非常清楚。


八、面试题:如何防止 Node.js 的 fs 被打进浏览器 Bundle?

核心思路(一句话)

第一优先级是让浏览器依赖图根本不要触达 fs;external 或排除配置只是兜底,不是架构解决方案。

正确优先级

text 复制代码
第一层:架构隔离
        ↓
Browser 不依赖 Node-only Package
        ↓
第二层:exports 条件导出
        ↓
Browser 解析 browser 入口
        ↓
第三层:构建工具 external / alias / exclude
        ↓
防止错误依赖继续进入 Bundle

例如 Vite/Rollup 场景:

ts 复制代码
// vite.config.ts

import { defineConfig } from "vite";

export default defineConfig({
  build: {
    rollupOptions: {
      // 这里只适用于明确知道某依赖不会由浏览器运行的情况。
      // 它不是解决 Node API 误进入浏览器依赖图的首选方案。
      external: ["node:fs", "node:path"]
    }
  }
});

更好的方式

不要:

text 复制代码
web
 ↓
shared
 ↓
node-utils
 ↓
fs

而应该:

text 复制代码
web
 ↓
shared-browser
 ↓
纯浏览器代码

以及:

text 复制代码
server
 ↓
shared-node
 ↓
node-utils
 ↓
fs

九、面试题:Shared Package 为什么需要同时生成 ESM、CommonJS 和 TypeScript 类型声明?

核心思路(一句话)

ESM/CommonJS解决运行时模块兼容,.d.ts解决TypeScript类型消费,它们解决的是三个不同问题。

典型产物:

text 复制代码
dist/
├── index.mjs
├── index.cjs
└── index.d.ts

对应:

text 复制代码
ESM消费者
   ↓
index.mjs

CommonJS消费者
   ↓
index.cjs

TypeScript
   ↓
index.d.ts

package.json

json 复制代码
{
  "name": "@company/shared",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  }
}

但要注意

如果整个项目:

text 复制代码
Node.js
  ↓
全部使用 ESM

那么没有必要为了"看起来完整"强行生成 CommonJS。

是否需要双格式,要看:

text 复制代码
消费者是谁?
↓
Node.js版本?
↓
构建工具?
↓
是否存在CommonJS消费者?

十、面试题:如何实现一个完整的 Shared Package?

下面给一个比较适合面试讲解的最小完整方案。

目录

text 复制代码
packages/shared/
├── src/
│   ├── index.ts
│   └── types.ts
├── tsconfig.json
├── package.json
└── tsup.config.ts

src/types.ts

ts 复制代码
// 这个文件只保存类型定义。
// 类型在 TypeScript 编译后不会产生 JavaScript 运行时代码,
// 因此非常适合被 Web 和 Node.js 双方共享。

export interface User {
  id: string;
  name: string;
}

src/index.ts

ts 复制代码
// export type 明确表示这是纯类型导出。
// TypeScript 编译后不会把 User 作为运行时代码打进 Bundle。

export type { User };

// 这是纯 JavaScript 逻辑。
// 没有使用 fs、path、process、window、document 等环境相关 API。
// 因此浏览器和 Node.js 都可以安全使用。

export function formatUser(user: User): string {
  return `${user.id}:${user.name}`;
}

tsup.config.ts

ts 复制代码
import { defineConfig } from "tsup";

export default defineConfig({
  // 构建入口
  entry: ["src/index.ts"],

  // 同时生成 ESM 和 CommonJS。
  // 实际项目是否需要两种格式,需要根据消费者决定。
  format: ["esm", "cjs"],

  // 生成 TypeScript 类型声明文件。
  dts: true,

  // 生产环境压缩。
  minify: true,

  // 清理旧的 dist。
  clean: true,

  // source map 方便调试。
  sourcemap: true
});

package.json

json 复制代码
{
  "name": "@company/shared",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/index.js",
  "module": "./dist/index.mjs",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  },
  "scripts": {
    "build": "tsup"
  },
  "devDependencies": {
    "tsup": "^8.0.0",
    "typescript": "^5.0.0"
  }
}

实际项目中应根据具体构建工具生成的文件名校正 exports 路径,不能机械照抄。


十一、面试题:为什么"给 Shared 包配置 ESM + CommonJS 两套产物"仍然可能解决不了问题?

核心思路(一句话)

模块格式兼容和运行时环境兼容是两个问题。

例如:

text 复制代码
shared
 ↓
index.mjs
 ↓
import fs from "node:fs"

即使:

text 复制代码
ESM ✔
CommonJS ✔

浏览器仍然不能使用:

text 复制代码
node:fs

所以:

text 复制代码
模块格式
    +
运行时环境

必须分别解决。

面试追问

"你生成了 ESM 和 CommonJS,是不是就能同时支持浏览器和 Node?"

正确回答:

不一定。ESM/CommonJS解决的是模块加载格式,而浏览器和Node.js的运行时能力不同。比如fs属于Node.js运行时能力,即使把代码编译成ESM,浏览器仍然无法运行。因此我会优先拆分browser/node入口,再结合exports条件导出,最后才使用构建工具的external等配置作为兜底。


十二、面试题:Monorepo 中如何实现 CI 增量构建?

核心思路(一句话)

先通过 Git Diff 找到变化,再沿 Monorepo 依赖图向上计算受影响的 Package,最后只执行受影响任务,并结合缓存跳过已经完成的任务。

完整流程图

text 复制代码
Git Push
   ↓
git diff
   ↓
找到变化文件
   ↓
映射到 Package
   ↓
计算依赖图
   ↓
找到受影响 Package
   ↓
┌─────────────────────┐
│ affected packages   │
│                     │
│ web                 │
│ server              │
│ shared              │
└─────────────────────┘
          ↓
依赖拓扑排序
          ↓
检查任务缓存
     ┌────┴────┐
     ↓         ↓
   Cache Hit  Cache Miss
     ↓         ↓
   复用结果   执行构建
     └────┬────┘
          ↓
       测试
          ↓
       部署

十三、面试题:如果 shared 修改了,如何知道 Web 和 Server 都需要重新构建?

核心思路(一句话)

关键不是"看谁改了",而是沿依赖图反向查找"谁依赖了它"。

假设:

text 复制代码
web ──────┐
          ↓
        shared
          ↑
server ───┘

修改:

text 复制代码
shared

影响:

text 复制代码
shared
  ↓
web
server

所以:

text 复制代码
shared changed
      ↓
affected(shared)
      ↓
shared + web + server

十四、面试题:为什么不能只根据 git diff 判断构建哪些项目?

核心思路(一句话)

Git Diff只能告诉你"文件发生了变化",依赖图才能告诉你"变化会影响谁"。

例如:

text 复制代码
git diff
 ↓
packages/shared/src/types.ts

不能简单得到:

text 复制代码
只构建 shared

因为:

text 复制代码
web ───→ shared
server ─→ shared

真正需要:

text 复制代码
shared
 ↓
反向依赖
 ↓
web + server

因此:

text 复制代码
Git Diff
+
Workspace Package Graph
=
Affected Analysis

十五、面试题:Turborepo / Nx 在这里解决什么问题?

核心思路(一句话)

pnpm负责Workspace依赖管理,Turborepo/Nx负责任务编排、依赖拓扑、Affected分析和缓存;三者职责不同。

工具职责

text 复制代码
                Monorepo
                   │
       ┌───────────┼────────────┐
       ↓           ↓            ↓
     pnpm       Turborepo       Nx
       │           │            │
  依赖管理       任务编排      任务编排
  workspace      依赖执行      影响分析
  安装依赖       并行执行      缓存
               本地缓存
               远程缓存

一个常见误区

不要说:

"pnpm负责Monorepo所有事情。"

更准确:

pnpm负责Workspace和依赖管理;Turborepo或Nx负责基于任务依赖图的构建编排、缓存和增量执行。


十六、面试题:什么是任务缓存?为什么能够把 CI 时间显著降低?

核心思路(一句话)

任务缓存的核心是:输入没有变化,输出就可以复用,不必重复执行构建。

流程

text 复制代码
Package
  ↓
源代码 + 配置 + 依赖 + 环境变量
  ↓
计算任务 Hash
  ↓
      ┌─────────────┐
      │ Cache Store │
      └──────┬──────┘
             │
       ┌─────┴─────┐
       ↓           ↓
    Hash存在     Hash不存在
       ↓           ↓
    恢复产物      执行任务
       ↓           ↓
    Cache Hit    Cache Miss

例如:

text 复制代码
shared build
Hash = ABC123

第一次:
ABC123不存在
→ build
→ 保存dist

第二次:
ABC123存在
→ 直接恢复dist
→ 不再build

远程缓存

text 复制代码
Developer A
    ↓
Build
    ↓
Remote Cache
    ↑
    │
CI
    ↓
发现相同 Hash
    ↓
直接恢复构建结果

这就是为什么团队规模变大以后,远程缓存价值非常高。


十七、面试题:如果修改的是 Shared 类型,影响分析应该怎么做?

这是这道题最值得深入回答的地方。

核心思路(一句话)

类型变化虽然可能不产生运行时代码,但它可能改变消费者的类型检查结果,因此不能简单认为"只改类型就不需要构建"。

例如:

ts 复制代码
// shared
export interface User {
  id: string;
}

修改:

ts 复制代码
export interface User {
  id: number;
}

依赖:

text 复制代码
shared
 ├── web
 └── server

那么:

text 复制代码
shared type changed
        ↓
web type-check
server type-check

至少应该触发:

text 复制代码
typecheck
lint
test

至于:

text 复制代码
production build

是否一定执行,要看你的 CI 策略和任务依赖。

更成熟的任务模型

text 复制代码
shared:typecheck
       ↓
web:typecheck
server:typecheck

shared:build
       ↓
web:build
server:build

可以把:

text 复制代码
build
test
lint
typecheck

设计成不同任务,而不是所有变化都执行完整 Pipeline。


十八、面试题:如何处理"类型共享"和"运行时代码共享"?

核心思路(一句话)

类型共享和运行时共享应该分离设计,这是 Monorepo 中非常重要的架构边界。

推荐:

text 复制代码
packages/
│
├── types/
│   └── 纯TypeScript类型
│
├── schemas/
│   └── 运行时Schema
│
├── utils/
│   └── 纯JS逻辑
│
├── browser/
│   └── 浏览器API
│
└── node/
    └── Node.js API

为什么 Schema 很重要?

例如前后端共享:

ts 复制代码
interface CreateUserRequest {
  name: string;
}

这个类型:

text 复制代码
TypeScript编译后不存在

运行时无法验证:

text 复制代码
HTTP 请求到底是不是合法数据?

可以使用:

text 复制代码
Zod

设计:

text 复制代码
             shared schema
                   │
        ┌──────────┴──────────┐
        ↓                     ↓
     Browser                Node.js
        │                     │
    类型推导                请求校验
        │                     │
        └──────────┬──────────┘
                   ↓
             同一份协议定义

这比只共享 .d.ts 更完整。


十九、面试题:如果让你设计一个 AI 前端 + Node.js BFF 的 Monorepo,你会怎么设计?

核心思路(一句话)

按照"应用层---共享协议层---运行时能力层---工程工具层"分层,保证浏览器和 Node.js 的依赖边界清晰。

推荐架构

text 复制代码
repo
│
├── apps
│   ├── ai-web
│   │   └── 浏览器应用
│   │
│   └── ai-server
│       └── Node.js BFF
│
├── packages
│   │
│   ├── contracts
│   │   ├── types
│   │   └── schemas
│   │
│   ├── utils
│   │   └── 纯逻辑
│   │
│   ├── browser
│   │   └── 浏览器能力
│   │
│   ├── node
│   │   └── Node.js能力
│   │
│   └── config
│       └── ESLint / TypeScript等配置
│
└── scripts
    └── 构建、部署、代码生成

依赖方向:

text 复制代码
                   contracts
                  ↙         ↘
              ai-web      ai-server
                 ↓            ↓
              browser       node
                  \          /
                    utils

严格避免:

text 复制代码
ai-web
  ↓
ai-server
  ↓
node/fs

以及:

text 复制代码
contracts
  ↓
fs / path

因为 contracts 应该尽可能成为跨运行环境的最底层共享协议层。


二十、这道题真正考什么?

主要矛盾

不是会不会写 pnpm-workspace.yaml,而是能不能建立"多 Package + 多运行环境 + 依赖图 + 增量构建"的完整工程模型。

次要矛盾

text 复制代码
① ESM / CommonJS
② exports 条件导出
③ TypeScript 类型声明
④ Browser / Node Runtime 隔离
⑤ affected analysis
⑥ 本地/远程缓存
⑦ CI 任务编排

把它们串起来:

text 复制代码
                Monorepo
                    │
          ┌─────────┴─────────┐
          ↓                   ↓
       Package             Runtime
        依赖图             环境边界
          │                   │
          ↓                   ↓
   affected analysis      exports
          │                   │
          ↓                   ↓
    增量构建/测试       ESM/CommonJS
          │                   │
          └─────────┬─────────┘
                    ↓
                 Cache
                    ↓
                   CI

二十一、面试官继续追问:如果你只能选择一个方案,你怎么回答?

不要直接开始讲工具。

应该按照:

text 复制代码
问题
 ↓
原因
 ↓
架构
 ↓
工具
 ↓
异常场景
 ↓
优化

回答。

例如:

我不会把 Monorepo 简单理解成一个 Git 仓库。这个场景真正的问题是 Web 和 Node.js 处于不同运行环境,共享 Package 同时涉及模块格式、运行时依赖和类型共享,所以我首先会划清依赖边界。

Web 和 Node.js 都依赖纯类型、Schema 和纯工具函数;Node-only 的 fs、path 等能力单独放到 Node Package,Browser-only 能力单独放到 Browser Package。对于确实需要同时支持不同模块消费者的 Package,再通过 exports 提供 import、require、types 等条件入口。

Package 管理使用 pnpm Workspace,任务编排使用 Turborepo 或 Nx。CI 不根据 Git Diff 简单决定构建范围,而是先定位变化 Package,再沿依赖图计算 affected Package,最后结合任务缓存,只执行受影响且没有缓存的任务。

所以这套方案解决的其实是四个问题:运行环境隔离、模块格式兼容、依赖影响分析、增量构建缓存。

这个回答已经覆盖了整道题的核心。


二十二、最终「满分答案」------能背下来 + 能展开讲 + 能应对追问

面试题

如何使用 Monorepo 管理 AI 前端、Node.js 中间层、共享类型和工具脚本?

核心思路(一句话)

Monorepo 的核心不是"一仓多项目",而是利用 Package 依赖图解决共享、运行时隔离、条件导出、影响分析和增量构建。

架构图

text 复制代码
                         Monorepo
                            │
        ┌───────────────────┼───────────────────┐
        ↓                   ↓                   ↓
      apps               packages             scripts
        │                   │
   ┌────┴────┐       ┌──────┼────────┐
   ↓         ↓       ↓      ↓        ↓
  web      server   types  utils    node/browser
   │         │       │      │
   └────┬────┴───────┴──────┘
        ↓
     Workspace
        ↓
    Package Graph
        ↓
 Git Diff → Affected Analysis
        ↓
   依赖拓扑排序
        ↓
   Cache Hit / Miss
      ↙       ↘
   复用结果    执行任务

第一层:Package 划分

text 复制代码
apps/web
apps/server

packages/types
packages/utils
packages/browser
packages/node
packages/config

scripts/

不要让一个 shared 包同时包含浏览器代码和 Node.js 的 fs、path。


第二层:模块和运行时兼容

text 复制代码
exports
  │
  ├── types   → .d.ts
  ├── import  → ESM
  ├── require → CommonJS
  ├── browser → Browser入口
  └── node    → Node入口

ESM/CommonJS解决模块格式;Browser/Node入口解决运行时环境。两者不能混为一谈。


第三层:依赖管理

text 复制代码
pnpm Workspace
      ↓
workspace:*
      ↓
Package依赖图

pnpm负责:

text 复制代码
Workspace
依赖安装
Package引用

Turborepo/Nx负责:

text 复制代码
任务依赖
Affected
并行执行
缓存

第四层:CI 增量构建

text 复制代码
Git Diff
   ↓
变化 Package
   ↓
依赖图反向追踪
   ↓
Affected Package
   ↓
typecheck / test / build
   ↓
检查缓存
   ↓
Cache Hit → 直接复用
Cache Miss → 执行并缓存

例如:

text 复制代码
shared 修改
   ↓
shared
 ├──→ web
 └──→ server
   ↓
shared + web + server

最后一针见血

普通开发者回答的是"pnpm Workspace 怎么配";高级前端回答的是"不同运行环境如何隔离";真正的工程化回答是"如何建立 Package 依赖图,并基于依赖图完成条件导出、影响分析、增量构建和缓存"。

这道题真正考的不是 Monorepo API,而是你能不能把"代码组织 → 模块解析 → 运行时边界 → 依赖图 → CI 增量构建"串成一套完整工程体系。

相关推荐
IT_陈寒1 小时前
Redis集群切换主节点时,服务竟然全员掉线
前端·人工智能·后端
wangbing11251 小时前
开发指南147-WebSocket-前端
前端·websocket·网络协议
吴声子夜歌1 小时前
Nginx应用与运维——Nginx Web服务应用实战(伪流媒体服务器的搭建)
运维·前端·nginx
ss2732 小时前
AI全栈实战 | 2.4-01 Node 与前端工程化:前端为什么离不开 Node,它不只是构建工具的宿主
前端
aixingpan2 小时前
aixingpan.cn API开发文档:api_docs_transit接口指南
前端·php
鬓戈2 小时前
Volta:解决 nvm「不能同时存在多个 Node 版本」的痛点
前端
吴声子夜歌2 小时前
Nginx应用与运维——Nginx Web服务应用实战(HTTP增强协议服务器的搭建)
运维·前端·nginx
扶风ff11 小时前
新品知识更新太快?用练题簿在线刷题,安排企业培训的小测与复盘
前端·学习·小程序
对空六课11 小时前
支持注意力分析的热力图工具有哪些?
前端·数据库·数据分析