脚手架开发的一些细节(一)
脚手架的一些名词
vue create vue-demo --force -r https://registry.npm.taobao.org
- 主命令
vue
- 子命令
create
- 命令参数
vue-demo
- 配置选项
force / r
- 配置选项的参数(如果为给定默认为true)
https://registry.npm.taobao.org
分析vue-cli脚手架执行原理
输入vue create vue-demo
- 解析
vue
命令 - 在环境变量中查找vue命令。
which vue
- 根据查找到的vue路径连接到实际的vue.js文件。
- node执行vue.js文件
- vue.js中解析command / options
- vue.js执行command
分析vue-cli安装三连问
- 为啥全局安装
@vue/cli
后会添加的命令为vue?
因为@vue/cli项目的package.json中的bin属性下指定了脚本命令名称。安装@vue/cli后,会分析该文件,并在node/bin目录下创建对应命令的软链接。
- 全局安装
@vue/cli
时发生了啥?
将项目下载到node/lib/node_modules中,如果有脚本命令将在node/bin目录下创建对应的软链接。
- 执行 vue 命令时发生了啥?为啥 vue 指向一个 js 文件,我们却可以直接通过vue命令执行它?
在环境变量中查找是否有该命令,没有则报错。在js文件开头加上#!/usr/bin/env node
即可直接通过命令执行js文件。他主要是告诉终端,去/usr/bin/env(环境变量目录)
中查找node命令去执行js文件。
npm包下载细节
如果在当前本地包目录下全局下载该包,那么下载的包在lib/node_modules下会软连接到本地包项目。而不是下载的npm仓库的包。就类似于执行了npm link
。
脚手架本地调试流程
将本地开发库连接到全局node/lib/node_modules下。
npm link
在当前项目中引入本地开发库。
npm link your-lib
如果直接link本地包到该项目,将会报错,所以需要先全局link。
将本地开发库从全局中移除
npm unlink -g your-lib
开发流程需要注意的是,我们在本地测试完毕后,需要将本地link的全局包删除,这样才能下载发布后的包进行测试。
项目管理工具lerna
lerna 是一个优化基于git + npm 的多package项目的管理工具。
优势
- 大幅减少重复操作。
- 提升操作的标准化。
lerna是架构优化的产物,他揭示了一个架构真理:项目复杂度提升后,就需要对项目进行架构优化。架构优化的主要目标往往就是以效能为核心。
lerna创建脚手架项目
lerna init
创建子包,注意为了防止包名被注册,我们可以先在npm中创建一个组织。包名命名成@mh-cli/core
lerna create core
为所有pacakge安装依赖,并在对应的package中的package.json中写入依赖的版本号。
lerna add <libname>
删除包中的node_modules。但是packages.json中的依赖版本没有删除,我们需要手动删除。
lerna clean
为packages中的包安装相互依赖,而不需要进入对应的包目录进行
npm link <libname>
。但是需要先在dependencies中添加本地包依赖。
lerna link
如果需要重新link,需要先通过lerna clean
删除node_modules,然后在执行lerna link
。
lerna clean
lerna link
执行每个包中脚本命令。
lerna exec <任何命令>
lerna run <packages中定义的命令>
changed与diff
lerna changed // 查看哪些包发生变更,然后需要发布
lerna diff // 查看两次commit的差异
修改版本号。当项目提交仓库后,发布前,需要更改version
lerna version
发布出现402错误,我们需要在package.json中加入。因为加了@前缀的包默认是private的,要在package.json中改变一下publishConfig。
publishConfig: {
access: "public"
}
如果我们未发布成功,我们需要手动改动版本号
yargs,解析命令行参数工具
#!/usr/bin/env node
const yargs = require("yargs")
// 解析参数
const {hideBin} = require("yargs/helpers")
/**
* ./src/index.js zh llm
* [ 'zh', 'llm' ]
*/
const argv = hideBin(process.argv) // 解析参数,只保留控制台输入的参数
const cli = yargs(arg)
// 处理命令提示
cli
// 脚手架通用模板格式。$0表示脚手架名称
.usage("Usage: $0 <command> [options]")
.argv
如果不知道严格模式,输入错误的命令将没有任何错误反馈。
// 严格模式。让输入不正确的命令或者参数出现错误提示。而不是没有任何反馈。
.strict()
约束最少输入的命令
// 约束最少输入的命令
.demandCommand(1, "A command is required. Pass --help to see all available commands and options.")
配置option别名
// 别名
.alias("h", "help")
.alias("v", "version")
修改终端信息展示宽度,让其有更好看的样式。
// 修改终端信息展示宽度
.wrap(cli.terminalWidth())
help中结尾内容的定义
.epilogue(`
When a command fails, all logs are written to lerna-debug.log in the current working directory.
For more information, check out the docs at https://lerna.js.org/docs/introduction
`)
增加全局的option选项,对所有命令都生效。
// 配置全局option(配置多个)
.options({
debug: {
type: "boolean",
describe: "dev debug",
default: true,
alias: "d",
// 表示隐藏命令,用于内部开发使用。他不会显示在options列表中,但是可以被使用。
hidden: true
}
})
// 配置全局option(配置一个)
.option("name", {
type: "string",
describe: "project name",
alias: "n"
})
进行options分组。
.group(["debug", "name"], "dev options")
.group(['version', 'help'], "global options")
定义子命令。两种方式
.command({
command: "init [name]",
aliases: ["i"],
describe: "List local packages",
// 执行这个命令之前回调
builder(yargs) {
yargs.option("name", {
type: "string",
describe: "a project name",
// 脚手架中的别名不能重复
alias: "n1"
})
},
handler(argv) {
console.log("argv", argv)
},
})
// 定义命令 方式二
.command('serve [port]', 'start the server', (yargs) => {
return yargs
.positional('port', {
describe: 'port to bind on',
default: 5000
})
}, (argv) => {
if (argv.verbose) console.info(`start server on :${argv.port}`)
serve(argv.port)
})
给出错误提示,匹配相近的命令提示用户。
// 输入错误命令,提示用户
.recommendCommands()
// 定制自己的错误提示信息
.fail((msg, err) => {
console.log(msg)
})
向参数列表中注入参数。我们不再通过
hideBin
来解析参数。
const { version } = require("../package.json")
const context = {
yargsStudy: version
}
const cli = yargs()
cli
.parse(process.argv.slice(2), context)
完整的yargs demo代码
#!/usr/bin/env node
const yargs = require("yargs")
// 解析参数
// const {hideBin} = require("yargs/helpers")
// 移除文本首行空格
// const dedent = require("dedent")
/**
* ./src/index.js zh llm
* [ 'zh', 'llm' ]
*/
// const arg = hideBin(process.argv) // 解析参数,只保留控制台输入的参数
// const cli = yargs(arg)
const { version } = require("../package.json")
const context = {
yargsStudy: version
}
const cli = yargs()
cli
// 脚手架通用模板格式。$0表示脚手架名称
.usage("Usage: $0 <command> [options]")
// 输入错误命令,提示用户
.recommendCommands()
// 定制自己的错误提示信息
.fail((msg, err) => {
console.log(msg)
})
// 严格模式。让输入不正确的命令或者参数出现错误提示。而不是没有任何反馈。
.strict()
// 约束最少输入的命令
.demandCommand(1, "A command is required. Pass --help to see all available commands and options.")
// 别名
.alias("h", "help")
.alias("v", "version")
// 修改终端信息展示宽度
.wrap(cli.terminalWidth())
// 结尾输出内容
.epilogue(`
When a command fails, all logs are written to lerna-debug.log in the current working directory.
For more information, check out the docs at https://lerna.js.org/docs/introduction
`)
// 配置全局option(配置多个)
.options({
debug: {
type: "boolean",
describe: "dev debug",
default: true,
alias: "d",
// 表示隐藏命令,用于内部开发使用
// hidden: true
}
})
// 配置全局option(配置一个)
.option("name", {
type: "string",
describe: "project name",
alias: "n"
})
.group(["debug", "name"], "dev options")
.group(['version', 'help'], "global options")
.command({
command: "init [name]",
aliases: ["i"],
describe: "List local packages",
// 执行这个命令之前回调,一般定义一些options
builder(yargs) {
yargs.option("name", {
type: "string",
describe: "a project name",
// 别名不能重复
alias: "n1"
})
},
handler(argv) {
console.log("argv", argv)
},
})
// 定义命令 方式二
.command('serve [port]', 'start the server', (yargs) => {
return yargs
.positional('port', {
describe: 'port to bind on',
default: 5000
})
}, (argv) => {
if (argv.verbose) console.info(`start server on :${argv.port}`)
// serve(argv.port)
})
// .argv
.parse(process.argv.slice(2), context)
总结
Yargs常用API
- Yargs.usage(提示脚手架用法)
- Yargs.strict(开启以后可以报错提示)
- Yargs.demandCommand(规定最少传几个command)
- Yargs.recommendCommands(在输入错误command以后可以给你推荐最接近的正确的command)
- Yargs.alias(起别名)
- Yargs.options(定义多个option)
- Yargs.option(定义option)
- Yargs.fail(错误处理方法)
- Yargs.group(给option分组)
- Yargs.wrap(命令行工具的宽度)
- Yargs.epilogue(命令行工具底部的提示)
Yargs开发流程
- 脚手架初始化(将process.argv当参数传递给Yargs())
- 脚手架命令注册(Yargs.command)
- 脚手架参数解析(Yargs.parse)
转载自:https://juejin.cn/post/7264920482412199992