摘要:本文主要是介绍开发一个简单的脚手架,了解开发的基本流程最终通过链接到全局包。完成之后,就可以把脚手架发布到上面,通过进行全局安装,就可以在自己本机上执行来初始化项目,这样便完成了一个简单的脚手架工具了。
脚手架,这个名词对于作为前端的我们来说,也许并不陌生吧,像vue-cli,react-native-cli等,全局安装后,只需要在命令行中敲入一个简单的命令,便可帮我们快速的生成一个初始项目,如vue init webpack projectName,即可生成一个初始的vue项目。
本文主要是介绍开发一个简单的脚手架,了解开发的基本流程、最终通过npm link链接到全局包。
1、减少时间,不必从零开始搭建初始项目,提高开发效率。
2、便于多人协作。
3、项目更新同步方便,只需要更新代码库中项目模板,即可下载最新的项目。
在开始项目前,先简单介绍下node相关的一些基础知识,通过npm init初始化一个node项目时,会生成一个package.json的配置文件,包括项目名称、版本、作者、依赖等相关信息,主要说一下其中的bin字段。
很多包都有一个或多个可执行的文件,希望放在PATH中,(实际上,就是这个功能让npm可执行的)。
当你要用这个功能时,需要给package.json中的bin字段添加一个命令名,并指向需要执行的文件(即后文的入口文件)。初始化的时候npm会将他链接到prefix/bin(全局初始化)或者./node_modules/.bin/(本地初始化)。
比如,npm有:
{ "bin" : { "npm" : "./npm-cli.js" } }
所以,当你初始化npm,它会创建一个符号链接到npm-cli.js脚本到/usr/local/bin/npm。
如果你只有一个可执行文件,并且名字和包名一样。那么你可以只用一个字符串,比如:
{ "name": "my-program" , "version": "1.2.5" , "bin": "./path/to/program" }
结果和这个一样:
{ "name": "my-program" , "version": "1.2.5" , "bin" : { "my-program" : "./path/to/program" } }
想要了解package.json更多的详细配置,请参考这篇文章
思路要开发脚手架,首先要理清思路,脚手架是如何工作的?我们可以借鉴 vue-cli 的基本思路。vue-cli 是将项目模板放在 git 上,运行的时候再根据用户交互下载不同的模板,经过模板引擎渲染出来,生成项目。这样将模板和脚手架分离,就可以各自维护,即使模板有变动,只需要上传最新的模板即可,而不需要用户去更新脚手架就可以生成最新的项目。那么就可以按照这个思路来进行开发了。
初始化项目新建一个文件夹,打开命令行工具,通过npm init 进行项目初始化,会在项目根目录下生成package.json文件。
npm init安装依赖
npm install commander download-git-repo inquirer handlebars ora chalk log-symbols shelljs -S
除此之外,还使用了nodejs的几个内置模块:fs、path、child_process
commander.js:可以自动的解析命令和参数,用于处理用户输入的命令。
download-git-repo:下载并提取 git 仓库,用于下载项目模板。
Inquirer.js:通用的命令行用户界面集合,用于和用户进行交互。
handlebars.js:模板引擎,将用户提交的信息动态填充到文件中。
ora:下载过程久的话,可以用于显示下载中的动画效果。
chalk:可以给终端的字体加上颜色。
log-symbols:可以在终端上显示出 √ 或 × 等的图标。
fs:node内置的文件处理模块。
path:node内置的路径处理、解析模块。
child_process:node中创建子进程模块。
配置入口文件1、在package.json文件中增加bin字段,暂且先命名为okcli吧。
"bin": { "okcli": "./index.js" }
2、在项目根目录新建index.js文件,在index.js文件顶部添加以下代码:
#!/usr/bin/env node
即可进行开发。
在这里,简单介绍下这行代码含义。
!/usr/bin/node是告诉操作系统执行这个脚本的时候,调用/usr/bin下的node解释器;
!/usr/bin/env node这种用法是为了防止操作系统用户没有将node装在默认的/usr/bin路径里。当系统看到这一行的时候,首先会到env设置里查找node的安装路径,再调用对应路径下的解释器程序完成操作。
!/usr/bin/node相当于写死了node路径;
!/usr/bin/env node会去环境设置寻找node目录,推荐这种写法
const program = require("commander"); const inquirer = require("inquirer"); const symbols = require("log-symbols"); const download = require("download-git-repo"); const handlebars = require("handlebars"); const chalk = require("chalk"); const ora = require("ora"); const shell = require("shelljs"); const child_process = require("child_process"); const fs = require("fs"); const path = require("path"); program.version("1.0.0", "-v, --version") .command("init") .action((name) => { console.log(name); }); program.parse(process.argv);
调用program.version("1.0.0", "-v, --version")会将-v和--version添加到命令行中,调用时可通过带上该参数获取该脚手架的版本号(命令 -v/--version),调用comand("init
action是执行command命令时发生的回调,参数为命令行中输入的name,即init
现在可以通过调用node index.js init test,可以看到控制台中已经打印了输入的项目名,也就是test。
其中:program.parse(process.argv)解析命令行中的参数,解析出name,并传入action回调。
通过download-git-repo或者直接使用shelljs或者child_process直接运行命令进行下载模块(个人选择的是第三种)。
download-git-repo 支持从 Github、Gitlab 和 Bitbucket 下载仓库,各自的具体用法可以参考官方文档。
download("https://github.com/jefferyE/webpack-configuration-for-vue", name, {clone: true}, (err) => { if(err){ // spinner.fail(); console.log(symbols.error, chalk.red(err)); }else{ // spinner.fail(); } })
或者
if (shell.exec("git clone https://github.com/jefferyE/webpack-configuration-for-vue").code == 0) { // spinner.succeed(); } else { // spinner.fail(); console.log(symbols.error, chalk.red("模板下载失败")) }
或者
child_process.exec("git clone https://github.com/jefferyE/webpack-configuration-for-vue", function(err, stdout, stderr) { if (err) { // spinner.fail(); console.log(symbols.error, chalk.red("模板下载失败")) } else { // spinner.succeed(); } })
其中:download() 第一个参数就是仓库地址,但是有一点点不一样。实际的仓库地址是 https://github.com/jefferyE/w... ,可以看到端口号后面的 ‘/‘ 在参数中要写成 ‘:’,#master 代表的就是分支名,不同的模板可以放在不同的分支中,更改分支便可以实现下载不同的模板文件了。第二个参数是路径,上面我们直接在当前路径下创建一个 name 的文件夹存放模板,也可以使用二级目录比如 test/${name}
交互命令行命令行交互功能可以在用户执行 init 命令后,向用户提出问题,接收用户的输入并作出相应的处理。这里使用 inquirer.js 来实现,也可以自己使用node内置的readline模块。
具体可以参考我的这篇文章
inquirer.prompt([ { type: "input", name: "author", message: "请输入作者名称" } ]).then((answers) => { console.log(answers.author); })
通过这里例子可以看出,问题就放在 prompt() 中,问题的类型为 input 就是输入类型,name 就是作为答案对象中的 key,message 就是问题了,用户输入的答案就在 answers 中,使用起来就是这么简单。更多的参数设置可以参考官方文档。
渲染package模板这里用 handlebars 的语法对 HTML5/H5Template 仓库的模板中的 package.json 文件做一些修改。并在下载模板完成之后将用户输入的答案渲染到 package.json 中。
{ "name": "{{name}}", "version": "1.0.0", "description": "{{description}}", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "author": "{{author}}", "license": "ISC" }
注意:由于我的项目没有更改package模板,因此通过在下载模板后,解析时进行动态修改的。
除此之外,通过ora、chalk模块也进行了一些视觉美化。具体请参考完整代码。
完整代码如下:
#!/usr/bin/env node const program = require("commander"); const chalk = require("chalk"); const ora = require("ora"); const fs = require("fs"); const inquirer = require("inquirer"); const shell = require("shelljs"); const symbols = require("log-symbols"); const download = require("download-git-repo"); const child_process = require("child_process"); const handlebars = require("handlebars"); const path = require("path"); program.version("1.0.0", "-v, --version"). command("init"). action(name => { console.log(name); if (!fs.existsSync(name)) { console.log("正在创建项目..."); inquirer.prompt([ { name: "description", message: "请输入项目描述" }, { name: "author", message: "请输入作者名称" } ]).then(answers => { console.log(answers) const spinner = ora("正在向下载模板... "); spinner.start(); child_process.exec("git clone https://github.com/jefferyE/webpack-configuration-for-vue", function(err, stdout, stderr) { if (err) { spinner.fail(); console.log(symbols.error, chalk.red("模板下载失败")) } else { spinner.succeed(); shell.mv(__dirname + "/webpack-configuration-for-vue", __dirname + "/" + name) const filename = `${name}/package.json`; const meta = { name, description: answers.description, author: answers.author } if (fs.existsSync(filename)) { const content = fs.readFileSync(filename).toString(); let dt = JSON.parse(content); dt.name = "{{name}}"; dt.description = "{{description}}" const result = handlebars.compile(JSON.stringify(dt, null, 2))(meta); fs.writeFileSync(filename, result); console.log(symbols.success, chalk.green("项目初始化完成")); } else { console.log(symbols.error, chalk.red("package不存在")) } } }) }) } else { console.log(symbols.error, chalk.red("项目已存在")); } }) program.parse(process.argv);
其中:
1、在用户输入答案之后,开始下载模板,这时候使用 ora 来提示用户正在下载中。
2、然后通过 chalk 来为打印信息加上样式,比如成功信息为绿色,失败信息为红色,这样子会让用户更加容易分辨,同时也让终端的显示更加的好看。
3、除了给打印信息加上颜色之外,还可以使用 log-symbols 在信息前面加上 √ 或 × 等的图标。
完成之后,就可以把脚手架发布到 npm 上面,通过 -g 进行全局安装,就可以在自己本机上执行 okcli init [name] 来初始化项目,这样便完成了一个简单的脚手架工具了。
链接全局命令在根目录运行 npm link命令,将包链接到全局环境。
npm link命令可以将一个任意位置的npm包链接到全局执行环境,从而在任意位置使用命令行都可以直接运行该npm包。
具体请参考这篇文章
感谢:
参考文章 文章1
文章2
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/95415.html
摘要:更多资源请文章转自月份前端资源分享的作用数组元素随机化排序算法实现学习笔记数组随机排序个变态题解析上个变态题解析下中的数字前端开发笔记本过目不忘正则表达式聊一聊前端存储那些事儿一键分享到各种写给刚入门的前端工程师的前后端交互指南物联网世界的 更多资源请Star:https://github.com/maidishike... 文章转自:https://github.com/jsfr...
摘要:目前网上关于插件开发的文章少得可怜,下面分享最近的经历,如何快速上手开发一个插件。第六步调试插件在打开的网页中可以看到工具栏中实现了插件。 TinyMCE是一个非常优秀的轻量级的所见即所得HTML编辑器,历史悠久,开源,在github的start也非常高的,且长期保持更新。TinyMCE的官方插件不少,基本能满足日常需求,但是有时候我们还需要一些结合业务的功能。这时官方插件无法满足,就...
摘要:下面来就来讲讲脚手架的安装吧。如何安装安装只需要在终端下输入下面一条命令自带不需要安装安装完成后使用即可。原因是的问题,要求版本是。想用安装,就必须用安装一下。 前言 Vue作为前端三大框架(Angular,React,Vue)之一,号称是最简单,最容易上手的框架,同时也是行内的大趋势,还可以用来开发最火的小程序。具有开发快,双向数据流等特点,有些人认为Vue是Angular和Reac...
摘要:下面来就来讲讲脚手架的安装吧。如何安装安装只需要在终端下输入下面一条命令自带不需要安装安装完成后使用即可。原因是的问题,要求版本是。想用安装,就必须用安装一下。 前言 Vue作为前端三大框架(Angular,React,Vue)之一,号称是最简单,最容易上手的框架,同时也是行内的大趋势,还可以用来开发最火的小程序。具有开发快,双向数据流等特点,有些人认为Vue是Angular和Reac...
阅读 1074·2021-11-16 11:45
阅读 2708·2021-09-27 13:59
阅读 1314·2021-08-31 09:38
阅读 3142·2019-08-30 15:52
阅读 1315·2019-08-29 13:46
阅读 2085·2019-08-29 11:23
阅读 1631·2019-08-26 13:47
阅读 2476·2019-08-26 11:54