1. 前言
1.1 环境
- 操作系统: macOS 11.5.2
- 浏览器: Chrome 94.0.4606.81
-
1.2 阅读该文章可以get以下知识点
打开了浏览器原理和源码实现
-
1.3 open的用法
open('https://sindresorhus.com', {app: {name: open.apps.chrome}})open('https://sindresorhus.com', {url: true})open('https://sindresorhus.com', {app: {name: open.apps.chrome, arguments: ['--incognito']}})open('index.js')
2. index.js 源码
2.1 vite 中调用的函数就是 open
// 如果target不是字符串,抛出错误const open = (target, options) => {if (typeof target !== 'string') {throw new TypeError('Expected a `target`');}return baseOpen({...options,target});};
2.2 baseOpen
const baseOpen = async options => {// 配置参数options = {wait: false,background: false,newInstance: false,allowNonzeroExitCode: false,...options};// app是不是数组,不是很了解app这个参数干嘛的if (Array.isArray(options.app)) {// 拿到可以执行的app,然后执行baseOpen,拿到第一个appreturn pTryEach(options.app, singleApp => baseOpen({...options,app: singleApp}));}let {name: app, arguments: appArguments = []} = options.app || {};appArguments = [...appArguments];// 如果里面还有app,继续执行在调用baseOpenif (Array.isArray(app)) {return pTryEach(app, appName => baseOpen({...options,app: {name: appName,arguments: appArguments}}));}let command;const cliArguments = [];const childProcessOptions = {};// linux平台if (platform === 'darwin') {command = 'open';// 添加一些参数if (options.wait) {cliArguments.push('--wait-apps');}if (options.background) {cliArguments.push('--background');}if (options.newInstance) {cliArguments.push('--new');}if (app) {cliArguments.push('-a', app);}// windows平台或者使用了wsl, wsl是windows子系统,一般是用的Ubuntu系统} else if (platform === 'win32' || (isWsl && !isDocker())) {// 获取加载路径const mountPoint = await getWslDrivesMountPoint();// wsl和正常window的命令不一样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 (!isWsl) {childProcessOptions.windowsVerbatimArguments = true;}const encodedArguments = ['Start'];if (options.wait) {encodedArguments.push('-Wait');}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 (app) {command = app;} else {// When bundled by Webpack, there's no actual package file path and no local `xdg-open`.const isBundled = !__dirname || __dirname === '/';// Check if local `xdg-open` exists and is executable.let exeLocalXdgOpen = false;try {await fs.access(localXdgOpenPath, fsConstants.X_OK);exeLocalXdgOpen = true;} catch {}const useSystemXdgOpen = process.versions.electron ||platform === 'android' || isBundled || !exeLocalXdgOpen;command = useSystemXdgOpen ? 'xdg-open' : localXdgOpenPath;}if (appArguments.length > 0) {cliArguments.push(...appArguments);}if (!options.wait) {// `xdg-open` will block the process unless stdio is ignored// and it's detached from the parent even if it's unref'd.childProcessOptions.stdio = 'ignore';childProcessOptions.detached = true;}}if (options.target) {cliArguments.push(options.target);}if (platform === 'darwin' && appArguments.length > 0) {cliArguments.push('--args', ...appArguments);}// 开启子进程,执行脚本const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);// 如果有wait,返回一个promiseif (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;};
3. 总结
open不止可以打开浏览器也可以打开文件,本质上执行的就是一个脚本
- max系统下,执行的脚本 open -a microsoft\ edge http://www.baidu.com,直接支持打开浏览器
不同的平台脚本不一样,主要是mac/linux/windows三种,windows下还分了wsl和正常系统
4. 参考文档
- github.com/sindresorhus/open
- https://www.npmjs.com/package/ava
