热门角色不仅是灵感来源,更是你的效率助手。通过精挑细选的角色提示词,你可以快速生成高质量内容、提升创作灵感,并找到最契合你需求的解决方案。让创作更轻松,让价值更直接!
我们根据不同用户需求,持续更新角色库,让你总能找到合适的灵感入口。
本提示词可对提供的代码片段进行分析,判断是否适用指定设计模式,并提供详细实现思路、重构建议和修改关键点,帮助开发者优化代码质量和可维护性,适用于前端组件设计与重构。
下面给出基于“策略模式 Strategy”的重构方案与完整 TypeScript 代码。目标是将基于条件分支的校验逻辑解耦为可组合策略集,并通过统一上下文与策略注册表实现:可组合/可配置规则、链式执行与短路、异步规则(如远程占用校验)、错误消息国际化与聚合、以及在不同表单间的复用与可观测性。
实现思路概述
重构后的代码 说明:
// ---------- 基础类型与上下文 ----------
type Values = Record<string, unknown>;
type Severity = 'error' | 'warn';
type RuleResult = {
ok: boolean;
code?: string; // 国际化消息编码
params?: Record<string, unknown>; // 国际化参数
stop?: boolean; // 指示短路(立即停止当前字段后续规则)
severity?: Severity;
};
export type ValidationError<Field extends string = string> = {
field: Field;
code: string;
message: string;
severity: Severity;
};
export type ValidationEvent =
| { type: 'rule:start'; field: string; rule: string; ts: number }
| { type: 'rule:end'; field: string; rule: string; ts: number; durationMs: number; ok: boolean }
| { type: 'field:end'; field: string; ts: number; durationMs: number; ok: boolean; errors: number };
export type Services = {
// 示例:远程校验邮箱是否占用
isEmailTaken?: (email: string, signal?: AbortSignal) => Promise<boolean>;
};
export type ValidationContext = {
values: Values;
locale: string;
t: (code: string, params?: Record<string, unknown>, locale?: string) => string;
services?: Services;
emit?: (e: ValidationEvent) => void; // 可观测性 hook
abortSignal?: AbortSignal;
};
// ---------- 策略与注册表 ----------
export interface RuleStrategy {
name: string;
validate(value: unknown, ctx: ValidationContext, field: string): Promise<RuleResult>;
}
export type RuleFactory<Opts = unknown> =
(opts: Opts, registry: RuleRegistry) => RuleStrategy;
export class RuleRegistry {
private map = new Map<string, RuleFactory<any>>();
register<Opts>(name: string, factory: RuleFactory<Opts>) {
if (this.map.has(name)) throw new Error(`Rule already registered: ${name}`);
this.map.set(name, factory);
}
create(name: string, opts: any): RuleStrategy {
const f = this.map.get(name);
if (!f) throw new Error(`Rule not found: ${name}`);
return f(opts, this);
}
}
// ---------- 组合描述与编译 ----------
export type RuleSpec = { use: string; opts?: any };
export type FieldPipelineConfig = {
rules: RuleSpec[];
shortCircuit?: boolean; // 字段层面默认短路
};
export type Schema<T extends Values> = {
[K in keyof T]?: FieldPipelineConfig;
};
type CompiledPipeline = {
field: string;
strategies: RuleStrategy[];
shortCircuit: boolean;
};
function compileSchema<T extends Values>(
schema: Schema<T>,
registry: RuleRegistry,
shortCircuitDefault = true
): CompiledPipeline[] {
const pipelines: CompiledPipeline[] = [];
for (const field of Object.keys(schema)) {
const conf = schema[field as keyof T]!;
const strategies = conf.rules.map(spec => registry.create(spec.use, spec.opts));
pipelines.push({
field,
strategies,
shortCircuit: conf.shortCircuit ?? shortCircuitDefault,
});
}
return pipelines;
}
// ---------- 国际化 ----------
const messages: Record<string, Record<string, string>> = {
'zh-CN': {
'required': '该字段为必填',
'email.invalid': '邮箱格式不正确',
'email.required': '邮箱必填',
'email.taken': '该邮箱已被占用',
'password.required': '密码必填',
'password.minLength': '密码至少{min}位',
'password.needUpper': '需包含大写字母',
'password.needDigit': '需包含数字',
'phone.invalid': '手机号格式错误',
'agree.trueRequired': '需同意协议',
},
'en': {
'required': 'This field is required',
'email.invalid': 'Invalid email format',
'email.required': 'Email is required',
'email.taken': 'Email is already taken',
'password.required': 'Password is required',
'password.minLength': 'Password must be at least {min} characters',
'password.needUpper': 'Must contain an uppercase letter',
'password.needDigit': 'Must contain a digit',
'phone.invalid': 'Invalid phone number',
'agree.trueRequired': 'You must agree to the terms',
},
};
function interpolate(template: string, params?: Record<string, unknown>): string {
if (!params) return template;
return template.replace(/\{(\w+)\}/g, (_, key) => String(params[key] ?? `{${key}}`));
}
export function t(code: string, params?: Record<string, unknown>, locale = 'zh-CN'): string {
const dict = messages[locale] ?? messages['zh-CN'];
const tpl = dict[code] ?? code;
return interpolate(tpl, params);
}
// ---------- 默认规则(策略)实现 ----------
// required: 空值(undefined, null, '')判定
function isEmpty(v: unknown): boolean {
return v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
}
const emailRegex = /^[^@]+@[^@]+\.[^@]+$/;
const phoneCnRegex = /^1\d{10}$/;
function asString(v: unknown): string | undefined {
if (typeof v === 'string') return v;
return v == null ? undefined : String(v);
}
function asBoolean(v: unknown): boolean | undefined {
if (typeof v === 'boolean') return v;
return v == null ? undefined : Boolean(v);
}
export function registerDefaultRules(registry: RuleRegistry) {
// 1) required 通用
registry.register('required', (_opts, _reg) => ({
name: 'required',
async validate(value) {
const ok = !isEmpty(value);
return ok ? { ok } : { ok: false, code: 'required' };
},
}));
// 2) email 基本格式
registry.register('email', (_opts, _reg) => ({
name: 'email',
async validate(value, _ctx, field) {
const s = asString(value);
const ok = !!s && emailRegex.test(s);
return ok ? { ok } : { ok: false, code: field === 'email' ? 'email.invalid' : 'email.invalid' };
},
}));
// 3) phone.cn(中国手机号)
registry.register('phone.cn', (_opts, _reg) => ({
name: 'phone.cn',
async validate(value) {
const s = asString(value);
const ok = !!s && phoneCnRegex.test(s);
return ok ? { ok } : { ok: false, code: 'phone.invalid' };
},
}));
// 4) agree.true
registry.register('agree.true', (_opts, _reg) => ({
name: 'agree.true',
async validate(value) {
const b = asBoolean(value);
const ok = b === true;
return ok ? { ok } : { ok: false, code: 'agree.trueRequired' };
},
}));
// 5) password 子规则
registry.register<{ min: number }>('password.minLength', (opts, _reg) => ({
name: 'password.minLength',
async validate(value) {
const s = asString(value) ?? '';
const ok = s.length >= (opts?.min ?? 8);
return ok ? { ok } : { ok: false, code: 'password.minLength', params: { min: opts?.min ?? 8 } };
},
}));
registry.register('password.needUpper', (_opts, _reg) => ({
name: 'password.needUpper',
async validate(value) {
const s = asString(value) ?? '';
const ok = /[A-Z]/.test(s);
return ok ? { ok } : { ok: false, code: 'password.needUpper' };
},
}));
registry.register('password.needDigit', (_opts, _reg) => ({
name: 'password.needDigit',
async validate(value) {
const s = asString(value) ?? '';
const ok = /\d/.test(s);
return ok ? { ok } : { ok: false, code: 'password.needDigit' };
},
}));
// 6) optional 包装:值为空则直接通过
registry.register<{ rule: RuleSpec }>('optional', (opts, registry) => {
if (!opts?.rule) throw new Error('optional requires inner rule');
const inner = registry.create(opts.rule.use, opts.rule.opts);
return {
name: `optional(${inner.name})`,
async validate(value, ctx, field) {
if (isEmpty(value)) return { ok: true, stop: true }; // 空值短路跳过后续
return inner.validate(value, ctx, field);
},
};
});
// 7) group 组合:按配置聚合多个子规则的结果
registry.register<{ rules: RuleSpec[]; shortCircuit?: boolean }>('group', (opts, registry) => {
if (!opts?.rules?.length) throw new Error('group requires rules');
const strategies = opts.rules.map(s => registry.create(s.use, s.opts));
const short = opts.shortCircuit ?? false; // group 内默认聚合
return {
name: `group[${strategies.map(s => s.name).join(', ')}]`,
async validate(value, ctx, field) {
const errors: RuleResult[] = [];
for (const s of strategies) {
const start = performance.now();
ctx.emit?.({ type: 'rule:start', field, rule: s.name, ts: start });
const res = await s.validate(value, ctx, field);
const end = performance.now();
ctx.emit?.({
type: 'rule:end',
field,
rule: s.name,
ts: end,
durationMs: end - start,
ok: res.ok,
});
if (!res.ok) {
errors.push(res);
if (short || res.stop) break;
}
}
// 聚合:若有错误,返回第一个错误;外层会继续收集并翻译所有错误
if (errors.length === 0) return { ok: true };
// 返回一个虚拟“失败但不携带具体 message”,由外层根据 errors 聚合
// 为了传递全部错误,我们借助 params.errors 附带所有子错误的信息
return { ok: false, code: '__group__', params: { errors } };
},
};
});
// 8) 远程邮箱占用校验
registry.register('remote.emailNotTaken', (_opts, _reg) => ({
name: 'remote.emailNotTaken',
async validate(value, ctx) {
const s = asString(value);
if (!s) return { ok: false, code: 'email.required' };
const fn = ctx.services?.isEmailTaken;
if (!fn) {
// 若服务未注入,保守认为可用,或返回 warn
return { ok: true };
}
const taken = await fn(s, ctx.abortSignal);
return taken ? { ok: false, code: 'email.taken' } : { ok: true };
},
}));
}
// ---------- 核心验证器 ----------
export type ValidatorOptions = {
defaultLocale?: string;
shortCircuitDefault?: boolean;
services?: Services;
emit?: (e: ValidationEvent) => void;
abortSignal?: AbortSignal;
t?: ValidationContext['t'];
};
export type ValidationResult<Field extends string = string> = {
ok: boolean;
errors: ValidationError<Field>[];
};
export function createValidator<T extends Values>(
schema: Schema<T>,
registry: RuleRegistry,
options: ValidatorOptions = {}
) {
const pipelines = compileSchema(schema, registry, options.shortCircuitDefault ?? true);
return async function validate(values: T, locale?: string): Promise<ValidationResult<Extract<keyof T, string>>> {
const loc = locale ?? options.defaultLocale ?? 'zh-CN';
const ctx: ValidationContext = {
values,
locale: loc,
t: options.t ?? t,
services: options.services,
emit: options.emit,
abortSignal: options.abortSignal,
};
const errors: ValidationError[] = [];
for (const p of pipelines) {
const fieldStart = performance.now();
const val = values[p.field as keyof T];
let fieldOk = true;
for (const s of p.strategies) {
const ruleStart = performance.now();
ctx.emit?.({ type: 'rule:start', field: p.field, rule: s.name, ts: ruleStart });
const res = await s.validate(val, ctx, p.field);
const ruleEnd = performance.now();
ctx.emit?.({
type: 'rule:end',
field: p.field,
rule: s.name,
ts: ruleEnd,
durationMs: ruleEnd - ruleStart,
ok: res.ok,
});
if (!res.ok) {
fieldOk = false;
if (res.code === '__group__' && res.params?.errors && Array.isArray(res.params.errors)) {
// 展开 group 子错误
for (const sub of res.params.errors as RuleResult[]) {
if (!sub.ok && sub.code) {
errors.push({
field: p.field,
code: sub.code,
message: ctx.t(sub.code, sub.params, loc),
severity: sub.severity ?? 'error',
});
}
}
} else if (res.code) {
errors.push({
field: p.field,
code: res.code,
message: ctx.t(res.code, res.params, loc),
severity: res.severity ?? 'error',
});
}
if (p.shortCircuit || res.stop) {
break;
}
}
}
const fieldEnd = performance.now();
ctx.emit?.({
type: 'field:end',
field: p.field,
ts: fieldEnd,
durationMs: fieldEnd - fieldStart,
ok: fieldOk,
errors: errors.filter(e => e.field === p.field).length,
});
}
return { ok: errors.length === 0, errors };
};
}
// ---------- 用于原始示例 Form 的重构 ----------
type Form = { email?: string; password?: string; phone?: string; agree?: boolean };
// 构建注册表并注册默认规则
const registry = new RuleRegistry();
registerDefaultRules(registry);
// 针对 Form 的 schema:
// - email: 必填 + 格式 + 远程占用(短路保证远程只在前置规则通过时调用)
// - password: 组内聚合子规则(不短路),字段层面仍短路(只有一个 group)
// - phone: optional 包裹手机号规则(为空pass,否则校验格式)
// - agree: 必须为 true
const formSchema: Schema<Form> = {
email: {
shortCircuit: true,
rules: [
{ use: 'required' },
{ use: 'email' },
{ use: 'remote.emailNotTaken' },
],
},
password: {
// 字段短路:只有一个 group;group 内不短路,聚合所有错误
shortCircuit: true,
rules: [
{
use: 'group',
opts: {
shortCircuit: false,
rules: [
{ use: 'required' },
{ use: 'password.minLength', opts: { min: 8 } },
{ use: 'password.needUpper' },
{ use: 'password.needDigit' },
],
},
},
],
},
phone: {
shortCircuit: true,
rules: [
{ use: 'optional', opts: { rule: { use: 'phone.cn' } } },
],
},
agree: {
shortCircuit: true,
rules: [
{ use: 'agree.true' },
],
},
};
// 创建验证器(可注入服务与可观测性)
export const validateForm = (() => {
// 注入示例远程服务:真实环境中改为 API 调用
const services: Services = {
isEmailTaken: async (email: string) => {
// demo:模拟 remote,占用 "taken@example.com"
await new Promise(r => setTimeout(r, 50));
return email.toLowerCase() === 'taken@example.com';
},
};
// 可观测性示例:输出到控制台(生产中可换为日志/埋点)
const emit = (e: ValidationEvent) => {
// 简化:只在开发时打印
// console.debug('[validation]', e);
};
const validator = createValidator<Form>(formSchema, registry, {
defaultLocale: 'zh-CN',
services,
emit,
});
// 兼容原导出签名:返回 ok + errors 的字符串数组(与原函数一致)
return async (f: Form) => {
const res = await validator(f, 'zh-CN');
return {
ok: res.ok,
errors: res.errors.map(e => e.message), // 保留原有返回结构
};
};
})();
// ---------- 使用示例(与原函数一致) ----------
// (async () => {
// const r = await validateForm({ email: 'taken@example.com', password: 'abc', phone: '123', agree: false });
// console.log(r);
// })();
设计亮点与扩展建议
如需将输出结构改为更丰富(携带 field、code),可直接返回 ValidationResult 而非字符串数组,前端按需展示即可。
下面给出重构思路与实现,采用抽象工厂(Abstract Factory)来统一产出同族 UI 组件(Button、Card),把主题分支隔离到具体工厂中。新增主题只需实现新的工厂类,无需改动现有组件逻辑。样式以纯对象返回,支持 SSR;按需引入不同主题工厂,利于树摇优化;同时统一设计令牌(Design Tokens)与语义色(Semantic Colors),提升一致性与扩展性。
实现思路
重构后的代码(按文件模块划分示例) /* ui/types.ts */ export type Style = Readonly<Record<string, string | number>>;
export type UIChild = string | UIElement;
export interface UIElement {
tag: string;
attrs?: Readonly<Record<string, string>>;
style?: Style;
children?: ReadonlyArray
export interface DesignTokens { space: { xs: number; sm: number; md: number; lg: number; }; radius: { sm: number; md: number; }; shadow: { low: string; high: string; }; }
export interface SemanticColors { surface: string; // 基础面 surfaceElevated: string; // 提升面(卡片) textPrimary: string; borderDefault: string; shadowLow: string; shadowHigh: string; }
export interface UIFactory { readonly tokens: DesignTokens; readonly semantic: SemanticColors; createButton(label: string): UIElement; createCard(title: string, content: string): UIElement; }
/* ui/renderers.ts - 可选:用于浏览器或 SSR 挂载 */
export function mountToDOM(el: UIElement): HTMLElement {
const node = document.createElement(el.tag);
if (el.attrs) {
Object.entries(el.attrs).forEach(([k, v]) => node.setAttribute(k, v));
}
if (el.style) {
Object.entries(el.style).forEach(([k, v]) => {
const value = typeof v === 'number' ? ${v}px : v;
// @ts-ignore
node.style[k] = value;
});
}
if (el.children) {
el.children.forEach(child => {
if (typeof child === 'string') {
node.appendChild(document.createTextNode(child));
} else {
node.appendChild(mountToDOM(child));
}
});
}
return node;
}
export function toHTML(el: UIElement): string {
const attrs = el.attrs
? ' ' + Object.entries(el.attrs)
.map(([k, v]) => ${k}="${escapeHtml(v)}")
.join(' ')
: '';
const style = el.style
? ' style="' +
Object.entries(el.style)
.map(([k, v]) => ${k}:${typeof v === 'number' ? ${v}px : v})
.join(';') +
'"'
: '';
const open = <${el.tag}${attrs}${style}>;
const inner = (el.children || [])
.map(child => (typeof child === 'string' ? escapeHtml(child) : toHTML(child)))
.join('');
const close = </${el.tag}>;
return open + inner + close;
}
function escapeHtml(s: string): string { return s .replace(/&/g, '&') .replace(/"/g, '"') .replace(/</g, '<') .replace(/>/g, '>'); }
/* ui/baseTokens.ts - 公共令牌 */ export const baseTokens: DesignTokens = { space: { xs: 4, sm: 8, md: 16, lg: 24 }, radius: { sm: 4, md: 8 }, // 阴影字符串由不同语义色决定,在工厂里组合;此处给出形态模板。 shadow: { low: '0 1px 3px', // 颜色在语义色里定义 high: '0 1px 3px', // 示例保持一致,颜色不同 }, } as const;
/* themes/light.ts - 具体工厂:浅色主题 */ import { UIFactory, UIElement, SemanticColors } from '../ui/types'; import { baseTokens } from '../ui/baseTokens';
export class LightFactory implements UIFactory { readonly tokens = baseTokens; readonly semantic: SemanticColors = { surface: '#ffffff', surfaceElevated: '#ffffff', textPrimary: '#222222', borderDefault: '#dddddd', shadowLow: 'rgba(0,0,0,.1)', shadowHigh: 'rgba(0,0,0,.2)', };
createButton(label: string): UIElement {
const { semantic, tokens } = this;
return {
tag: 'button',
style: {
background: semantic.surface,
color: semantic.textPrimary,
border: 1px solid ${semantic.borderDefault},
padding: ${tokens.space.sm}px ${tokens.space.md}px,
borderRadius: tokens.radius.sm,
},
children: [label],
};
}
createCard(title: string, content: string): UIElement {
const { semantic, tokens } = this;
return {
tag: 'div',
attrs: {
'data-title': title,
'data-content': content,
},
style: {
background: semantic.surfaceElevated,
// 合成阴影时组合令牌形态与语义色
boxShadow: ${tokens.shadow.low} ${semantic.shadowLow},
padding: tokens.space.md,
borderRadius: tokens.radius.md,
},
children: [],
};
}
}
/* themes/dark.ts - 具体工厂:深色主题 */ import { UIFactory, UIElement, SemanticColors } from '../ui/types'; import { baseTokens } from '../ui/baseTokens';
export class DarkFactory implements UIFactory { readonly tokens = baseTokens; readonly semantic: SemanticColors = { surface: '#222222', surfaceElevated: '#1b1b1b', textPrimary: '#ffffff', borderDefault: '#444444', shadowLow: 'rgba(0,0,0,.6)', shadowHigh: 'rgba(0,0,0,.7)', };
createButton(label: string): UIElement {
const { semantic, tokens } = this;
return {
tag: 'button',
style: {
background: semantic.surface,
color: semantic.textPrimary,
border: 1px solid ${semantic.borderDefault},
padding: ${tokens.space.sm}px ${tokens.space.md}px,
borderRadius: tokens.radius.sm,
},
children: [label],
};
}
createCard(title: string, content: string): UIElement {
const { semantic, tokens } = this;
return {
tag: 'div',
attrs: {
'data-title': title,
'data-content': content,
},
style: {
background: semantic.surfaceElevated,
boxShadow: ${tokens.shadow.low} ${semantic.shadowLow},
padding: tokens.space.md,
borderRadius: tokens.radius.md,
},
children: [],
};
}
}
/* components/index.ts - 组件层(不关心主题分支) */ import { UIFactory, UIElement } from '../ui/types';
export function renderButton(factory: UIFactory, label: string): UIElement { // 组件仅依赖工厂,不做 theme 判断 return factory.createButton(label); }
export function renderCard(factory: UIFactory, title: string, content: string): UIElement { return factory.createCard(title, content); }
/* index.ts - 使用示例(按需引入,利于树摇) */ // 选择主题工厂:只引入需要的主题 import { LightFactory } from './themes/light'; // 或:import { DarkFactory } from './themes/dark';
import { renderButton, renderCard } from './components'; import { mountToDOM, toHTML } from './ui/renderers';
const factory = new LightFactory();
const btnModel = renderButton(factory, 'Click Me'); const cardModel = renderCard(factory, 'Title', 'Content');
// 浏览器挂载 document.body.appendChild(mountToDOM(btnModel)); document.body.appendChild(mountToDOM(cardModel));
// SSR 渲染(示例) const html = toHTML(cardModel); // 在 SSR 环境返回 html 字符串即可
关键改动说明
迁移建议
这样重构后,主题扩展成本显著降低,代码更具一致性与可维护性,同时满足 SSR 与按需引入的工程化要求。
实现思路(Observer 模式落地要点)
重构后的代码(复杂度较高,含注释与测试钩子) /**
/** 简单的相等比较,可按需替换 */ function defaultEquals(a, b) { return a === b; }
const hasFinalizationRegistry = typeof FinalizationRegistry === 'function'; const hasWeakRef = typeof WeakRef === 'function';
let __subId = 0; function nextId() { __subId += 1; return __subId; }
/**
/**
/**
export class LocaleSubject { /**
this._state = initial;
this._pending = { has: false, value: undefined };
this._scheduled = false;
this.subscribers = new Map(); // id -> SubscriberRecord
// 对象订阅去重:WeakMap<object, Map<dedupeKey, id>>
this.objectIndex = new WeakMap();
// 函数订阅去重:Map<fn, id>
this.fnIndex = new Map();
// Finalization: 自动清理弱引用订阅
if (hasFinalizationRegistry) {
this.finalizer = new FinalizationRegistry((token) => {
// token -> id 用来移除 subscriber
const id = token && token.__id;
if (id && this.subscribers.has(id)) {
this.subscribers.delete(id);
// 无法从 objectIndex 反查对象,但弱引用已回收,冗余记录影响不大
this._metrics.subscriberAutoCollected += 1;
}
});
} else {
this.finalizer = null;
}
// metrics for testing/observability
this._metrics = {
name: this.name,
totalBroadcasts: 0,
totalSkippedEqualValue: 0,
lastBroadcastAt: 0,
lastBroadcastId: 0,
subscriberCount: 0,
subscriberAutoCollected: 0,
lastErrors: [],
};
// testing taps
this._taps = new Set();
// 暴露全局 hook(可选)
// 可在 E2E 中通过 window.__LOCALE_BUS__ 访问
if (typeof window !== 'undefined') {
if (!window.__LOCALE_BUS__) window.__LOCALE_BUS__ = {};
window.__LOCALE_BUS__[this.name] = this;
}
}
// 当前状态 getState() { return this._state; }
// 内部:调度 microtask 合并广播 _ensureScheduled() { if (this._scheduled && this.coalesceMicrotask) return; this._scheduled = true; const schedule = typeof queueMicrotask === 'function' ? queueMicrotask : (fn) => Promise.resolve().then(fn); schedule(() => { this._scheduled = false; this._flush(); }); }
// 外部:设置下一语言 next(lang, options = {}) { const force = !!options.force; if (this.dedupeSameValue && !force && this.equals(this._state, lang)) { this._metrics.totalSkippedEqualValue += 1; return; } // 合并策略:仅保留最后一次 this._pending.has = true; this._pending.value = lang; this._ensureScheduled(); }
// 订阅(对象弱引用版本) // subject.subscribe(object, 'setLang', { immediate: true, tag: 'header' }) subscribe(targetOrFn, maybeMethodNameOrOptions, maybeOptions) { if (typeof targetOrFn === 'function') { // 函数订阅 const fn = targetOrFn; const options = (maybeMethodNameOrOptions && typeof maybeMethodNameOrOptions === 'object') ? maybeMethodNameOrOptions : (maybeOptions || {}); return this._subscribeFunction(fn, options); }
const target = targetOrFn;
const methodName = typeof maybeMethodNameOrOptions === 'string'
? maybeMethodNameOrOptions
: null;
const options = (typeof maybeMethodNameOrOptions === 'object')
? maybeMethodNameOrOptions
: (maybeOptions || {});
if (!target || typeof methodName !== 'string') {
throw new Error('subscribe(object, methodName, options) 需要 object + methodName');
}
return this._subscribeObject(target, methodName, options);
}
_subscribeFunction(fn, options) { if (typeof fn !== 'function') throw new Error('subscribe(fn) fn 必须是函数');
// 防重复订阅:同一函数引用只保留一个
if (this.fnIndex.has(fn)) {
const id = this.fnIndex.get(fn);
const rec = this.subscribers.get(id);
if (rec && rec.active) {
if (options?.immediate) {
try {
fn(this._state);
} catch (err) {
this._recordError(err, { phase: 'immediate', subscriberId: rec.id, tag: rec.tag });
}
}
return { unsubscribe: () => this._unsubscribeById(id), token: id };
}
}
const rec = new SubscriberRecord('function', { fn }, options);
this.subscribers.set(rec.id, rec);
this.fnIndex.set(fn, rec.id);
this._syncMetrics();
if (options?.immediate) {
try {
fn(this._state);
} catch (err) {
this._recordError(err, { phase: 'immediate', subscriberId: rec.id, tag: rec.tag });
}
}
return { unsubscribe: () => this._unsubscribeById(rec.id), token: rec.id };
}
_subscribeObject(target, methodName, options) { const dedupeKey = options?.dedupeKey || methodName;
// 防重复订阅:同一对象 + dedupeKey
let perObj = this.objectIndex.get(target);
if (perObj && perObj.has(dedupeKey)) {
const id = perObj.get(dedupeKey);
const rec = this.subscribers.get(id);
if (rec && rec.active) {
if (options?.immediate) {
const receiver = target;
if (typeof receiver[methodName] === 'function') {
try {
receiver[methodName](this._state);
} catch (err) {
this._recordError(err, { phase: 'immediate', subscriberId: rec.id, tag: rec.tag });
}
}
}
return { unsubscribe: () => this._unsubscribeById(id), token: id };
}
}
// 构造弱引用
const objectRef = hasWeakRef ? new WeakRef(target) : target;
const finalizerToken = { __id: null };
const rec = new SubscriberRecord('object', { objectRef, methodName, finalizerToken }, options);
this.subscribers.set(rec.id, rec);
finalizerToken.__id = rec.id;
if (!perObj) {
perObj = new Map();
this.objectIndex.set(target, perObj);
}
perObj.set(dedupeKey, rec.id);
// 注册最终化清理
if (this.finalizer && hasWeakRef) {
this.finalizer.register(target, finalizerToken, finalizerToken);
}
this._syncMetrics();
if (options?.immediate) {
const receiver = target;
if (typeof receiver[methodName] === 'function') {
try {
receiver[methodName](this._state);
} catch (err) {
this._recordError(err, { phase: 'immediate', subscriberId: rec.id, tag: rec.tag });
}
}
}
return { unsubscribe: () => this._unsubscribeById(rec.id), token: rec.id };
}
unsubscribeById(id) { const rec = this.subscribers.get(id); if (!rec) return; rec.active = false; this.subscribers.delete(id); if (rec.kind === 'function') { this.fnIndex.forEach((val, key) => { if (val === id) this.fnIndex.delete(key); }); } else if (rec.kind === 'object') { // 无法反查原始对象(弱引用),但 objectIndex 中的记录不影响 GC,仅是少量冗余 if (this.finalizer && rec.finalizerToken) { try { this.finalizer.unregister(rec.finalizerToken); } catch () {} } } this._syncMetrics(); }
_flush() { // 没有 pending 则不广播 if (!this._pending.has) return;
const nextValue = this._pending.value;
this._pending.has = false;
this._pending.value = undefined;
if (this.dedupeSameValue && this.equals(this._state, nextValue)) {
this._metrics.totalSkippedEqualValue += 1;
return;
}
// 广播
const prev = this._state;
this._state = nextValue;
const notifyId = ++this._metrics.lastBroadcastId;
const now = Date.now();
this._metrics.totalBroadcasts += 1;
this._metrics.lastBroadcastAt = now;
const errors = [];
// 快照当前订阅者,防止回调过程中增删订阅影响本轮
const list = Array.from(this.subscribers.values());
for (const rec of list) {
if (!rec.active) continue;
try {
if (rec.kind === 'function') {
rec.fn(nextValue);
} else {
// 对象弱引用
const obj = hasWeakRef ? rec.objectRef.deref() : rec.objectRef;
if (!obj) {
// 已被 GC
this.subscribers.delete(rec.id);
continue;
}
const method = obj[rec.methodName];
if (typeof method === 'function') {
method.call(obj, nextValue);
}
}
} catch (err) {
errors.push({ err, subscriberId: rec.id, tag: rec.tag });
this._recordError(err, { phase: 'broadcast', subscriberId: rec.id, tag: rec.tag });
}
if (rec.once) {
this._unsubscribeById(rec.id);
}
}
// 可观测性事件
const evt = {
type: 'locale:changed',
name: this.name,
prev,
next: nextValue,
at: now,
notifyId,
subscriberCount: this.subscribers.size,
errors,
};
if (this.onEvent) {
try { this.onEvent(evt); } catch (_) {}
}
for (const tap of this._taps) {
try { tap(evt); } catch (_) {}
}
if (this.emitDomEvent && typeof window !== 'undefined' && typeof window.dispatchEvent === 'function') {
try {
window.dispatchEvent(new CustomEvent('locale:changed', { detail: evt }));
} catch (_) {}
}
}
_recordError(err, ctx) { this.metrics.lastErrors.push({ at: Date.now(), err, ctx }); if (this.onError) { try { this.onError(err, ctx); } catch () {} } }
_syncMetrics() { this._metrics.subscriberCount = this.subscribers.size; }
// 测试与可观测性钩子 get testing() { return { getState: () => this._state, flush: () => this._flush(), getSubscriberSnapshot: () => Array.from(this.subscribers.values()).map(s => ({ id: s.id, kind: s.kind, tag: s.tag, once: s.once, active: s.active, })), getMetrics: () => ({ ...this._metrics }), tap: (fn) => { this._taps.add(fn); return () => this._taps.delete(fn); }, unTap: (fn) => { this._taps.delete(fn); }, }; } }
/** --------- 使用方式与对原代码的重构 --------- **/
// 1) 创建全局可复用的 localeBus export const localeBus = new LocaleSubject('en-US', { name: 'LocaleBus', emitDomEvent: false, // 若前端 E2E 想用 DOM 事件,改为 true onError: (err, ctx) => { // 可接入监控平台 console.error('[LocaleBus] observer error:', err, ctx); }, onEvent: (evt) => { // 可接入可观测性系统 / 日志 // console.log('[LocaleBus] event:', evt); }, });
// 2) 组件侧:按需订阅与退订(弱引用,避免强耦合) const components = { header: { setLang: (l) => console.log('header ->', l) }, sidebar: { setLang: (l) => console.log('sidebar ->', l) }, footer: { setLang: (l) => console.log('footer ->', l) }, forms: [{ setLang: (l) => console.log('form1 ->', l) }], };
// 初次装配:谁需要语言就订阅,新增/懒加载不会改 controller
export function wireLocaleForInitialComponents() {
// immediate: 订阅时立刻拿到当前语言
localeBus.subscribe(components.header, 'setLang', { immediate: true, tag: 'header' });
localeBus.subscribe(components.sidebar, 'setLang', { immediate: true, tag: 'sidebar' });
localeBus.subscribe(components.footer, 'setLang', { immediate: true, tag: 'footer' });
components.forms.forEach((f, i) => {
localeBus.subscribe(f, 'setLang', { immediate: true, tag: form-${i + 1} });
});
}
// 懒加载组件:在组件 mount 时调用 export function onLazyComponentMounted(comp, tag) { // 去重:同一个 comp + 'setLang' 重复调用不会重复订阅 const sub = localeBus.subscribe(comp, 'setLang', { immediate: true, tag }); // 若组件有卸载周期:return sub.unsubscribe 在卸载时调用 return sub; }
// 3) 替换原 applyLocale:控制器不再关心具体组件 export function applyLocale(lang) { localeBus.next(lang); // 如需强制广播(即使相等),使用:localeBus.next(lang, { force: true }) }
// 4) 用户操作:仅决定下一语言,交给主题广播 export function onUserToggleLocale() { const next = Math.random() > 0.5 ? 'zh-CN' : 'en-US'; applyLocale(next); }
/** --------- 端到端测试与可观测示例 --------- **/
// E2E: 订阅探针收集事件 export function attachE2EProbe() { const events = []; const detach = localeBus.testing.tap((evt) => events.push(evt)); return { events, detach, metrics: () => localeBus.testing.getMetrics(), state: () => localeBus.testing.getState(), flush: () => localeBus.testing.flush(), }; }
// 示例:初始化并触发一次 wireLocaleForInitialComponents(); // applyLocale('zh-CN'); // 正常广播 // applyLocale('zh-CN'); // 因去重跳过 // applyLocale('en-US'); // 广播
需要修改的关键点
加分说明
帮助软件开发者结合设计模式优化现有代码,提高代码的可读性、可维护性与复用性,以及提升系统整体质量。
借助提示词学习设计模式的实际应用方法,并提升代码质量与编程能力。
优化现有代码结构,解决复杂问题,快速打磨高质量代码。
为团队制定一致的代码重构规范,提升整体代码维护性与减少技术债务。
将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。
把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。
在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。
免费获取高级提示词-优惠即将到期