时隔16年Jeff Barr重返10.23-25 QCon上海站,带你看透AI如何重塑软件开发! 了解详情
写点什么

前端工具开发系列 (一) cli 工具开发

  • 2019-09-27
  • 本文字数:3580 字

    阅读完需:约 12 分钟

前端工具开发系列(一) cli 工具开发

前端开发过程中,尤其是新开项目的时候,经常会用到各种脚手架工具,比如 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


安装相关依赖包


1.2├── bin3         ├──gframe // 脚本文件4├── lib5         ├──init.js // 逻辑处理文件6├── node_modules7├── package-lock.json8├── package.json // 项目配置文件 1// 项目依赖的第三方包 2{ 3    ... 4    "dependencies": { 5        "bluebird": "^3.5.3", // promise库,让callback以.then的形式执行 6        "chalk": "^2.4.2", // 设置终端输出的样式 7        "commander": "^2.19.0", // 解析命令行参数并触发响应的动作 8        "download-git-repo": "^1.1.0",// 下载git仓库 9        "inquirer": "^6.2.2", // 收集命令行交互信息10        "ora": "^3.2.0" // 终端小图标11    }12  ...13}
复制代码

3 脚本文件 bin/gframe

该文件主要是通过 commander 注册了一个 init 命令,并对终端输入的参数进行收集和解析,如果有匹配的命令,则执行相应的 action。相关 api 介绍及代码如下:


version 方法输出版本信息


command 注册命令,参数是命令名称和回传给 action 方法的参数


description 输出该命令的描述


action 订阅了该命令触发时的回调函数


parse 对传入的参数进行解析并执行相应的回调


 1#!/usr/bin/env node  // 文件头部加上`#!/usr/bin/env node`,它指明了执行这个脚本文件的解释程序 2const fs = require('fs'); 3const chalk = require('chalk'); 4const program = require('commander'); 5const pkg = require('../package.json'); 6const { inquirerFn, downloadFn } = require('../lib/init'); 7program.version(pkg.version, '-v,--version'); 8program 9    .command('init <dirname>')10    .description(pkg.description)11    .action(dirname => {12        // 命令init触发时的回掉函数13        if (fs.existsSync(dirname)) {14            return console.log(chalk.red(`dirname ${dirname} is exist`));15        }16        inquirerFn().then(answers => {17            downloadFn(answers, dirname);18        });19    });20// 如果输入没有注册的命令,输出帮助提示21program.arguments('<command>').action(cmd => {22    program.outputHelp();23    console.log(' ');24    console.log(`error: unknown option '${cmd}'`);25});26program.parse(process.argv);27// 如果没写参数,输出帮助提示28if (!process.argv.slice(2).length) {29    program.outputHelp();30}
复制代码

4 逻辑处理文件 lib/init.js

该文件主要包括两部分逻辑,一部分是收集用户输入的交互信息,另一部分是从 github 上下载项目模版,并根据用户交互的内容渲染并输入模版。


  • 使用 inquirer 进行命令行交互


1.通过 prompt 方法配置交互方式


2.prompt 方法返回一个 promise 对象,可通过.then 中的参数获取交互结果


 1var inquirer = require('inquirer'); 2function inquirerFn() { 3    return inquirer.prompt([ 4        { 5            type: 'list', 6            name: 'frame', 7            message: '请选择开发用的脚手架:', 8            choices: ['react', 'vue'] 9        },10        {11            type: 'input',12            name: 'name',13            message: '请输入项目名称:'14        },15        {16            type: 'input',17            name: 'description',18            message: '请输入项目简介:'19        }20    ]);21}
复制代码


  • 使用 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 字段的命令名了


1{2    "bin": {3        "gframe": "./bin/gframe"4    }5}
复制代码


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


2019-09-27 13:045728

评论

发布
暂无评论
发现更多内容

学习大数据开发技术能参加培训吗?

小谷哥

深耕数字办公,华为云桌面怎样带来高效办公体验?

路过的憨憨

国产ETL 星光不问赶路人 时我不待

weigeonlyyou

oracle Prometheus Clickhouse MySQL 数据库 InfluxDB Cluster

开发者可以选择哪些小游戏分发平台?

FN0

小游戏 小游戏开发 小程序化

ins快拍下载保存到手机相册最新图文教学!

frank

ins图片下载 ins

代码质量与安全 | 如何将清洁代码标准扩展到整个企业,促进业务上的成功?

龙智—DevSecOps解决方案

代码质量 代码安全

DevSecOps 需要知道的十大 K8s 安全风险及建议

SEAL安全

k8s DevSecOps 12 月 PK 榜

高可用软件什么意思?哪些高可用软件好用?

行云管家

高可用 双机热备 高可用软件

前端开发培训后可以从事哪些方面的工作

小谷哥

启科量子或将开源环境部署工具Runtime

启科量子开发者官方号

#开源 #量子计算

版本控制 | SVN,Git与Helix Core,谁的数据管理基础设施更安全?

龙智—DevSecOps解决方案

git svn 版本控制 版本控制系统

数据代码如何“产地直销”,做到持续集成持续发布?

数造万象

三个延伸打法,撑起华为云桌面的7年领跑

路过的憨憨

隐私计算之多方安全计算(MPC,Secure Multi-Party Computation)

京东科技开发者

大数据 隐私安全 多方安全 MPC OT

那些专注小程序语法编译的跨端开发平台

FinFish

前端框架 跨端开发 跨端框架 前端开发框架 前端开发平台

大数据开发技术有好的培训机构吗

小谷哥

尚硅谷发布JDBC新版视频教程

小谷哥

老板要的物联网可视化大屏,我30分钟就搞定了

华为云开发者联盟

物联网 华为云 12 月 PK 榜

昇腾CANN:为你开启机器人开发的Buff 加成

华为云开发者联盟

人工智能 华为云 昇腾AI 12 月 PK 榜

高性能网关基石——OpenResty

ooooooh灰灰

lua nginx openresty

【2023】合肥市等保测评有哪些公司?地址在哪里?

行云管家

等级保护 等保测评 等保2.0 合肥

今明两天,eBPF 技术探索和 Intel Arch 两大技术 SIG 继续开讲 | 第 57-58 期

OpenAnolis小助手

开源 ebpf intel 龙蜥大讲堂 浪潮信息

web前端培训机构怎么选?

小谷哥

有序存储对于高性能的意义

华为云开发者联盟

后端 开发 华为云 12 月 PK 榜

持续发力折叠屏生态,OPPO 携手开发者探索大屏应用新体验

科技热闻

还原火山引擎A/B测试产品——DataTester 私有化部署实践经验

字节跳动数据平台

大数据 AB testing实战 12 月 PK 榜

【5000字长文】从 S3 到 DataZone,亚马逊云科技用16年讲完一个数据的故事

亚马逊云科技 (Amazon Web Services)

亚马逊云科技 Builder 专栏

LED显示屏企业需要抓住直播的风口吗?

Dylan

LED显示屏 全彩LED显示屏 led显示屏厂家

安装 Azure CL 并生成 service principal 文件

HummerCloud

云计算 azure

支持API 9的Sample已上新,速来拿走

HarmonyOS开发者

HarmonyOS

管理有方!华为云数据库为医药行业管理加速

华为云开发者联盟

数据库 后端 华为云 12 月 PK 榜 医药

前端工具开发系列(一) cli 工具开发_大前端_风雷_InfoQ精选文章