TypeORM 是一个成熟的对象关系映射框架,支持 TypeScript 和 JavaScript,能够在 Node.js 环境中操作 MySQL、PostgreSQL、SQLite、SQL Server 等多种数据库。它通过装饰器定义实体与列,提供 Repository 和 QueryBuilder 两种数据访问方式,让开发者用面向对象的思维管理关系型数据。但很多前端同学会想:能不能像引入 Axios 那样,在 HTML 里加一个 <script> 标签从 CDN 加载 TypeORM,然后在浏览器里直接用?这篇内容就围绕这个疑问展开,同时梳理 TypeORM 的标准使用方式。

为什么 TypeORM 不能通过传统 CDN 的 script 标签直接使用?
传统 CDN 使用方式是在 HTML 中通过 <script src="..."> 加载一个 UMD 或全局变量形式的库。例如 Vue、React 都有专门的浏览器构建版本,加载后会在 window 对象上挂载一个全局变量。但 TypeORM 并没有提供这样的官方浏览器 UMD 构建。查看 TypeORM 的 npm 包内容,它的入口是 CommonJS 模块,依赖 Node.js 的 fs、path、net 等内置模块,这些模块在浏览器环境中根本不存在。
即使强行通过 esm.sh 或 jsdelivr 的 ES 模块转换服务把 TypeORM 转成浏览器可加载的 ES Module,还会遇到两个更大的问题。第一,TypeORM 依赖 reflect-metadata 来读取装饰器元数据,而 reflect-metadata 在浏览器中需要额外的 polyfill,并且装饰器提案在浏览器原生支持上还不稳定。第二,数据库驱动是真正的拦路虎。TypeORM 连接 MySQL 需要 mysql2 或 mysql 驱动,连接 PostgreSQL 需要 pg 驱动,这些驱动底层依赖 TCP 套接字和 Node.js 的网络 API,浏览器出于安全沙箱限制无法直接建立原始 TCP 连接。所以从架构层面看,TypeORM 无法像普通前端库那样通过 CDN 直接引入。
这里有一个常见误区:有人觉得既然 TypeORM 支持 sqljs 类型,是不是就能在浏览器里用 CDN 加载 TypeORM 操作 SQLite?实际上 sqljs 驱动基于 sql.js,这是一个编译成 WebAssembly 的 SQLite,确实可以在浏览器中运行。但 TypeORM 本身还需要被正确打包成浏览器可用的模块格式,而不是简单加一个 script 标签。所以结论是:传统 CDN script 方式不可行,但借助 ES 模块 CDN 和 sql.js,浏览器内使用 TypeORM 存在可行的变通方案。
浏览器中使用 TypeORM 的可行方案:esm.sh + sql.js
既然原生 script 标签走不通,我们来看看现代 ES 模块 CDN 服务怎么解决。esm.sh 和 skypack 这类服务可以把 npm 包及其依赖转换成浏览器可加载的 ES Module。TypeORM 依赖的 reflect-metadata 和一些纯 JavaScript 模块可以经过转换后加载。关键是要使用 sqljs 驱动,因为它不需要 TCP 连接,所有数据都保存在内存或浏览器本地存储中。
下面是一个在浏览器模块脚本中使用 TypeORM 连接 SQLite 的示例。首先需要在 HTML 中以 module 方式引入 TypeORM 和 reflect-metadata,然后定义实体并初始化数据源。注意这里的代码运行在支持 ES Module 的现代浏览器中,需要通过本地 HTTP 服务访问,不能直接打开 file 协议。
// 使用 esm.sh 引入 TypeORM 和 reflect-metadata
import 'https://esm.sh/reflect-metadata';
import { Entity, PrimaryGeneratedColumn, Column, DataSource } from 'https://esm.sh/typeorm';
@Entity()
class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
age: number;
}
const AppDataSource = new DataSource({
type: 'sqljs',
location: 'mydb',
autoSave: true,
entities: [User],
synchronize: true,
logging: false
});
async function main() {
await AppDataSource.initialize();
const userRepo = AppDataSource.getRepository(User);
await userRepo.save({ name: 'Alice', age: 28 });
await userRepo.save({ name: 'Bob', age: 34 });
const users = await userRepo.find();
console.log(users);
// 获取数据库导出的二进制数据,可以用于本地保存
const driver = AppDataSource.driver;
const rawDb = driver.export();
console.log('数据库字节大小:', rawDb.length);
}
main().catch(console.error);
这段代码演示了在浏览器中创建 User 实体、初始化 sqljs 数据源、插入两条记录并查询的全过程。sqljs 的 autoSave 选项会把数据库状态自动序列化到内存中,import 和 export 方法可以配合 localStorage 或 IndexedDB 实现持久化。虽然这种用法在真实项目中比较少见,但对于离线演示、教学场景或纯前端数据管理工具来说,是一个不错的思路。
需要提醒的是,esm.sh 转换 TypeORM 时可能会遇到 Node.js 内置模块的 polyfill 问题,例如 tty、fs 等。如果遇到 import 失败,可以尝试指定 external 参数或使用 skypack 的别名功能。另外,装饰器语法需要浏览器支持或使用 TypeScript 编译成 JavaScript 后再运行。直接在原生 HTML 中写 TypeScript 是不可能的,上面的示例其实已经是编译后的 JavaScript,只是保留了类型标注供阅读。
Node.js 标准项目中 TypeORM 的核心用法
对于绝大多数开发者来说,TypeORM 的正确打开方式还是在 Node.js 后端项目中通过 npm 安装使用。标准流程是安装 typeorm、reflect-metadata 以及对应的数据库驱动,然后在 tsconfig.json 中开启装饰器相关配置。下面通过一个完整的示例来梳理实体定义、数据源配置和增删改查操作。
第一步是项目初始化。假设使用 TypeScript 和 PostgreSQL,安装命令如下:
npm install typeorm reflect-metadata pg npm install -D @types/node typescript ts-node
在 tsconfig.json 中需要开启 experimentalDecorators 和 emitDecoratorMetadata,否则装饰器不会生效。
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strictPropertyInitialization": false,
"esModuleInterop": true
}
}
第二步定义实体。实体对应数据库中的表,使用装饰器标注类和列。关系映射可以用 @OneToMany、@ManyToOne、@ManyToMany 等装饰器表达。
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, JoinColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 100 })
name: string;
@Column({ type: 'int', default: 0 })
age: number;
@OneToMany(() => Post, (post) => post.author)
posts: Post[];
}
@Entity('posts')
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 200 })
title: string;
@Column({ type: 'text', nullable: true })
content: string;
@ManyToOne(() => User, (user) => user.posts)
@JoinColumn({ name: 'author_id' })
author: User;
}
上面的代码定义了两个实体 User 和 Post,它们之间是一对多关系。注意 @JoinColumn 指定外键列名,TypeORM 会根据装饰器元数据自动生成对应的外键约束。strictPropertyInitialization 设为 false 是因为 TypeScript 严格模式下像 id: number; 这种未初始化属性会报错。
第三步是初始化数据源并执行操作。在应用启动时调用 initialize 方法,之后就可以通过 getRepository 获取仓储对象。
import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { User, Post } from './entity';
const AppDataSource = new DataSource({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'secret',
database: 'testdb',
entities: [User, Post],
synchronize: true, // 生产环境建议关闭,改用 migration
logging: false
});
async function run() {
await AppDataSource.initialize();
const userRepo = AppDataSource.getRepository(User);
const postRepo = AppDataSource.getRepository(Post);
const newUser = await userRepo.save({ name: '王小明', age: 30 });
await postRepo.save({ title: '第一篇文章', content: 'TypeORM 使用笔记', author: newUser });
const userWithPosts = await userRepo.findOne({
where: { id: newUser.id },
relations: ['posts']
});
console.log(userWithPosts);
const count = await userRepo.count({ where: { age: 30 } });
console.log('年龄为30的用户数量:', count);
}
run().catch(console.error);
这段代码展示了保存、关联查询和计数操作。Repository API 提供了 find、findOne、save、update、delete 等常用方法,如果遇到复杂查询条件,可以使用 QueryBuilder 进行链式调用。例如要查询所有标题包含“TypeORM”的文章,可以这样写:
const posts = await postRepo.createQueryBuilder('post')
.where('post.title like :keyword', { keyword: '%TypeORM%' })
.leftJoinAndSelect('post.author', 'author')
.getMany();
QueryBuilder 是 TypeORM 强大且灵活的特性,适合处理动态条件和多表联查。对于生产环境,建议关闭 synchronize 并使用 migration 管理数据库结构变更,保证变更可追踪和可回滚。
总结与选型建议
回到文章开头的问题:CDN TypeORM 行不行?传统 CDN 的 script 标签方式不可行,根本原因是 TypeORM 依赖 Node.js 环境和数据库驱动。但通过 ES 模块 CDN 配合 sql.js,可以在浏览器中运行 TypeORM 操作内存数据库,适合实验和轻量工具场景。而对于正式业务开发,TypeORM 的最佳位置仍然是 Node.js 后端,借助 npm 生态和完整的驱动支持发挥其全部能力。
如果项目需要前后端统一使用 TypeScript,可以选择将 TypeORM 实体定义抽成共享包,前端通过 API 获取数据而不是直接连接数据库。如果确实需要在浏览器端操作关系型数据,可以考虑使用 sql.js 直接编写 SQL,或者使用更轻量的 Dexie.js 包装 IndexedDB。TypeORM 的浏览器方案目前还不够成熟,社区维护力度有限,需要谨慎评估。
TypeORMTypeScript ORMCDN修改时间:2026-10-03 03:20:15