vue create my-vue-project
执行后产生了什么,程序执行
- 【零碎】零碎定位到
bin/vue.js
文件,通过node bin/vue.js create my-project
来执行该文件; - 【vue.js】
bin/vue.js
利用commander
来定义命令选项create
,将create
命令匹配到create办法(lib/create.js
),执行该办法; - 【create.js】
lib/create.js
应用Inquirer.js来询问用户,进行我的项目配置; - 【Creator.js】依据用户配置生成
package.json
文件(根底信息,从我的项目配置中注入对应的开发依赖devDependencies); - 【Creator.js】执行
npm i
来装置依赖;(PS: 这里封装了罕用的npm操作,能够间接拷贝到本人我的项目中应用) - 【Creator.js】加载
vue-cli
插件(@vue/cli-service是第一个被执行的插件); - 【Generator.js】执行所有插件(执行cli-service插件会生成我的项目文件构造);
- 【Creator.js】生成
README.md
文件;
下面就是create
命令的根本执行过程,如果咱们想扩大create
办法,例如依照咱们的定义的模板生成目录构造,能够新建一个插件(generator,能够参考cli-service),在插件里生成自定义的目录构造即可。
一,命令执行过程
当你在控制台敲下npm -v
并回车的时候,到底产生了什么?
咱们先来看看npm
这个命令在哪。通过执行which npm
,(不理解which命令的同学能够参考这里:linux命令之which),能够看到,npm
命令的可执行文件在/usr/local/bin/npm
,关上文件夹一看,是个替身,右键“显示原身”(Mac用户的办法,其余用户请搜寻“软链接”),就能定位到该命令在哪:/usr/local/lib/node_modules/npm/bin/npm-cli.js
,看到是js代码置信大家曾经松了一口气,咱们先不急着看npm-cli.js
外面的源,咱们先思考一个问题:
Q1:零碎是怎么找到npm
这个这个命令的可执行文件的?A1:理解
which
命令的同学都晓得,which
命令会依照PATH
变量中的门路程序来查找可执行文件。执行echo $PATH
能够打印出该变量内容:/usr/local/bin:/usr/bin:/bin
(例如这是我的局部内容,目录间用‘:’分隔),所以零碎会先在/usr/local/bin
上面找npm
执行文件,/usr/local/bin/npm
链接到/usr/local/lib/node_modules/npm/bin/npm-cli.js
。所以调用npm
命令相当于执行npm-cli.js
。
总的来说:在控制台执行命令时,零碎会先去环境门路(PATH)中找到可执行文件,而后执行该文件。
那么,有同学好奇:
Q2:零碎又是怎么执行这些文件(例如下面的npm-cli)的呢?A2:关上
npm-cli.js
文件,咱们能看到的第一行代码就是:#!/usr/bin/env node
,这行代码到底有什么用呢?具体可参考:stackoverflow - #!/usr/bin/env到底有什么用?,大抵意思是告知零碎用什么解释程序来执行该文件,例如#!/usr/bin/env node
就是告知零碎,npm-cli.js
要用node
来执行。因而npm -v
相当于node /usr/local/lib/node_modules/npm/bin/npm-cli.js -v
。
$ node /usr/local/lib/node_modules/npm/bin/npm-cli.js -v6.4.1
二,npm包打造cli的原理
理解完命令执行过程之后,咱们就能够打造本人的cli命令了。
①先编写my-cli.js
文件:
#!/usr/bin/env nodeconsole.log('Hello cli!');
②在/usr/local/bin
下(或者PATH
里的任意门路下)创立软链接:
ln -s my-cli.js my-cli
③给my-cli命令增加可执行权限:(若不增加权限,会报错bash: /usr/local/bin/my-cli: Permission denied
)
chmod 777 my-cli
④验证成果:
$ my-cliHello cli!
在下面的根底上,咱们尽管能打造本人的命令,然而这个命令要想给团队应用,就须要每个人都拷贝my-cli.js
文件,创立软链接,增加可执行权限,十分繁琐。怎么将本人的命令散发进来给他人应用呢?
咱们再往前摸索一步,一起打造一个基于npm散发的命令。
咱们在下载应用一个npm模块命令的时候,咱们会这样:
npm install -g @vue/clivue create my-project
全局装置vue-cli
这个npm模块之后,咱们全局新增了vue
命令,这背地到底产生了什么?是npm install
帮咱们把下面提到的②③步主动执行了(如有谬误,欢送指出)。既然npm
曾经帮咱们实现这些简略然而繁琐的脚本操作,那咱们只须要依照npm
的标准来配置一下代码即可。流程比较简单,请参考:通过npm包来制作命令行工具的原理。
总结一下开发过程:
npm init
新建npm模块目录;- 开发命令(例如下面的
my-cli.js
); package.json
中增加bin
字段(bin: { "my-cli": "./my-cli.js" }
);- 公布npm;
- 全局装置即可应用
my-cli
;
三,VUE-CLI中外围库
vue-cli源码 理解怎么优雅地实现一个cli。
介绍几个vue-cli
外面用到的几个库:
- commander - 命令行参数解析库;
- Inquirer.js - 命令行罕用交互模式汇合(问答,抉择...);
- chalk - 在命令行款式丑化;
- ora - 命令行loader;
commander
简直是开发cli必不可少的工具,原理(参考commander源码),根本应用办法如下:
#!/usr/bin/env nodevar program = require('commander');program .version('0.1.0') .option('-p, --peppers', 'Add peppers') .option('-P, --pineapple', 'Add pineapple') .option('-b, --bbq-sauce', 'Add bbq sauce') .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') .parse(process.argv);
外围流程如下:
1. 通过option定义收集命令的性能选项;
2. parse解析命令参数(有process取得);
3. 由命令参数去匹配后面收集到的性能选项,执行后面的办法(将参数传入);
/** * Parse `argv`, settings options and invoking commands when defined. * * @param {Array} argv * @return {Command} for chaining * @api public */Command.prototype.parse = function(argv) { // implicit help if (this.executables) this.addImplicitHelpCommand(); // store raw args this.rawArgs = argv; // guess name this._name = this._name || basename(argv[1], '.js'); // github-style sub-commands with no sub-command if (this.executables && argv.length < 3 && !this.defaultExecutable) { // this user needs help argv.push('--help'); } // process argv var parsed = this.parseOptions(this.normalize(argv.slice(2))); var args = this.args = parsed.args; var result = this.parseArgs(this.args, parsed.unknown); // executable sub-commands var name = result.args[0]; var aliasCommand = null; // check alias of sub commands if (name) { aliasCommand = this.commands.filter(function(command) { return command.alias() === name; })[0]; } if (this._execs[name] && typeof this._execs[name] !== 'function') { return this.executeSubCommand(argv, args, parsed.unknown); } else if (aliasCommand) { // is alias of a subCommand args[0] = aliasCommand._name; return this.executeSubCommand(argv, args, parsed.unknown); } else if (this.defaultExecutable) { // use the default subcommand args.unshift(this.defaultExecutable); return this.executeSubCommand(argv, args, parsed.unknown); } return result;};
原文参考: cli原理解析