仿vue-cli搭建属于自己的脚手架的方法步骤


Posted in Javascript onApril 17, 2019

脚手架是啥

从前我总觉得脚手架是个很高大上的东西,好像得牛叉:ox:一点的人才写的出来,可望而不可即。其实并不是因为困难使我们放弃,而是因为放弃才显得困难(这是个好词好句:see_no_evil:)。只要你肯花个一天半天的时间:fist:,也能写出属于你自己的脚手架。

早前脚手架这个词是从 vue-cli 这里认识的,我们通过 npm install -g vue-cli 命令全局安装脚手架后, 再执行 vue init webpack project-name 就能初始化好一个自己的项目,真是尼玛的神奇:hushed:。但你有没有想过为什么我们执行 vue init 这个命令就能有个自己的项目呢。今天,就让我们一起来揭开庐山真面目吧!

等等:hand:,扯了一堆,你好像还没说下啥是脚手架?emmm... 它就是个工具,方便我们新建项目用的,有了这个项目我们就能直接开发了。其实我们本可以用 git clone url 来新建(复制)项目,再 cuo 一点的方法就是复制粘贴整个文件夹,一样也能达到初始化的目的。脚手架的本质也是从远程下载一个模板来进行一个新项目。额。所以。。。有什么不同呢?就高大上啊:anguished:。当然不止于此啦,脚手架可是高级版的克隆,它主要是提供了交互式的命令让我们可以动态的更改模板,然后用一句命令就可以一劳永逸了(当然还是要维护的),这应该是最主要的区别吧,反正现在我是这么想的:cry:。

好了,本章的目的就是带领大家写一个简易版的脚手架 xr-cli(名字爱取啥取啥),目标是实现一个 xr init template-name project-name 这样的命令,废话少说,开始进入正题吧:rocket::rocket::rocket:。

源码地址: https://github.com/lgq627628/xr-cli

前置知识

其实一个简易版的 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,有了上面的知识储备之后,我们就正式开始撸了。

首先我们要创建一个文件夹,并取名叫 xr-cli;

在该目录下执行 npm init 命令(你应该有安装 node 吧:joy:),一路回车,就会生成一个生成 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"
}

新建一个 bin 文件夹,并在 bin 目录下新建一个无后缀名的 xr 文件,并写上:

#!/usr/bin/env node
console.log('hello');

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

仿vue-cli搭建属于自己的脚手架的方法步骤

这里要注意开头的 #!/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 运行一下,就能看到如下结果:

仿vue-cli搭建属于自己的脚手架的方法步骤

当然,你可能会觉得每次输入 node ./bin/xr

这个命令有点麻烦,没关系,我们可以在 package.json 里面写入已下内容:

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

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

仿vue-cli搭建属于自己的脚手架的方法步骤

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

仿vue-cli搭建属于自己的脚手架的方法步骤

同样的,我们修改一下 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 重新把命令绑定到全局,就像下面这样:

仿vue-cli搭建属于自己的脚手架的方法步骤

最后顺便在根目录下新建一个 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-cli搭建属于自己的脚手架的方法步骤

这里的模板名称(自己随便取)相当于 vue init webpack project-name 当中的 webpack

;模板地址要注意一下,像下面这样写就可以,这里以 github 为例:

仿vue-cli搭建属于自己的脚手架的方法步骤

xr-delete

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

#!/usr/bin/env node

const 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 看下效果:

仿vue-cli搭建属于自己的脚手架的方法步骤

xr-list

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

#!/usr/bin/env node

const tplObj = require(`${__dirname}/../template`)
console.log(tplObj)

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

仿vue-cli搭建属于自己的脚手架的方法步骤

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

xr-init

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

#!/usr/bin/env node

const 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

仿vue-cli搭建属于自己的脚手架的方法步骤

现在我们就可以在左侧的目录中看到 test 项目了,如下图:

仿vue-cli搭建属于自己的脚手架的方法步骤

至此,一个小小的脚手架就做完了。:rose::rose::rose:此处应该有鲜花和掌声:clap::clap::clap:

发布到 npm

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

  • 删除 test 文件夹,它就本地测试用的,用完就抛弃它(当然做人不能这样)
  • 在根目录下新建 README.md 文件,随便写点使用说明,假装正经一下
  • 在根目录下新建 .npmignore 文件,并写入 /node_modules ,意思就是发布的时候忽略 node_modules 文件夹,
  • 去 npm 官网注册个账号(很简单的),同时搜索一下 xr-cli 这个名字,看看有没有人用,有的话就换一个罗

仿vue-cli搭建属于自己的脚手架的方法步骤

现在让我们回到项目根目录,执行 npm login 登入 npm 账号,再执行 npm publish 发布,就像下面这样:

仿vue-cli搭建属于自己的脚手架的方法步骤

没错,就是这样两个简单的命令,我们就发布成功啦,真是可喜可贺:beer::beer::beer:。大概过一分钟左右(反正挺快的),我们再去 npm 官网搜下 xr-cli,就可以看到自己的脚手架啦,哈哈哈哈,贼开心:+1::+1::+1:。

仿vue-cli搭建属于自己的脚手架的方法步骤

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

小试牛刀

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

仿vue-cli搭建属于自己的脚手架的方法步骤

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

仿vue-cli搭建属于自己的脚手架的方法步骤仿vue-cli搭建属于自己的脚手架的方法步骤

:six::six::six:,大赞无疆,大。。赞。。。无疆!!!

结语

上面的操作只要你熟悉了几遍之后,再去看看vue-cli 的源码结构,你就会有种拨开云雾见月明的感觉(它只是比我们这个脚手架完善很多很多很多而已:sob::sob::sob:)。

当然了,这只是渣渣版本。你可以往里面添加更多的东西,比如自动化构建和动态模板啊(其实动态模板是个大头),然后尝试写下更多更好的交互和功能,这样你就也能拥有一个属于自己的脚手架啦,心动不如行动,还等什么呢,不要998,只要有键盘,赶紧敲吧同志们,Let's go!:rainbow:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
srcElement表格样式
Sep 03 Javascript
JavaScript 事件记录使用说明
Oct 20 Javascript
深入理解JavaScript 闭包究竟是什么
Apr 12 Javascript
Microsfot .NET Framework4.0框架 安装失败的解决方法
Aug 14 Javascript
返回页面顶部top按钮通过锚点实现(自写)
Aug 30 Javascript
使用POST方式弹出窗口的两种方法示例介绍
Jan 29 Javascript
兼容各大浏览器的JavaScript阻止事件冒泡代码
Jul 09 Javascript
JavaScript基于ajax编辑信息用法实例
Jul 15 Javascript
js如何判断输入字符串长度
Dec 16 Javascript
JavaScript从0开始构思表情插件
Jul 26 Javascript
详解React-Router中Url参数改变页面不刷新的解决办法
May 08 Javascript
vue组件库的在线主题编辑器的实现思路
Apr 03 Javascript
一篇文章,教你学会Vue CLI 插件开发
Apr 17 #Javascript
ES6知识点整理之函数数组参数的默认值及其解构应用示例
Apr 17 #Javascript
mpvue性能优化实战技巧(小结)
Apr 17 #Javascript
node.js监听文件变化的实现方法
Apr 17 #Javascript
vue中格式化时间过滤器代码实例
Apr 17 #Javascript
postman自定义函数实现 时间函数的思路详解
Apr 17 #Javascript
vue指令之表单控件绑定v-model v-model与v-bind结合使用
Apr 17 #Javascript
You might like
PHP 高手之路(一)
2006/10/09 PHP
PHP中10个不常见却非常有用的函数
2010/03/21 PHP
.htaccess文件保护实例讲解
2011/02/06 PHP
如何在symfony中导出为CSV文件中的数据
2011/10/06 PHP
php selectradio和checkbox默认选择的实现方法详解
2013/06/29 PHP
js预载入和JavaScript Image()对象使用介绍
2011/08/28 Javascript
js调用图片隐藏&amp;显示实现代码
2013/09/13 Javascript
用C/C++来实现 Node.js 的模块(二)
2014/09/24 Javascript
jquery实现通用的内容渐显Tab选项卡效果
2015/09/07 Javascript
通过Jquery.cookie.js实现展示浏览网页的历史记录超管用
2015/10/23 Javascript
js剪切板应用clipboardData实例解析
2016/05/29 Javascript
jQuery 操作input中radio的技巧
2016/07/18 Javascript
JavaScript中子对象访问父对象的方式详解
2016/09/01 Javascript
Vue.js系列之项目结构说明(2)
2017/01/03 Javascript
jQuery动态追加页面数据以及事件委托详解
2017/05/06 jQuery
javascript 面向对象实战思想分享
2017/09/07 Javascript
import与export在node.js中的使用详解
2017/09/28 Javascript
angularJS开发注意事项
2018/05/26 Javascript
jQuery实现列表的增加和删除功能
2018/06/14 jQuery
原生js实现移动小球(碰撞检测)
2020/12/17 Javascript
[02:17]DOTA2亚洲邀请赛 RAVE战队出场宣传片
2015/02/07 DOTA
Windows下python2.7.8安装图文教程
2016/05/26 Python
利用python爬取散文网的文章实例教程
2017/06/18 Python
Python turtle画图库&amp;&amp;画姓名实例
2020/01/19 Python
记一次python 爬虫爬取深圳租房信息的过程及遇到的问题
2020/11/24 Python
html5 Canvas画图教程(7)—canvas里画曲线之quadraticCurveTo方法
2013/01/09 HTML / CSS
Crocs卡骆驰洞洞鞋日本官方网站:Crocs日本
2016/08/25 全球购物
Timberland澳大利亚官网:全球领先的户外品牌
2019/12/10 全球购物
房地产开发计划书
2014/01/10 职场文书
企业管理培训感言
2014/01/27 职场文书
一年级学生评语大全
2014/04/21 职场文书
爱国卫生月活动总结范文
2014/04/25 职场文书
公司管理建议书
2015/09/14 职场文书
如何在pycharm中快捷安装pip命令(如pygame)
2021/05/31 Python
Java Optional<Foo>转换成List<Bar>的实例方法
2021/06/20 Java/Android
分布式Redis Cluster集群搭建与Redis基本用法
2022/02/24 Redis