211 lines
4.3 KiB
Markdown
211 lines
4.3 KiB
Markdown
# 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
|
||
```
|
||
|
||
## 惰性 Getter(Tree-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'
|
||
```
|