[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"menus-public":3,"blog-categories":83,"blog-list":94},[4,15,43,66],{"id":5,"title":6,"link":7,"icon":8,"target":9,"sort_order":10,"is_active":11,"parent_id":12,"created_at":13,"children":14},10001,"首页","\u002F","","_blank",0,true,null,"2026-04-04T12:03:45",[],{"id":16,"title":17,"link":18,"icon":8,"target":19,"sort_order":20,"is_active":11,"parent_id":12,"created_at":21,"children":22},10038,"墨言博客","\u002Fblog","_self",1,"2026-10-30T23:08:14",[23,29,36],{"id":24,"title":25,"link":26,"icon":8,"target":19,"sort_order":20,"is_active":11,"parent_id":16,"created_at":27,"children":28},10039,"技术前沿","\u002Fblog?category=10001","2026-10-30T23:10:46",[],{"id":30,"title":31,"link":32,"icon":8,"target":19,"sort_order":33,"is_active":11,"parent_id":16,"created_at":34,"children":35},10042,"生活随笔","\u002Fblog?category=20007",2,"2026-10-30T23:55:19",[],{"id":37,"title":38,"link":39,"icon":8,"target":19,"sort_order":40,"is_active":11,"parent_id":16,"created_at":41,"children":42},10043,"旅游日记","\u002Fblog?category=20006",3,"2026-10-31T00:00:11",[],{"id":33,"title":44,"link":45,"icon":8,"target":19,"sort_order":33,"is_active":11,"parent_id":12,"created_at":46,"children":47},"新闻中心","\u002Fnews","2026-10-24T19:09:40",[48,54,60],{"id":49,"title":50,"link":51,"icon":8,"target":19,"sort_order":20,"is_active":11,"parent_id":33,"created_at":52,"children":53},10046,"产品动态","\u002Fnews?category=20010","2026-10-31T00:17:38",[],{"id":55,"title":56,"link":57,"icon":8,"target":19,"sort_order":33,"is_active":11,"parent_id":33,"created_at":58,"children":59},10047,"新闻热点","\u002Fnews?category=20009","2026-10-31T00:17:55",[],{"id":61,"title":62,"link":63,"icon":8,"target":19,"sort_order":40,"is_active":11,"parent_id":33,"created_at":64,"children":65},10045,"公司动态","\u002Fnews?category=20008","2026-10-31T00:17:11",[],{"id":67,"title":68,"link":69,"icon":8,"target":19,"sort_order":67,"is_active":11,"parent_id":12,"created_at":46,"children":70},4,"精彩视频","\u002Fvideo",[71,77],{"id":72,"title":73,"link":74,"icon":8,"target":19,"sort_order":10,"is_active":11,"parent_id":67,"created_at":75,"children":76},13,"活动记录","\u002Fvideo?category=6","2026-10-25T21:04:39",[],{"id":78,"title":79,"link":80,"icon":8,"target":19,"sort_order":20,"is_active":11,"parent_id":67,"created_at":81,"children":82},11,"产品展示","\u002Fvideo?category=5","2026-10-25T21:02:59",[],[84,88,91],{"id":5,"name":85,"type":86,"sort":40,"created_at":87},"技术分享","blog","2026-04-04T12:03:44",{"id":89,"name":31,"type":86,"sort":33,"created_at":90},20007,"2026-10-30T23:52:45",{"id":92,"name":38,"type":86,"sort":20,"created_at":93},20006,"2026-10-30T23:52:19",{"list":95,"total":166,"page":20,"size":167},[96,105,113,120,128,136,144,152,159],{"id":97,"category_id":5,"title":98,"author":8,"summary":99,"cover":100,"content":101,"tags":8,"seo_title":8,"seo_description":8,"seo_keywords":8,"og_image":8,"views":102,"comment_count":10,"is_top":103,"is_published":11,"created_at":104},10057,"动态网站前后端布暑","本项目是一个以内容呈现为核心、带完整用户与权限体系的前后端分离 Web 应用，涵盖博客、新闻、视频、动态导航、轮播图五大内容功能域，以及用户管理（认证、角色权限、个人资料、头像） 这一基础能力域。前端负责 SSR\u002FSSG 渲染与用户交互，后端提供纯 REST API 与数据持久化。","\u002Fstatic\u002Fuploads\u002Fblogs\u002F1793240130667-92277984.jpg","# 项目总览：Nuxt 3 + NestJS + Prisma 前后端分离内容平台\n\n## 1. 项目定位与技术选型\n\n本项目是一个以内容呈现为核心、带完整用户与权限体系的前后端分离 Web 应用，涵盖**博客、新闻、视频、动态导航、轮播图**五大内容功能域，以及**用户管理（认证、角色权限、个人资料、头像）** 这一基础能力域。前端负责 SSR\u002FSSG 渲染与用户交互，后端提供纯 REST API 与数据持久化。\n\n**技术栈**\n\n| 层级 | 技术 | 版本基线 | 角色 |\n|:---|:---|:---|:---|\n| 前端 | Nuxt 3 | ^3.14+ | SSR\u002FSSG、页面路由、API 代理、状态管理 |\n| 后端 | NestJS | ^10.x | REST API、模块化业务逻辑、认证授权 |\n| ORM | **Prisma** | ^6.x \u002F ^7.x | 类型安全的数据访问与迁移 |\n| 数据库 | PostgreSQL | ^16 | 主数据存储 |\n| 语言 | TypeScript（全栈 strict） | — | 前后端统一语言，共享类型契约 |\n| 对象存储 | S3 兼容（MinIO \u002F R2 \u002F S3） | — | 头像与媒体文件直传 |\n\n### 1.1 为什么选 Prisma 而不是 TypeORM\n\n这是一个需要明确记录的决策。TypeORM 在 NestJS 生态中历史更久，不少企业级模板默认使用它；但 Prisma 在**查询边界的类型安全**上明显更深一层：`select`、`include`、`where` 的返回形状由 schema 编译生成，字段重命名会在编译期报错，而不是等到运行时才暴露。对于本项目这种内容类型多、关联关系频繁（文章 ↔ 分类 ↔ 标签、用户 ↔ 角色 ↔ 权限、视频 ↔ 作者）的场景，Prisma 生成的精确类型能显著降低 DTO 与数据库之间的漂移风险。Prisma 的迁移文件也是自动生成的，`prisma migrate dev` 会基于 schema 差异产出 SQL，对独立开发或小团队更为顺手。\n\n代价也需要提前知晓：Prisma 的嵌套过滤写法比 TypeORM 的 QueryBuilder 更啰嗦，`prisma generate` 在活跃开发中会增加一步操作感，极端高吞吐场景下 WASM 引擎仍略慢于调优后的查询构建器。对本项目的预期流量而言，这些代价可以接受。如果后续出现极复杂的报表查询，可以在 NestJS 中通过 `$queryRaw` 局部绕过，不必整体切换 ORM。\n\n### 1.2 为什么选独立 NestJS 后端而非 Nitro 全栈\n\nNuxt 3 的 Nitro 足以承载 MVP 级后端，但本项目预期包含**角色权限、内容审核、对象存储直传、多模块业务规则**，且未来可能扩展为多端消费（Web + 小程序 + 移动端）。将后端独立为 NestJS 服务，可以获得更强的架构约束、更清晰的模块边界和更成熟的 DI 生态；前后端通过 REST 通信，天然支持横向扩展与独立部署。代价是需要维护两个进程与一套 API 契约，这部分复杂度由 Prisma 的类型导出与轻量类型共享机制来缓解。\n\n---\n\n## 2. 项目结构\n\n采用**扁平双应用结构**，而不是 Nx monorepo。Nx 适合多应用、多团队共享库的大型工程，但会引入生成器、项目图和缓存层的心智负担。对于功能域明确、前后端各一的中型项目，两个独立目录配合同步的类型文件已经足够。\n\n```\nproject-root\u002F\n├── frontend\u002F                      # Nuxt 3 应用\n│   ├── components\u002F\n│   │   ├── content\u002F               # 文章卡片、视频卡片、轮播图等展示组件\n│   │   ├── navigation\u002F            # 动态导航渲染\n│   │   ├── account\u002F               # 个人资料、头像上传、密码修改\n│   │   └── ui\u002F\n│   ├── composables\u002F\n│   │   ├── useApi.ts\n│   │   ├── useContent.ts\n│   │   └── useAuth.ts             # 登录态、角色、权限、can()\n│   ├── layouts\u002F\n│   ├── middleware\u002F\n│   │   ├── auth.ts                # 登录校验\n│   │   └── admin.ts               # ADMIN 角色校验\n│   ├── pages\u002F\n│   │   ├── index.vue              # 首页（轮播图 + 最新内容聚合）\n│   │   ├── blog\u002F\n│   │   ├── news\u002F\n│   │   ├── videos\u002F\n│   │   ├── login.vue\n│   │   ├── account\u002F\n│   │   │   ├── profile.vue        # 个人资料与头像\n│   │   │   └── security.vue       # 修改密码、会话管理\n│   │   └── admin\u002F                 # 内容与用户管理（受保护）\n│   ├── server\u002F\n│   │   └── api\u002F\n│   │       └── [...path].ts       # catch-all 代理到 NestJS\n│   ├── nuxt.config.ts\n│   └── .env\n│\n├── backend\u002F                       # NestJS 应用\n│   ├── src\u002F\n│   │   ├── common\u002F                # 全局拦截器、过滤器、分页 DTO、装饰器\n│   │   ├── prisma\u002F                # PrismaModule + PrismaService（@Global）\n│   │   ├── auth\u002F                  # 登录、JWT、刷新令牌\n│   │   ├── users\u002F                 # 用户、个人资料、头像、角色管理\n│   │   ├── content\u002F               # 内容核心域\n│   │   │   ├── posts\u002F             # 博客 + 新闻（统一 Post 模型）\n│   │   │   ├── videos\u002F\n│   │   │   ├── categories\u002F\n│   │   │   └── tags\u002F\n│   │   ├── banners\u002F               # 轮播图\n│   │   ├── navigation\u002F            # 动态导航菜单\n│   │   └── app.module.ts\n│   ├── prisma\u002F\n│   │   ├── schema.prisma\n│   │   └── seed.ts                # 内置角色与权限种子\n│   ├── .env\n│   └── package.json\n│\n└── README.md\n```\n\n**共享类型的处理**：优先在 `frontend\u002Ftypes\u002F` 中通过 `import type` 引用后端导出的 DTO 类型定义。如果前后端不在同一 TypeScript 项目引用范围内，采用“后端导出 `.d.ts` → 前端复制为契约文件”的轻量方式，避免为此搭建完整的 monorepo 共享包体系。\n\n---\n\n## 3. 后端模块划分（NestJS）\n\nNestJS 的核心优势在于**按领域分 Module**，每个模块自带 controller \u002F service \u002F DTO，边界清晰。模块划分如下。\n\n### 3.1 Prisma 全局模块\n\n`PrismaService` 继承 `PrismaClient`，实现 `OnModuleInit` 与 `OnModuleDestroy`，在应用启动时 `$connect()`，关闭时 `$disconnect()`。使用 `@Global()` 装饰 `PrismaModule`，使所有业务模块无需重复 import 即可注入 PrismaService。\n\n```\nbackend\u002Fsrc\u002Fprisma\u002F\n├── prisma.module.ts      # @Global()\n└── prisma.service.ts     # extends PrismaClient, implements OnModuleInit\u002FOnModuleDestroy\n```\n\n### 3.2 `AuthModule` —— 认证\n\n负责注册、登录、刷新令牌与登出。登录成功后签发 Access Token（短期，如 15 分钟）与 Refresh Token（长期，如 7 天），Refresh Token 的哈希存入 `Session` 表，支持令牌轮换与服务端主动吊销。密码使用 `argon2` 或 `bcrypt` 哈希，**绝不存储明文**。\n\n`JwtAuthGuard` 注册为全局守卫，默认所有路由都需要登录，再用 `@Public()` 装饰器标记登录、注册等公开端点。\n\n### 3.3 `UsersModule` —— 用户、资料、头像、角色\n\n提供两类端点，权限要求截然不同：\n\n**个人资料端点（登录用户本人可操作）**\n\n| 方法 | 路径 | 说明 |\n|:---|:---|:---|\n| `GET` | `\u002Fusers\u002Fme` | 获取当前登录用户的完整资料（含 profile、roles、permissions） |\n| `PATCH` | `\u002Fusers\u002Fme` | 更新 `displayName`、`bio`、`website`、`timezone` |\n| `POST` | `\u002Fusers\u002Fme\u002Favatar\u002Fpresign` | 获取头像上传的预签名 URL |\n| `PATCH` | `\u002Fusers\u002Fme\u002Favatar` | 确认头像已上传，更新 `avatarUrl` 与 `avatarKey` |\n| `DELETE` | `\u002Fusers\u002Fme\u002Favatar` | 移除头像，回退到默认占位图 |\n| `PATCH` | `\u002Fusers\u002Fme\u002Fpassword` | 修改密码（需验证旧密码） |\n\n**管理端点（仅 `ADMIN` 可访问）**\n\n| 方法 | 路径 | 说明 |\n|:---|:---|:---|\n| `GET` | `\u002Fadmin\u002Fusers` | 分页用户列表，支持按邮箱、状态、角色筛选 |\n| `PATCH` | `\u002Fadmin\u002Fusers\u002F:id\u002Fstatus` | 启用 \u002F 禁用用户 |\n| `PUT` | `\u002Fadmin\u002Fusers\u002F:id\u002Froles` | 全量替换用户的角色集合 |\n| `GET` | `\u002Fadmin\u002Froles` | 角色列表（含每个角色的权限） |\n| `PATCH` | `\u002Fadmin\u002Froles\u002F:id\u002Fpermissions` | 调整角色的权限组合 |\n\n### 3.4 内容核心模块：`PostsModule`\n\n**博客与新闻共用一个 `Post` 模型，通过 `type` 字段区分**（`\"blog\"` 或 `\"news\"`）。两者在数据形态上高度重叠（标题、slug、正文、摘要、封面、作者、发布时间、状态），分表会带来大量重复的 controller 和 service；统一模型配合类型过滤，在 NestJS 中可以很自然地实现“同一个模块，两套查询入口”。\n\n`PostsController` 提供：\n\n- `GET \u002Fposts?type=blog&page=1&limit=10&category=...&tag=...&status=published`\n- `GET \u002Fposts\u002F:slug`\n- `POST \u002Fposts`（拥有 `post:create` 权限的角色）\n- `PATCH \u002Fposts\u002F:id`（拥有 `post:update` 权限的角色）\n- `DELETE \u002Fposts\u002F:id`（拥有 `post:delete` 权限的角色，软删除）\n\n分页通过通用的 `PaginationQueryDto`（`page`、`limit`）配合 Prisma 的 `skip` \u002F `take` 实现，返回结构统一为 `{ data: [], meta: { total, page, limit, totalPages } }`。\n\n**作者边界**：`AUTHOR` 角色在写操作时，service 层通过 `authorId` 过滤，确保其仅能修改\u002F删除自己创建的文章。这一约束可在后续引入 CASL 时升级为行级权限规则。\n\n### 3.5 `VideosModule`\n\n视频是独立模型，不复用 `Post`。原因在于视频的元数据维度不同：需要 `duration`、`coverUrl`、`videoUrl`、`provider`（自托管 \u002F 外链）、`viewCount` 等字段，且未来可能接入 HLS 分片播放。将视频与文章硬合并到一个模型中会导致大量字段为 `null`，得不偿失。\n\n视频播放本身不在 NestJS 中做流媒体转码或切片；后端只负责元数据 CRUD 与文件上传对接，前端通过 HLS 播放器消费视频地址。\n\n### 3.6 `BannersModule` —— 轮播图\n\n轻量但独立的模块，包含：\n\n- `imageUrl`、`title`、`subtitle`、`linkUrl`、`sortOrder`、`isActive`、`startAt`、`endAt`\n\n对外提供 `GET \u002Fbanners\u002Factive`，只返回当前在生效时间内且 `isActive = true` 的轮播项，按 `sortOrder` 升序。轮播图不直接关联到具体的 Post 或 Video，而是通过 `linkUrl` 自由指向任意前端路由或外部链接，保持模块的低耦合。\n\n### 3.7 `NavigationModule` —— 动态导航\n\n动态导航的复杂度取决于项目是否需要**基于角色的菜单可见性**。当前采用轻量模型：`Menu { id, label, path, icon, sortOrder, parentId, isExternal, isActive }`，返回树形结构供前端渲染。\n\n如果后续需要管理后台的**动态路由与权限菜单**，则扩展为 `User ↔ Role ↔ Menu` 多对多关联，后端根据当前用户角色返回过滤后的菜单树。初期先实现**无权限的前台导航**，将 RBAC 菜单作为后续迭代内容。\n\n---\n\n## 4. 数据模型（Prisma Schema 核心片段）\n\n以下为 `schema.prisma` 中与核心功能直接相关的模型设计。\n\n```prisma\n\u002F\u002F ═══════════════════════════════════════════════\n\u002F\u002F 用户与权限\n\u002F\u002F ═══════════════════════════════════════════════\n\nmodel User {\n  id           String    @id @default(cuid())\n  email        String    @unique\n  passwordHash String\n  isActive     Boolean   @default(true)\n  lastLoginAt  DateTime?\n  createdAt    DateTime  @default(now())\n  updatedAt    DateTime  @updatedAt\n\n  profile      UserProfile?\n  userRoles    UserRole[]\n  posts        Post[]\n  sessions     Session[]\n\n  @@index([email])\n}\n\nmodel UserProfile {\n  id          String   @id @default(cuid())\n  userId      String   @unique\n  user        User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n  displayName String\n  bio         String?\n  avatarUrl   String?\n  avatarKey   String?\n  website     String?\n  timezone    String?  @default(\"Asia\u002FShanghai\")\n  createdAt   DateTime @default(now())\n  updatedAt   DateTime @updatedAt\n}\n\nmodel Role {\n  id          String           @id @default(cuid())\n  name        String           @unique         \u002F\u002F ADMIN \u002F EDITOR \u002F AUTHOR \u002F MEMBER\n  description String?\n  isSystem    Boolean          @default(false) \u002F\u002F 系统内置角色，不允许删除\n  userRoles   UserRole[]\n  permissions RolePermission[]\n\n  @@index([name])\n}\n\nmodel Permission {\n  id          String           @id @default(cuid())\n  resource    String                           \u002F\u002F post \u002F video \u002F banner \u002F menu \u002F user\n  action      String                           \u002F\u002F create \u002F read \u002F update \u002F delete \u002F manage\n  description String?\n  rolePerms   RolePermission[]\n\n  @@unique([resource, action])\n}\n\nmodel UserRole {\n  userId String\n  roleId String\n  user   User @relation(fields: [userId], references: [id], onDelete: Cascade)\n  role   Role @relation(fields: [roleId], references: [id], onDelete: Cascade)\n\n  @@id([userId, roleId])\n  @@index([roleId])\n}\n\nmodel RolePermission {\n  roleId       String\n  permissionId String\n  role         Role       @relation(fields: [roleId], references: [id], onDelete: Cascade)\n  permission   Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)\n\n  @@id([roleId, permissionId])\n  @@index([permissionId])\n}\n\nmodel Session {\n  id               String    @id @default(cuid())\n  userId           String\n  user             User      @relation(fields: [userId], references: [id], onDelete: Cascade)\n  refreshTokenHash String                    \u002F\u002F 存储刷新令牌的哈希，不存明文\n  userAgent        String?\n  ip               String?\n  expiresAt        DateTime\n  revokedAt        DateTime?\n  createdAt        DateTime  @default(now())\n\n  @@index([userId, expiresAt])\n}\n\n\u002F\u002F ═══════════════════════════════════════════════\n\u002F\u002F 内容核心：博客 + 新闻\n\u002F\u002F ═══════════════════════════════════════════════\n\nmodel Post {\n  id          String     @id @default(cuid())\n  type        PostType   @default(BLOG)          \u002F\u002F BLOG | NEWS\n  title       String\n  slug        String     @unique\n  excerpt     String?\n  body        String                              \u002F\u002F Markdown\n  coverUrl    String?\n  status      PostStatus @default(DRAFT)         \u002F\u002F DRAFT | PUBLISHED | ARCHIVED\n  isFeatured  Boolean    @default(false)\n  readTime    Int?\n  publishedAt DateTime?\n  deletedAt   DateTime?                           \u002F\u002F 软删除\n  authorId    String\n  author      User       @relation(fields: [authorId], references: [id])\n  categoryId  String?\n  category    Category?  @relation(fields: [categoryId], references: [id])\n  tags        PostTag[]\n  createdAt   DateTime   @default(now())\n  updatedAt   DateTime   @updatedAt\n\n  @@index([type, status, publishedAt])\n  @@index([slug])\n  @@index([deletedAt])\n}\n\nenum PostType   { BLOG NEWS }\nenum PostStatus { DRAFT PUBLISHED ARCHIVED }\n\n\u002F\u002F ═══════════════════════════════════════════════\n\u002F\u002F 分类与标签\n\u002F\u002F ═══════════════════════════════════════════════\n\nmodel Category {\n  id    String @id @default(cuid())\n  name  String @unique\n  slug  String @unique\n  posts Post[]\n}\n\nmodel Tag {\n  id    String    @id @default(cuid())\n  name  String    @unique @db.VarChar(50)        \u002F\u002F 存储小写\n  slug  String    @unique\n  posts PostTag[]\n}\n\nmodel PostTag {\n  postId String\n  tagId  String\n  post   Post @relation(fields: [postId], references: [id], onDelete: Cascade)\n  tag    Tag  @relation(fields: [tagId], references: [id], onDelete: Cascade)\n\n  @@id([postId, tagId])\n  @@index([tagId])\n}\n\n\u002F\u002F ═══════════════════════════════════════════════\n\u002F\u002F 视频\n\u002F\u002F ═══════════════════════════════════════════════\n\nmodel Video {\n  id          String        @id @default(cuid())\n  title       String\n  slug        String        @unique\n  description String?\n  coverUrl    String?\n  videoUrl    String                              \u002F\u002F 播放地址（MP4 \u002F HLS master）\n  provider    VideoProvider @default(SELF_HOSTED)\n  duration    Int?                                \u002F\u002F 秒\n  viewCount   Int           @default(0)\n  status      PostStatus    @default(DRAFT)\n  publishedAt DateTime?\n  deletedAt   DateTime?\n  createdAt   DateTime      @default(now())\n  updatedAt   DateTime      @updatedAt\n\n  @@index([status, publishedAt])\n}\n\nenum VideoProvider { SELF_HOSTED EXTERNAL HLS }\n\n\u002F\u002F ═══════════════════════════════════════════════\n\u002F\u002F 轮播图\n\u002F\u002F ═══════════════════════════════════════════════\n\nmodel Banner {\n  id        String    @id @default(cuid())\n  imageUrl  String\n  title     String?\n  subtitle  String?\n  linkUrl   String?\n  sortOrder Int       @default(0)\n  isActive  Boolean   @default(true)\n  startAt   DateTime?\n  endAt     DateTime?\n  createdAt DateTime  @default(now())\n  updatedAt DateTime  @updatedAt\n\n  @@index([isActive, sortOrder])\n}\n\n\u002F\u002F ═══════════════════════════════════════════════\n\u002F\u002F 动态导航（前台轻量版）\n\u002F\u002F ═══════════════════════════════════════════════\n\nmodel Menu {\n  id         String  @id @default(cuid())\n  label      String\n  path       String\n  icon       String?\n  sortOrder  Int     @default(0)\n  parentId   String?\n  parent     Menu?   @relation(\"MenuChildren\", fields: [parentId], references: [id])\n  children   Menu[]  @relation(\"MenuChildren\")\n  isExternal Boolean @default(false)\n  isActive   Boolean @default(true)\n\n  @@index([parentId, sortOrder])\n}\n```\n\n**设计取舍说明**\n\n- **用户资料拆分**：将 `displayName`、`bio`、`avatarUrl` 等公开资料从 `User` 拆分到 `UserProfile`。`User` 只承载认证与授权相关的敏感字段（`passwordHash`、`isActive`），未来对外提供“用户公开主页”或“作者信息”接口时，可以直接查询 `UserProfile` 而不必在 `select` 中小心排除密码字段，降低敏感字段泄露风险。代价是个人资料读取需要多一次 `include`，在 Prisma 中只是一行配置。\n- **软删除**：`Post` 与 `Video` 的 `deletedAt` 字段用于软删除。查询时在 service 层默认追加 `deletedAt: null` 条件，或在 Prisma Client Extension 中统一拦截。项目初期先通过 service 层约定实现，保持依赖精简，不引入第三方软删除扩展。\n- **权限二元组**：`Permission` 以 `(resource, action)` 唯一约束，`action` 使用 `create` \u002F `read` \u002F `update` \u002F `delete` \u002F `manage` 五种。`manage` 作为通配动作覆盖该资源所有具体动作，简化 `ADMIN` 的规则定义。\n\n---\n\n## 5. 用户管理与权限体系详解\n\n用户管理在本项目中承担两个职责：**前台用户的个人身份**（资料、头像、偏好）与**后台内容管理的操作权限**（谁可以发布文章、删除视频、管理轮播图）。两者共用同一套 `User` 实体，但权限边界必须清晰——前台注册用户默认**没有任何后台内容管理权限**，后台能力通过角色显式授予。\n\n### 5.1 认证与授权的分层\n\n认证（Authentication）回答“你是谁”，授权（Authorization）回答“你能做什么”。本项目采用 **JWT 认证 + RBAC 授权** 的分层结构：\n\n- **认证层**：`AuthModule` 负责登录、JWT 签发与校验。`JwtAuthGuard` 解析请求中的 JWT，将用户信息挂载到 `request.user`。这一层不关心角色，只确认身份有效。\n- **粗粒度授权层**：`RolesGuard` 配合 `@Roles()` 装饰器，在 controller 或路由方法级别检查用户是否拥有所需角色。适合“整个模块仅 Admin 可访问”这类简单场景，例如 `BannersController` 的写操作。\n- **细粒度授权层**：当权限需求超出“角色存在性检查”（例如“编辑只能改自己创建的文章”“作者不能删除已发布内容”），引入 **CASL + Prisma**，将权限规则定义为 Prisma `WhereInput`，实现**行级**和**字段级**的精细控制。\n\n推荐**先落地 RolesGuard，再按需引入 CASL**。过早引入 CASL 会增加 Ability 定义与调试成本；一旦出现“同一角色内部权限因资源归属不同而分化”的需求，CASL 几乎是 NestJS 生态中的标准答案。\n\n**守卫绑定方式**：`JwtAuthGuard` 注册为全局守卫，默认所有路由都需要登录，再用 `@Public()` 装饰器标记登录、注册等公开端点。`RolesGuard` 同样全局注册，但在没有 `@Roles()` 元数据时直接放行，只有声明了角色要求的路由才会实际校验。这种“默认拒绝 + 按需声明”的模式比在每个 controller 上手动挂守卫更不容易遗漏。\n\n### 5.2 内置角色与权限种子数据\n\n项目初始化时通过 Prisma seed 写入以下系统角色和权限组合。角色名称保持英文枚举风格，便于代码中引用。\n\n| 角色 | 权限范围 | 适用人员 |\n|:---|:---|:---|\n| `ADMIN` | 全部资源的 `manage`（含用户管理、角色分配） | 平台管理员 |\n| `EDITOR` | `post` \u002F `video` \u002F `banner` \u002F `menu` 的 `create`、`read`、`update`、`delete` | 内容运营 |\n| `AUTHOR` | `post` 的 `create`、`read`、`update`（仅自己的文章）；`video` 的 `read` | 投稿作者 |\n| `MEMBER` | 仅前台个人资料读写，无任何内容管理端点访问权 | 普通注册用户 |\n\n### 5.3 头像上传：预签名 URL 直传\n\n头像文件**不经过 NestJS 服务端内存**。流程如下：\n\n1. 前端调用 `POST \u002Fusers\u002Fme\u002Favatar\u002Fpresign`，后端校验用户身份，生成对象存储（S3 \u002F R2 \u002F MinIO 兼容）的预签名上传 URL，并返回该 URL 与最终的 `objectKey`。\n2. 前端直接将图片文件 `PUT` 到预签名 URL，**完全绕过 NestJS 服务器**。这是文件上传能够水平扩展的关键模式，避免了 Multer 内存存储在大文件或高并发下的内存压力。\n3. 上传成功后，前端调用 `PATCH \u002Fusers\u002Fme\u002Favatar`，将 `objectKey` 提交给后端。后端验证对象确实存在（通过存储服务的 HeadObject），更新 `UserProfile.avatarKey` 与 `avatarUrl`，并删除旧头像对象（如果存在且不同于新 key）。\n\n**安全要点**：预签名 URL 的过期时间建议设为 60–120 秒；后端在生成预签名时限定 `Content-Type` 为 `image\u002Fjpeg`、`image\u002Fpng`、`image\u002Fwebp`，并限制文件大小（如 2 MB）。**不要信任客户端提交的文件扩展名或 Content-Type**。如果图片需要进一步处理（压缩、裁剪），应在对象存储的 `onUpload` 事件或队列 worker 中异步完成，而不是在 HTTP 请求周期内同步处理。\n\n如果项目初期不想引入对象存储依赖，可以暂时使用 NestJS 的 `FileInterceptor` + 本地磁盘存储作为过渡，但需要将头像上传的 service 接口设计为可替换的抽象（如 `AvatarStorageService` 接口，本地实现与 S3 实现分别注入），避免未来迁移时牵动 controller 层。\n\n### 5.4 前端（Nuxt 3）的权限对接\n\n**用户状态管理**：使用 `useAuth()` composable 封装登录状态、当前用户、角色与权限集合。用户信息在 SSR 期间通过 `useFetch` 配合 Cookie 中的 JWT 获取，确保首屏渲染时权限状态已经就绪，避免“先渲染再重定向”的闪烁。建议暴露以下派生状态：\n\n```ts\n\u002F\u002F composables\u002FuseAuth.ts 的核心接口\nconst { user, roles, permissions, isLoggedIn } = useAuth()\n\u002F\u002F isLoggedIn: 是否有有效会话\n\u002F\u002F roles: string[]，如 ['EDITOR']\n\u002F\u002F permissions: string[]，如 ['post:create', 'post:update']\n\u002F\u002F can(perm: string): boolean\n```\n\n**路由中间件**：使用**命名中间件**，而不是在组件内做权限判断。`auth` 中间件检查登录状态，未登录时重定向到登录页并携带 `redirect` 参数；`admin` 中间件在 `auth` 之后运行，检查用户是否拥有 `ADMIN` 角色，不满足时调用 `abortNavigation()` 而非重定向，避免将用户推向另一个同样会被拒绝的页面。\n\n```ts\n\u002F\u002F middleware\u002Fadmin.ts\nexport default defineNuxtRouteMiddleware(() => {\n  const { user } = useAuth()\n  if (!user.value) return navigateTo('\u002Flogin')\n  if (!user.value.roles?.includes('ADMIN')) return abortNavigation()\n})\n```\n\n在页面中使用时按顺序声明：`definePageMeta({ middleware: ['auth', 'admin'] })`。中间件数组保证 `auth` 先建立用户上下文，`admin` 再进行角色判断。\n\n**按钮\u002F元素的权限控制**：对于页面内“是否显示删除按钮”“是否允许编辑”这类细粒度 UI 控制，使用 `v-if=\"can('post:delete')\"` 配合 `useAuth()` 中的 `can` 方法。需要明确的是，**前端的权限判断只影响用户体验，不构成安全边界**。所有真正的权限校验必须在 NestJS 端完成；前端隐藏按钮只是为了避免用户点击后才收到 403 的挫败感。\n\n**个人资料页面路由**：`\u002Faccount\u002Fprofile`（查看与编辑资料、更换头像）、`\u002Faccount\u002Fsecurity`（修改密码、查看登录会话）。这两个页面挂载 `auth` 中间件即可，无需角色限制。头像上传组件在获取预签名 URL 后直接 `fetch(presignedUrl, { method: 'PUT', body: file })`，然后在确认接口成功后刷新 `useAuth()` 中的用户信息。\n\n**JWT 存储**：Access Token 推荐存储在 **Cookie** 中（而非 `localStorage`），因为 Cookie 在 SSR 期间可被 `useCookie` 读取，且 Nuxt 的 `useCookie` 是 SSR-safe 的。NestJS 端签发 JWT 后通过 `Set-Cookie` 返回，前端在 `useFetch` \u002F `$fetch` 中依赖浏览器自动携带，无需在每个请求中手动拼接 Authorization 头。如果安全要求更高，可改用 `httpOnly` Cookie 并由 Nuxt 服务端路由代理所有 API 请求，但会牺牲部分客户端直接调用的便利性。\n\n---\n\n## 6. 前后端对接的关键配置\n\n### 6.1 Nuxt 3 侧的环境变量与 runtimeConfig\n\n后端地址**不能硬编码**。在 `nuxt.config.ts` 中使用 `runtimeConfig` 管理 API 基地址，并区分服务端与客户端可见性：\n\n```ts\n\u002F\u002F frontend\u002Fnuxt.config.ts\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    apiSecret: '',                    \u002F\u002F 仅服务端可见\n    public: {\n      apiBase: process.env.NUXT_PUBLIC_API_BASE || 'http:\u002F\u002Flocalhost:3001\u002Fapi',\n    },\n  },\n})\n```\n\nNuxt 3 的 `runtimeConfig` 是推荐的运行时配置方式，构建后可通过环境变量覆盖，无需重新构建。对应的 `.env`：\n\n```\nNUXT_PUBLIC_API_BASE=http:\u002F\u002Flocalhost:3001\u002Fapi\n```\n\n### 6.2 API 调用的代理策略\n\n前后端分离下，Nuxt 端调用 NestJS API 有三种可选路径，建议**开发环境用 Nitro 代理，生产环境用同域反向代理（Nginx \u002F Caddy）**。\n\n开发环境下，`nitro.devProxy` 在涉及 SSR 的相对路径调用时存在已知局限：Nitro 会将 `\u002Fapi\u002F*` 识别为内部调用并尝试模拟，而不是真正转发到外部后端。更稳妥的开发代理方案是在 `server\u002Fapi\u002F[...path].ts` 中使用 `proxyRequest` 做 catch-all 转发：\n\n```ts\n\u002F\u002F frontend\u002Fserver\u002Fapi\u002F[...path].ts\nexport default defineEventHandler((event) => {\n  const config = useRuntimeConfig()\n  const target = `${config.public.apiBase}\u002F**`\n  return proxyRequest(event, target.replace('\u002F**', event.context.params.path))\n})\n```\n\n前端所有请求统一走 `\u002Fapi\u002F*`，SSR 与客户端行为一致，且 `apiBase` 可在运行时通过环境变量切换。\n\n### 6.3 认证的令牌存储\n\nJWT 的 Access Token 推荐存储在 Cookie 中（详见 5.4）。Refresh Token 建议使用 `httpOnly` Cookie，由 `\u002Fauth\u002Frefresh` 端点负责轮换；`Session` 表存储 Refresh Token 的哈希，支持“主动踢下线”与“查看登录设备”。\n\n---\n\n## 7. 功能模块与前端路由对应\n\n| 功能 | 后端模块 | 前端路由 | 权限要求 |\n|:---|:---|:---|:---|\n| 博客列表 \u002F 详情 | `PostsModule`（`type=blog`） | `\u002Fblog`、`\u002Fblog\u002F[slug]` | 公开 |\n| 新闻列表 \u002F 详情 | `PostsModule`（`type=news`） | `\u002Fnews`、`\u002Fnews\u002F[slug]` | 公开 |\n| 视频列表 \u002F 播放 | `VideosModule` | `\u002Fvideos`、`\u002Fvideos\u002F[slug]` | 公开 |\n| 首页轮播 | `BannersModule` | `\u002F`（`index.vue`） | 公开 |\n| 动态导航 | `NavigationModule` | 全局 layout | 公开 |\n| 登录 \u002F 注册 | `AuthModule` | `\u002Flogin`、`\u002Fregister` | 公开 |\n| 个人资料与头像 | `UsersModule` | `\u002Faccount\u002Fprofile` | 登录用户 |\n| 修改密码 \u002F 会话 | `AuthModule` | `\u002Faccount\u002Fsecurity` | 登录用户 |\n| 内容管理 | 各内容模块 Admin 端点 | `\u002Fadmin\u002Fcontent\u002F...` | `ADMIN`、`EDITOR`、`AUTHOR`（按资源） |\n| 用户管理 | `UsersModule`（Admin） | `\u002Fadmin\u002Fusers` | `ADMIN` |\n| 角色与权限管理 | `UsersModule`（Admin） | `\u002Fadmin\u002Froles` | `ADMIN` |\n\n**Admin 端点的粗粒度权限要求**\n\n| 端点前缀 | 最低角色 |\n|:---|:---|\n| `\u002Fposts`（写） | `AUTHOR`（仅自己的）\u002F `EDITOR` \u002F `ADMIN` |\n| `\u002Fvideos`（写） | `EDITOR` \u002F `ADMIN` |\n| `\u002Fbanners`（写） | `EDITOR` \u002F `ADMIN` |\n| `\u002Fmenus`（写） | `EDITOR` \u002F `ADMIN` |\n| `\u002Fadmin\u002Fusers`、`\u002Fadmin\u002Froles` | `ADMIN` |\n\n---\n\n## 8. 开发与部署基线\n\n**本地启动**：后端 `pnpm start:dev`（默认 `3001`），前端 `pnpm dev`（`3000`）。前端通过上述 catch-all 代理将 `\u002Fapi\u002F*` 转发至 `http:\u002F\u002Flocalhost:3001\u002Fapi`。\n\n**数据库迁移流程**：修改 `schema.prisma` → `npx prisma migrate dev --name \u003C描述>` → `npx prisma generate`。后端 service 中直接注入 `PrismaService`，无需额外维护 repository 层。\n\n**种子数据**：`npx prisma db seed` 执行 `prisma\u002Fseed.ts`，写入内置角色（`ADMIN` \u002F `EDITOR` \u002F `AUTHOR` \u002F `MEMBER`）、权限二元组、角色权限映射，以及一个初始管理员账号（凭据从环境变量读取，不硬编码）。\n\n**部署**：前后端作为两个独立服务部署。生产环境由 Nginx \u002F Caddy 将 `\u002Fapi\u002F*` 反向代理到 NestJS 服务，Nuxt 的 SSR 服务处理其余路由。这种部署方式天然规避 CORS，且前端构建产物与后端进程解耦，可以独立扩缩容。\n\n**安全基线**\n\n- `passwordHash` 使用 `argon2` 或 `bcrypt`（cost ≥ 12），任何接口响应中不得包含该字段。\n- JWT 签名密钥、对象存储凭证、数据库连接串全部来自环境变量，`.env` 不提交仓库。\n- 预签名 URL 有效期 60–120 秒，限定 Content-Type 与文件大小。\n- Admin 端点全部挂 `JwtAuthGuard` + `RolesGuard`，缺省拒绝。\n- 登录接口做速率限制（`@nestjs\u002Fthrottler`），防止暴力破解。\n\n---\n\n## 9. 建议的迭代顺序\n\n**Phase 1（基础内容骨架）**：`PrismaModule` + `PostsModule`（博客 + 新闻）+ Nuxt 博客\u002F新闻页面。先跑通“后端 CRUD → 前端 SSR 渲染 → 代理链路”的最短闭环。\n\n**Phase 2（多媒体与首页）**：`VideosModule` + `BannersModule` + 首页聚合。视频先以 MP4 直链或外链嵌入起步，HLS 适配作为后续优化。\n\n**Phase 2.5（用户与权限基础）**：`AuthModule` + `UsersModule` + `UserProfile` + `RolesGuard` + 角色\u002F权限种子数据 + 前端 `useAuth` 与 `auth`\u002F`admin` 中间件 + 头像预签名直传。这一步放在 Admin 管理界面之前，确保后续所有后台页面都建立在有效的登录与角色体系之上，而不是先做无保护的 Admin 页面再回头补权限。\n\n**Phase 3（动态导航与管理界面）**：`NavigationModule` + Admin 内容管理界面（文章、视频、轮播图）+ Admin 用户与角色管理界面。导航先做前台配置版；如果需要多角色后台，再引入 RBAC 菜单权限。\n\n**Phase 4（体验与工程化）**：搜索（`Post` 的全文索引）、SEO 增强（`useSeoMeta`、sitemap）、缓存策略（NestJS 端对轮播图和导航做短 TTL 缓存）、CASL 细粒度权限（当“作者只能改自己的文章”等规则复杂到 RolesGuard 难以表达时）。\n\n---\n\n## 附录：关键决策速查\n\n| 决策点 | 选择 | 主要理由 |\n|:---|:---|:---|\n| 后端形态 | 独立 NestJS 服务 | 预期多端消费、复杂业务规则、独立扩缩容 |\n| ORM | Prisma | 查询边界类型安全、迁移自动生成、与 TS 深度契合 |\n| 博客\u002F新闻 | 统一 `Post` + `type` 字段 | 数据形态高度重叠，避免重复 controller\u002Fservice |\n| 视频 | 独立 `Video` 模型 | 元数据维度不同，避免大量 null 字段 |\n| 用户资料 | 拆出 `UserProfile` | 敏感字段与公开字段隔离，降低泄露风险 |\n| 权限模型 | RBAC（Role ↔ Permission 多对多） | 覆盖粗粒度场景，为 CASL 行级权限预留扩展点 |\n| 授权实现 | RolesGuard 先行，CASL 按需引入 | 避免过早引入 Ability 定义成本 |\n| 头像上传 | 预签名 URL 直传对象存储 | 绕过服务端内存，水平扩展友好 |\n| 前端权限 | 中间件 + `can()` 控制 UI | UI 控制仅为体验，安全边界在后端 |\n| JWT 存储 | Cookie（SSR-safe） | Nuxt `useCookie` 原生支持，避免手动拼 Authorization |\n| 开发代理 | `server\u002Fapi\u002F[...path].ts` + `proxyRequest` | 规避 `devProxy` 在 SSR 相对路径上的局限 |\n| 部署 | Nginx\u002FCaddy 同域反代 `\u002Fapi\u002F*` | 天然规避 CORS，前后端独立扩缩容 |",33,false,"2026-10-29T10:15:39",{"id":106,"category_id":5,"title":107,"author":8,"summary":108,"cover":109,"content":110,"tags":8,"seo_title":8,"seo_description":8,"seo_keywords":8,"og_image":8,"views":111,"comment_count":10,"is_top":103,"is_published":11,"created_at":112},10055,"powershell5及powershell7","PowerShell 5.1 与 PowerShell 7 并非简单的“升级”关系，它们是两个独立的产品，可以并行安装和运行。PowerShell 5.1 是 Windows 的内置组件，而 PowerShell 7 是需要单独安装的、面向未来的跨平台版本。","\u002Fstatic\u002Fuploads\u002Fblogs\u002F1793008327745-720344879.jpg","PowerShell 5.1 与 PowerShell 7 并非简单的“升级”关系，它们是**两个独立的产品，可以并行安装和运行**。PowerShell 5.1 是 Windows 的内置组件，而 PowerShell 7 是需要单独安装的、面向未来的跨平台版本。\n\n以下是它们在核心维度的主要区别：\n\n### 🏗️ 核心架构与定位\n\n| 维度 | Windows PowerShell 5.1 | PowerShell 7 |\n| :--- | :--- | :--- |\n| **核心定位** | Windows 内置的**传统\u002F维护模式**版本 | **活跃开发的主线**，面向现代自动化 |\n| **底层框架** | .NET Framework v4.5 | .NET (原 .NET Core)，版本随 PowerShell 更新 |\n| **跨平台性** | **仅限 Windows** | **跨平台**：Windows, macOS, Linux |\n| **可执行文件** | `powershell.exe` | `pwsh.exe` |\n| **开发状态** | 不再添加新功能，支持周期跟随 Windows | 微软**主要开发线**，持续增加新功能 |\n| **安装方式** | 随 Windows 预装 | 需**单独安装**（MSI, ZIP, Store 等） |\n\n### ⚙️ 功能与性能特性\n\nPowerShell 7 引入了大量新功能，旨在提升脚本能力和执行效率。\n\n*   **并行处理**：这是最受关注的改进之一。PowerShell 7 新增了 **`ForEach-Object -Parallel`** 参数，允许你并行处理管道中的多个项目，能显著缩短可并行工作负载的执行时间。\n*   **新语言特性**：支持**三元运算符** (`? :`)、空合并运算符 (`??`) 等现代语言特性，使脚本更简洁。\n*   **性能提升**：得益于 .NET Core 的优化，PowerShell 7 的**启动速度通常比 5.1 快约 30%**。\n*   **现代化远程处理**：支持基于 **SSH 的远程处理**，与 5.1 基于 WinRM 的远程方式不同，这使其能更好地管理 Linux 等非 Windows 系统。\n*   **容器支持**：原生支持 **Docker 容器**，便于在容器化环境中使用。\n\n### 🔄 兼容性与迁移关键点\n\n尽管 PowerShell 7 是发展方向，但迁移时需注意以下关键差异：\n\n*   **模块兼容性**：许多旧模块仍依赖完整的 .NET Framework，无法直接在 PowerShell 7 中加载。为此，PowerShell 7 提供了 **Windows PowerShell 兼容性功能**。你可以通过 `Import-Module -UseWindowsPowerShell` 参数来加载这些旧模块，它会在后台启动一个 5.1 的进程来运行它们。\n*   **默认字符编码**：这是一个常见的迁移陷阱。**PowerShell 5.1 的默认编码因命令而异**（例如 `Out-File` 是 UTF-16LE，`Get-Content` 是 ANSI），而 **PowerShell 7 统一使用不带 BOM 的 UTF-8**。如果你的脚本处理包含中文、日文等非英文字符的文件，迁移到 7 后可能会因编码不匹配而出现乱码。\n*   **Cmdlet 行为差异**：部分 Cmdlet 的默认行为或输出格式可能有所不同。例如，`Get-Process` 命令的默认显示列在 5.1 和 7 中就可能存在差异。在迁移自动化脚本时，需要对此进行测试。\n\n### 💎 总结与选择建议\n\n**PowerShell 5.1** 是稳定、兼容性优先的选择，适合维护现有的、依赖 .NET Framework 的传统 Windows 自动化脚本和系统管理任务。\n\n**PowerShell 7** 是面向未来的选择，适合编写**新的自动化脚本**，尤其是在需要跨平台管理（Windows\u002FLinux\u002FmacOS）、追求更高性能、使用并行处理或容器化环境时。由于二者可以并行共存，你可以同时在系统中保留两者，根据具体任务灵活选用。",15,"2026-10-26T17:52:17",{"id":114,"category_id":5,"title":115,"author":8,"summary":116,"cover":117,"content":118,"tags":8,"seo_title":8,"seo_description":8,"seo_keywords":8,"og_image":8,"views":111,"comment_count":20,"is_top":103,"is_published":11,"created_at":119},10054,"Fastapi服务器uvicron","Uvicorn 是 FastAPI 的默认 ASGI 服务器，在 FastAPI 项目中启动服务本质上就是启动一个 Uvicorn 进程。下面从基础实例出发，逐步拆解命令行用法、编程式用法、核心参数以及开发\u002F生产环境的配置差异。","\u002Fstatic\u002Fuploads\u002Fblogs\u002F1792983466649-546593858.jpg","Uvicorn 是 FastAPI 的默认 ASGI 服务器，在 FastAPI 项目中启动服务本质上就是启动一个 Uvicorn 进程。下面从**基础实例**出发，逐步拆解命令行用法、编程式用法、核心参数以及开发\u002F生产环境的配置差异。\n\n---\n\n## 先准备一个可运行的 FastAPI 实例\n\n新建 `main.py`：\n\n```python\n# main.py\nfrom fastapi import FastAPI\n\napp = FastAPI(title=\"Uvicorn 用法演示\")\n\n\n@app.get(\"\u002F\")\nasync def root():\n    return {\"message\": \"Hello FastAPI + Uvicorn\"}\n\n\n@app.get(\"\u002Fitems\u002F{item_id}\")\nasync def read_item(item_id: int, q: str | None = None):\n    return {\"item_id\": item_id, \"q\": q}\n```\n\n这个 `app` 对象就是 Uvicorn 要加载的 **ASGI 应用**。“`main:app`”的含义是：从 `main` 模块中导入名为 `app` 的对象。\n\n---\n\n## 方式一：命令行启动（最常用）\n\n### 最简启动\n\n```bash\nuvicorn main:app\n```\n\n默认绑定 `127.0.0.1:8000`，仅本机可访问。\n\n### 开发环境：热重载\n\n```bash\nuvicorn main:app --reload\n```\n\n修改 `main.py` 后，服务器会自动重启，无需手动按 `Ctrl+C` 再启动。\n\n### 生产环境：多进程 + 对外开放\n\n```bash\nuvicorn main:app --host 0.0.0.0 --port 8080 --workers 4\n```\n\n`--host 0.0.0.0` 允许外部访问；`--workers 4` 启动 4 个工作进程，充分利用多核 CPU。FastAPI 官方文档也使用同样的命令形式演示多 worker 启动。\n\n### 带常用调优参数的完整示例\n\n```bash\nuvicorn main:app \\\n  --host 0.0.0.0 \\\n  --port 8080 \\\n  --workers 4 \\\n  --loop uvloop \\\n  --limit-concurrency 1000 \\\n  --timeout-keep-alive 10 \\\n  --log-level warning \\\n  --no-access-log\n```\n\n这条命令启用了 `uvloop` 高性能事件循环、限制最大并发连接数为 1000、将 keep-alive 超时设为 10 秒、降低日志级别并关闭访问日志。\n\n---\n\n## 方式二：编程式启动（`uvicorn.run`）\n\n当需要把启动逻辑写在 Python 代码中时，可以使用 `uvicorn.run()`：\n\n```python\n# main.py\nimport uvicorn\nfrom fastapi import FastAPI\n\napp = FastAPI()\n\n\n@app.get(\"\u002F\")\nasync def root():\n    return {\"ok\": True}\n\n\nif __name__ == \"__main__\":\n    uvicorn.run(\n        \"main:app\",          # 必须用导入字符串，不能用 app 对象\n        host=\"0.0.0.0\",\n        port=8000,\n        reload=True,          # 开发环境\n        # workers=4,          # 生产环境，不能与 reload 同时使用\n    )\n```\n\n**关键点**：当使用 `reload=True` 或 `workers` 参数时，`uvicorn.run()` 的第一个参数**必须写成导入字符串** `\"main:app\"`，而不能直接传入 `app` 对象；否则热重载和多进程会失效或报错。同时，这行代码必须放在 `if __name__ == \"__main__\":` 保护块内，避免多进程启动时的重复加载问题。\n\n更底层的写法是使用 `uvicorn.Config` + `uvicorn.Server`，适合需要精细控制 Server 生命周期的场景：\n\n```python\nimport uvicorn\n\nif __name__ == \"__main__\":\n    config = uvicorn.Config(\"main:app\", host=\"127.0.0.1\", port=8000, log_level=\"info\")\n    server = uvicorn.Server(config)\n    server.run()\n```\n\n这种写法与 `uvicorn.run()` 等价，但为后续手动调用 `server.serve()` 或嵌入异步环境提供了入口。\n\n---\n\n## 核心参数分类详解\n\n### 应用与网络绑定\n\n| 参数 | 作用 | 关键说明 |\n|---|---|---|\n| `APP`（位置参数） | ASGI 应用路径 | 格式 `模块:变量`，如 `main:app` |\n| `--host` | 绑定地址 | 默认 `127.0.0.1`；`0.0.0.0` 允许外部访问 |\n| `--port` | 端口 | 默认 `8000` |\n| `--uds` | Unix 域套接字 | 如 `--uds \u002Ftmp\u002Fuvicorn.sock`，常用于反向代理场景 |\n\n### 性能与并发\n\n| 参数 | 作用 |\n|---|---|\n| `--workers` \u002F `-w` | 工作进程数，利用多核 CPU |\n| `--loop` | 事件循环实现，Linux\u002FmacOS 上 `uvloop` 性能最佳 |\n| `--limit-concurrency` | 最大并发连接数，超过时返回 503，起到熔断保护作用 |\n| `--limit-max-requests` | 每个进程处理的最大请求数，可用于规避内存泄漏累积 |\n| `--backlog` | 等待连接队列长度，默认 2048，高并发场景可适当调高 |\n\n`--workers` 是生产环境提升吞吐量的核心参数；`--limit-concurrency` 则是在流量突增时保护进程不被压垮的兜底手段。\n\n### 开发调试\n\n| 参数 | 作用 |\n|---|---|\n| `--reload` | 代码变更自动重启 |\n| `--reload-dir` | 限定监控目录，避免日志\u002F静态文件触发频繁重启 |\n| `--reload-include` \u002F `--reload-exclude` | 指定或排除监控的文件模式（需 `watchfiles`） |\n| `--log-level` | 日志级别，开发时可设为 `debug` 查看细节 |\n\n只监控 `.\u002Fapp` 目录的写法：\n\n```bash\nuvicorn main:app --reload --reload-dir .\u002Fapp\n```\n\n这样日志目录、静态资源目录的变动就不会触发不必要的重启。\n\n### 超时与优雅关闭\n\n| 参数 | 默认值 | 作用 |\n|---|---|---|\n| `--timeout-keep-alive` | 5 秒 | Keep-Alive 连接空闲多久后关闭 |\n| `--timeout-graceful-shutdown` | — | 优雅关闭时等待请求处理完成的最长时间 |\n\n生产环境中适当调大 `--timeout-keep-alive` 可以减少连接重建开销，但不宜过大，否则空闲连接会长期占用资源。\n\n### 反向代理与安全\n\n如果 Uvicorn 前面有 Nginx、Traefik 等反向代理，需要加：\n\n```bash\n--proxy-headers\n```\n\n这样才能通过 `X-Forwarded-For` 获取用户的真实 IP，而不是代理服务器的 IP。如果应用部署在子路径下（如 `example.com\u002Fapi\u002Fv1`），还应设置 `--root-path \u002Fapi\u002Fv1`，否则 Swagger UI 等文档路径可能不正确。\n\nUvicorn 自身也支持直接加载 SSL 证书：\n\n```bash\nuvicorn main:app \\\n  --ssl-keyfile .\u002Fkey.pem \\\n  --ssl-certfile .\u002Fcert.pem\n```\n\n但在生产环境中，更常见的做法是 SSL 终止在 Nginx 或负载均衡器上，Uvicorn 只处理 HTTP。\n\n---\n\n## 开发环境 vs 生产环境：配置策略\n\n### 一条必须记住的规则：`--reload` 与 `--workers` 互斥\n\n`--reload` 和 `--workers` 不能同时使用。`--reload` 是单进程带热重载，服务于开发迭代；`--workers` 是多进程无热重载，服务于生产并发。同时使用会导致不可预期的行为。\n\n### 开发环境典型命令\n\n```bash\nuvicorn main:app --reload --log-level debug\n```\n\n### 生产环境典型命令\n\n```bash\nuvicorn main:app \\\n  --host 0.0.0.0 \\\n  --port 8080 \\\n  --workers 4 \\\n  --loop uvloop \\\n  --limit-concurrency 1000 \\\n  --timeout-keep-alive 10 \\\n  --log-level warning \\\n  --no-access-log \\\n  --proxy-headers\n```\n\n### Worker 数量怎么定\n\n一个常见的经验法则是 `(2 × CPU 核心数) + 1`，但这不是硬性规定。实际取值应结合每个请求的 CPU 密集程度、内存占用以及是否使用异步 I\u002FO 来调整。如果应用是纯异步 I\u002FO 密集型，过多的 worker 并不会带来线性收益，反而会增加内存开销。\n\n---\n\n## 进阶：Gunicorn 作为进程管理器\n\nUvicorn 自身可以用 `--workers` 管理多进程，但在更成熟的生产环境中，常采用 **Gunicorn 作为进程管理器 + Uvicorn 作为 ASGI Worker** 的组合，以获得更完善的进程监控、优雅重启和日志管理能力：\n\n```bash\ngunicorn main:app \\\n  -w 4 \\\n  -k uvicorn.workers.UvicornWorker \\\n  -b 0.0.0.0:8000\n```\n\n- `-k uvicorn.workers.UvicornWorker`：指定 Uvicorn 提供的 ASGI worker 类；\n- `-w 4`：4 个 worker 进程；\n- `-b 0.0.0.0:8000`：绑定地址与端口。\n\n需要注意的是，如果已经使用 Gunicorn 管理进程，就**不要再给 Uvicorn 传 `--workers`**，进程数由 Gunicorn 的 `-w` 控制，否则会出现进程管理层级混乱。\n\n---\n\n## 小结\n\nUvicorn 的用法可以按一条主线来理解：**开发用 `--reload`（单进程、自动重启），生产用 `--workers`（多进程、无热重载），两者互斥**。命令行方式适合直接启动和容器 entrypoint，编程式 `uvicorn.run(\"main:app\", ...)` 适合把启动逻辑嵌入项目代码，但必须注意使用导入字符串并放在 `if __name__ == \"__main__\":` 下。在此基础上，根据并发量、代理环境和日志需求，按需叠加 `--limit-concurrency`、`--proxy-headers`、`--timeout-keep-alive` 等调优参数即可。","2026-10-26T10:57:48",{"id":121,"category_id":5,"title":122,"author":8,"summary":123,"cover":124,"content":125,"tags":8,"seo_title":8,"seo_description":8,"seo_keywords":8,"og_image":8,"views":126,"comment_count":10,"is_top":103,"is_published":11,"created_at":127},10053,"网络IP的使用","ip 命令来自 iproute2 套件，是现代 Linux 网络管理的标准工具。在 Ubuntu 18.04 及之后的版本中，ifconfig 所属的 net-tools 默认已不再安装，ip 命令不仅覆盖了旧工具的全部功能，还支持策略路由、网络命名空间、隧道、VLAN 等现代特性。","\u002Fstatic\u002Fuploads\u002Fblogs\u002F1792651544352-416407629.jpg","`ip` 命令来自 `iproute2` 套件，是现代 Linux 网络管理的标准工具。在 Ubuntu 18.04 及之后的版本中，`ifconfig` 所属的 `net-tools` 默认已不再安装，`ip` 命令不仅覆盖了旧工具的全部功能，还支持策略路由、网络命名空间、隧道、VLAN 等现代特性。\n\n---\n\n## 一、基本语法与常用选项\n\n```bash\nip [ OPTIONS ] OBJECT { COMMAND | help }\n```\n\n`OBJECT` 是操作对象（如 `addr`、`link`、`route`），`COMMAND` 是具体动作（如 `show`、`add`、`del`、`set`）。直接运行 `ip OBJECT help` 可以查看该对象下的完整子命令。\n\n常用选项：\n\n| 选项 | 作用 |\n|---|---|\n| `-br` | 简洁表格输出，适合快速查看和脚本处理 |\n| `-s` \u002F `-s -s` | 显示统计信息（`-s -s` 包含错误计数） |\n| `-4` \u002F `-6` | 仅显示 IPv4 \u002F IPv6 |\n| `-j` | JSON 格式输出，便于 `jq` 解析 |\n| `-d` | 显示详细信息（如 VLAN 的 id、协议、父接口） |\n| `-o` | 单行输出（oneline） |\n\n---\n\n## 二、`ip addr`：IP 地址管理\n\n### 查看地址\n\n```bash\nip addr show              # 查看所有接口的地址\nip addr show eth0         # 查看指定接口\nip -br addr show          # 简洁表格：接口名、状态、IP\nip -4 addr show           # 仅 IPv4\nip -6 addr show           # 仅 IPv6\n```\n\n`ip -br addr` 的输出形如：\n\n```text\nlo               UNKNOWN        127.0.0.1\u002F8 ::1\u002F128\neth0             UP             192.168.1.100\u002F24 fe80::5054:ff:feab:cdef\u002F64\n```\n\n### 添加 \u002F 删除 IP 地址（临时）\n\n```bash\nsudo ip addr add 192.168.1.100\u002F24 dev eth0      # 添加\nsudo ip addr del 192.168.1.100\u002F24 dev eth0      # 删除\n```\n\n`ip` 原生支持在同一接口上添加多个 IP 地址，直接再次执行 `add` 并指定不同地址即可，无需像 `ifconfig` 那样借助 `eth0:0` 之类的别名。\n\n### 与 `ifconfig` 的对应关系\n\n| `ifconfig` | `ip` 等价命令 |\n|---|---|\n| `ifconfig -a` | `ip addr show` |\n| `ifconfig eth0` | `ip addr show eth0` |\n| `ifconfig eth0 192.168.1.50 netmask 255.255.255.0` | `ip addr add 192.168.1.50\u002F24 dev eth0` |\n| `ifconfig eth0 0.0.0.0` | `ip addr del 192.168.1.50\u002F24 dev eth0` |\n\n---\n\n## 三、`ip link`：链路层与接口管理\n\n### 启用 \u002F 禁用接口\n\n```bash\nsudo ip link set eth0 up\nsudo ip link set eth0 down\n```\n\n### 修改 MTU\n\n```bash\nip link show eth0                  # 查看当前 MTU\nsudo ip link set eth0 mtu 9000\n```\n\n### 修改 MAC 地址\n\n需要先 `down`，改完再 `up`：\n\n```bash\nsudo ip link set eth0 down\nsudo ip link set eth0 address 00:11:22:33:44:55\nsudo ip link set eth0 up\n```\n\n### 查看统计信息\n\n```bash\nip -s link show eth0\nip -s -s link show eth0      # 额外显示错误计数\n```\n\n如果 `RX errors` 或 `TX dropped` 持续增长，通常指向物理链路或驱动问题。\n\n### 混杂模式\n\n```bash\nsudo ip link set eth0 promisc on\nsudo ip link set eth0 promisc off\n```\n\n---\n\n## 四、`ip route`：路由管理\n\n### 查看路由表\n\n```bash\nip route show                  # 主路由表\nip route show table main       # 指定表\nip route show 192.168.0.0\u002F16   # 指定目标网段\n```\n\n### 添加路由\n\n```bash\n# 静态路由：到达 192.168.2.0\u002F24 走网关 10.0.0.1\nsudo ip route add 192.168.2.0\u002F24 via 10.0.0.1\n\n# 指定出口接口\nsudo ip route add 192.168.2.0\u002F24 dev eth0\n\n# 同时指定网关和接口\nsudo ip route add 192.168.2.0\u002F24 via 10.0.0.1 dev eth0\n\n# 默认路由\nsudo ip route add default via 192.168.1.1\nsudo ip route add default via 192.168.1.1 dev eth0\n\n# 单主机路由（\u002F32）\nsudo ip route add 10.10.10.1\u002F32 via 192.168.1.1\n```\n\n### 删除与替换\n\n```bash\nsudo ip route del 192.168.2.0\u002F24\nsudo ip route del default via 192.168.1.1\nsudo ip route replace 192.168.2.0\u002F24 via 10.0.0.2   # 不先删除，直接替换\n```\n\n### `ip route get`：排障利器\n\n`ip route get` 不修改任何配置，而是**模拟内核的路由决策**，告诉你访问某个目标时实际会使用哪个出口、哪个网关、哪个源 IP：\n\n```bash\nip route get 8.8.8.8\nip route get 192.168.2.100\n```\n\n在多网卡、多路由表的场景中，这是定位“为什么走了错误的线路”最直接的方法。\n\n---\n\n## 五、`ip neigh`：邻居表 \u002F ARP 管理\n\n`ip neigh` 管理的是协议地址与链路层地址之间的绑定关系（IPv4 场景即 ARP 表）。\n\n### 查看与添加\n\n```bash\nip neigh show                                    # 查看邻居表\nsudo ip neigh add 192.168.1.1 lladdr 00:11:22:33:44:55 dev eth0\n```\n\n### 删除与清空\n\n```bash\nsudo ip neigh del 192.168.1.1 dev eth0\nsudo ip neigh flush dev eth0                    # 清空 eth0 的邻居缓存\n```\n\n邻居条目的 `nud` 状态包括 `permanent`、`reachable`、`stale`、`failed`、`incomplete` 等，`ip neigh show` 输出中的状态字段可以直接用于判断 ARP 解析是否正常。\n\n---\n\n## 六、`ip rule` 与自定义路由表：策略路由\n\n默认路由只依据**目标地址**做决策。策略路由可以在此基础上加入**源地址**等条件，实现多 ISP、多网卡场景下的分流。\n\n### 创建自定义路由表\n\n在 `\u002Fetc\u002Fiproute2\u002Frt_tables` 中添加名称映射，便于记忆：\n\n```bash\nsudo tee -a \u002Fetc\u002Fiproute2\u002Frt_tables \u003C\u003C 'EOF'\n100 isp-a\n200 isp-b\nEOF\n```\n\n路由表编号可用范围为 1–252，253（default）、254（main）、255（local）为系统保留。\n\n### 填充表路由与添加规则\n\n以双 ISP 场景为例：\n\n```bash\n# 为 isp-a 表设置默认路由，走 ISP-A 网关\nsudo ip route add default via 203.0.113.1 dev eth0 table isp-a\nsudo ip route add 203.0.113.0\u002F24 dev eth0 table isp-a\n\n# 为 isp-b 表设置默认路由，走 ISP-B 网关\nsudo ip route add default via 198.51.100.1 dev eth1 table isp-b\nsudo ip route add 198.51.100.0\u002F24 dev eth1 table isp-b\n\n# 添加规则：来自 ISP-A 地址的流量查 isp-a 表，来自 ISP-B 的查 isp-b 表\nsudo ip rule add from 203.0.113.10 table isp-a priority 100\nsudo ip rule add from 198.51.100.10 table isp-b priority 200\n```\n\n`priority` 数值越小越先匹配。验证规则和实际路由走向：\n\n```bash\nip rule show\nip route get 8.8.8.8 from 203.0.113.10\n```\n\n输出会明确显示使用了哪张表、哪个网关、哪个出口接口。\n\n---\n\n## 七、`ip tunnel` 与 `ip link`（虚拟接口）：隧道、VLAN、网桥\n\n### GRE \u002F VXLAN 隧道\n\n```bash\n# GRE\nsudo ip tunnel add gre1 mode gre remote 203.0.113.1 local 192.168.1.100 ttl 255\nsudo ip link set gre1 up\nsudo ip addr add 10.0.0.1\u002F30 dev gre1\n\n# VXLAN\nsudo ip link add vxlan0 type vxlan id 42 dstport 4789 remote 203.0.113.2\nsudo ip link set vxlan0 up\n```\n\n### VLAN 子接口\n\n```bash\nsudo ip link add link eth0 name eth0.100 type vlan id 100\nsudo ip link set eth0.100 up\nsudo ip addr add 192.168.100.1\u002F24 dev eth0.100\n```\n\n### 网桥\n\n```bash\nsudo ip link add name br0 type bridge\nsudo ip link set eth0 master br0\nsudo ip link set br0 up\n```\n\n创建后可以用 `ip -d link show eth0.100` 确认 VLAN 的 `id`、`protocol` 和父接口是否正确。\n\n---\n\n## 八、`ip netns`：网络命名空间\n\n网络命名空间是 `ip` 超越 `ifconfig` 的核心能力之一，也是理解 Docker \u002F Kubernetes 网络模型的基础。\n\n```bash\nsudo ip netns add testns              # 创建\nip netns list                          # 列出\nsudo ip netns exec testns ip addr     # 在命名空间内执行命令\nsudo ip netns exec testns ip link set lo up\nsudo ip netns del testns              # 删除\n```\n\n创建命名空间后，回环接口默认是 `DOWN` 状态，通常需要先 `ip netns exec \u003Cns> ip link set lo up` 才能进行本机连通性测试。\n\n---\n\n## 九、实用技巧\n\n**实时监控**：`ip monitor link` 监控链路状态变化，`ip monitor route` 监控路由变化。\n\n**批量执行**：将多条 `ip` 子命令写入文件，用 `ip -batch` 一次性执行：\n\n```bash\nip -batch -force \u003C config.txt\n```\n\n**JSON 输出配合 `jq`**：适合脚本自动化，例如列出所有 UP 状态接口的名称：\n\n```bash\nip -j link show | jq '.[] | select(.operstate == \"UP\") | .ifname'\n```\n\n---\n\n## 十、关键注意事项：持久化\n\n**所有通过 `ip` 命令做出的修改都是临时的，重启后丢失。** 持久化需要借助 Ubuntu 的配置管理层：\n\n- **Netplan（Ubuntu 默认）**：编辑 `\u002Fetc\u002Fnetplan\u002F*.yaml`，用 `netplan apply` 生效。\n- **NetworkManager**：使用 `nmcli connection modify` 配置网关和路由。\n- **systemd-networkd**：在 `.network` 文件中使用 `[Route]` 段。\n\n不要将 `ip` 命令直接写入启动脚本作为“持久化方案”的唯一依赖，优先使用发行版推荐的配置工具。\n\n---\n\n## 附：网络故障快速排查顺序\n\n一条实用的排查链：\n\n```bash\nip -br addr            # 1. IP 配了吗？接口是 UP 吗？\nip route show          # 2. 默认网关和必要路由存在吗？\nip route get 8.8.8.8   # 3. 实际路由决策是否符合预期？\nip neigh show          # 4. ARP \u002F 邻居解析是否正常？\n```",16,"2026-10-22T14:47:08",{"id":129,"category_id":5,"title":130,"author":8,"summary":131,"cover":132,"content":133,"tags":8,"seo_title":8,"seo_description":8,"seo_keywords":8,"og_image":8,"views":134,"comment_count":10,"is_top":103,"is_published":11,"created_at":135},10052,"Markdown 使用教程","面向初学者与日常写作者的 Markdown 语法手册。涵盖基础语法、扩展语法、Obsidian 专用写法以及常见问题排查。","\u002Fstatic\u002Fuploads\u002Fblogs\u002F1792592834478-663915515.jpg","# Markdown 使用教程\n\n> 面向初学者与日常写作者的 Markdown 语法手册。\n> 涵盖基础语法、扩展语法、Obsidian 专用写法以及常见问题排查。\n\n---\n\n## 目录\n\n- [[#1.Markdown简介|1.Markdown简介]]\n- [[#2.工具与环境|2. 工具与环境]]\n- [[#3.基础语法|3. 基础语法]]\n  - [[#3.1标题|3.1 标题]]\n  - [[#3.2段落与换行|3.2 段落与换行]]\n  - [[#3.3强调|3.3 强调]]\n  - [[#3.4分隔线|3.4 分隔线]]\n- [[#4.列表|4. 列表]]\n  - [[#4.1无序列表|4.1 无序列表]]\n  - [[#4.2有序列表|4.2 有序列表]]\n  - [[#4.3嵌套列表|4.3 嵌套列表]]\n  - [[#4.4任务列表|4.4 任务列表]]\n- [[#5.链接与图片|5. 链接与图片]]\n  - [[#5.1行内链接|5.1 行内链接]]\n  - [[#5.2自动链接|5.2 自动链接]]\n  - [[#5.3引用式链接|5.3 引用式链接]]\n  - [[#5.4图片|5.4 图片]]\n- [[#6.引用|6. 引用]]\n- [[#7.代码|7. 代码]]\n  - [[#7.1行内代码|7.1 行内代码]]\n  - [[#7.2代码块|7.2 代码块]]\n  - [[#7.3代码块中显示反引号|7.3 代码块中显示反引号]]\n- [[#8.表格|8. 表格]]\n  - [[#8.1基本语法|8.1 基本语法]]\n  - [[#8.2对齐方式|8.2 对齐方式]]\n  - [[#8.3单元格内的特殊内容|8.3 单元格内的特殊内容]]\n- [[#9.扩展语法|9. 扩展语法]]\n  - [[#9.1删除线|9.1 删除线]]\n  - [[#9.2脚注|9.2 脚注]]\n  - [[#9.3上下标|9.3 上下标]]\n  - [[#9.4数学公式|9.4 数学公式]]\n  - [[#9.5Mermaid图表|9.5 Mermaid 图表]]\n  - [[#9.6HTML混排|9.6 HTML 混排]]\n  - [[#9.7目录TOC|9.7 目录 TOC]]\n- [[#10.转义字符|10. 转义字符]]\n- [[#11.Obsidian专用语法|11. Obsidian 专用语法]]\n  - [[#11.1双向链接|11.1 双向链接]]\n  - [[#11.2标题锚点|11.2 标题锚点]]\n  - [[#11.3块引用与嵌入|11.3 块引用与嵌入]]\n  - [[#11.4标签|11.4 标签]]\n  - [[#11.5Callout提示块|11.5 Callout 提示块]]\n  - [[#11.6YAML属性|11.6 YAML 属性]]\n- [[#12.实战模板|12. 实战模板]]\n  - [[#12.1README模板|12.1 README 模板]]\n  - [[#12.2技术笔记模板|12.2 技术笔记模板]]\n- [[#13.语法速查表|13. 语法速查表]]\n- [[#14.常见问题|14. 常见问题]]\n\n---\n\n## 1.Markdown简介\n\nMarkdown 是一种**轻量级标记语言**，由 John Gruber 在 2004 年创建。它用纯文本加少量符号来表达格式，文件是 `.md` 后缀，任何文本编辑器都能打开。\n\n它的核心特点：\n\n- **纯文本**：不依赖特定软件，几十年后依然可读\n- **易读易写**：源文件本身就接近排版后的样子\n- **可转换**：能转成 HTML、PDF、Word、幻灯片等格式\n- **版本友好**：适合 Git 管理，便于对比差异\n\n常见的使用场景：\n\n| 场景 | 说明 |\n| --- | --- |\n| 个人笔记 | Obsidian、Typora 等本地笔记工具 |\n| 项目文档 | README、CHANGELOG、接口文档 |\n| 博客写作 | 静态站点生成器（Hexo、Hugo） |\n| 协作平台 | GitHub、GitLab、语雀、飞书文档 |\n| 技术出版 | 电子书、课程讲义、幻灯片 |\n\n### 1.1方言说明\n\nMarkdown 没有唯一标准，常见方言有：\n\n| 方言                             | 特点                           |\n| ------------------------------ | ---------------------------- |\n| CommonMark                     | 最严格的通用规范，多数解析器兼容             |\n| GFM (GitHub Flavored Markdown) | 增加表格、任务列表、删除线、自动链接           |\n| Obsidian Flavored Markdown     | 在 GFM 基础上增加双链、块引用、Callout、公式 |\n\n> 📌 本文以 **GFM + Obsidian** 两种环境都能正常渲染为目标编写。遇到平台差异时会单独标注。\n\n---\n\n## 2.工具与环境\n\n| 工具              | 平台                        | 特点                  |\n| --------------- | ------------------------- | ------------------- |\n| Obsidian        | Win \u002F macOS \u002F Linux \u002F 移动端 | 本地优先，双链与图谱强大，适合知识库  |\n| Typora          | Win \u002F macOS \u002F Linux       | 所见即所得，写作体验流畅        |\n| VS Code         | 全平台                       | 配合插件可预览、可导出，适合写代码文档 |\n| GitHub \u002F GitLab | 网页                        | 仓库内自动渲染 README 与文档  |\n| 语雀 \u002F 飞书         | 网页                        | 团队协作，支持大部分 GFM 语法   |\n\n写文件时的三个基本要求：\n\n1. 文件后缀用 `.md`（少数平台也认 `.markdown`）\n2. 编码统一使用 **UTF-8**，避免中文乱码\n3. 段落之间留**一个空行**，这是 Markdown 最重要的排版习惯\n\n---\n\n## 3.基础语法\n\n### 3.1标题\n\n用 `#` 表示标题，一级标题 1 个 `#`，最多到六级：\n\n```markdown\n# 一级标题\n## 二级标题\n### 三级标题\n#### 四级标题\n##### 五级标题\n###### 六级标题\n```\n\n各级标题的层级关系：\n\n| 写法         | 层级    | 典型用途        |\n| ---------- | ----- | ----------- |\n| `#`        | 一级    | 文章标题，全文只用一个 |\n| `##`       | 二级    | 主要章节        |\n| `###`      | 三级    | 章节下的小节      |\n| `####` 及以下 | 四级至六级 | 更细的划分，尽量少用  |\n\n要点：\n\n- `#` 后面建议加一个空格，兼容性最好\n- 一篇文章通常只用一个一级标题（文章标题）\n- 不要跳级使用（比如从 `##` 直接到 `####`），方便生成目录\n\n另一种写法是 Setext 风格（仅支持一到二级），用下划线表示：\n\n```markdown\n一级标题\n========\n\n二级标题\n--------\n```\n\n> ⚠️ 这种写法容易和分隔线、列表混淆，日常不推荐。\n\n### 3.2段落与换行\n\n**段落**：连续的文字算一段，段落之间用**空行**分隔。直接回车换行在多数渲染器里会被合并成同一段。\n\n```markdown\n这是第一段。\n这行仍然属于第一段。\n\n这是第二段。\n```\n\n**段内换行**有三种方式：\n\n| 方式        | 写法          | 说明                    |\n| --------- | ----------- | --------------------- |\n| 行尾两个空格    | `文字␣␣` + 回车 | 最通用，但空格看不见            |\n| 行尾反斜杠     | `文字\\` + 回车  | 直观，GFM 支持             |\n| HTML 换行标签 | `\u003Cbr>`      | 兼容性最好，但不够“纯 Markdown” |\n\n```markdown\n第一行结尾加两个空格  \n第二行\n```\n\n### 3.3强调\n\n```markdown\n*斜体* 或 _斜体_\n**粗体** 或 __粗体__\n***粗斜体***\n~~删除线~~\n```\n\n效果：*斜体*、**粗体**、***粗斜体***、~~删除线~~\n\n注意：\n\n- 强调符号**紧贴文字**，不要写成 `* 斜体 *`\n- 中文里用 `_` 容易在词中间失效，推荐统一用 `*`\n- 删除线属于 GFM 扩展，极少数老解析器不支持\n\n### 3.4分隔线\n\n单独一行写三个或更多的 `-`、`*`、`_`：\n\n```markdown\n---\n\n***\n\n___\n```\n\n效果：\n\n---\n\n要点：分隔线**前后各留一个空行**，否则可能被当成二级标题（Setext）或列表的一部分。\n\n---\n\n## 4.列表\n\n### 4.1无序列表\n\n用 `-`、`*`、`+` 开头，后面跟一个空格：\n\n```markdown\n- 苹果\n- 香蕉\n- 橙子\n```\n\n效果：\n\n- 苹果\n- 香蕉\n- 橙子\n\n> 📌 同一篇文档里**统一用一种符号**，混用虽然能渲染，但不利于后续维护。\n\n### 4.2有序列表\n\n用数字加点开头：\n\n```markdown\n1. 准备食材\n2. 处理食材\n3. 下锅烹饪\n```\n\n效果：\n\n1. 准备食材\n2. 处理食材\n3. 下锅烹饪\n\n关键点：**后续编号写什么数字都不影响渲染**，解析器会自动按顺序编号。所以下面这种写法渲染结果和上面完全一样：\n\n```markdown\n1. 准备食材\n1. 处理食材\n1. 下锅烹饪\n```\n\n这种写法在增删条目时不用重新编号，是被广泛推荐的写法。但如果你需要**从指定数字开始**（比如从 3 开始），就必须显式写第一个数字：\n\n```markdown\n3. 第三步\n4. 第四步\n```\n\n### 4.3嵌套列表\n\n子列表用 **2 个或 4 个空格**缩进（推荐 4 个空格或与父级内容对齐的 Tab）：\n\n```markdown\n- 前端\n    - HTML\n    - CSS\n    - JavaScript\n- 后端\n    1. Node.js\n    2. Python\n```\n\n效果：\n\n- 前端\n    - HTML\n    - CSS\n    - JavaScript\n- 后端\n    1. Node.js\n    2. Python\n\n常见坑：缩进不足会被当成同级；缩进过多可能被当成代码块（4 空格缩进在部分解析器里等于代码块，Obsidian 中只要紧跟在列表项后就没问题）。\n\n### 4.4任务列表\n\nGFM 扩展语法，用 `- [ ]` 表示未完成，`- [x]` 表示已完成：\n\n```markdown\n- [x] 完成需求评审\n- [x] 编写接口文档\n- [ ] 联调测试\n- [ ] 上线发布\n```\n\n效果：\n\n- [x] 完成需求评审\n- [x] 编写接口文档\n- [ ] 联调测试\n- [ ] 上线发布\n\n在 Obsidian 里点击方框即可切换状态，是管理待办最轻量的方式。\n\n---\n\n## 5.链接与图片\n\n### 5.1行内链接\n\n```markdown\n[显示文字](https:\u002F\u002Fexample.com)\n[带标题的链接](https:\u002F\u002Fexample.com \"鼠标悬停显示的文字\")\n```\n\n效果：[显示文字](https:\u002F\u002Fexample.com)\n\n指向本文其他位置的写法见 [[#11.2标题锚点|11.2 标题锚点]]。\n\n### 5.2自动链接\n\n尖括号包裹网址或邮箱，会直接变成可点击链接：\n\n```markdown\n\u003Chttps:\u002F\u002Fexample.com>\n\u003Csomeone@example.com>\n```\n\n效果：\u003Chttps:\u002F\u002Fexample.com>\n\nGFM 还支持裸网址自动识别，如 `https:\u002F\u002Fexample.com` 直接写成文本也能点击。但中文语境下裸链接容易和标点粘连，推荐仍用尖括号。\n\n### 5.3引用式链接\n\n把网址集中放在文末，正文只写引用标识，适合长文或同一链接多次出现：\n\n```markdown\n这是[官方文档][docs]的地址，也可以看[入门指南][docs]。\n\n[docs]: https:\u002F\u002Fexample.com\u002Fdocs \"官方文档\"\n```\n\n效果：这是[官方文档][docs]的地址。\n\n[docs]: https:\u002F\u002Fexample.com\u002Fdocs \"官方文档\"\n\n### 5.4图片\n\n语法只比链接多一个感叹号：\n\n```markdown\n![图片描述](https:\u002F\u002Fexample.com\u002Fpic.png)\n![图片描述](.\u002Fimages\u002Fpic.png \"图片标题\")\n```\n\n要点：\n\n- `图片描述` 是**替代文本**，图片加载失败时显示，也利于无障碍阅读\n- 路径支持相对路径、绝对路径、网络地址\n- Obsidian 中更常用 `![[图片名.png]]`，见 [[#11.3块引用与嵌入|11.3 块引用与嵌入]]\n\n调整图片尺寸（GitHub 与部分平台支持 HTML 写法）：\n\n```html\n\u003Cimg src=\".\u002Fimages\u002Fpic.png\" width=\"300\" alt=\"示意图\">\n```\n\n---\n\n## 6.引用\n\n用 `>` 开头表示引用块：\n\n```markdown\n> 这是一段引用。\n> 引用可以有多行。\n>\n> 空一行后仍在同一个引用块内。\n```\n\n效果：\n\n> 这是一段引用。\n> 引用可以有多行。\n>\n> 空一行后仍在同一个引用块内。\n\n引用可以嵌套，也可以容纳列表、代码等块级内容：\n\n````markdown\n> 外层引用\n> > 嵌套引用\n>\n> - 引用中的列表\n> - 第二项\n>\n> ```js\n> console.log(\"引用中的代码\");\n> ```\n````\n\n要点：\n\n- 每行都要以 `>` 开头，空行也要写 `>`（或留空但保持缩进）\n- 引用内的 Markdown 语法照常生效\n\n---\n\n## 7.代码\n\n### 7.1行内代码\n\n用一对反引号包裹：\n\n```markdown\n使用 `npm install` 安装依赖。\n```\n\n效果：使用 `npm install` 安装依赖。\n\n如果代码本身包含反引号，用双反引号包裹：\n\n```markdown\n``代码里有 `反引号` 的情况``\n```\n\n### 7.2代码块\n\n用三个反引号包裹，并在开头标注语言以启用高亮：\n\n````markdown\n```javascript\nfunction greet(name) {\n  console.log(`Hello, ${name}`);\n}\n```\n````\n\n效果：\n\n```javascript\nfunction greet(name) {\n  console.log(`Hello, ${name}`);\n}\n```\n\n常用语言标注：\n\n| 标注 | 语言 | 标注 | 语言 |\n| --- | --- | --- | --- |\n| `bash` \u002F `shell` | Shell | `js` \u002F `javascript` | JavaScript |\n| `python` | Python | `ts` \u002F `typescript` | TypeScript |\n| `json` | JSON | `yaml` \u002F `yml` | YAML |\n| `html` | HTML | `css` | CSS |\n| `sql` | SQL | `markdown` \u002F `md` | Markdown |\n| `java` | Java | `go` | Go |\n| `c` \u002F `cpp` | C \u002F C++ | `diff` | 差异对比 |\n\n不写语言标注也能渲染成代码块，但没有高亮。\n\n### 7.3代码块中显示反引号\n\n如果代码块内部本身包含三个反引号（比如你要教别人写代码块），外层要用**四个**反引号：\n\n``````markdown\n````markdown\n```bash\necho \"hello\"\n```\n````\n``````\n\n规则很简单：**外层反引号数量 > 内层最多连续反引号数量**。\n\n---\n\n## 8.表格\n\n### 8.1基本语法\n\n用 `|` 分隔单元格，第二行用 `-` 定义表头分隔线：\n\n```markdown\n| 姓名 | 年龄 | 城市 |\n| --- | --- | --- |\n| 张三 | 25 | 北京 |\n| 李四 | 30 | 上海 |\n```\n\n效果：\n\n| 姓名 | 年龄 | 城市 |\n| --- | --- | --- |\n| 张三 | 25 | 北京 |\n| 李四 | 30 | 上海 |\n\n要点：\n\n- 表格**前后必须有空行**，否则不会渲染（最常见的报错原因）\n- 第二行的分隔线不能省略\n- 列数不必对齐，源文件丑一点不影响渲染\n\n### 8.2对齐方式\n\n在分隔线中加入冒号控制对齐：\n\n```markdown\n| 左对齐 | 居中 | 右对齐 |\n| :--- | :---: | ---: |\n| A | B | C |\n```\n\n效果：\n\n| 左对齐 | 居中 | 右对齐 |\n| :--- | :---: | ---: |\n| A | B | C |\n\n### 8.3单元格内的特殊内容\n\n**竖线转义**：单元格内容里要写 `|`，用 `\\|`：\n\n```markdown\n| 运算符 | 含义 |\n| --- | --- |\n| `a \\| b` | 按位或 |\n```\n\n**单元格内换行**：标准 Markdown 不支持，只能用 `\u003Cbr>`：\n\n```markdown\n| 项目 | 说明 |\n| --- | --- |\n| 安装 | 第一步\u003Cbr>第二步 |\n```\n\n**单元格内写代码**：用反引号，但如果代码里同时有竖线，需要同时处理转义。\n\n---\n\n## 9.扩展语法\n\n以下语法属于扩展，GFM 或 Obsidian 支持，但并非所有平台都认，跨平台发布时需注意。\n\n### 9.1删除线\n\n```markdown\n~~这段文字已作废~~\n```\n\n效果：~~这段文字已作废~~\n\n### 9.2脚注\n\n```markdown\nMarkdown 由 John Gruber 创建[^1]。\n\n[^1]: 官方站点：https:\u002F\u002Fdaringfireball.net\u002Fprojects\u002Fmarkdown\u002F\n```\n\n效果：Markdown 由 John Gruber 创建[^1]。\n\n[^1]: 官方站点：https:\u002F\u002Fdaringfireball.net\u002Fprojects\u002Fmarkdown\u002F\n\n### 9.3上下标\n\n标准 Markdown 没有上下标，用 HTML 标签实现：\n\n```markdown\nH\u003Csub>2\u003C\u002Fsub>O\nx\u003Csup>2\u003C\u002Fsup>\n```\n\n效果：H\u003Csub>2\u003C\u002Fsub>O、x\u003Csup>2\u003C\u002Fsup>\n\n### 9.4数学公式\n\nObsidian、Typora、GitHub 均支持 LaTeX 公式。行内用 `$...$`，独立成块用 `$$...$$`：\n\n```markdown\n质能方程：$E = mc^2$\n\n$$\n\\sum_{i=1}^{n} i = \\frac{n(n+1)}{2}\n$$\n```\n\n效果：\n\n质能方程：$E = mc^2$\n\n$$\n\\sum_{i=1}^{n} i = \\frac{n(n+1)}{2}\n$$\n\n注意：`$` 与公式之间不要留空格，中文环境里 `$` 也可能触发意外解析，必要时用 `\\$` 转义。\n\n### 9.5Mermaid图表\n\nObsidian、GitHub、Typora 支持用代码块直接画图：\n\n````markdown\n```mermaid\ngraph TD\n    A[开始] --> B{条件判断}\n    B -->|是| C[执行操作]\n    B -->|否| D[结束]\n    C --> D\n```\n````\n\n效果：\n\n```mermaid\ngraph TD\n    A[开始] --> B{条件判断}\n    B -->|是| C[执行操作]\n    B -->|否| D[结束]\n    C --> D\n```\n\n除了流程图（`graph` \u002F `flowchart`），还支持时序图（`sequenceDiagram`）、甘特图（`gantt`）、类图（`classDiagram`）、饼图（`pie`）等。\n\n### 9.6HTML混排\n\nMarkdown 允许直接嵌入 HTML，用于实现 Markdown 做不到的效果：\n\n```html\n\u003Cdetails>\n\u003Csummary>点击展开\u003C\u002Fsummary>\n\n这里是被折叠的内容。\n\n\u003C\u002Fdetails>\n```\n\n效果：\n\n\u003Cdetails>\n\u003Csummary>点击展开\u003C\u002Fsummary>\n\n这里是被折叠的内容。\n\n\u003C\u002Fdetails>\n\n要点：\n\n- `\u003Cdetails>` 内部**前后要留空行**，否则内部 Markdown 不解析\n- 出于安全考虑，GitHub 会过滤 `script`、`style` 等标签\n- 不要滥用 HTML，会破坏纯文本可读性\n\n### 9.7目录TOC\n\nGitHub 会自动为标题生成锚点，但**不会自动生成目录**，需要手动写或借助工具。常见写法：\n\n```markdown\n- [第一节](#第一节)\n- [第二节](#第二节)\n```\n\n锚点规则各平台不同，是最容易踩坑的地方：\n\n| 平台 | 锚点规则 | 示例（标题 `## 1. 环境要求`） |\n| --- | --- | --- |\n| GitHub | 转小写、去标点、空格转 `-` | `#1-环境要求` |\n| Obsidian | 标题原文，保留标点与空格 | `#1. 环境要求` |\n\n> ⚠️ 在 Obsidian 中必须使用双链格式 `[[#标题原文|显示文字]]`，用 GitHub 风格锚点会跳转失败。详见 [[#11.2标题锚点|11.2 标题锚点]]。\n\n---\n\n## 10.转义字符\n\n在 Markdown 中有特殊含义的符号，前面加反斜杠 `\\` 可以按字面显示：\n\n```markdown\n\\*不是斜体\\*\n\\# 不是标题\n\\[不是链接\\]\n1\\. 不是列表\n```\n\n可转义的字符包括：\n\n`\\` `` ` `` `*` `_` `{` `}` `[` `]` `(` `)` `#` `+` `-` `.` `!` `|` `>` `~`\n\n常见使用场景：\n\n- 写 `C++`、`C#` 这类词时通常无需转义，但如果出现在行首要注意\n- 表格单元格内写竖线用 `\\|`\n- 行首写 `2024.` 这类年份时可能被识别成有序列表，写成 `2024\\.`\n\n---\n\n## 11.Obsidian专用语法\n\nObsidian 在 GFM 基础上扩展了一套“知识库”语法，是本目录文档的主要查看环境。\n\n### 11.1双向链接\n\n用双方括号引用库内其他笔记：\n\n```markdown\n[[另一篇笔记]]\n[[另一篇笔记|显示为别的文字]]\n[[另一篇笔记#某个标题]]\n```\n\n要点：\n\n- 链接的是**文件名**（不含 `.md` 后缀）\n- 文件名重复时要写路径，如 `[[文件夹\u002F笔记名]]`\n- 被链接的笔记会自动出现在“反向链接”面板中\n\n### 11.2标题锚点\n\n跳转到本文某个标题，可写 `[[#标题]]` 或 `[[#标题|显示文字]]`：\n\n```markdown\n[[#3.2段落与换行|段落与换行]]\n[[#11.Obsidian专用语法]]\n```\n\n**关键规则**：锚点必须是标题 `##` 之后的**原文**，不能按 GitHub 规则转换。\n\n| 标题写法 | 正确锚点 | 错误锚点 |\n| --- | --- | --- |\n| `## 1.Markdown简介` | `[[#1.Markdown简介]]` | `[[#1-markdown简介]]` |\n| `## 3.2段落与换行` | `[[#3.2段落与换行]]` | `[[#32段落与换行]]` |\n| `## 六、Docker命令` | `[[#六、Docker命令]]` | `[[#六docker命令]]` |\n\n> ⚠️ **标题中不要出现 ASCII 空格**。Obsidian 的 `[[#锚点|别名]]` 遇到标题里的空格时容易解析失败。推荐写成 `## 3.2段落与换行` 这种连写形式，并在链接里用别名显示成 `3.2 段落与换行`。\n>\n> 本文所有标题均已按此规则处理，可直接作为模板参考。\n\n### 11.3块引用与嵌入\n\n**块引用**：在某个段落末尾写 `^块标识`，就可以从别处精确引用这一段：\n\n```markdown\n这句话很重要，需要被引用。 ^important-note\n```\n\n然后在其他笔记里引用：\n\n```markdown\n[[笔记名#^important-note]]\n```\n\n**嵌入**：链接前加感叹号，把内容直接显示出来：\n\n```markdown\n![[另一篇笔记]]                嵌入整篇笔记\n![[另一篇笔记#某个标题]]        嵌入某个章节\n![[图片.png]]                  嵌入图片（可加尺寸：![[图片.png|300]]）\n![[文档.pdf]]                  嵌入 PDF\n```\n\n### 11.4标签\n\n用 `#标签` 做横向分类，与文件夹的纵向结构互补：\n\n```markdown\n#项目\u002F进行中\n#技术\u002F前端\u002FReact\n#待整理\n```\n\n要点：\n\n- 标签**不能包含空格**，多层级用 `\u002F` 分隔\n- 与标题冲突：行首的 `#标题` 若后面没有空格可能被识别为标签，写标题时保持 `#` 后有空格\n- 在搜索面板输入 `tag:#项目` 可筛选\n\n### 11.5Callout提示块\n\n用 `> [!类型]` 生成带颜色和图标的高亮块：\n\n```markdown\n> [!note] 说明\n> 这是一条补充说明。\n\n> [!warning] 注意\n> 这里容易出错。\n\n> [!tip]- 可折叠提示\n> 标题带减号表示默认折叠。\n```\n\n支持的常用类型：\n\n| 类型 | 用途 | 类型 | 用途 |\n| --- | --- | --- | --- |\n| `note` | 普通说明 | `tip` | 技巧建议 |\n| `info` | 信息 | `warning` | 警告 |\n| `danger` | 危险操作 | `success` | 成功\u002F完成 |\n| `question` | 疑问 | `quote` | 引用 |\n| `example` | 示例 | `bug` | 缺陷记录 |\n\n可折叠写法：`[!note]-` 默认收起，`[!note]+` 默认展开。\n\n### 11.6YAML属性\n\n文件最顶部用三条横线包裹 YAML，可定义标题、标签、别名等元数据：\n\n```yaml\n---\ntitle: Markdown 使用教程\ntags:\n  - 教程\n  - 写作\naliases:\n  - MD教程\ncreated: 2026-10-21\n---\n```\n\n这些属性会被 Obsidian 的搜索、别名跳转、模板插件读取。注意：\n\n- 必须位于**文件第一行**，前面不能有空行\n- `---` 前后不要缩进\n- 如果文档不需要属性，可以完全不写\n\n---\n\n## 12.实战模板\n\n### 12.1README模板\n\n````markdown\n# 项目名称\n\n一句话说明这个项目是做什么的。\n\n## 功能特性\n\n- 特性一\n- 特性二\n\n## 快速开始\n\n```bash\ngit clone https:\u002F\u002Fexample.com\u002Frepo.git\ncd repo\nnpm install\nnpm run dev\n```\n\n## 配置说明\n\n| 变量 | 说明 | 默认值 |\n| --- | --- | --- |\n| `PORT` | 服务端口 | `3000` |\n\n## 目录结构\n\n```text\nsrc\u002F\n├── components\u002F\n├── pages\u002F\n└── utils\u002F\n```\n\n## 许可证\n\nMIT\n````\n\n### 12.2技术笔记模板\n\n````markdown\n---\ntitle: 主题名称\ntags:\n  - 技术\ncreated: 2026-10-21\n---\n\n# 主题名称\n\n## 背景与问题\n\n为什么要记录这篇笔记。\n\n## 核心结论\n\n> [!tip] 结论先行\n> 用两三句话把最重要的结论写在这里。\n\n## 详细内容\n\n### 原理\n\n### 实践步骤\n\n1. 第一步\n2. 第二步\n\n```bash\n# 可执行的命令\n```\n\n### 踩坑记录\n\n| 现象 | 原因 | 解决 |\n| --- | --- | --- |\n| 报错信息 | 原因分析 | 处理方式 |\n\n## 参考资料\n\n- [官方文档](https:\u002F\u002Fexample.com)\n````\n\n---\n\n## 13.语法速查表\n\n| 想要的效果 | 写法 |\n| --- | --- |\n| 标题 | `# 一级` 到 `###### 六级` |\n| 粗体 | `**文字**` |\n| 斜体 | `*文字*` |\n| 粗斜体 | `***文字***` |\n| 删除线 | `~~文字~~` |\n| 分隔线 | `---` |\n| 无序列表 | `- 项目` |\n| 有序列表 | `1. 项目` |\n| 任务列表 | `- [ ] 待办` \u002F `- [x] 完成` |\n| 行内代码 | `` `code` `` |\n| 代码块 | 三个反引号 + 语言名 |\n| 链接 | `[文字](url)` |\n| 图片 | `![描述](url)` |\n| 引用 | `> 内容` |\n| 表格 | `\\| 表头 \\|` + `\\| --- \\|` |\n| 脚注 | `文字[^1]` + `[^1]: 内容` |\n| 公式 | `$公式$` \u002F `$$公式$$` |\n| 换行 | 行尾两个空格或 `\u003Cbr>` |\n| 转义 | `\\*` |\n| Obsidian双链 | `[[笔记名]]` |\n| 标题锚点 | `[[#标题原文\\|别名]]` |\n| 嵌入 | `![[笔记名]]` |\n| 标签 | `#分类\u002F子类` |\n| 提示块 | `> [!note] 标题` |\n\n---\n\n## 14.常见问题\n\n### 14.1表格不渲染\n\n**原因**：表格前缺少空行，或第二行分隔线写错。\n\n**排查**：确认表格上方有一个空行；确认第二行是 `| --- | --- |` 形式且列数与表头一致。\n\n### 14.2回车换行不生效\n\n**原因**：Markdown 把单个换行视为空格。\n\n**解决**：行尾加两个空格、加 `\\`，或使用 `\u003Cbr>`。段落之间直接空一行最稳。\n\n### 14.3有序列表编号乱掉\n\n**原因**：列表中间插入了空行或非列表内容，导致列表被切断。\n\n**解决**：保持列表连续；需要插入说明文字时，用两个以上空格缩进使其属于列表项。\n\n### 14.4代码块里的内容被解析\n\n**原因**：代码块用反引号包裹，但代码内部含有反引号。\n\n**解决**：外层反引号数量大于内层，例如外层四个反引号包裹内层三个。\n\n### 14.5图片不显示\n\n**原因**：路径错误或大小写不一致。Windows 在资源管理器里不区分大小写，但 GitHub 等平台区分。\n\n**解决**：使用相对路径 `.\u002Fimages\u002Fpic.png`，并核对文件名大小写与扩展名。\n\n### 14.6Obsidian目录点击无反应\n\n**原因**：锚点用了 GitHub 的 slug 规则，或标题中含 ASCII 空格导致链接被截断。\n\n**解决**：锚点改为标题原文，标题尽量去掉空格，并在链接里用别名控制显示效果。\n\n### 14.7中文标点导致链接失效\n\n**原因**：全角括号、全角冒号不能用于语法结构。\n\n**解决**：语法符号一律用半角：`()`、`:`、`|`、`#`；中文标点只能出现在正文里。\n\n### 14.8加粗在中文里不生效\n\n**原因**：强调符号与文字之间有空格，或符号紧贴中文标点。\n\n**解决**：写成 `**加粗文字**`，符号与文字之间不留空格。\n\n---\n\n## 附：写作习惯建议\n\n1. **一屏能读完的段落**：长段落拆短，Markdown 的可读性来自空行与列表\n2. **标题即大纲**：写完先检查标题层级，能直接当目录用\n3. **少用花哨语法**：Callout、折叠块、Mermaid 适度使用，过多会喧宾夺主\n4. **链接用相对路径**：文档随仓库迁移时不至于全部失效\n5. **源文件也要可读**：Markdown 的价值在于即使不渲染也看得懂",21,"2026-10-21T22:27:42",{"id":137,"category_id":5,"title":138,"author":8,"summary":139,"cover":140,"content":141,"tags":8,"seo_title":8,"seo_description":8,"seo_keywords":8,"og_image":8,"views":142,"comment_count":10,"is_top":103,"is_published":11,"created_at":143},10050,"Docker 命令使用教程","Docker 是一个开源的容器化平台，它把应用及其依赖打包成一个镜像（Image），运行起来就是容器（Container)。","\u002Fstatic\u002Fuploads\u002Fblogs\u002F1792563241353-504883897.jpg","# Docker 命令使用教程\n\n> 面向初学者与日常使用者的 Docker 命令行速查与详解手册。\n> 涵盖镜像、容器、网络、数据卷、Compose 以及实用技巧。\n\n---\n\n## 目录\n\n1. [Docker 简介](#1-docker-简介)\n2. [命令通用格式](#2-命令通用格式)\n3. [镜像管理命令](#3-镜像管理命令)\n4. [容器管理命令](#4-容器管理命令)\n5. [容器内操作与日志](#5-容器内操作与日志)\n6. [数据卷（Volume）命令](#6-数据卷volume命令)\n7. [网络（Network）命令](#7-网络network命令)\n8. [系统维护与清理命令](#8-系统维护与清理命令)\n9. [Docker Compose 命令](#9-docker-compose-命令)\n10. [Dockerfile 常用指令](#10-dockerfile-常用指令)\n11. [实用技巧与常见问题](#11-实用技巧与常见问题)\n12. [命令速查表](#12-命令速查表)\n\n---\n\n## 1. Docker 简介\n\nDocker 是一个开源的容器化平台，它把应用及其依赖打包成一个**镜像（Image）**，运行起来就是**容器（Container）**。\n\n核心概念：\n\n| 概念 | 说明 |\n| --- | --- |\n| 镜像 Image | 只读模板，包含应用与依赖，是容器的“源代码” |\n| 容器 Container | 镜像的运行实例，可启动、停止、删除 |\n| 仓库 Registry | 存放镜像的地方，如 Docker Hub |\n| 数据卷 Volume | 用于持久化容器数据的存储 |\n| 网络 Network | 容器之间、容器与外部通信的通道 |\n\n查看版本与环境信息：\n\n```bash\ndocker --version          # 查看 Docker 版本\ndocker version            # 查看客户端与服务端详细版本\ndocker info               # 查看 Docker 系统信息（镜像数、容器数、存储驱动等）\n```\n\n---\n\n## 2. 命令通用格式\n\nDocker 命令遵循统一格式：\n\n```bash\ndocker [子命令] [选项] [参数]\n```\n\n- **子命令**：如 `run`、`ps`、`images`\n- **选项**：一般以 `-` 或 `--` 开头，如 `-d`、`--name`\n- **参数**：操作对象，如镜像名、容器名\n\n查看帮助：\n\n```bash\ndocker --help             # 查看所有子命令\ndocker run --help         # 查看某个子命令的帮助\ndocker images help        # 部分子命令也可用 help 查看\n```\n\n---\n\n## 3. 镜像管理命令\n\n### 3.1 拉取镜像\n\n```bash\ndocker pull nginx                # 拉取最新版（latest）\ndocker pull nginx:1.25           # 拉取指定版本\ndocker pull nginx:1.25-alpine    # 拉取 alpine 精简版\n```\n\n### 3.2 查看本地镜像\n\n```bash\ndocker images                    # 列出本地所有镜像\ndocker images nginx              # 只列出 nginx 相关镜像\ndocker image ls -a               # 同上（新语法）\n```\n\n### 3.3 搜索镜像\n\n```bash\ndocker search nginx              # 在 Docker Hub 搜索镜像\ndocker search --filter stars=100 nginx   # 按星标过滤\n```\n\n### 3.4 构建镜像\n\n```bash\ndocker build -t myapp:1.0 .              # 基于当前目录 Dockerfile 构建\ndocker build -t myapp:1.0 -f .\u002FDockerfile.dev .   # 指定 Dockerfile\ndocker build --no-cache -t myapp:1.0 .   # 不使用缓存构建\n```\n\n### 3.5 打标签与推送\n\n```bash\ndocker tag myapp:1.0 user\u002Fmyapp:1.0      # 给镜像打新标签\ndocker push user\u002Fmyapp:1.0               # 推送到仓库\n```\n\n### 3.6 登录与登出仓库\n\n```bash\ndocker login                             # 登录 Docker Hub\ndocker login registry.example.com        # 登录私有仓库\ndocker logout                            # 登出\n```\n\n### 3.7 导出与导入镜像\n\n```bash\ndocker save -o myapp.tar myapp:1.0       # 导出镜像为 tar 文件\ndocker load -i myapp.tar                 # 从 tar 文件导入镜像\n```\n\n### 3.8 删除镜像\n\n```bash\ndocker rmi nginx:1.25                    # 删除指定镜像\ndocker rmi -f nginx:1.25                 # 强制删除\ndocker image prune                       # 删除未被使用的悬空镜像\ndocker image prune -a                    # 删除所有未使用镜像\n```\n\n---\n\n## 4. 容器管理命令\n\n### 4.1 创建并启动容器（run）\n\n`docker run` 是最常用的命令，常用选项如下：\n\n| 选项 | 说明 |\n| --- | --- |\n| `-d` | 后台运行（detached） |\n| `-it` | 交互式终端 |\n| `--name` | 指定容器名称 |\n| `-p` | 端口映射 `宿主机端口:容器端口` |\n| `-v` | 数据卷挂载 `宿主机路径:容器路径` |\n| `-e` | 设置环境变量 |\n| `--rm` | 容器停止后自动删除 |\n| `--network` | 指定网络 |\n| `--restart` | 重启策略：`no`、`on-failure`、`always`、`unless-stopped` |\n\n示例：\n\n```bash\n# 后台运行 nginx，映射 8080 端口\ndocker run -d --name web -p 8080:80 nginx\n\n# 交互式运行 ubuntu 并进入 shell\ndocker run -it --rm ubuntu:22.04 bash\n\n# 带环境变量与挂载运行 MySQL\ndocker run -d --name mysql \\\n  -p 3306:3306 \\\n  -e MYSQL_ROOT_PASSWORD=123456 \\\n  -v \u002Fdata\u002Fmysql:\u002Fvar\u002Flib\u002Fmysql \\\n  --restart unless-stopped \\\n  mysql:8.0\n```\n\n### 4.2 查看容器\n\n```bash\ndocker ps                        # 查看运行中的容器\ndocker ps -a                     # 查看所有容器（含已停止）\ndocker ps -q                     # 只显示容器 ID\ndocker ps --format \"table {{.Names}}\\t{{.Status}}\"   # 自定义输出格式\n```\n\n### 4.3 启动 \u002F 停止 \u002F 重启 \u002F 删除\n\n```bash\ndocker start web                 # 启动已存在的容器\ndocker stop web                  # 停止容器\ndocker restart web               # 重启容器\ndocker kill web                  # 强制杀死容器\ndocker rm web                    # 删除已停止的容器\ndocker rm -f web                 # 强制删除运行中的容器\n```\n\n### 4.4 暂停与恢复\n\n```bash\ndocker pause web                 # 暂停容器\ndocker unpause web               # 恢复容器\n```\n\n### 4.5 查看容器详情\n\n```bash\ndocker inspect web               # 查看容器详细配置（JSON）\ndocker inspect -f '{{.State.Status}}' web   # 用模板提取字段\ndocker top web                   # 查看容器内进程\ndocker stats                     # 实时查看资源占用\ndocker stats web                 # 只看某个容器\n```\n\n### 4.6 重命名与提交\n\n```bash\ndocker rename web web2           # 重命名容器\ndocker commit web myapp:1.0      # 将容器当前状态提交为新镜像\n```\n\n---\n\n## 5. 容器内操作与日志\n\n### 5.1 进入容器\n\n```bash\ndocker exec -it web bash         # 在运行中的容器内开 shell（推荐）\ndocker exec -it web sh           # 容器无 bash 时用 sh\ndocker attach web                # 连接到容器主进程（不推荐，退出可能停止容器）\n```\n\n### 5.2 在容器内执行单条命令\n\n```bash\ndocker exec web ls \u002Fetc          # 执行一条命令并返回结果\ndocker exec -u root web whoami   # 以指定用户执行\n```\n\n### 5.3 查看日志\n\n```bash\ndocker logs web                  # 查看日志\ndocker logs -f web               # 实时跟踪日志\ndocker logs --tail 100 web       # 只看最后 100 行\ndocker logs -t web               # 显示时间戳\ndocker logs --since 10m web      # 查看最近 10 分钟日志\n```\n\n### 5.4 文件复制\n\n```bash\ndocker cp web:\u002Fetc\u002Fnginx\u002Fnginx.conf .\u002Fnginx.conf   # 容器 → 宿主机\ndocker cp .\u002Findex.html web:\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002F  # 宿主机 → 容器\n```\n\n### 5.5 查看容器文件变更\n\n```bash\ndocker diff web                  # 查看容器文件系统改动\n```\n\n---\n\n## 6. 数据卷（Volume）命令\n\n数据卷用于持久化与共享数据，独立于容器生命周期。\n\n```bash\ndocker volume create mydata             # 创建数据卷\ndocker volume ls                        # 列出所有数据卷\ndocker volume inspect mydata            # 查看数据卷详情（含挂载路径）\ndocker volume rm mydata                 # 删除数据卷\ndocker volume prune                     # 删除未使用的数据卷\n```\n\n挂载方式对比：\n\n```bash\n# 1. 命名卷（推荐，由 Docker 管理）\ndocker run -d -v mydata:\u002Fdata nginx\n\n# 2. 绑定挂载宿主机目录\ndocker run -d -v \u002Fhost\u002Fpath:\u002Fcontainer\u002Fpath nginx\n\n# 3. --mount 语法（更清晰，推荐用于生产）\ndocker run -d --mount type=volume,src=mydata,dst=\u002Fdata nginx\ndocker run -d --mount type=bind,src=\u002Fhost\u002Fpath,dst=\u002Fcontainer\u002Fpath nginx\n```\n\n> 提示：绑定挂载时源路径必须是**绝对路径**，且建议加 `:ro` 只读，如 `-v \u002Fhost\u002Fpath:\u002Fapp:ro`。\n\n---\n\n## 7. 网络（Network）命令\n\n```bash\ndocker network ls                       # 列出网络\ndocker network create mynet             # 创建 bridge 网络\ndocker network create -d overlay mynet  # 创建指定驱动网络\ndocker network inspect mynet            # 查看网络详情\ndocker network connect mynet web        # 把容器连接到网络\ndocker network disconnect mynet web     # 断开容器\ndocker network rm mynet                 # 删除网络\ndocker network prune                    # 删除未使用网络\n```\n\n自定义网络内容器可通过**容器名**互相访问：\n\n```bash\ndocker network create appnet\ndocker run -d --name db --network appnet mysql:8.0\ndocker run -d --name api --network appnet myapi:1.0\n# 此时 api 容器内可直接用 \"db\" 作为主机名访问数据库\n```\n\n端口映射说明：\n\n```bash\n-p 80:80          # 宿主机 80 -> 容器 80\n-p 127.0.0.1:80:80  # 仅绑定本地回环\n-p 8080:80\u002Fudp    # UDP 端口\n```\n\n---\n\n## 8. 系统维护与清理命令\n\n```bash\ndocker system df                 # 查看磁盘占用汇总\ndocker system df -v              # 查看详细占用\ndocker system prune              # 清理停止的容器、未用网络、悬空镜像、构建缓存\ndocker system prune -a           # 清理所有未使用镜像\ndocker system events             # 实时查看 Docker 事件\n```\n\n清理组合示例：\n\n```bash\n# 停止所有容器\ndocker stop $(docker ps -q)\n\n# 删除所有容器\ndocker rm $(docker ps -aq)\n\n# 删除所有镜像\ndocker rmi $(docker images -q)\n```\n\n> 警告：`prune` 类命令会删除数据，执行前请确认。\n\n---\n\n## 9. Docker Compose 命令\n\nCompose 用于编排多容器应用，配置文件为 `docker-compose.yml`（新版为 `compose.yaml`）。\n\n示例 `compose.yaml`：\n\n```yaml\nservices:\n  web:\n    image: nginx:1.25\n    ports:\n      - \"8080:80\"\n    volumes:\n      - .\u002Fhtml:\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml:ro\n    depends_on:\n      - api\n  api:\n    build: .\u002Fapi\n    environment:\n      - DB_HOST=db\n    depends_on:\n      - db\n  db:\n    image: mysql:8.0\n    environment:\n      MYSQL_ROOT_PASSWORD: 123456\n    volumes:\n      - dbdata:\u002Fvar\u002Flib\u002Fmysql\nvolumes:\n  dbdata:\n```\n\n常用命令：\n\n```bash\ndocker compose up -d             # 后台启动所有服务\ndocker compose up -d --build     # 先重新构建镜像再启动\ndocker compose down              # 停止并删除容器、网络\ndocker compose down -v           # 同时删除数据卷\ndocker compose ps                # 查看服务状态\ndocker compose logs -f           # 跟踪所有服务日志\ndocker compose logs -f web       # 只看某服务\ndocker compose exec web bash     # 进入某服务容器\ndocker compose restart web       # 重启某服务\ndocker compose build             # 构建\u002F重建镜像\ndocker compose pull              # 拉取最新镜像\ndocker compose config            # 校验并输出最终配置\n```\n\n> 旧版命令为 `docker-compose`（带连字符），新版已集成到 `docker compose`。\n\n---\n\n## 10. Dockerfile 常用指令\n\n| 指令 | 作用 |\n| --- | --- |\n| `FROM` | 指定基础镜像 |\n| `WORKDIR` | 设置工作目录 |\n| `COPY` | 复制文件到镜像 |\n| `ADD` | 复制文件（支持 URL 与自动解压 tar） |\n| `RUN` | 构建时执行命令 |\n| `ENV` | 设置环境变量 |\n| `ARG` | 构建参数 |\n| `EXPOSE` | 声明暴露端口 |\n| `CMD` | 容器启动默认命令（可被覆盖） |\n| `ENTRYPOINT` | 容器启动入口（不易被覆盖） |\n| `VOLUME` | 声明数据卷 |\n| `USER` | 指定运行用户 |\n| `HEALTHCHECK` | 健康检查 |\n\n示例 Dockerfile：\n\n```dockerfile\nFROM node:20-alpine\nWORKDIR \u002Fapp\nCOPY package*.json .\u002F\nRUN npm ci --omit=dev\nCOPY . .\nENV NODE_ENV=production\nEXPOSE 3000\nUSER node\nCMD [\"node\", \"server.js\"]\n```\n\n构建并运行：\n\n```bash\ndocker build -t mynode:1.0 .\ndocker run -d --name mynode -p 3000:3000 mynode:1.0\n```\n\n---\n\n## 11. 实用技巧与常见问题\n\n### 11.1 常用技巧\n\n```bash\n# 批量清理停止的容器\ndocker container prune\n\n# 查看容器 IP 地址\ndocker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web\n\n# 进入正在运行的容器但容器没有 shell（用 nsenter 或临时容器）\ndocker run --rm -it --pid=container:web --net=container:web alpine sh\n\n# 格式化输出容器列表\ndocker ps --format \"table {{.ID}}\\t{{.Names}}\\t{{.Ports}}\\t{{.Status}}\"\n\n# 使用别名简化（加入 ~\u002F.bashrc）\nalias dk='docker'\nalias dkc='docker compose'\n```\n\n### 11.2 常见问题\n\n**Q1：容器启动后马上退出？**\n容器主进程执行完就退出，属于正常现象。需要长期运行的进程（如守护进程）或使用 `-it` 保持前台。\n\n**Q2：端口被占用，启动失败？**\n更换宿主机端口，例如改成 `-p 8081:80`；或查杀占用进程 `lsof -i:8080`。\n\n**Q3：修改容器内文件后重启丢失？**\n容器内文件是临时层，重启（重新创建）会丢失。应通过 `-v` 挂载宿主机目录或数据卷持久化。\n\n**Q4：如何从容器内访问宿主机？**\nLinux 上可用 `host.docker.internal`（需 `--add-host=host.docker.internal:host-gateway`），或使用宿主机实际 IP。\n\n**Q5：镜像拉取慢？**\n配置镜像加速器（如国内镜像源），在 `\u002Fetc\u002Fdocker\u002Fdaemon.json` 中设置 `registry-mirrors` 后重启 Docker。\n\n---\n\n## 12. 命令速查表\n\n| 功能 | 命令 |\n| --- | --- |\n| 查看版本 | `docker --version` |\n| 拉取镜像 | `docker pull 镜像名:标签` |\n| 查看镜像 | `docker images` |\n| 构建镜像 | `docker build -t 名:标签 .` |\n| 删除镜像 | `docker rmi 镜像名:标签` |\n| 运行容器 | `docker run -d -p 宿主:容器 --name 名 镜像` |\n| 查看容器 | `docker ps -a` |\n| 进入容器 | `docker exec -it 名 bash` |\n| 查看日志 | `docker logs -f 名` |\n| 停止容器 | `docker stop 名` |\n| 删除容器 | `docker rm 名` |\n| 查看资源 | `docker stats` |\n| 创建数据卷 | `docker volume create 名` |\n| 创建网络 | `docker network create 名` |\n| 清理系统 | `docker system prune -a` |\n| Compose 启动 | `docker compose up -d` |\n| Compose 停止 | `docker compose down` |\n\n---\n\n## 附：学习路径建议\n\n1. 先用 `docker run` 跑通 nginx、redis 等官方镜像；\n2. 学会 `-p`、`-v`、`-e` 三个最常用参数；\n3. 写出第一个 Dockerfile 并构建自己的镜像；\n4. 掌握数据卷与自定义网络，实现多容器互通；\n5. 使用 Docker Compose 编排完整项目；\n6. 最后学习镜像优化、私有仓库与生产部署。\n\n---\n\n> 文档版本：v1.0 ｜ 适用于 Docker 24+ 及 Docker Compose v2.0",73,"2026-10-22T14:50:23",{"id":145,"category_id":5,"title":146,"author":8,"summary":147,"cover":148,"content":149,"tags":8,"seo_title":8,"seo_description":8,"seo_keywords":8,"og_image":8,"views":150,"comment_count":10,"is_top":103,"is_published":11,"created_at":151},10048,"在vscode中安装及配置markdown插件","安装及配置markdown插件","\u002Fstatic\u002Fuploads\u002Fblogs\u002F1792477022535-336041452.jpg","在 VS Code 中配置 Markdown，其实是一个从**基础编辑**到**专业写作**的渐进过程，核心思路是：先用好内置功能，再通过安装插件来补齐预览、导出、格式检查等能力。\n### 📝 第一步：启用 VS Code 内置功能（零配置）\n\nVS Code 本身就支持 Markdown 的基础编辑和预览，无需安装任何插件。\n\n- **实时预览**：打开或新建一个 `.md` 文件，按下 `Ctrl+Shift+V`（macOS: `Cmd+Shift+V`），就会在侧边打开预览窗口，编辑时内容会实时同步。\n- **分栏预览**：如果想保持编辑器和预览左右并排，可以使用快捷键 `Ctrl+K` 然后按 `V`。\n\n不过，内置预览的功能比较简单，不支持流程图、数学公式，也无法直接导出为 PDF 等格式。\n\n### 🚀 第二步：安装核心插件，解锁专业能力\n\n要实现更强大的功能，就需要安装以下几个广受推荐的插件。你可以在 VS Code 左侧的扩展面板（`Ctrl+Shift+X`）中搜索并安装它们。\n\n#### 1. Markdown All in One（编辑增强必备）\n这是一个**编辑增强神器**，几乎是所有 Markdown 用户的必装插件。\n- **自动目录**：在文档中输入 `[TOC]` 或通过命令面板（`Ctrl+Shift+P`）执行 `Create Table of Contents`，即可自动生成并更新目录。\n- **快捷键**：支持 `Ctrl+B` 加粗、`Ctrl+I` 斜体等常用格式化快捷键，并自动续写列表编号。\n- **数学公式**：配合预览插件，可以很好地支持 LaTeX 数学公式。\n\n#### 2. Markdown Preview Enhanced（预览与导出核心）\n这是功能最强大的预览与导出插件，能替代 VS Code 的默认预览。\n- **增强渲染**：原生支持 **Mermaid 流程图**、**PlantUML**、**数学公式**、时序图等复杂内容的渲染。\n- **强大的导出功能**：在预览界面右键选择 `Export`，即可将文档导出为 **PDF、HTML、PNG** 等多种格式。\n- **使用方式**：在 `.md` 文件中右键，选择 `Markdown Preview Enhanced: Open Preview to the Side`，即可打开功能更丰富的预览窗口。\n\n#### 3. markdownlint（格式规范检查）\n如果你希望文档格式保持整洁、统一，这个插件可以充当 Markdown 的“代码检查器”。它会提示你标题层级是否混乱、列表缩进是否正确、空格使用是否规范等问题。\n\n#### 4. Paste Image（图片粘贴神器）\n写技术文档时经常需要截图。安装后，你可以直接将剪贴板中的图片粘贴到 Markdown 文件中，它会**自动将图片保存到指定目录，并生成对应的 Markdown 图片链接**，省去手动保存和引用的麻烦。\n\n### ⚙️ 第三步：关键配置与进阶用法\n\n#### 配置导出 PDF 的中文排版与样式\n使用 **Markdown PDF** 插件（或 MPE 插件）导出 PDF 时，默认的中文字体排版可能不够理想。你可以通过自定义 CSS 来优化。\n\n1.  **创建 CSS 文件**：在你的项目根目录创建一个文件，例如 `markdown-style.css`，并加入中文字体优化代码：\n    ```css\n    body {\n      font-family: \"Source Han Sans SC\", \"Microsoft YaHei\", sans-serif; \u002F* 优先使用思源黑体或微软雅黑 *\u002F\n      line-height: 1.8;\n      font-size: 16px;\n    }\n    h1, h2, h3, h4, h5, h6 {\n      font-family: \"Source Han Sans SC\", \"Microsoft YaHei\", sans-serif;\n      line-height: 1.5;\n    }\n    ```\n    \n\n2.  **在 VS Code 中指定样式**：打开 VS Code 设置（`Ctrl+,`），搜索 `markdown-pdf.styles`，添加你刚才创建的 CSS 文件路径。如果使用相对路径，格式为 `\".\u002Fmarkdown-style.css\"`。\n\n#### 配置导出 PDF 的页面参数\n在设置（`settings.json`）中，你还可以调整更多 PDF 导出选项，例如：\n```json\n{\n  \"markdown-pdf.styles\": [\".\u002Fmarkdown-style.css\"],\n  \"markdown-pdf.format\": \"A4\",\n  \"markdown-pdf.margin.top\": \"25mm\",\n  \"markdown-pdf.margin.bottom\": \"25mm\",\n  \"markdown-pdf.convertOnSave\": false\n}\n```\n你可以根据需要开启 `convertOnSave`（保存时自动转换），或调整页边距等参数。\n\n#### 关于 PDF 导出的额外依赖\n- **通过 Chrome 导出（推荐）**：在 MPE 插件中导出 PDF 时，选择通过 Chrome (Puppeteer) 导出，通常效果最接近预览。这需要你的系统已安装 Chrome 浏览器。\n- **通过 Pandoc 导出**：如果你需要更复杂的格式转换（例如导出 Word），可能需要额外安装 **Pandoc** 软件，并在 MPE 的导出选项中选择 Pandoc。\n- **Princexml**：部分教程会提到使用 Princexml 作为导出引擎，但它并非必需，且配置相对繁琐，建议优先使用 Chrome 方式。\n\n### 💎 总结\n\n总的来说，一个高效的 VS Code Markdown 工作流可以这样搭建：\n\n1.  **轻量编辑**：直接使用 VS Code 内置的预览功能（`Ctrl+Shift+V`）。\n2.  **日常写作**：安装 **Markdown All in One**，获得目录生成、快捷键等便利。\n3.  **专业输出**：安装 **Markdown Preview Enhanced** 来处理图表渲染和导出 PDF\u002FHTML。同时可搭配 **markdownlint** 保持格式规范，用 **Paste Image** 简化图片处理。\n4.  **精细控制**：如果需要，通过自定义 CSS 和 `settings.json` 来优化导出文档的中文排版和页面样式。",39,"2026-10-20T14:39:49",{"id":153,"category_id":5,"title":154,"author":8,"summary":155,"cover":156,"content":157,"tags":8,"seo_title":8,"seo_description":8,"seo_keywords":8,"og_image":8,"views":126,"comment_count":10,"is_top":103,"is_published":11,"created_at":158},10040,"AI带给我们什么","人工智能：从科幻到日常 智能体如何重塑我们的生活","\u002Fstatic\u002Fuploads\u002Fblogs\u002F1792323627143-416660152.jpg","# 人工智能：从科幻到日常 智能体如何重塑我们的生活\n\n理解 AI · 善用 AI · 与 AI 协作\n\n📖 科普长文 🤖 AI × 生活 📅 2026.10\n\n### 📑 目录\n\n1.  [引言](#引言)\n2.  [人工智能能干什么](#一、人工智能能干什么)\n3.  [人工智能在生活中的应用](#二、人工智能在生活中的应用)\n4.  [Agent 智能体：AI 走入生活的下一站](\u003C#三、Agent 智能体：AI 走入生活的下一站>)\n5.  [挑战与展望](#四、挑战与展望)\n6.  [结语](#结语)\n\n## 引言\n\n>\u003Cmark style=\"background-color:#faf8f2\">\u003Cmark style=\"background-color:#faf9f6\">\"曾几何时，人工智能还只是科幻小说中的想象——而今天，它已成为我们日常生活不可或缺的一部分。\"\n曾几何时，\"人工智能\"还只是科幻小说和电影中的想象——会思考的机器人、能对话的电脑、自动驾驶的飞车，都让人觉得遥不可及。然而今天，人工智能（Artificial Intelligence，简称 AI）已经悄然走进了千家万户，成为我们日常生活不可或缺的一部分。从清晨叫醒你的手机语音助手，到夜晚为你推荐剧集的算法，AI 正以前所未有的速度改变着世界。\u003C\u002Fmark>\u003C\u002Fmark>\n\n那么，人工智能究竟能做什么？它在生活中有哪些具体应用？被称为\"下一代入口\"的智能体（Agent）又将如何融入我们的日常？让我们一起来探讨。\n\n![智能家居场景](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=modern%20smart%20home%20living%20room%20with%20glowing%20AI%20assistant%20device%2C%20warm%20morning%20light%2C%20futuristic%20but%20cozy%2C%20high%20quality%20photography&image_size=landscape_16_9)\n\nAI 已经渗透到日常生活的每个角落\n\n## 一、人工智能能干什么\n\n要理解 AI 能做什么，首先要明白它的三大核心能力：\n\n### 1感知能力——\"看得见、听得懂\"\n\nAI 可以通过摄像头、麦克风等传感器感知世界。计算机视觉让机器能识别图像中的人脸、物体、文字甚至情绪；语音识别技术能将人说话的内容转化为文字，准确率已接近甚至超过人类水平。\n\n![AI 感知能力](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=AI%20computer%20vision%20concept%2C%20camera%20scanning%20face%20with%20digital%20grid%20overlay%2C%20facial%20recognition%20points%2C%20blue%20tech%20glow%2C%20futuristic%20interface&image_size=landscape_16_9)\n\n计算机视觉与语音识别：AI 的\"眼睛和耳朵\"\n\n### 2认知能力——\"想得通、理解得了\"\n\n借助大语言模型（如 GPT、GLM 等），AI 能够理解自然语言、推理问题、总结信息、翻译语言、甚至创作诗歌和代码。它不再是简单的关键词匹配，而是真正\"读懂\"你的意图。\n\n### 3执行能力——\"做得到、办得成\"\n\n结合机械臂、API 接口、自动化工具，AI 能够采取实际行动——操控机器人完成装配、调用接口下单购物、自动生成并提交报表。\n\n![AI 执行能力](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=robotic%20arm%20in%20modern%20factory%20performing%20precise%20assembly%2C%20clean%20white%20background%2C%20industrial%20automation%2C%20professional%20photography&image_size=landscape_16_9)\n\n从感知到认知再到执行：AI 完整能力链\n\n这三层能力叠加，使 AI 从\"会算\"升级为\"会想\"，再从\"会想\"迈向\"会做\"。\n\n## 二、人工智能在生活中的应用\n\nAI 早已渗透到生活的方方面面，下面这些场景你一定不陌生：\n\n### 1智能家居\n\n\"小爱同学，把客厅灯关掉。\"\"Siri，设定明天 7 点的闹钟。\"智能音箱、扫地机器人、智能空调、安防摄像头，都在用 AI 让家变得更\"懂事\"。它们能学习你的作息习惯，自动调节温度、灯光，甚至识别陌生面孔并报警。\n\n![智能家居](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=smart%20home%20devices%2C%20voice%20assistant%20speaker%2C%20smart%20lighting%2C%20robot%20vacuum%2C%20cozy%20modern%20interior%2C%20warm%20ambient%20lighting&image_size=landscape_16_9)\n\n智能音箱与各类家居设备协同工作\n\n### 2个性化推荐\n\n打开短视频 App，你刷到的内容总是\"正合心意\"；逛电商平台，首页推荐的都是你想要的；听音乐时，歌单仿佛懂你的心情。这些背后都是 AI 推荐算法在分析你的行为数据，精准匹配你的兴趣。\n\n### 3智能出行\n\n导航软件用 AI 实时预测路况、规划最优路线；网约车平台用算法匹配司机与乘客、动态定价；自动驾驶汽车更是把 AI 推向了\"开车\"这件事的巅峰，激光雷达、视觉感知、决策模型协同工作，让汽车自己安全行驶。\n\n![智能出行](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=self%20driving%20car%20on%20city%20road%2C%20autonomous%20vehicle%20with%20lidar%20sensors%2C%20futuristic%20HUD%20navigation%2C%20urban%20street%2C%20sunset%20light&image_size=landscape_16_9)\n\n自动驾驶与智能导航：AI 让出行更高效\n\n### 4医疗健康\n\nAI 能在 CT 影像中识别早期肺癌，比普通医生更敏锐；智能手环通过 AI 分析心率、睡眠，提前预警健康风险；在线问诊平台上，AI 助手能初步分析症状、给出建议，让偏远地区也能享受到优质医疗资源。\n\n### 5教育学习\n\nAI 个性化学习系统能根据每个学生的薄弱点推送练习题，做到\"因材施教\"；语言学习 App 用语音识别纠正发音；翻译软件让跨语言交流毫无障碍。如今的大模型还能充当\"24 小时家教\"，随时解答问题、批改作业。\n\n![教育学习](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=student%20using%20AI%20tutoring%20app%20on%20tablet%2C%20personalized%20learning%20interface%2C%20modern%20study%20desk%2C%20bright%20friendly%20atmosphere&image_size=landscape_16_9)\n\nAI 让\"因材施教\"成为可能\n\n### 6办公生产\n\n写邮件、做 PPT、整理会议纪要、生成代码……AI 办公助手大幅提升了工作效率。设计师用 AI 生成图片，程序员用 AI 补全代码，市场人员用 AI 撰写文案，每个人都拥有了\"超级助理\"。\n\n### 7内容创作\n\n写小说、作画、剪辑视频、配乐——AI 正在打破\"创作\"的人类专属壁垒。一幅 AI 绘画作品能在数秒内完成，一首 AI 作曲的乐曲听起来毫不逊色于人类作品。\n\n![内容创作](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=creative%20workspace%20with%20AI%20art%20generation%2C%20digital%20drawing%20tablet%2C%20colorful%20abstract%20art%20on%20screen%2C%20artist%20studio%2C%20vibrant%20colors&image_size=landscape_16_9)\n\nAI 让每个人都能成为创作者\n\n## 三、Agent 智能体：AI 走入生活的下一站\n\n如果说 ChatGPT 这样的对话机器人是\"问一句答一句\"的助手，那么 **Agent（智能体）** 就是能\"自主思考、自主行动\"的 AI 进化形态。\n\n### 什么是智能体\n\n智能体是一种能够感知环境、理解目标、自主规划步骤、调用工具并执行任务，最终完成目标的 AI 系统。简单说，它不只是\"回答问题\"，而是\"完成任务\"。\n\n举例对比：\n\n**传统 AI 助手** 你问\"明天北京天气怎么样？\"  \n——它告诉你天气预报。\n\n**智能体** 你说\"帮我安排明天去北京的出差。\"  \n——它查天气、订机票、订酒店、安排接机、同步日历、提醒带伞。\n\n![智能体概念](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=AI%20agent%20concept%2C%20autonomous%20digital%20assistant%20orchestrating%20multiple%20tasks%2C%20connected%20icons%20of%20calendar%20travel%20shopping%20email%2C%20network%20nodes%2C%20blue%20glowing%20connections&image_size=landscape_16_9)\n\n智能体：能自主规划、调用工具、完成任务的 AI\n\n### 智能体如何融入生活\n\n#### 1. 个人生活管家\n\n未来的智能体可以成为每个人的\"私人管家\"。早上起床，它已经根据你的日程、天气、健康状况安排好了当天的穿搭建议、早餐食谱和通勤路线；下班回家，它已经替你订好了喜欢的生鲜、预约了体检、回复了不重要的消息。\n\n#### 2. 购物与比价\n\n你说\"我想买一台性价比高的扫地机器人，预算 2000 元\"，智能体会自动浏览多个电商平台、对比参数与价格、查看真实评价，最终给出推荐清单，甚至直接帮你下单。\n\n#### 3. 旅行规划\n\n\"下个月带爸妈去云南玩 5 天，预算人均 5000。\"智能体会综合考虑老人的身体状况、季节气候、景点特色，规划出一份含机票、酒店、行程、餐厅、备用方案在内的完整路书，并一键预订。\n\n![旅行规划](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=travel%20planning%20concept%2C%20map%20of%20Yunnan%20China%20with%20route%20markers%2C%20flight%20tickets%2C%20hotel%20booking%2C%20scenic%20mountains%2C%20itinerary%20cards%20floating&image_size=landscape_16_9)\n\n智能体自动规划完整旅行路书\n\n#### 4. 学习与成长陪伴\n\n智能体可以成为孩子的\"成长伙伴\"——根据年龄和兴趣推荐读物、用讲故事的方式讲解知识、监督作业进度、与家长同步反馈学习情况，既不打扰孩子，又能让家长安心。\n\n#### 5. 工作流自动化\n\n在职场中，智能体能够串联各种办公软件：自动从邮件中提取任务、写入待办清单、调用日程工具安排会议、生成会议纪要、跟踪任务进度，让重复性工作\"自动跑起来\"。\n\n#### 6. 健康与情绪监测\n\n结合可穿戴设备，智能体能持续监测你的心率、睡眠、压力水平，并在异常时主动提醒；在你情绪低落时，它会用合适的方式陪伴——也许是推荐一首歌、也许是建议你出去走走、也许是提醒你联系朋友。\n\n![健康监测](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=smartwatch%20health%20monitoring%2C%20fitness%20tracker%20on%20wrist%2C%20heart%20rate%20data%20visualization%2C%20wellness%20concept%2C%20soft%20green%20background%2C%20lifestyle%20photography&image_size=landscape_16_9)\n\n智能体 + 可穿戴设备：7×24 健康守护\n\n### 智能体的关键特征\n\n**自主性** 不需要人类逐步指挥，能自己拆解任务、规划步骤\n\n**工具调用** 能使用搜索、日历、支付、邮件等外部工具完成任务\n\n**记忆能力** 能记住你的偏好、历史对话，提供个性化服务\n\n**多体协作** 多个专业智能体协同完成复杂任务\n\n## 四、挑战与展望\n\n智能体走入生活固然令人兴奋，但也面临挑战：\n\n1.  **隐私安全**：智能体需要大量个人数据才能提供贴心服务，如何保护隐私是首要问题。\n2.  **可靠性**：AI 偶尔会\"一本正经地胡说\"，在医疗、法律等高风险领域尤其需要人类把关。\n3.  **就业冲击**：重复性、规则性的工作会被替代，人类需要向创造性、情感性领域转型。\n4.  **伦理边界**：AI 自主决策的尺度在哪里？责任如何界定？这些都需要社会共同探讨。\n\n![伦理与挑战](https:\u002F\u002Ftrae-api-cn.mchost.guru\u002Fapi\u002Fide\u002Fv1\u002Ftext_to_image?prompt=balance%20scale%20representing%20AI%20ethics%2C%20human%20hand%20and%20robotic%20hand%20holding%20scale%2C%20privacy%20and%20security%20concept%2C%20blue%20tone%2C%20symbolic%20photography&image_size=landscape_16_9)\n\n在便利与风险之间寻找平衡\n\n不过，挑战并不意味着停滞。随着技术进步与制度完善，智能体有望成为像水电一样的基础设施——无处不在、默默服务，却又不可或缺。\n\n## 结语\n\n> 从\"会算\"到\"会想\"，从\"会想\"到\"会做\"，人工智能的进化史，也是人类生活的升级史。今天，AI 已经在悄悄替我们推荐歌曲、翻译外语、规划路线；明天，智能体将替我们订机票、管日程、协调工作，让每个人都能拥有一个 24 小时在线的\"私人助理\"。\n 未来已来，它不在远方，而在你每一次和 AI 的对话中、每一次被智能服务触达的瞬间里。理解 AI、善用 AI、与 AI 协作，将是这个时代每个人最重要的\"新技能\"。\n\n© 2026 人工智能科普 · 由 AI 协作生成","2026-10-18T19:53:01",{"id":160,"category_id":5,"title":161,"author":8,"summary":162,"cover":163,"content":164,"tags":8,"seo_title":8,"seo_description":8,"seo_keywords":8,"og_image":8,"views":102,"comment_count":10,"is_top":103,"is_published":11,"created_at":165},10036,"Curl命令使用","curl 是一款跨平台命令行工具，用于网络数据传输，支持 HTTP\u002FHTTPS、FTP、SFTP 等协议，常用于接口调试、爬虫、文件上传下载、自动化脚本。Windows10+\u002FMac\u002FLinux 自带。","\u002Fstatic\u002Fuploads\u002Fblogs\u002F1792160711919-554183248.png","# curl 使用教程\n\ncurl（Client URL）是一个命令行工具，用于通过各种协议（HTTP、HTTPS、FTP 等）传输数据。它是调试 API、下载文件、测试接口的利器。\n\n## 目录\n\n- [基本语法](#基本语法)\n- [发送 GET 请求](#发送GET请求)\n- [发送 POST 请求](#发送POST请求)\n- [设置请求头](#设置请求头)\n- [处理响应](#处理响应)\n- [下载与上传文件](#下载与上传文件)\n- [认证](#认证)\n- [常用选项速查](#常用选项速查)\n- [实战示例](#实战示例)\n\n## 基本语法\n\n```bash\ncurl [选项] [URL]\n```\n\n不带任何选项时，curl 默认发送 GET 请求并把响应体打印到终端：\n\n```bash\ncurl https:\u002F\u002Fapi.example.com\u002Fusers\n```\n\n## 发送GET请求 \n\n### 带查询参数\n\n```bash\ncurl \"https:\u002F\u002Fapi.example.com\u002Fusers?page=1&limit=10\"\n```\n\n也可以用 `--data-urlencode` 配合 `-G` 自动编码参数：\n\n```bash\ncurl -G https:\u002F\u002Fapi.example.com\u002Fsearch \\\n  --data-urlencode \"q=hello world\" \\\n  --data-urlencode \"lang=zh\"\n```\n\n### 跟随重定向\n\n默认不跟随 301\u002F302 重定向，加 `-L` 即可：\n\n```bash\ncurl -L https:\u002F\u002Fexample.com\u002Fredirect\n```\n\n## 发送POST请求\n\n### 提交 JSON 数据\n\n```bash\ncurl -X POST https:\u002F\u002Fapi.example.com\u002Fusers \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"name\": \"张三\", \"age\": 28}'\n```\n\n`-d` 会自动使用 POST 方法，因此 `-X POST` 可省略。\n\n### 提交表单数据\n\n```bash\ncurl -X POST https:\u002F\u002Fapi.example.com\u002Flogin \\\n  -d \"username=admin\" \\\n  -d \"password=123456\"\n```\n\n### 从文件读取请求体\n\n```bash\ncurl -X POST https:\u002F\u002Fapi.example.com\u002Fusers \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d @data.json\n```\n\n### 其他 HTTP 方法\n\n```bash\ncurl -X PUT    https:\u002F\u002Fapi.example.com\u002Fusers\u002F1 -d '{\"age\": 29}'\ncurl -X PATCH  https:\u002F\u002Fapi.example.com\u002Fusers\u002F1 -d '{\"age\": 30}'\ncurl -X DELETE https:\u002F\u002Fapi.example.com\u002Fusers\u002F1\n```\n\n## 设置请求头\n\n使用 `-H` 添加请求头，可多次使用：\n\n```bash\ncurl https:\u002F\u002Fapi.example.com\u002Fusers \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -H \"Accept: application\u002Fjson\" \\\n  -H \"Authorization: Bearer your-token-here\" \\\n  -H \"User-Agent: MyApp\u002F1.0\"\n```\n\n## 处理响应\n\n### 查看响应头\n\n```bash\n# 同时显示响应头和响应体\ncurl -i https:\u002F\u002Fapi.example.com\n\n# 只看响应头（相当于发 HEAD 请求）\ncurl -I https:\u002F\u002Fapi.example.com\n```\n\n### 保存响应到文件\n\n```bash\ncurl -o output.json https:\u002F\u002Fapi.example.com\u002Fusers\n```\n\n### 显示详细过程（调试）\n\n```bash\ncurl -v https:\u002F\u002Fapi.example.com\n```\n\n### 只显示错误信息\n\n```bash\ncurl -sS https:\u002F\u002Fapi.example.com\n```\n\n### 格式化输出（配合 jq）\n\n```bash\ncurl -s https:\u002F\u002Fapi.example.com\u002Fusers | jq .\n```\n\n### 查看请求耗时等统计信息\n\n```bash\ncurl -o \u002Fdev\u002Fnull -s -w \"HTTP状态码: %{http_code}\\n总耗时: %{time_total}s\\n下载速度: %{speed_download} B\u002Fs\\n\" \\\n  https:\u002F\u002Fapi.example.com\n```\n\n## 下载与上传文件\n\n### 下载文件\n\n```bash\n# 保存为指定文件名\ncurl -o myfile.zip https:\u002F\u002Fexample.com\u002Ffile.zip\n\n# 使用服务器上的原始文件名\ncurl -O https:\u002F\u002Fexample.com\u002Ffile.zip\n\n# 断点续传\ncurl -C - -O https:\u002F\u002Fexample.com\u002Fbigfile.zip\n\n# 限制下载速度（100KB\u002Fs）\ncurl --limit-rate 100k -O https:\u002F\u002Fexample.com\u002Ffile.zip\n```\n\n### 上传文件（multipart 表单）\n\n```bash\ncurl -X POST https:\u002F\u002Fapi.example.com\u002Fupload \\\n  -F \"file=@\u002Fpath\u002Fto\u002Fphoto.jpg\" \\\n  -F \"description=我的照片\"\n```\n\n### 上传文件（PUT 二进制）\n\n```bash\ncurl -X PUT https:\u002F\u002Fapi.example.com\u002Ffiles\u002Fphoto.jpg \\\n  -H \"Content-Type: image\u002Fjpeg\" \\\n  --data-binary @\u002Fpath\u002Fto\u002Fphoto.jpg\n```\n\n## 认证\n\n### Basic Auth\n\n```bash\ncurl -u username:password https:\u002F\u002Fapi.example.com\u002Fprivate\n```\n\n### Bearer Token\n\n```bash\ncurl -H \"Authorization: Bearer eyJhbGciOi...\" https:\u002F\u002Fapi.example.com\u002Fprivate\n```\n\n### Cookie\n\n```bash\n# 发送 Cookie\ncurl -b \"session_id=abc123\" https:\u002F\u002Fapi.example.com\n\n# 从文件读取 Cookie\ncurl -b cookies.txt https:\u002F\u002Fapi.example.com\n\n# 保存服务端返回的 Cookie\ncurl -c cookies.txt https:\u002F\u002Fexample.com\u002Flogin\n```\n\n### 忽略 SSL 证书验证（仅用于测试）\n\n```bash\ncurl -k https:\u002F\u002Fself-signed.example.com\n```\n\n## 常用选项速查\n\n| 选项 | 说明 |\n| ---- | ---- |\n| `-X \u003C方法>` | 指定 HTTP 方法（GET\u002FPOST\u002FPUT\u002FDELETE 等） |\n| `-H \u003C头>` | 添加请求头 |\n| `-d \u003C数据>` | 发送请求体数据（自动用 POST） |\n| `-F \u003C字段>` | 以 multipart\u002Fform-data 形式提交 |\n| `-o \u003C文件>` | 输出保存到指定文件 |\n| `-O` | 以远端文件名保存 |\n| `-L` | 跟随重定向 |\n| `-i` | 输出包含响应头 |\n| `-I` | 只获取响应头 |\n| `-v` | 显示详细调试信息 |\n| `-s` | 静默模式 |\n| `-S` | 静默模式下仍显示错误 |\n| `-k` | 忽略 SSL 证书校验 |\n| `-u \u003C用户:密码>` | Basic 认证 |\n| `-b \u003Ccookie>` | 发送 Cookie |\n| `-c \u003C文件>` | 保存 Cookie 到文件 |\n| `-C -` | 断点续传 |\n| `--proxy \u003C地址>` | 使用代理 |\n| `--max-time \u003C秒>` | 设置超时时间 |\n\n## 实战示例\n\n### 1. 测试本地 API 是否存活\n\n```bash\ncurl -s -o \u002Fdev\u002Fnull -w \"%{http_code}\" http:\u002F\u002Flocalhost:3000\u002Fhealth\n```\n\n### 2. 登录并保存 Cookie，再访问受保护页面\n\n```bash\n# 登录\ncurl -c cookies.txt -X POST https:\u002F\u002Fexample.com\u002Flogin \\\n  -d \"username=admin&password=123456\"\n\n# 携带 Cookie 访问\ncurl -b cookies.txt https:\u002F\u002Fexample.com\u002Fdashboard\n```\n\n### 3. 调用 GitHub API 获取用户信息\n\n```bash\ncurl -s https:\u002F\u002Fapi.github.com\u002Fusers\u002Foctocat \\\n  -H \"Accept: application\u002Fvnd.github+json\" | jq .name\n```\n\n### 4. 通过代理访问\n\n```bash\ncurl -x http:\u002F\u002F127.0.0.1:7890 https:\u002F\u002Fapi.example.com\n```\n\n### 5. 模拟慢速网络下的超时控制\n\n```bash\ncurl --connect-timeout 5 --max-time 30 https:\u002F\u002Fapi.example.com\n```\n\n### 6. 将浏览器请求复制为 curl\n\n在 Chrome\u002FEdge 开发者工具中：Network 面板 → 右键请求 → Copy → Copy as cURL，可直接粘贴到终端重放请求。\n\n---\n\n> 提示：Windows 10 1803 之后系统自带 curl；macOS 和大多数 Linux 发行版也默认安装。输入 `curl --version` 可查看版本信息。","2026-10-18T15:05:20",19,9]