likes
comments
collection
share

解析项目启动时根据配置自动打开浏览器访问的原理

作者站长头像
站长
· 阅读数 10

本文参加了由公众号@若川视野 发起的每周源码共读活动,     点击了解详情一起参与。

这是源码共读的第13期 | open 打开浏览器

前言

vue-clicreate-react-app创建的项目启动时(vuecli指定参数--open), 会自动帮我们打开浏览器进行访问, 在webpack 中 也可以通过devServe.open:true 来达到执行Webpack服务时自动打开浏览器访问.

它们都使用了open 这个package 来实现这个功能,接下来我们学习它的源码和原理

包源码

git clone https://github.com/lxchuan12/open-analysis.git 
# npm i -g yarn cd open && yarn

使用

npm install open

open 的主要核心就是调用Node下的 child_process 子线程模块的 spawn,用系统命令打开浏览器

const open = require('open');

// Opens the image in the default image viewer and waits for the opened app to quit.
await open('unicorn.png', {wait: true});
console.log('The image viewer app quit');

// Opens the URL in the default browser.
await open('https://sindresorhus.com');

// Opens the URL in a specified browser.
await open('https://sindresorhus.com', {app: {name: 'firefox'}});

// Specify app arguments.
await open('https://sindresorhus.com', {app: {name: 'google chrome', arguments: ['--incognito']}});

// Open an app
await open.openApp('xcode');

// Open an app with arguments
await open.openApp(open.apps.chrome, {arguments: ['--incognito']});

依赖

is-wsl

判断是否为 wsl环境,即判断是否是Windows下的Linux子系统 is-wsl

is-docker

判断是否为Docker容器环境中

define-lazy-prop

在对象上定义延迟求值的属性

特地跑去源码看了下,使用Object.defineProperty将我们传递的给对象使用函数包裹的value值, 写入到对象的getter中,当getter被真正触发时,也就是这个key被实际访问使用之后,才会在getter函数中 去取到真正的value值设置到对象上并返回, 实现 延迟赋值的作用。

核心源码

module.exports = open;

我们可以在代码底部找到核心函数的导出,跟着看open函数

open函数

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

	return baseOpen({
		...options,
		target
	});
};

对输入的内容做判断,然后接着去调用baseOpen函数

baseOpen核心函数

baseOpen 针对运行客户端不同以及参数做了许多处理, 这里我省略一部分代码将核心代码和结构展出

const baseOpen = async options => {
	//.....省略若干代码
	if (platform === 'darwin') {
		command = 'open'; //mac 命令
                //....省略若干代码
	} 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`;

		//...省略若干代码
		const encodedArguments = ['Start']; //win 命令

		//...省略若干代码
	} else {
		if (app) {
			command = app;
		} else {
                //...省略若干代码
        
        // 核心代码执行
	const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);

	//...省略若干代码
        //独立子进程出去, 父进程的事件循环不必再等待子进程的执行
	subprocess.unref();

	return subprocess;
};

使用process.platform针对mac系统 以及 win,Linux等环境都做了判断,以及一些参数和特殊环境下的判断处理。最后判断决定我们要使用的调用命令,即command变量。

比如它在Win系统下使用Start命令,你可以尝试在控制台执行Start https://www.juejin.cn. windows根据系统的默认应用去使用浏览器打开这个网址,至此就明白关键的核心原理就在于命令执行。

在最后使用child_process.spawn做命令调用。

总结

记得前不久才有过类似的疑问,Node怎么调起浏览器访问指定网址, 今天看完这一篇是明白了,本质还是对于命令调用. 还学习了define-lazy-prop的实现,明白了其中的作用。