简介
脚手架本质是一个运行在操作系统上的一个工具、一个客户端。它通过命令执行。
意义
- 摆脱构建工程时的重复性工作。一行命令初始化好一个项目。
- 将研发过程:自动化、标准化、可量化[直接评估业务开发即可]
- 统一目录结构
- 统一工作流程
- 统一配置,降低管理成本
- 实现项目0配置
自己维护一套脚手架,可控性高,能满足团队特定需求的研发。
命令
命令由四个部分组成:
- 主命令
- 子命令
- 参数
- 配置(option)
例如:vue create my-app
它是vue-cli脚手架创建一个my-app项目的命令。空格分开的分别对应 主、子、参三个部分。
例如:vue create my-app --force
这里的 --force
就是option部分,用来辅助在某个参数场景下用户的一种细分选择。
ps: 一些option也是可以简写的。比如:--registry
简写:-r
脚手架执行原理
- 终端输入 vue create vue-test
- 终端解析出vue命令
- 终端在环境变量中找到vue命令
- 终端根据vue命令链接到实际文件vue.js
- 终端利用node执行vue.js
- vue.js 解析 command/options
- vue.js 执行 command
- 执行完毕,退出执行
设计思路
- 解耦:脚手架与模板分离
- 脚手架负责构建流程,通过命令行与用户交互,获取项目信息
- 模板负责统一项目结构、工作流程、依赖项管理
- 脚手架需要检测模板的版本是否有更新,支持模板的删除与新建
可 参考vue-cli。
脚手架实现过程
- 定制脚手架的主命令(node本身就支持);
- 添加子命令、选项,同时解析命令参数(commander);
- 与用户交互(用户输入,选择),发问后接受用户应答(inquirer);
- 根据用户输入的项目名称,模板来下载(axios/request/download-git-repo) ;
- 解压模板(decompress)
- 修改模板里边的文件(package.json,index.html等)(fs.writeFileSync) ;
- 为项目安装依赖,结束(child_process.spawn 开启一个子进程,执行下载)。
node.js 内置了对命令行操作的支持,
package.json
中的bin
字段可以定义命令名和关联的执行文件。在package.json
中添加bin
字段
一. 初始化项目
生成一个package.json
npm init -y
二. 配置bin
bin:配置内部命令对应的可执行文件位置,配置命令后,npm 会寻找到对应的可执行文件,然后在 node_modules/.bin 目录下建立对应的符号链接。 由于 node_modules/.bin 会在运行时候加入到系统的环境变量,因此我们可以通过 npm 调用命令来执行脚本。 所有 node_modules/.bin 目录下的命令都可以通过 npm run [命令] 执行。 所以我们需要在 package.json 配置入口:
"bin": {
"vue3-cli": "./bin/vue3-cli.js"
},
然后创建bin/vue3-cli.js
文件,添加测试代码:
注意:第一行代码必须写。告知此文件用node去解析。
#!/usr/bin/env node
console.log('hello I am web cli base vue3!')
三. 全局安装下本地这个项目
在当前项目目录下执行
npm install . -g
或者,
sudo npm link
在开发公司内部npm包时候,为了提升开发效率,不想每次先提交git,再执行 npm install
命令来看效果,npm link
可以帮助我们很方便的实现这样的功能。
然后可以测试下此命令是否生效,任意目录下开cmd,输入:
发现可以正常执行打印出了log.
ps: 如果你是windows用户,或是远程虚拟机办公的场景,你一定是需要管理员权限的,不然npm link注册失败。
运行也会失败。
这时候,我们管理身份进入系统,通过cmd去做相关操作发现可行。
四. 为命令加参数
上面的步骤,已经在系统里成功的添加了自己的命令。那如何丰富我们的命令,可以做更多的事情呢。接下来,需要借助工具 commander 帮我们加参数了。
4.1 安装
npm i commander@11 -S
4.3 书写测试代码
我们可以先实现一个简单的功能。 为命令添加一个 查看版本 的参数选项。
bin/vue3-cli.js
#!/usr/bin/env node
// console.log('hello I am web cli base vue3!')
const {Command} = require("commander")
const pkg = require("../package.json")
const program = new Command()
program.version(pkg.version,"-v --version")
program.parse()
运行:
4.4 定义子命令
主命令,由node自身定义好了,为了丰富主命令的功能,我们开支散叶,定制子命令。
bin/vue3-cli-create.js
console.log('I am sub command!')
入口主程序:bin/vue3-cli.js
#!/usr/bin/env node
const {Command} = require("commander")
const pkg = require("../package.json")
const program = new Command()
// 选项
program.version(pkg.version,"-v --version")
// 子命令
program.command('create [projectName]','create a new project')//[projectName] 是可选参
program.parse(process.argv)
运行:
五. 其他生态插件集成
5.1 安装
npm i inquirer@7.3 chalk@4 ora@5 decompress@4 request@2.8 -S
注意: 这里一般都指定了大版本,这里这些版本的node包依旧是遵循commonJs规范的,也就是使用require()语法导入的。默认安装最新包的话,一般都是使用ESM规范import导入了。
5.2 create子命令书写
bin/vue3-cli-create.js
// console.log('I am sub command!')
const {Command} = require("commander")
const inquirer = require("inquirer")
const chalk = require("chalk")
const program = new Command()
let projectName = undefined;
let force = undefined;
// create 子命名
program
.arguments("[projectName]")
.description("初始化项目")
.option("-f --force", "如果存在输入的项目目录,强制删除项目目录")
.action((name,cmd)=>{
projectName = name
force = cmd.force
console.log('come in');
})
program.parse(process.argv)
// 设置交互问题
const questions = [
{
type:"input",
name:"projectName",
message:chalk.yellow("请输入你的项目名称")
},{
type:"list",
name:"template",
choices:[
{name:"webpack5-vue2-template",value:"vue2-webpack5-admin"},
{name:"vite-vue3-template",value:"vite-vue3-template"},
{name:"webpack-react-template",value:"lb-react-apps-template"}
]
}
]
// 执行与用户的交互
inquirer
.prompt(questions)
.then((answers)=>{
if(answers.projectName){
projectName = answers.projectName
}
const templateName = answers.template
if(!templateName || !projectName){
// 退出
console.log("参数不传递,直接退出了")
}else{
// go to download
console.log(" 我去下载模板")
}
})
运行:
5.4 下载模板
从上面 5.3 我们把下载流程代码写完了。下载过程我们可以进一步细化,同时下载后我们还有做一些事情来增强我们的脚手架。
六. 书写全部程序
按照流程图,我们一一实现.
七. 发布脚手架到npm
这里npm服务是采用 nexus3 版本搭建的。这个东西是一款对java里 Maven 包管理的私服工具,同时他还支持 npm 、docker 、yum 等等。搭建好后,需要做的配置工作:
ps:如果你已经配置好了,直接看 步骤八.
步骤一 首先,登录后,setting/创建存储/创建 npm Repositories。
然后可以看到3个npm:
- npm (proxy) 代理仓库 [代理到npm or 淘宝npm 的公共包]
- npm (hosted) 是私有仓库 [自己发布的包]
- npm (group) 是组合前面两个,最终暴露出的 [供自己本地下载安装的大库]
步骤二 设置代理仓库
- Name: 仓库的名字
- Remote storage: 远程仓库地址
- Blob store:选择我们刚刚创建好的存储
我们自己的npm服会定期去淘宝源里取包。
步骤三 创建hosted仓库
步骤四 创建group仓库
注意下 Members 的顺序,下载npm包时候会按照这个顺序先去私有库hosted里找,找不到再去代理库里查找。所以即便是重名也会优先下载私服的模块。
步骤五 创建用户
这里可以创建一个新用户,也可以使用管理员账号直接登录发布。
步骤六 配置.npmrc文件
取的是group的地址:
registry=http://192.168.x.y:8081/repository/npm-group/
步骤七 配置package.json
取的是hosted的地址:
"publishConfig": {
"registry": "http://192.168.x.y:8081/repository/npm-hosted"
},
步骤八 发布
前期的准备工作完毕。接着我们需要把这个工具发布到npm 上,供公司内部开发同事下载使用。发布到内部的npm服上过程:
1.切换本地npm代理源:
npm config set registry https://192.168.x.y:8081/repository/npm-group
2.要发布的项目包下开cmd ,然后登录:npm login
输入账号密码,注册邮箱。
3.npm publish
当然,如果你使用命令发布失败了。也可以选择自己手动上传npm包。过程如下:
1.改package.json的版本号。每次发布前都要给个新值。
2.要发布的项目包下开cmd ,然后npm pack
3.登录到npm网页版,选择 upload ,进入 npm-hosted选择刚打包的文件即可。
扩展说明
commander API
- usage(): 设置 usage 值
- command(): 定义一个命令名字
- description(): 设置 description 值
- option(): 定义参数,需要设置“关键字”和“描述”,关键字包括“简写”和“全写”两部分,以”,”,”|”,”空格”做分隔。
- parse(): 解析命令行参数 argv
- action(): 注册一个 callback 函数
- version() : 终端输出版本号
inquirer API
- type:表示提问的类型,包括:input, confirm, list, rawlist, expand, checkbox, password, editor;
- name: 存储当前问题回答的变量;
- message:问题的描述;
- default:默认值;
- choices:列表选项,在某些 type 下可用,并且包含一个分隔符(separator);
- validate:对用户的回答进行校验;
- filter:对用户的回答进行过滤处理,返回处理后的值;
- when:根据前面问题的回答,判断当前问题是否需要被回答;
- prefix:修改 message 默认前缀;
- suffix:修改 message 默认后缀。
reuqest库已经弃用
npm 上已经search 不到了。一个紧随 nodeJs 诞生的 请求库由于一些原因弃用了。
看下它的起源,已经有十几年历史了。
模板源切到公司内部gitlab
实际应用上来看,大部分是公司内部项目。所以你的模板源大概率是不会开源的。都是托管在公司内部的代码仓库,这里以 内部的gitlab 举例子。这里可以参照另外一篇文章:脚手架gitlab模板下载