execa介绍+在Vue3.x中的实践☘️

1,184 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 12 天,点击查看活动详情

近期,Vue 3.2发布,出于好奇,去瞄了一眼Vue 3.2的源码,发现其中有一个execa库很有意思,于是去学习了一番。在学习的基础上,写了这篇总结。本文主要介绍execa的概念、常用API以及execa在Vue3.x中的使用。

1、execa简介

execa的文档中对其的介绍如下:

Process execution for humans

人类的流程执行

换成人话,execa是一个子进程(child_process)管理库。

Node官网上对 child_process的介绍如下:

child_process模块提供了以与 popen(3)类似但不完全相同的方式衍生子进程的能力

Node的child_process提供了exec、expecFile、spawn和fork。

execa是对child_process的改进:

  • 提供Promise接口

  • 输出会删除最后的换行符,不必执行stdout.trim()

  • 支持跨平台的shebang(#!)二进制文件

  • Windows支持友好

  • 最大缓冲区为100MB而不是200kb。

  • 可以按名称执行本地安装的二进制文件

  • 在父进程终止时清除派生的进程

  • stdoutstderr获得交错输出,类似于在终端上打印的输出。(异步)

  • 可以指定文件和参数作为一个单一的字符串

  • 报错更具描述性

由此可知,execa是一个加强版的子进程管理库。如果想要运行一个 shell 命令或者生成一个子进程,execa 是一个较好的选择。

2、execa使用

这里主要是execa的安装、基本API以及错误捕获使用。

  1. 安装
yarn add execa --dev

OR

npm instal execa --save-dev
……

"scripts": {

    "echo": "node scripts/echo.js",

    "err": "node scripts/err.js"

 },

 "devDependencies": {

    "execa": "^5.1.1",

  }

…… 
  1. 使用
const execa = require('execa');



(async () => {

    const {stdout} = await execa('echo', ['unicorns']);

    console.log(stdout);

    //=> 'unicorns'

})();
  1. 错误处理
const execa = require('execa');



(async () => {

  // Catching an error

  try {

    await execa('unknown', ['command']);

  } catch (error) {

    console.log(error);

    /*

    {

      message: 'Command failed with ENOENT: unknown command spawn unknown ENOENT',

      errno: -2,

      code: 'ENOENT',

      syscall: 'spawn unknown',

      path: 'unknown',

      spawnargs: ['command'],

      originalMessage: 'spawn unknown ENOENT',

      shortMessage: 'Command failed with ENOENT: unknown command spawn unknown ENOENT',

      command: 'unknown command',

      stdout: '',

      stderr: '',

      all: '',

      failed: true,

      timedOut: false,

      isCanceled: false,

      killed: false

    }

    */

  }



})();

3、API介绍

execa()、execa.sync()、execa.command、execa.commandSync、execa.node是execa的几个比较常用的API。这几个API的介绍如下:

API解释
execa(file, arguments, options?)相当于child_process.execFile() 和child_process.spawn()
execa.sync(file, arguments?, options?)同步执行文件
execa.command(command, options?)与execa()相同,上文中execa('echo', ['unicorns'])与execa.command('echo unicorns')相同
execa.commandSync(command, options?)与execa.sync()相同
execa.node(scriptPath, arguments?, options?)执行Node脚本

参数说明如下:

  • file:将要运行的命令;
  • arguments:参数数组;
  • options:选项对象,如用shell,可以设置为{shell:true}。
  • command:命令+参数的集合;
  • scriptPath: 运行模块路径;

其中,?修饰的参数是可以省参数。

execa()的源码如下:

const execa = (file, args, options) => {

 ...

 return mergePromise(spawned, handlePromiseOnce);

}



...

// The return value is a mixin of `childProcess` and `Promise`

const mergePromise = (spawned, promise) => {

        for (const [property, descriptor] of descriptors) {

                // Starting the main `promise` is deferred to avoid consuming streams

                const value = typeof promise === 'function' ?

                        (...args) => Reflect.apply(descriptor.value, promise(), args) :

                        descriptor.value.bind(promise);



                Reflect.defineProperty(spawned, property, {...descriptor, value});

        }



        return spawned;

};

...

module.exports = execa;

execa()的源码如下:

module.exports.sync = (file, args, options) => {

 ...

 result = childProcess.spawnSync(parsed.file, parsed.args, parsed.options);

 ...

 const stdout = handleOutput(parsed.options, result.stdout, result.error);

 const stderr = handleOutput(parsed.options, result.stderr, result.error);

    return {

        command,

        escapedCommand,

        exitCode: 0,

        stdout,

        stderr,

        failed: false,

        timedOut: false,

        isCanceled: false,

        killed: false

    };

}

execa.command()的源码如下:

// https://github.com/sindresorhus/execa/blob/main/index.js

// ...

module.exports.command = (command, options) => {

    const [file, ...args] = parseCommand(command);

    return execa(file, args, options);

};

// ...

execa.node() 的源码如下:

module.exports.node = (scriptPath, args, options = {}) => {

    if (args && !Array.isArray(args) && typeof args === 'object') {

        options = args;

        args = [];

    }

    

    const stdio = normalizeStdio.node(options);

    const defaultExecArgv = process.execArgv.filter(arg => !arg.startsWith('--inspect'));

    

    const {

        nodePath = process.execPath,

        nodeOptions = defaultExecArgv

    } = options;

    

    return execa(

        nodePath,

        [

                ...nodeOptions,

                scriptPath,

                ...(Array.isArray(args) ? args : [])

        ],

        {

            ...options,

            stdin: undefined,

            stdout: undefined,

            stderr: undefined,

            stdio,

            shell: false

        }

    );

};

4、 Vue 3.x中execa的应用

Vue 3.x中execa的应用如下图1所示。Vue2.x中类似功能如图2所示。通过Vue 3.x与Vue 2.x的scripts比较可知,execa在Vue 3.x中不仅可以为scripts提供兼容性,特别是Windows系统、Mac OS系统、Linux系统,同时可以提供扩展性,可以动态添加参数。

图 1

图 2

5、参考资料

www.npmjs.com/package/exe…

github.com/sindresorhu…

nodejs.cn/api/child_p…

man7.org/linux/man-p…

blog.csdn.net/weixin_4538…

zxljack.com/2019/02/exe…

juejin.cn/post/699794…

有bug?想补充?

感谢大家观看这篇文章,有任何问题或想和我交流,请直接留言,

发现文章有不妥之处,也可指出交流,感谢阅读~

53875b26-8251-4ccc-ad02-70badf65d662.gif