vscode里面的常见设计模式

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);

在其它类接收事件

// 调用Emitter类的event方法,传入一个回调函数,fire时触发
this._register(SidebarPart.onHandleChangeSearchDetail(flag => {
    
    
    console.log(333, flag);
}));

src/vs/base/common/event.ts

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());
	}
}

猜你喜欢

转载自blog.csdn.net/woyebuzhidao321/article/details/131574851