前端开发过程中,尤其是新开项目的时候,经常会用到各种脚手架工具,比如 dva-cli、vue-cli 和 ant-design-pro-cli 等,这些都是基于 node 开发的命令行工具,本文以开发一个生成脚手架的命令行工具为例学习具体的开发流程。实现的需求就是通过 npm 全局安装这个 node 工具包,根据提示在命令行进行选择、输入等操作,最终生成一个以 react 或 vue 为基础的脚手架.当然,前提是必须要有现成的 react 或 vue 脚手架模版(这个不是本文的重点)。
1 实现思路
借鉴 ant-design-pro-cli 的实现原理, 把项目模版和命令行工具分离(这样做的目的是为了方便独立维护),cli 运行时收集并解析命令行参数(args)和用户交互内容(prompts),然后根据这些信息从 github 上下载项目模版(templates),如果有需要也可对模版中的目录和文件进行操作后重新渲染(render),比如修改 package.json 中的 name、description 等信息。最终在本地生成一个项目脚手架(local project)。
了解了需求和实现思路,就可以开始本次需求的开发了。
2 初始化项目
创建目录并命名为 gframe(generate frame 的缩写)
npm init -y 生成 package.json 并修改 name,version,description 的值
mkdir bin lib
touch bin/gframe lib/init.js
安装相关依赖包
3 脚本文件 bin/gframe
该文件主要是通过 commander 注册了一个 init 命令,并对终端输入的参数进行收集和解析,如果有匹配的命令,则执行相应的 action。相关 api 介绍及代码如下:
version 方法输出版本信息
command 注册命令,参数是命令名称和回传给 action 方法的参数
description 输出该命令的描述
action 订阅了该命令触发时的回调函数
parse 对传入的参数进行解析并执行相应的回调
4 逻辑处理文件 lib/init.js
该文件主要包括两部分逻辑,一部分是收集用户输入的交互信息,另一部分是从 github 上下载项目模版,并根据用户交互的内容渲染并输入模版。
使用 inquirer 进行命令行交互
1.通过 prompt 方法配置交互方式
2.prompt 方法返回一个 promise 对象,可通过.then 中的参数获取交互结果
使用 download-git-repo 下载模版
1.通过 bluebird 的 promisify 包装,用.then 替换 callback
2.download 的两个必须参数,repo 指仓库地址,dest 指下载的目标路径
3.需要注意的是仓库地址 com 或者端口号后面的/要改为:,最后面还需要用 #拼上分支名
使用 ora 创建小图标
因为下载模版是个异步的过程,为了提高用户体验,需要加入 loading 效果。使用 ora 要注意的一点就是要手动调用 spinner.stop 方法结束,否则程序不会退出,因为内部是通过 setInterval 定时器实现的
1const fs = require(‘fs’);
2const ora = require(‘ora’);
3const chalk = require(‘chalk’);
4const Promise = require(‘bluebird’);
5const download = Promise.promisify(require(‘download-git-repo’));
6const spinner = ora(‘正在下载模板…’);
7/**
8 * 从 github 上下载已有的模版
9 * @param answers 命令行收集到的交互信息
10 * @param dirname 最终生成的项目名
11 */
12function downloadFn(answers, dirname) {
13 const { frame, name = dirname, description = dirname } = answers;
14 // 从 github 上找了两个 star 比较多的脚手架模版,一个 react,一个 vue
15 let url = ‘https://github.com:bodyno/react-starter-kit#master’;
16 if (frame === ‘vue’) {
17 url = ‘https://github.com:Mrminfive/vue-multiple-page#master’;
18 }
19 spinner.start();
20 download(url, dirname, { clone: false })
21 .then(() => {
22 spinner.stop(); // 关闭 loading 动效
23 console.log(chalk.green(‘download template success’));
24 // 重写 package 中的 name、description 等项目信息
25 const pkg = process.cwd() + /${dirname}/package.json
;
26 const content = JSON.parse(fs.readFileSync(pkg, ‘utf8’));
27 content.name = name;
28 content.description = description;
29 const result = JSON.stringify(content);
30 fs.writeFileSync(pkg, result);
31 })
32 .catch(err => {
33 spinner.stop(); // 关闭 loading 动效
34 console.log(chalk.red(‘download template failed’));
35 console.log(err);
36 });
37}
5 本地测试
package.json 中增加 bin 字段,它是一个可执行命令和本地文件名的映射
在项目根目录下执行 npm link,这样会在全局的 node_modules 下生成一个符号链接,此时就可以在全局使用 package.json 中 bin 字段的命令名了
6npm 发布
在https://www.npmjs.com上注册账号,已有 npm 账号的直接登录
项目根目录下执行 npm adduser,输入用户名、密码、邮箱后,如果登录成功会提示:Logged in as ‘username’ on https://registry.npmjs.org/。
项目根目录下执行 npm publish
发布过程中如果提示 You do not have permission to publish “packagename”. Are you logged in as the correct user?先检查本地登录的用户是否和 npm 官网上的用户是否一致,如果没问题,则把 package.json 中的 name 的值改掉,因为包名已经被别人使用了,所以不能正常发布
7 使用
1.npm install gframe -g
2.gframe init my-app
3.根据提示选择并输入相关信息
当前执行命令的目录下就会新增一个 my-app 的项目,里边有现成的脚手架,然后就可以愉快地开发了。
8 结束语
除了实现下载和渲染模版的功能,还可以在此基础上进一步拓展,比如重置模版的 git 地址、自动安装依赖等。当然,除了快速生成脚手架, cli 工具还可以做很多事情,比如代码校验、自动化测试等,这些都可以作为开发者的辅助工具,实实在在的提高开发效率。本文只是带大家入门,感兴趣的同学可以继续深入尝试。
作者介绍:
风雷(企业代号名),目前负责贝壳找房租赁轻托管业务前端开发工作。
本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。
原文链接:
https://mp.weixin.qq.com/s/_ZBps4oNpnQLlXro2e128A
评论