vscode里面最常见的就是命令模式,无论是菜单还是按钮的点击事件大多都是以命令形式去执行的,这样的设计模式在代码的可读上更清晰明了。
在早我写过一篇有关js几种常见设计模式的demo,感兴趣的看我这篇文章:https://blog.csdn.net/woyebuzhidao321/article/details/120235389。
命令模式
我们看下vscode里面最简单的命令注册
src/vs/workbench/contrib/files/browser/fileActions.ts
CommandsRegistry.registerCommand({
id: NEW_FILE_COMMAND_ID,
handler: async (accessor) => {
await openExplorerAndCreate(accessor, false);
}
});
这里的id
是唯一的,handler
是对应的执行函数。
我们跳入registerCommand
方法里面。
export const CommandsRegistry: ICommandRegistry = new class implements ICommandRegistry {
private readonly _commands = new Map<string, LinkedList<ICommand>>();
private readonly _onDidRegisterCommand = new Emitter<string>();
readonly onDidRegisterCommand: Event<string> = this._onDidRegisterCommand.event;
registerCommand(idOrCommand: string | ICommand, handler?: ICommandHandler): IDisposable {
if (!idOrCommand) {
throw new Error(`invalid command`);
}
if (typeof idOrCommand === 'string') {
if (!handler) {
throw new Error(`invalid command`);
}
return this.registerCommand({ id: idOrCommand, handler });
}
// add argument validation if rich command metadata is provided
if (idOrCommand.description) {
const constraints: Array<TypeConstraint | undefined> = [];
for (let arg of idOrCommand.description.args) {
constraints.push(arg.constraint);
}
const actualHandler = idOrCommand.handler;
idOrCommand.handler = function (accessor, ...args: any[]) {
validateConstraints(args, constraints);
return actualHandler(accessor, ...args);
};
}
// find a place to store the command
const { id } = idOrCommand;
let commands = this._commands.get(id);
if (!commands) {
// 以健值对形式存储到map对象里统一管理
commands = new LinkedList<ICommand>();
this._commands.set(id, commands);
}
let removeFn = commands.unshift(idOrCommand);
let ret = toDisposable(() => {
removeFn();
const command = this._commands.get(id);
if (command?.isEmpty()) {
this._commands.delete(id);
}
});
// tell the world about this command
this._onDidRegisterCommand.fire(id);
return ret;
}
...
}
可以看到以健值对形式存储到_commands map对象里统一管理起来。id作为key,value是链表数据结构
接下来我们看看是如何通过按钮去调用的
src/vs/workbench/contrib/files/browser/views/explorerView.ts
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.files.action.createFileFromExplorer',
title: nls.localize('createNewFile', "New File"),
f1: false,
icon: Codicon.newFile,
precondition: ExplorerResourceNotReadonlyContext,
menu: {
id: MenuId.ViewTitle,
group: 'navigation',
when: ContextKeyExpr.equals('view', VIEW_ID),
order: 10
}
});
}
run(accessor: ServicesAccessor): void {
const commandService = accessor.get(ICommandService);
commandService.executeCommand(NEW_FILE_COMMAND_ID);
}
});
可以看到通过执行 executeCommand
方法去调用,我们看看executeCommand方法里面实现了什么
src/vs/workbench/services/commands/common/commandService.ts
async executeCommand<T>(id: string, ...args: any[]): Promise<T> {
...
return this._tryExecuteCommand(id, args);
....
}
private _tryExecuteCommand(id: string, args: any[]): Promise<any> {
// 获取链表节点
const command = CommandsRegistry.getCommand(id);
if (!command) {
return Promise.reject(new Error(`command '${id}' not found`));
}
try {
this._onWillExecuteCommand.fire({ commandId: id, args });
// 这里调用handler方法
const result = this._instantiationService.invokeFunction(command.handler, ...args);
this._onDidExecuteCommand.fire({ commandId: id, args });
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
}
}
可以看到通过getCommand
方法通过id获取到了链表的节点,我们看下invokeFunction函数实现
src/vs/platform/instantiation/common/instantiationService.ts
invokeFunction<R, TS extends any[] = []>(fn: (accessor: ServicesAccessor, ...args: TS) => R, ...args: TS): R {
let _trace = Trace.traceInvocation(fn);
let _done = false;
try {
const accessor: ServicesAccessor = {
get: <T>(id: ServiceIdentifier<T>) => {
if (_done) {
throw illegalState('service accessor is only valid during the invocation of its target method');
}
const result = this._getOrCreateServiceInstance(id, _trace);
if (!result) {
throw new Error(`[invokeFunction] unknown service '${id}'`);
}
return result;
}
};
return fn(accessor, ...args);
} finally {
_done = true;
_trace.stop();
}
}
可以看到其实它就是调用了handler方法。这样从注册到执行的过程就通了。
发布订阅模式
在类里面定义事件
static readonly _onHandleChangeSearchDetail = new Emitter<boolean>();
static onHandleChangeSearchDetail: Event<boolean> = SidebarPart._onHandleChangeSearchDetail.event;
事件分发
SidebarPart._onHandleChangeSearchDetail.fire(true);
在其它类接收事件文章来源:https://www.toymoban.com/news/detail-528927.html
// 调用Emitter类的event方法,传入一个回调函数,fire时触发
this._register(SidebarPart.onHandleChangeSearchDetail(flag => {
console.log(333, flag);
}));
src/vs/base/common/event.ts文章来源地址https://www.toymoban.com/news/detail-528927.html
export class Emitter<T> {
private readonly _options?: EmitterOptions;
private readonly _leakageMon?: LeakageMonitor;
private readonly _perfMon?: EventProfiling;
private _disposed: boolean = false;
private _event?: Event<T>;
private _deliveryQueue?: LinkedList<[Listener<T>, T]>;
protected _listeners?: LinkedList<Listener<T>>;
constructor(options?: EmitterOptions) {
this._options = options;
this._leakageMon = _globalLeakWarningThreshold > 0 ? new LeakageMonitor(this._options && this._options.leakWarningThreshold) : undefined;
this._perfMon = this._options?._profName ? new EventProfiling(this._options._profName) : undefined;
}
...
/**
* For the public to allow to subscribe
* to events from this Emitter
*/
// 取.event的时候,执行的就是这里,它其实返回了一个方法:
get event(): Event<T> {
if (!this._event) {
this._event = (callback: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore) => {
if (!this._listeners) {
this._listeners = new LinkedList();
}
const firstListener = this._listeners.isEmpty();
if (firstListener && this._options?.onFirstListenerAdd) {
this._options.onFirstListenerAdd(this);
}
let removeMonitor: Function | undefined;
let stack: Stacktrace | undefined;
if (this._leakageMon && this._listeners.size >= 30) {
// check and record this emitter for potential leakage
stack = Stacktrace.create();
removeMonitor = this._leakageMon.check(stack, this._listeners.size + 1);
}
if (_enableDisposeWithListenerWarning) {
stack = stack ?? Stacktrace.create();
}
const listener = new Listener(callback, thisArgs, stack);
const removeListener = this._listeners.push(listener);
if (firstListener && this._options?.onFirstListenerDidAdd) {
this._options.onFirstListenerDidAdd(this);
}
if (this._options?.onListenerDidAdd) {
this._options.onListenerDidAdd(this, callback, thisArgs);
}
const result = listener.subscription.set(() => {
if (removeMonitor) {
removeMonitor();
}
if (!this._disposed) {
removeListener();
if (this._options && this._options.onLastListenerRemove) {
const hasListeners = (this._listeners && !this._listeners.isEmpty());
if (!hasListeners) {
this._options.onLastListenerRemove(this);
}
}
}
});
if (disposables instanceof DisposableStore) {
disposables.add(result);
} else if (Array.isArray(disposables)) {
disposables.push(result);
}
return result;
};
}
return this._event;
}
/**
* To be kept private to fire an event to
* subscribers
*/
// 循环派发了所有注册的事件
fire(event: T): void {
if (this._listeners) {
// put all [listener,event]-pairs into delivery queue
// then emit all event. an inner/nested event might be
// the driver of this
if (!this._deliveryQueue) {
this._deliveryQueue = new LinkedList();
}
for (let listener of this._listeners) {
this._deliveryQueue.push([listener, event]);
}
// start/stop performance insight collection
this._perfMon?.start(this._deliveryQueue.size);
while (this._deliveryQueue.size > 0) {
const [listener, event] = this._deliveryQueue.shift()!;
try {
listener.invoke(event);
} catch (e) {
onUnexpectedError(e);
}
}
this._perfMon?.stop();
}
}
hasListeners(): boolean {
if (!this._listeners) {
return false;
}
return (!this._listeners.isEmpty());
}
}
到了这里,关于vscode里面的常见设计模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!