首頁 > 軟體

open 開啟瀏覽器的過程原理範例解析

2022-12-27 14:01:32

前言

啟動專案時,在本地伺服器啟動後會自動幫我們開啟瀏覽器,程式是如何做到呢?又是哪些程式碼在起作用呢?希望通過本章節的學習,達成如下目標:

  • 學習程式自動開啟瀏覽的原理
  • 學會使用 Node.js 強大的 child_process 模組

原始碼地址:sindresorhus/open

npm: open - npm (npmjs.com)

使用

設定 webpack 的 devServer 選項:

module.exports = {
  //...
  devServer: {
    open: true,
  },
};

告訴 dev-server 在伺服器啟動後開啟瀏覽器。將其設定為 true 以開啟預設瀏覽器。

DevServer | webpack

如果你使用的是 ue-cli,則在啟動命令後面新增引數 --open

# yarn serve 不會自動開啟瀏覽器 
yarn serve 
# --open 引數後會自動開啟瀏覽器 
yarn serve --open

open

無論是webpack還是vue-cli,他們能夠實現在瀏覽器中開啟網頁的功能,主要依賴 open 這個包。

看一下他的 slogan :

Open stuff like URLs, files, executables. Cross-platform.

開啟像 URL、檔案、可執行檔案之類的東西。跨平臺。

它有以下優點:

  • 這個倉庫更新維護及時
  • 豐富的引數
  • 安全性
  • 解決了大多數 node-open 產生的問題
  • 跨平臺

得益於以上優點,這個包每週有兩千多萬的下載量:

open 的實現原理

入口檔案:

定位到 open 函數:

const open = (target, options) => {
	if (typeof target !== 'string') {
		throw new TypeError('Expected a `target`');
	}
	return baseOpen({
		...options,
		target
	});
};

可以看到核心實現邏輯在 baseOpen 函數:

const path = require('path');
const childProcess = require('child_process');
const {promises: fs, constants: fsConstants} = require('fs');
const {platform, arch} = process;
const baseOpen = async options => {
	options = {
		wait: false,
		background: false,
		newInstance: false,
		allowNonzeroExitCode: false,
		...options
	};
	// ... 部分程式碼...
	let command;
	const cliArguments = [];
	const childProcessOptions = {};
	if (platform === 'darwin') {
		command = 'open';
		if (options.wait) {
			cliArguments.push('--wait-apps');
		}
                // ...省略一些判斷,
	} else if (platform === 'win32' || (isWsl && !isDocker())) {
		const mountPoint = await getWslDrivesMountPoint();
		command = isWsl ?
			`${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe` :
			`${process.env.SYSTEMROOT}\System32\WindowsPowerShell\v1.0\powershell`;
		cliArguments.push(
			'-NoProfile',
			'-NonInteractive',
			'–ExecutionPolicy',
			'Bypass',
			'-EncodedCommand'
		);
		if (app) {
			// Double quote with double quotes to ensure the inner quotes are passed through.
			// Inner quotes are delimited for PowerShell interpretation with backticks.
			encodedArguments.push(`"`"${app}`""`, '-ArgumentList');
			if (options.target) {
				appArguments.unshift(options.target);
			}
		} else if (options.target) {
			encodedArguments.push(`"${options.target}"`);
		}
		if (appArguments.length > 0) {
			appArguments = appArguments.map(arg => `"`"${arg}`""`);
			encodedArguments.push(appArguments.join(','));
		}
		// Using Base64-encoded command, accepted by PowerShell, to allow special characters.
		options.target = Buffer.from(encodedArguments.join(' '), 'utf16le').toString('base64');
	} else {
           // ...省略 其他情況
	}
	if (options.target) {
		cliArguments.push(options.target);
	}
	if (platform === 'darwin' && appArguments.length > 0) {
		cliArguments.push('--args', ...appArguments);
	}
	const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
	if (options.wait) {
		return new Promise((resolve, reject) => {
			subprocess.once('error', reject);
			subprocess.once('close', exitCode => {
				if (options.allowNonzeroExitCode && exitCode > 0) {
					reject(new Error(`Exited with code ${exitCode}`));
					return;
				}
				resolve(subprocess);
			});
		});
	}
	subprocess.unref();
	return subprocess;
};

首先程式,使用 Node.js 中的 process.platform 屬性來獲取當前作業系統平臺的值。字串 'darwin' 用於標識 macOS。'win32' 則表示 windows作業系統了。

對不同作業系統進行不同的引陣列織:

  • macos : 根據 options 中的引數一一新增到 cliArguments 變數中
  • windows: 主要是獲取powershell程式的路徑。
    • wsl:根據子系統掛載點路徑獲取
    • win:根據 process.env.SYSTEMROOT 獲取作業系統的根路徑

process.env.SYSTEMROOT 是一個由 Node.js 提供的全域性變數,表示當前系統的根目錄的路徑。 在 Windows 作業系統中,根目錄通常是 C:Windows。在其他作業系統中,此變數的值可能為空或不存在。

之後使用 Node.js child_process 模組中的 childProcess.spawn 函數,以啟動新的子程序並執行命令。

它將 commandcliArguments 變數作為引數傳遞給 childProcess.spawn,以及一個名為 childProcessOptions 的物件,該物件包含子程序的選項。

childProcess.spawn 函數返回一個表示已生成子程序的 ChildProcess 物件。如果 options.wait 屬性為 true,則程式碼會返回一個新的 Promise,該Promise 物件根據子程序的回撥函數做出reject或者resolve迴應。

兩個事件:

  • 'error' 事件偵聽 器會監控到發生的錯誤,reject.
  • 'close' 事件偵聽 器會在退出程式碼為零(或 options.allowNonzeroExitCode 屬性為 true)時使用 subprocess 物件解析承諾。如果退出程式碼為非零且 options.allowNonzeroExitCode 屬性為 false,則 reject('錯誤程式碼')

最後使用 subprocess.unref 方法保持子程序執行,目的是為了使子程序在後臺執行。

總結

總的來說,open原理是:針對不同的系統,使用Node.js的子程序 child_process 模組的spawn方法,呼叫系統的命令開啟瀏覽器。

通過本章節課程的學習,學習到了如何使用 nodejs 的內建模組對作業系統型別的判斷以及childProcess建立子程序的方式,更多關於open開啟瀏覽器原理的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com