open 打开浏览器的过程原理示例解析

目录
  • 前言
  • 使用
  • open
  • open 的实现原理
  • 总结

前言

启动项目时,在本地服务器启动后会自动帮我们打开浏览器,程序是如何做到呢?又是哪些代码在起作用呢?希望通过本章节的学习,达成如下目标:

  • 学习程序自动打开浏览的原理
  • 学会使用 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打开浏览器原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • window.open()详解及浏览器兼容性问题示例探讨

    一.基本语法: window.open(pageURL,name,parameters) 其中: pageURL 为子窗口路径 name 为子窗口名字 parameters 为窗口参数(各参数用逗号分隔) 二.示例 复制代码 代码如下: <script type="text/javascript"> window.open('page.html','newwindow','height=500,width=800,top=0,left=0, toolbar=no,menub

  • 解决window.open()被浏览器拦截的问题

    一.问题描述 最近在做项目的时候碰到了使用window.open被浏览器拦截的情况,虽然在自己的环境可以对页面进行放行,但是对用户来说,不能要求用户都来通过拦截.何况当出现拦截时,很多用户根本不知道发生了啥,不知道在哪里看被拦截的页面.因此必须通过代码来解决这个问题! 以下是浏览器拦截示例: 二.问题分析 浏览器之所以拦截新开窗口是因为该操作并不是用户主动触发的,所以它认为这是不安全的就拦截了,即使 ajax 回调函数中模拟执行 click 或者 submit 等用户行为(trigger('cl

  • window.open关于浏览器拦截问题分析及解决方法

    window.open是javascript函数,该函数的作用是打开一个新窗口或这改变原来的窗口,不过一般用来的是打开新窗口,因为修改原来的网页地址,可以有另一个函数,那就是window.location,他可以重定向网页地址,使网页跳转到另一个页面. 我现在要说的是window.open函数的几个使用策略,一般情况下,如果你直接在js中调用window.open()函数去打开一个新窗口,浏览器会拦截你,认为你将弹出广告等用户不想得到的窗体,所以如果不想让浏览器拦截你,你可以将这个函数改为用户点

  • window.open被浏览器拦截后的自定义提示效果代码

    现在越来越多的浏览器有拦截弹出窗口的功能.广告弹出来给拦掉了就无所谓,要是客户在付款时给拦掉了可就不能乱算了. Gmail的"哎呀"算是经典,可是,前天心云给出了更帅的提示=.= 记得打开浏览器拦截后 测试一下,在线阅读器里不知道代码会不会给过滤..代码在下边,其实没什么技术含量滴.挖哈哈... 复制代码 代码如下: window._open=window.open;  window.open=function(sURL,sName,sFeatures,bReplace){    if

  • open 打开浏览器的过程原理示例解析

    目录 前言 使用 open open 的实现原理 总结 前言 启动项目时,在本地服务器启动后会自动帮我们打开浏览器,程序是如何做到呢?又是哪些代码在起作用呢?希望通过本章节的学习,达成如下目标: 学习程序自动打开浏览的原理 学会使用 Node.js 强大的 child_process 模块 源码地址:sindresorhus/open npm: open - npm (npmjs.com) 使用 配置 webpack 的 devServer 选项: module.exports = { //..

  • 网页资源阻塞浏览器加载的原理示例解析

    目录 正文 测试前环境准备 图片会造成阻塞吗? CSS 加载阻塞 CSS 会阻塞后面 JS 的执行吗? JS 加载阻塞 defer 和 async 动态脚本会造成阻塞吗? DOMContentLoaded 和 onload DOMContentLoaded 遇到脚本 DOMContentLoaded 遇到样式 正文 一个页面允许加载的外部资源有很多,常见的有脚本.样式.字体.图片和视频等,对于这些外部资源究竟是如何影响整个页面的加载和渲染的呢?今天来一探究竟. 如何用 Chrome 定制网络加载

  • react fiber执行原理示例解析

    目录 为什么要使用fiber,要解决什么问题? fiber是什么? 数据结构 执行单元 浏览器工作: Fiber执行原理 workInProgress tree: currentFiber tree: Effects list: render阶段: 遍历节点过程: 收集effect list: commit阶段: 为什么commit必须是同步的操作的? 为什么要使用fiber,要解决什么问题? 在 react16 引入 Fiber 架构之前,react 会采用递归方法对比两颗虚拟DOM树,找出需

  • Kotlin协程之Flow基础原理示例解析

    目录 引言 一.Flow的创建 二.Flow的消费 1.SafeFlow类 2.AbstractFlow类 3. SafeCollector类 4.消费过程中的挂起 引言 本文分析示例代码如下: launch(Dispatchers.Main) { flow { emit(1) emit(2) }.collect { delay(1000) withContext(Dispatchers.IO) { Log.d("liduo", "$it") } Log.d(&qu

  • go语言csrf库使用实现原理示例解析

    目录 引言 csrf小档案 一.CSRF及其实现原理 CSRF攻击示例 二.如何预防 三.CSRF包的使用及实现原理 csrf包的安装 基本使用 使用net/http包启动的服务 echo框架下使用csrf包 gin框架下使用csrf包 beego框架下使用csrf包 实现原理 csrf结构体 csrf包的工作流程 为什么GET.HEAD.OPTIONS.TRACE的请求方法不需要token验证 总结 引言 今天给大家推荐的是web应用安全防护方面的一个包:csrf.该包为Go web应用中常见

  • java LockSupport实现原理示例解析

    目录 引言 LockSupport常见函数 LockSupport.park LockSupport.unpark 引言 前文中了解到AQS借助LockSupport.park和LockSupport.unpark完成线程的阻塞和唤醒,那么LockSupport内部又是怎么实现的?这是一个什么类? LockSupport是用于使用锁阻塞线程的基础实现,是其他同步类的基础,这个类为每个使用它的线程关联一个许可证(有点类似于Semaphore),如果许可证可用,线程调用park方法时会立即返回,线程

  • OPENMP SECTIONS CONSTRUCT原理示例解析

    目录 前言 编译器角度分析 动态库函数分析 总结 前言 在本篇文章当中主要给大家介绍 OpenMP 当中主要给大家介绍 OpenMP 当中 sections construct 的实现原理以及他调用的动态库函数分析.如果已经了解过了前面的关于 for 的调度方式的分析,本篇文章就非常简单了. 编译器角度分析 在这一小节当中我们将从编译器角度去分析编译器会怎么处理 sections construct ,我们以下面的 sections construct 为例子,看看编译器是如何处理 sectio

  • PHP文件打开关闭及读写操作示例解析

    前言 PHP支持文件上传功能,因此学习PHP文件的相关操作是必须的,这篇笔记会去记录PHP文件系统的相关知识. 文件打开与关闭 文件处理包括读取.关闭.重写等,例如,访问一个文件需要3步:打开文件.读写文件和关闭文件.其他的操作要么是包含在读写文件中(如显示内容.写入内容等),要么与文件自身的属性有关系(如文件遍历.文件改名等). 1.打开文件 在PHP中使用fopen()函数打开文件,fopen()函数的语法如下: resource fopen ( string filename, strin

  • useReducer createContext代替Redux原理示例解析

    目录 前言 采用react-redux实现 采用react hooks模拟redux实现 异步action 总结 前言 最近看到很多采用useReducer + createContext 实现一个简易的redux的方案,今天亲自试了一下,发现还是会有一些区别的. 采用react-redux实现 这里使用react-redux 实现一个简单的状态管理例子. App.jsx根组件 import React from 'react'; import { Button } from './Button

  • flutter Bloc 实现原理示例解析

    目录 序言 1. 事件流 > 状态流 (中转) 2. 使用 BlocBuilder 实时监听状态变更, 如何实现的呢? 总结 扩展 序言 在flutter开发中,我们使用 bloc 框架,基于状态变更进行响应式开发.本篇文章,小轰将 bloc 核心业务块进行拆解简化,聊一聊它的实现思想,bloc 核心能力分为如下两点: 添加事件 event,将 '事件流' 转换为 '状态流' state 监听 bloc 流,每次 state 状态变更,通知 widget 更新 下面,用自定义Bloc的方式,来给

随机推荐