脚手架是啥

从前我总觉得脚手架是个很高大上的东西,好像得牛叉?一点的人才写的出来,可望而不可即。其实并不是因为困难使我们放弃,而是因为放弃才显得困难(这是个好词好句?)。只要你肯花个一天半天的时间✊,也能写出属于你自己的脚手架。
早前脚手架这个词是从 vue-cli 这里认识的,我们通过 npm install -g vue-cli 命令全局安装脚手架后, 再执行 vue init webpack project-name 就能初始化好一个自己的项目,真是尼玛的神奇?。但你有没有想过为什么我们执行 vue init 这个命令就能有个自己的项目呢。今天,就让我们一起来揭开庐山真面目吧!
等等✋,扯了一堆,你好像还没说下啥是脚手架?emmm... 它就是个工具,方便我们新建项目用的,有了这个项目我们就能直接开发了。其实我们本可以用 git clone url 来新建(复制)项目,再 cuo 一点的方法就是复制粘贴整个文件夹,一样也能达到初始化的目的。脚手架的本质也是从远程下载一个模板来进行一个新项目。额。所以。。。有什么不同呢?就高大上啊?。当然不止于此啦,脚手架可是高级版的克隆,它主要是提供了交互式的命令让我们可以动态的更改模板,然后用一句命令就可以一劳永逸了(当然还是要维护的),这应该是最主要的区别吧,反正现在我是这么想的?。
好了,本章的目的就是带领大家写一个简易版的脚手架 xr-cli(名字爱取啥取啥),目标是实现一个 xr init template-name project-name 这样的命令,废话少说,开始进入正题吧???。

源码地址:github.com/lgq627628/x…

前置知识

其实一个简易版的 xr-cli 的代码量并不多,所以这里我们先来小小介绍一下其中要依赖的包,如果你用过这些工具可以跳过,没用过的请务必一定要瞟一眼。

commander

这是用来编写指令和处理命令行的,具体用法如下:

const program = require("commander");
// 定义指令
program.version('0.0.1').command('init', 'Generate a new project from a template').action(() => {// 回调函数})
// 解析命令行参数
program.parse(process.argv);
复制代码

回忆一下,我们曾用过的 vue init 的命令就是这样声明的。

inquirer

这是个强大的交互式命令行工具,具体用法如下:

const inquirer = require('inquirer');
inquirer.prompt([// 一些交互式的问题]).then(answers => {// 回调函数,answers 就是用户输入的内容,是个对象});
复制代码

想象一下我们用 vue init webpack project-name 之后是不是会有几个交互问题,问你文件名啊、作者啊、描述啊、要不要用 eslint 啊等等之类的,就是用这个来写的。

chalk

这是用来修改控制台输出内容样式的,比如颜色啊,具体用法如下:

const chalk = require('chalk');
console.log(chalk.green('success'));
console.log(chalk.red('error'));
复制代码

ora

这是一个好看的加载,就是你下载的时候会有个转圈圈的那种效果,用法如下:

const ora = require('ora')
let spinner = ora('downloading template ...')
spinner.start()
复制代码

download-git-repo

看名字很明显了,这是用来下载远程模板的,支持 GitHub、 GitLab 和 Bitbucket 等,用法如下:

const download = require('download-git-repo')
download(repository, destination, options, callback)
复制代码

其中 repository 是远程仓库地址;destination 是存放下载的文件路径,也可以直接写文件名,默认就是当前目录;options 是一些选项,比如 { clone:boolean } 表示用 http download 还是 git clone 的形式下载。

目录搭建

ok,有了上面的知识储备之后,我们就正式开始撸了。

  1. 首先我们要创建一个文件夹,并取名叫 xr-cli;
  2. 在该目录下执行 npm init 命令(你应该有安装 node 吧?),一路回车,就会生成一个生成 package.json 文件,在 package.json 里面写入以下依赖并执行 npm install 安装,如下:
"dependencies": {"chalk": "^2.4.2","commander": "^2.19.0","download-git-repo": "^1.1.0","inquirer": "^6.2.2","ora": "^3.2.0"
}
复制代码
  1. 新建一个 bin 文件夹,并在 bin 目录下新建一个无后缀名的 xr 文件,并写上:
#!/usr/bin/env node
console.log('hello');
复制代码

这个文件就是我们整个脚手架的入口文件,我们用 node ./bin/xr 运行一下,就能在控制台打印出 hello,如下图:

这里要注意开头的 #!/usr/bin/env node 这个语句必须加上,主要是为了让系统看到这一行的时候,会沿着该路径去查找 node 并执行,主要是为了兼容 Mac ,确保可执行。

bin 目录初始化

当前,bin 目录下就只有一个文件,就是入口文件 xr。所以现在我们先来编写这个文件,由于内容较少,我们直接看代码:

#!/usr/bin/env node
const program = require('commander')// 定义当前版本
// 定义使用方法
// 定义四个指令
program.version(require('../package').version).usage('<command> [options]').command('add', 'add a new template').command('delete', 'delete a template').command('list', 'list all the templates').command('init', 'generate a new project from a template')// 解析命令行参数
program.parse(process.argv)
复制代码

这个文件的主要作用就是定义指令,现在我们用 node ./bin/xr 运行一下,就能看到如下结果:

当然,你可能会觉得每次输入 node ./bin/xr 这个命令有点麻烦,没关系,我们可以在 package.json 里面写入已下内容:

// bin 用来指定每个命令所对应的可执行文件的位置
"bin": {"xr": "bin/xr"
}
复制代码

然后在根目录下执行 npm link(就是把命令挂载到全局的意思),这样我们每次只要输入 xr,就可以直接运行了,so cool,就像下面这样:

是不是好像有点样子了呢???,那就让我们继续完善下 bin 目录吧!ok,让我们在 bin 目录下再新建四个文件,分别对应上面的四个指令,然后分别处理四个指令要做的事情,如下图: 同样的,我们修改一下 package.json 里面的 bin 内容,如下:

"bin": {"xr": "bin/xr","xr-add": "bin/xr-add","xr-delete": "bin/xr-delete","xr-list": "bin/xr-list","xr-init": "bin/xr-init"
}
复制代码

然后执行 npm unlink 解绑全局命令,再执行 npm link 重新把命令绑定到全局,就像下面这样:

最后顺便在根目录下新建一个 template.json 文件,里面的内容就是一个 {}

编写具体指令

好了,一切准备就绪,接下来就让我们来写下具体的四个指令吧。

xr-add

这个内容也是比较少,直接看代码:

#!/usr/bin/env node// 交互式命令行
const inquirer = require('inquirer')
// 修改控制台字符串的样式
const chalk = require('chalk')
// node 内置文件模块
const fs = require('fs')
// 读取根目录下的 template.json
const tplObj = require(`${__dirname}/../template`)// 自定义交互式命令行的问题及简单的校验
let question = [{name: "name",type: 'input',message: "请输入模板名称",validate (val) {if (val === '') {return 'Name is required!'} else if (tplObj[val]) {return 'Template has already existed!'} else {return true}}},{name: "url",type: 'input',message: "请输入模板地址",validate (val) {if (val === '') return 'The url is required!'return true}}
]inquirer.prompt(question).then(answers => {// answers 就是用户输入的内容,是个对象let { name, url } = answers;// 过滤 unicode 字符tplObj[name] = url.replace(/[\u0000-\u0019]/g, '')// 把模板信息写入 template.json 文件中fs.writeFile(`${__dirname}/../template.json`, JSON.stringify(tplObj), 'utf-8', err => {if (err) console.log(err)console.log('\n')console.log(chalk.green('Added successfully!\n'))console.log(chalk.grey('The latest template list is: \n'))console.log(tplObj)console.log('\n')})})
复制代码

这个文件主要目的就是添加模板并存储起来,上面的注释应该都写的挺清楚了。我们执行 xr add 来看看效果:

这里的模板名称(自己随便取)相当于 vue init webpack project-name 当中的 webpack;模板地址要注意一下,像下面这样写就可以,这里以 github 为例: 这里补充一下 xr add 怎么对应到 xr-add 的:我们前面在定义 program.command('add').action(() => {}) 的时候没有写 action 这个回调函数,而当我们执行 xr add 的时候,commander 会尝试在入口脚本的目录中搜索可执行文件,找到形如 program-command(这里就是 xr-add)的命令来执行,大概是这么个意思,下面的命令也是一样的道理。

xr-delete

如果你理解了上面的那个步骤,这步对你来说应该也是洒洒水啦!上代码:

#!/usr/bin/env nodeconst inquirer = require('inquirer')
const chalk = require('chalk')
const fs = require('fs')
const tplObj = require(`${__dirname}/../template`)let question = [{name: "name",message: "请输入要删除的模板名称",validate (val) {if (val === '') {return 'Name is required!'} else if (!tplObj[val]) {return 'Template does not exist!'} else  {return true}}}
]inquirer.prompt(question).then(answers => {let { name } = answers;delete tplObj[name]// 更新 template.json 文件fs.writeFile(`${__dirname}/../template.json`, JSON.stringify(tplObj), 'utf-8', err => {if (err) console.log(err)console.log('\n')console.log(chalk.green('Deleted successfully!\n'))console.log(chalk.grey('The latest template list is: \n'))console.log(tplObj)console.log('\n')})})
复制代码

应该很好理解,就不过多解释了,我们直接执行 xr delete 看下效果:

xr-list

这个更简单了,两行代码搞定:

#!/usr/bin/env nodeconst tplObj = require(`${__dirname}/../template`)
console.log(tplObj)
复制代码

是不是简单到爆?。我们执行 xr list 看看效果:

因为刚才一添加一删除,所以目前没有模板,就输出 {}

xr-init

这应该是最主要(但不难)的一步了,毕竟我们写到现在还没有通过命令初始化过一个项目呢?。所以这步的重点就是执行 download 方法,并传入相应参数,具体看代码:

#!/usr/bin/env nodeconst program = require('commander')
const chalk = require('chalk')
const ora = require('ora')
const download = require('download-git-repo')
const tplObj = require(`${__dirname}/../template`)program.usage('<template-name> [project-name]')
program.parse(process.argv)
// 当没有输入参数的时候给个提示
if (program.args.length < 1) return program.help()// 好比 vue init webpack project-name 的命令一样,第一个参数是 webpack,第二个参数是 project-name
let templateName = program.args[0]
let projectName = program.args[1]
// 小小校验一下参数
if (!tplObj[templateName]) {console.log(chalk.red('\n Template does not exit! \n '))return
}
if (!projectName) {console.log(chalk.red('\n Project should not be empty! \n '))return
}url = tplObj[templateName]console.log(chalk.white('\n Start generating... \n'))
// 出现加载图标
const spinner = ora("Downloading...");
spinner.start();
// 执行下载方法并传入参数
download (url,projectName,err => {if (err) {spinner.fail();console.log(chalk.red(`Generation failed. ${err}`))return}// 结束加载图标spinner.succeed();console.log(chalk.green('\n Generation completed!'))console.log('\n To get started')console.log(`\n    cd ${projectName} \n`)}
)
复制代码

ok,我们执行一下 xr init simple test,记得先执行一下 xr add

现在我们就可以在左侧的目录中看到 test 项目了,如下图: 至此,一个小小的脚手架就做完了。???此处应该有鲜花和掌声???

发布到 npm

既然以上命令都执行成功了,那接下来我们就把它发布到 npm 上吧(写都写了,不能浪费?)。

  • 删除 test 文件夹,它就本地测试用的,用完就抛弃它(当然做人不能这样)
  • 在根目录下新建 README.md 文件,随便写点使用说明,假装正经一下
  • 在根目录下新建 .npmignore 文件,并写入 /node_modules,意思就是发布的时候忽略 node_modules 文件夹,
  • 去 npm 官网注册个账号(很简单的),同时搜索一下 xr-cli 这个名字,看看有没有人用,有的话就换一个罗 现在让我们回到项目根目录,执行 npm login 登入 npm 账号,再执行 npm publish 发布,就像下面这样: 没错,就是这样两个简单的命令,我们就发布成功啦,真是可喜可贺???。大概过一分钟左右(反正挺快的),我们再去 npm 官网搜下 xr-cli,就可以看到自己的脚手架啦,哈哈哈哈,贼开心???。

这里补充说明一点:根据规范,只有在发包的24小时内才允许撤销发布的包,所以为了不污染 npm 网站,如果只是测试的话就执行 npm unpublish --force 删除吧,毕竟我们都是有素质的人。

小试牛刀

别急,还没有结束?‍♀️。发都发出去了,怎么也得验证一波撒。嗯,说的有道理,无法反驳,那就赶紧验收吧!这里我们记得先用 npm unlink 解绑一下命令,不然会相互影响。下面我们打开终端,输入 npm i xr-cli -g 全局安装一下脚手架,然后执行 xr,如果出现下图中的模样就说明已经安装成功了。

接下来进入到桌面,执行 xr init simple xr-test,不一会就可以在桌面上看到自己的项目啦。 6️⃣6️⃣6️⃣,大赞无疆,大。。赞。。。无疆!!!

结语

上面的操作只要你熟悉了几遍之后,再去看看 vue-cli 的源码结构,你就会有种拨开云雾见月明的感觉(它只是比我们这个脚手架完善很多很多很多而已???)。
当然了,这只是渣渣版本。你可以往里面添加更多的东西,比如自动化构建和动态模板啊(其实动态模板是个大头),然后尝试写下更多更好的交互和功能,这样你就也能拥有一个属于自己的脚手架啦,心动不如行动,还等什么呢,不要998,只要有键盘,赶紧敲吧同志们,Let's go!?

转载于:https://juejin.im/post/5c94fef7f265da60fd0c15e8

仿 vue-cli 搭建属于自己的脚手架相关推荐

  1. vue = 什么意思_记录使用@vue/cli搭建Vue3项目完整流程

    最近发现vue两大UI框架Element UI和Ant Design Vue都已经支持Vue3了,如果再不学习Vue3就落伍了,此文章记录下使用@vue/cli搭建Vue3项目完整流程. 1 安装vu ...

  2. cli vue 卸载_记录使用@vue/cli搭建Vue3项目完整流程

    最近发现vue两大UI框架Element UI和Ant Design Vue都已经支持Vue3了,如果再不学习Vue3就落伍了,此文章记录下使用@vue/cli搭建Vue3项目完整流程. 1 安装vu ...

  3. vue cli 4 多环境_Vue 笔记整理19

    19.1 vue-cli起步 19.1.1 什么是vue-cli Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供: - 通过 @vue/cli 搭建交互式的项目脚手架. - 通 ...

  4. VSCode 搭建Vue开发环境之Vue CLI

    2019独角兽企业重金招聘Python工程师标准>>> 一.简介说明 1.关于VS Code开发工具,安装和配置,更多可以参考以前文章 2.关于Vue.js,Vue是一个优秀的渐进式 ...

  5. Vue笔记——搭建脚手架并快速创建Vue项目

    现在的Vue脚手架已经升级到3.x版本,即vue-cli3. 脚手架升级之后,安装的命令发生了变化,所以这篇文章会跟大家演示新旧版本的脚手架安装过程,以及使用新旧版本脚手架创建项目的过程. 下面的安装 ...

  6. Vue 项目搭建流程和使用大全

    Vue 项目搭建流程及项目中遇到的问题 项目搭建流程 1.使用vue cli 搭建项目框架 cnpm install -g vue-cli 安装vue cli 脚手架 vue init webpack ...

  7. Vue学习笔记(九) Vue CLI

    1.简介 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,它包括三个独立的部分: CLI:一个全局安装的 npm 包,提供在终端里使用的命令 CLI 服务:一个局部安装在使用了 @v ...

  8. 安装Vue CLI项目(Vue2.0)

    一.Vue CLI脚手架(Vue2.0) Vue CLI官方文档:官方文档 1.什么是脚手架 ​ 命令行界面(英语:command-line interface,缩写:CLI)是在图形用户界面得到普及 ...

  9. Vue CLI 3 脚手架搭建

    @[TOC](Vue CLI 3 脚手架搭建) 注意 see -> cli.vuejs.org/zh/guide/ Vue CLI 的包名称由 vue-cli 改成了 @vue/cli Vue ...

  10. vue脚手架搭建配置试调地址和端口号_全栈的自我修养: 002使用@vue/cli进行vue环境搭建 (使用Vue,SpringBoot,Flask完成前后端分离)...

    全栈的自我修养: 使用@vue/cli进行vue.js环境搭建 Success, real success, is being willing to do the things that other ...

最新文章

  1. python面向对象的优点_Python面向对象编程——总结面向对象的优点
  2. 微软(MICROSOFT)试用版企业软件下载地址
  3. 【Groovy】MOP 元对象协议与元编程 ( 使用 Groovy 元编程进行函数拦截 | 使用 MetaClass 进行方法拦截 | 对象上拦截方法 | 类上拦截方法 )
  4. 详解平衡二叉树(AVL),红黑树与平衡二叉树的区别
  5. Delphi指针用法
  6. 2017-7-18-每日博客-关于Linux下的鲜为人知的10条命令.doc
  7. linux常用命令总结
  8. IC inbox toolbar button的determine逻辑
  9. javascript option 菜单图标_MacBook上神奇的Option键
  10. 江苏省计算机一级考试基础知识,江苏省计算机一级考试大纲及复习指南
  11. IIS日志分析[资源]
  12. ListCtrl使用
  13. linux中SPI相关API函数,linux spi驱动开发学习(一)-----spi子系统架构
  14. VPX,CompactPCI serial 总线
  15. Import REC修复的时候找不到被调试程序进程
  16. 有没有什么推荐的视频剪辑软件
  17. HOOK拦截消息代码
  18. Java虚拟机(三)--------GC算法和收集器
  19. Mysql数据库使用规范
  20. 多个excel表格合并成一个表格文件

热门文章

  1. PHP数组和Json之间的转换
  2. ubuntu 安装chrome浏览器
  3. [原]CentOS 6.5 上安装 MySQL 5.6
  4. Apache的多处理模块MPM
  5. Perl 数字与字符串运算符之区别
  6. (zt)OpenGL中的Alpha测试,深度测试,模板测试,裁减测试
  7. 有用的正则表达式或JS方法
  8. 【azkaban】学习azkaban的笔记以及心得
  9. 对Spring Boot还陌生吗?
  10. 02-08 Python库-yaml