Excalidraw 开源虚拟白板的架构剖析:npm 包集成、核心特性与本地开发全流程
【免费下载链接】excalidrawVirtual whiteboard for sketching hand-drawn like diagrams项目地址: https://gitcode.com/GitHub_Trending/ex/excalidraw
Excalidraw 是一个开源的虚拟手绘风格白板,支持实时协作与端到端加密。本文以仓库根目录 README.md 为主线,完整覆盖其特性清单、excalidraw.com 应用架构、@excalidraw/excalidrawnpm 包的集成方法,并结合 monorepo 源码结构、packages/excalidraw包配置与开发文档,讲透从安装到本地运行的每一步,帮助你既能快速把 Excalidraw 嵌入自己的 React 应用,也能在本地完整跑起开发、测试与容器化部署。
项目定位与仓库总体结构
按 README.md 的定义,Excalidraw 是一个开源、协作、端到端加密的虚拟手绘风格白板,可用于绘制手绘风格的图表、线框图或任何你喜欢的东西。它有两个交付形态:
- npm 包(
@excalidraw/excalidraw):以 React 组件形式嵌入你自己的应用,核心源码位于 packages/excalidraw; - excalidraw.com 在线应用:README 将其定位为“用 Excalidraw 可以构建什么”的最小化展示(minimal showcase),源码就在本仓库的 excalidraw-app 目录中。
从 根目录 package.json 看,这是一个基于 Yarn workspaces 的 monorepo(excalidraw-monorepo),工作区声明为excalidraw-app、packages/*、examples/*,并要求 Node.js>=18.0.0。packages/下的子包划分揭示了清晰的模块边界:
| 包目录 | 职责 |
|---|---|
| packages/excalidraw | 对外发布的 React 组件包@excalidraw/excalidraw(当前版本 0.18.0),包含编辑器、渲染器、组件、动作系统、i18n 等 |
| packages/element | 元素模型与几何逻辑(对齐、分组、帧、箭头绑定、碰撞检测、文本换行等),测试覆盖binding.test.tsx、elbowArrow.test.tsx等场景 |
| packages/common | 跨包共享的基础工具:bounds、colors、keys、promise-pool、random、url 等 |
| packages/math | 数学运算子包 |
| packages/fractional-indexing | 分数索引(用于协作场景下的有序列表) |
| packages/laser-pointer | 激光笔动画轨迹子包 |
| packages/utils | 通用工具 |
excalidraw-app的 package.json 则依赖firebase、socket.io-client、@sentry/browser等,印证了在线应用的协作与监控能力;仓库根部的 firebase-project 目录存放了 Firestore 规则、索引等协作后端配置。
npm 包(编辑器)支持的核心特性
README.md 的 Features 一节列出了 Excalidraw 编辑器(npm 包)支持的能力,这里完整继承:
- 免费且开源(MIT 协议);
- 基于 Canvas 的无限画布白板;
- 手绘风格渲染;
- 暗色模式;
- 可定制;
- 图片支持;
- 图形库(shape libraries)支持;
- 本地化(i18n)支持;
- 导出为 PNG、SVG 与剪贴板;
- 开放格式——将绘图导出为
.excalidrawJSON 文件; - 丰富的工具集:矩形、圆形、菱形、箭头、直线、自由绘制、橡皮擦等;
- 箭头绑定与带标签箭头(labeled arrows);
- 撤销 / 重做;
- 缩放与平移支持。
这些特性在源码中都有对应实现可以印证:
- 手绘风格来自依赖
roughjs(见 packages/excalidraw/package.json 的 dependencies),它负责生成“抖动”的手绘线条; - 工具与动作系统位于 packages/excalidraw/actions,如 actionAlign.tsx、actionExport.tsx、actionGroup.tsx、actionZindex.tsx,配合 shortcuts.ts 定义快捷键;
- 渲染管线在 packages/excalidraw/renderer,包含动态场景(interactiveScene)、静态导出(staticScene、staticSvgScene,对应 PNG/SVG 导出);
- i18n 由 packages/excalidraw/i18n.ts 驱动,packages/excalidraw/locales 目录下收录了
zh-CN、zh-TW、ja-JP、de-DE、fr-FR等 60 余种语言的翻译文件; - 开放格式
.excalidrawJSON 的读取/保存逻辑在 packages/excalidraw/data(json、encode、encryption、restore 等模块)。
excalidraw.com 应用:协作、加密与本地优先
README 指出,excalidraw.com 是 Excalidraw 能力的最小化展示,其源码包含在仓库中(excalidraw-app),并且具备以下特性:
- PWA 支持(可离线工作):从 excalidraw-app/index.tsx 可以看到
registerSW()(Service Worker 注册)调用,根 package.json 的 devDependencies 中也有vite-plugin-pwa与pwacompat; - 实时协作:excalidraw-app/collab 目录包含协作 UI(Collab.tsx、CollabError.tsx、Portal.tsx),依赖
socket.io-client与 Firebase; - 端到端加密:客户端加密工具在 packages/excalidraw/data/encryption.ts;
- 本地优先(自动保存到浏览器):excalidraw-app/data 中的 LocalData、localStorage、tabSync 等模块负责浏览器端自动保存与多标签页同步;
- 可分享链接(导出只读链接):excalidraw-app/share 目录提供分享对话框与 QR 码能力。
README 还明确说明:这些应用级特性未来会作为“即插即用插件(drop-in plugins)”提供给 npm 包。
快速开始:把 Excalidraw 集成进你的 React 应用
README 的 Quick start 指出:以下说明针对将 npm 包集成进你自己的应用的场景;若要在本地运行仓库进行开发,则应参考开发指南(见下文“本地开发”一节)。安装命令:
npm install react react-dom @excalidraw/excalidraw # 或 yarn add react react-dom @excalidraw/excalidraw从 packages/excalidraw/README.md 与 packages/excalidraw/package.json 可以得到几个关键事实:
- React 版本约束(peerDependencies):
react与react-dom需为^17.0.2 || ^18.2.0 || ^19.0.0之一; - 包版本:当前发布版本为
0.18.0(如需试用未发布变更可用@excalidraw/excalidraw@next); - exports 映射:包根入口按条件导出
development/production/default指向dist/dev/index.js或dist/prod/index.js,样式单独从@excalidraw/excalidraw/index.css引入(同样区分 dev/prod 两份 CSS)。
最小可运行配置的两个易错点
子包 README 特别强调“最小工作配置有两个容易漏掉的要求”:
- 必须引入包的 CSS:
import "@excalidraw/excalidraw/index.css";- 必须在非零高度的容器中渲染(Excalidraw 会填满父容器的 100% 宽高,父容器没有高度时画布不可见):
import { Excalidraw } from "@excalidraw/excalidraw"; import "@excalidraw/excalidraw/index.css"; export default function App() { return ( <div style={{ height: "100vh" }}> <Excalidraw /> </div> ); }Next.js 等 SSR 框架下的客户端渲染
Excalidraw 必须在客户端渲染。在 Next.js 这类 SSR 框架中,应使用 client component 并以禁用 SSR 的方式动态加载:
// app/components/ExcalidrawClient.tsx "use client"; import { Excalidraw } from "@excalidraw/excalidraw"; import "@excalidraw/excalidraw/index.css"; export default function ExcalidrawClient() { return ( <div style={{ height: "100vh" }}> <Excalidraw /> </div> ); }// app/page.tsx import dynamic from "next/dynamic"; const ExcalidrawClient = dynamic( () => import("./components/ExcalidrawClient"), { ssr: false }, ); export default function Page() { return <ExcalidrawClient />; }仓库自带了两套完整示例可作参考:examples/with-nextjs(其中 excalidrawWrapper.tsx 展示了"use client"+ 组件包装的写法)与 examples/with-script-in-browser(Vite 驱动的浏览器脚本集成示例,入口见 index.html 与 ExampleApp.tsx)。
字体自托管与 0.18.x 迁移注意
- 字体自托管:默认情况下 Excalidraw 会从 CDN 下载所需字体;自托管时需将
node_modules/@excalidraw/excalidraw/dist/prod/fonts的内容拷贝到应用的静态资源路径(如public/),并设置window.EXCALIDRAW_ASSET_PATH指向同一路径; - 0.18.x 类型导入路径变更:0.18.x 移除了旧的
types/前缀深层导入路径,子包 README 给出了完整对照表:
| 旧路径 | 新路径 |
|---|---|
@excalidraw/excalidraw/types/data/transform.js | @excalidraw/excalidraw/element/transform |
@excalidraw/excalidraw/types/data/types.js | @excalidraw/excalidraw/data/types |
@excalidraw/excalidraw/types/element/types.js | @excalidraw/excalidraw/element/types |
@excalidraw/excalidraw/types/utility-types.js | @excalidraw/excalidraw/common/utility-types |
@excalidraw/excalidraw/types/types.js | @excalidraw/excalidraw/types |
这些深层子路径仅用于import type;运行时导入应从包根入口引入,样式则从@excalidraw/excalidraw/index.css引入。这与 packages/excalidraw/package.json 中exports字段对./common/*、./element/*、./math/*、./utils/*的类型映射一致。
本地开发:从零跑起 Excalidraw 仓库
README 提示本地开发请参阅开发指南,对应仓库内的 dev-docs/docs/introduction/development.mdx。该指南给出的完整流程如下:
环境要求
- Node.js
- Yarn(v1 或 v2.4.2+;仓库
packageManager字段声明为yarn@1.22.22) - Git
克隆并安装
git clone https://github.com/excalidraw/excalidraw.git yarn启动开发服务器
yarn start启动后即可访问http://localhost:3000开始编码。对照根 package.json,start实际转发到excalidraw-app工作区执行yarn && vite(见 excalidraw-app/package.json 的 scripts),即通过 Vite 驱动开发服务。
协作功能的前置条件
指南指出:要本地体验实时协作,需要自行部署协作服务器(collab server,即独立的 excalidraw-room 项目)。仓库内 firebase-project 提供了 Firestore 规则与索引配置,属于该协作体系的组成部分。
常用命令一览
以下命令均收录于开发指南,并与根 package.json 的 scripts 一一对应:
yarn # 安装依赖 yarn start # 运行项目(Vite 开发服务器) yarn fix # 用 Prettier + ESLint 格式化全部文件 yarn test # 运行测试(vitest) yarn test:update # 更新测试快照 yarn test:code # Prettier/ESLint 格式检查此外,test:all会依次执行test:typecheck(tsc 类型检查)、test:code(ESLint,--max-warnings=0)、test:other(Prettier 检查)与test:app(vitest 用例)。测试框架为 vitest,配置见 vitest.config.mts 与 setupTests.ts。
Docker Compose 方式
如果不想配置 Node.js 环境,指南提供了 Docker Compose 方案(仓库根目录含 docker-compose.yml):
docker-compose up --build -d自托管:Docker 镜像构建与运行
开发指南的 Self-hosting 一节说明,官方发布了 Excalidraw 客户端的 Docker 镜像,可用于在自有域名下、Kubernetes、AWS ECS 等环境中自托管。构建与运行命令:
docker build -t excalidraw/excalidraw . docker run --rm -dit --name excalidraw -p 5000:80 excalidraw/excalidraw:latest镜像内容不含分析与追踪类库。指南同时提醒:目前自托管实例不支持分享与协作功能(需要协作服务端支持),团队正在朝完整的自托管方案努力。
这一流程与仓库根部的 Dockerfile 完全吻合,其实现细节值得注意:
- 构建阶段基于
node:24多平台镜像,使用yarn --frozen-lockfile安装依赖(并注释说明不能忽略可选依赖,否则会出现Cannot find module @rollup/rollup-linux-x64-gnu错误); - 构建命令为
yarn build:app:docker,对应 excalidraw-app/package.json 中的cross-env VITE_APP_DISABLE_SENTRY=true vite build——即在 Docker 构建时禁用 Sentry; - 运行阶段为
nginx:stable-alpine-slim,将excalidraw-app/build产物拷入/usr/share/nginx/html,并带wget健康检查。
测试、构建与发布脚本
根 package.json 的 scripts 揭示了完整的工程化流水线:
- 子包构建:
build:packages按依赖顺序依次构建 common → fractional-indexing → laser-pointer → math → element → excalidraw(每个子包执行各自的build:esm),最终产物只发布dist/*(见 packages/excalidraw/package.json 的files字段); - 应用构建:
build:app在excalidraw-app中执行cross-env VITE_APP_GIT_SHA=... VITE_APP_ENABLE_TRACKING=true vite build,window.__EXCALIDRAW_SHA__即由该环境变量注入(见 excalidraw-app/index.tsx); - 发布:
release/release:next/release:latest脚本调用 scripts/release.js; - 其他工具:
locales-coverage(统计翻译覆盖度)、buildWasm.js(构建 harfbuzz/woff2 子集化 wasm,产物位于 scripts/wasm)、字体子集化相关脚本等。
集成生态与参与贡献
README 列出的集成包括 VS Code 扩展与 npm 包两条外部入口;仓库内examples/下则提供了可直接运行的参考实现(with-nextjs 与 with-script-in-browser)。README 还列出了 Google Cloud、Meta、CodeSandbox、Obsidian Excalidraw、Replit、Slite、Notion、HackerRank 等在集成 Excalidraw。
贡献方面,README 给出三条路径:
- 缺少功能或发现 Bug:通过仓库 issue 报告;
- 参与贡献:参考 dev-docs/docs/introduction/contributing.mdx(仓库根部的 CONTRIBUTING.md 也指向该文档);
- 参与翻译:参见贡献指南中的 translating 章节,翻译文件位于 packages/excalidraw/locales。
小结
回到 README.md 的主线:Excalidraw 以@excalidraw/excalidrawReact 组件包为能力核心,以 excalidraw-app 作为协作、加密、PWA、本地优先等特性的在线展示,两者共享同一 monorepo 中的packages/*子包实现。集成侧的关键要点是“CSS 引入 + 非零高度容器 + SSR 场景禁用 SSR”,开发侧的关键路径是“yarn 安装 → yarn start 本地开发 → vitest 测试 → Docker 构建自托管镜像”。文中所有命令、路径与配置均可在当前仓库中直接查证,适合作为集成或二次开发的起点。
【免费下载链接】excalidrawVirtual whiteboard for sketching hand-drawn like diagrams项目地址: https://gitcode.com/GitHub_Trending/ex/excalidraw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考