Files
2026-07-13 21:37:05 +08:00

211 lines
4.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API 设计模式
## Options 模式
面向用户的选项及其内部解析后的版本:
```typescript
export interface Options {
verbose?: boolean
include?: string[]
exclude?: string[]
}
export interface ResolvedOptions extends Required<Options> {
root: string
}
function resolveOptions(options: Options = {}): ResolvedOptions {
return {
verbose: options.verbose ?? false,
include: options.include ?? ['**/*'],
exclude: options.exclude ?? ['node_modules'],
root: process.cwd(),
}
}
```
## 工厂函数
创建配置好的实例:
```typescript
export function createContext(options: Options = {}) {
const resolved = resolveOptions(options)
const filter = createFilter(resolved.include, resolved.exclude)
return {
options: resolved,
filter,
transform(code: string, id: string) { /* ... */ },
async scanDirs() { /* ... */ },
}
}
// 用法
const ctx = createContext({ verbose: true })
await ctx.scanDirs()
```
## Builder 模式
支持类型累积的可链式调用 API
```typescript
export function createBuilder<TContext = unknown>() {
return {
context<T>(): Builder<T, unknown, unknown> {
return this as any
},
input<T>(schema: T): Builder<TContext, T, unknown> {
return this as any
},
output<T>(schema: T): Builder<TContext, unknown, T> {
return this as any
},
build(): Procedure<TContext> { /* ... */ },
}
}
// 用法 - 类型在链中传递
const procedure = createBuilder()
.context<{ user: User }>()
.input(z.object({ id: z.string() }))
.build()
```
## 插件模式(unplugin
从单一实现生成通用插件:
```typescript
import { createUnplugin } from 'unplugin'
export default createUnplugin<Options>((options) => {
const ctx = createContext(options)
return {
name: 'my-plugin',
enforce: 'pre',
transformInclude(id) {
return ctx.filter(id)
},
transform(code, id) {
return ctx.transform(code, id)
},
// 特定于打包工具的钩子
vite: {
configResolved(config) { /* Vite 特有 */ },
},
webpack(compiler) {
compiler.hooks.watchRun.tap('my-plugin', () => { /* ... */ })
},
}
})
```
按打包工具分别导出入口:
```typescript
// src/vite.ts
import unplugin from '.'
export default unplugin.vite
// src/webpack.ts
import unplugin from '.'
export default unplugin.webpack
```
## 惰性 GetterTree-shaking
将特定于打包工具的代码延迟到被访问时才加载:
```typescript
export function createPlugin<T>(factory: PluginFactory<T>) {
return {
get vite() { return getVitePlugin(factory) },
get webpack() { return getWebpackPlugin(factory) },
get rollup() { return getRollupPlugin(factory) },
}
}
```
只有被访问的 getter 会执行,其余部分被 tree-shaking 移除。
## 智能默认值
检测环境而非要求配置:
```typescript
import { isPackageExists } from 'local-pkg'
function resolveOptions(options: Options) {
return {
vue: options.vue ?? isPackageExists('vue'),
react: options.react ?? isPackageExists('react'),
typescript: options.typescript ?? isPackageExists('typescript'),
}
}
```
## Resolver 模式
通过函数或对象实现灵活解析:
```typescript
export type Resolver = ResolverFunction | ResolverObject
export type ResolverFunction = (name: string) => ResolveResult | undefined
export interface ResolverObject {
type: 'component' | 'directive'
resolve: ResolverFunction
}
export function ElementPlusResolver(): Resolver[] {
return [
{ type: 'component', resolve: (name) => resolveComponent(name) },
{ type: 'directive', resolve: (name) => resolveDirective(name) },
]
}
```
## Fluent API(验证)
通过克隆实现不可变性的方法链:
```typescript
class Schema<T> {
private _def: SchemaDef
min(value: number): Schema<T> {
return new Schema({ ...this._def, min: value })
}
max(value: number): Schema<T> {
return new Schema({ ...this._def, max: value })
}
optional(): Schema<T | undefined> {
return new Schema({ ...this._def, optional: true })
}
}
// 用法
const schema = z.string().min(5).max(10).optional()
```
## Barrel 导出
简洁的公开 API
```typescript
// src/index.ts
export * from './config'
export * from './types'
export { createContext } from './context'
export { default } from './plugin'
```