为什么需要脚手架?怎么搭建脚手架?下面本篇文章介绍一下node构建脚手架的步骤,希望对大家有所帮助!
1 为什么需要脚手架
- 根据交互动态生成项目结构和配置文件等。
- 用户通过命令交互的方式下载不同的模版
- 经过模版引擎渲染定制项目模版
- 模版变动,只需更新模版即可,不需要用户更新脚手架 【相关教程推荐:nodejs视频教程、编程教学】
2 构建步骤
新建
mycli
文件夹(可自定义文件名),下方新建bin
文件,bin
文件新建index.js
,这个index.js
就是入口文件,index.js
文件头部加入#!/usr/bin/env node
代码生成
package.json
文件,此时会有个bin配置对象,key值即为全局脚手架名称,value是入口文件bin文件的index.js
路径。
npm init -y
npm install
将脚手架全局命令链接到全局,终端打印
mycli
即链接成功。//命令可以将一个任意位置的npm包链接到全局执行环境,从而在任意位置使用命令行都可以直接运行该npm包。 npm link
安装依赖
npm install commander inquirer@8.2.5 download-git-repo chalk@4.1.2 ora@5.4.1 figlet handlebars
commander
:命令行工具,有了它我们就可以读取命令行命令,知道用户想要做什么了inquirer
: 交互式命令行工具,给用户提供一个漂亮的界面和提出问题流的方式download-git-repo
:下载远程模板工具,负责下载远程仓库的模板项目chalk
:颜色插件,用来修改命令行输出样式,通过颜色区分 info、error 日志,清晰直观ora
:用于显示加载中的效果,类似于前端页面的 loading 效果,像下载模板这种耗时的操作,有了 loading 效果可以提示用户正在进行中,请耐心等待figlet
:镂空字体样式
注意:下方代码都放在bin文件index.js
进行调试
2.1 commander.js概述
commander.js
是一个工具,用来构建node的命令行程序,使得能够使用自定义指令在全局命令行运行node脚本。本来我们只能在脚本所在文件的根目录里通过node xxx.js
运行脚本,通过commander构建命令行程序后,就能在任意一个目录里,比如桌面,比如用户目录,直接输入自定义的那个指令,就能直接运行脚本,更加简便。
#!/usr/bin/env node
//就是解决了不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件。
//node.js内置了对命令行操作的支持,在 package.json 中的 bin 字段可以定义命令名和关联的执行文件。
const program = require("commander")
program.version('1.1.0')
function getFramwork (val) {
console.log(val);
}
const myhelp = function (program) {
program.option('-f --framwork <framwork>', '设置框架', getFramwork)
}
const createProgress = function (program) {
program.command('create <progress> [other...]')
.alias('crt')
.description('创建项目')
.action((progress, arg) => {
console.log(progress, arg);
})
}
myhelp(program);
createProgress(program);
program.parse(process.argv)
// 补充
.parse()
// 作用就是解析,参数就是要解析的字符串,一般使用时参数就是用process.argv,就是用户输入参数
执行全局命令mycli
即可输出所有命令~~
2.2 download-git-repo
download
(repository, destination, options, callback)repository
:下载地址destination
:下载路径options
:配置项 {clone:true}callback
:下载后的回调
#!/usr/bin/env node
const download = require('download-git-repo');
download('direct:https://gitlab.com/flippidippi/download-git-repo-fixture.git', "xxx", { clone: true }, (err) => {
console.log(err ? 'Error' : 'Success')
})
执行mycli
即可看到文件下生成一个xxx
文件
2.3 Inquirer(命令交互)
inquirer 是一个常用的交互式终端用户界面集合。 简单来说 inquirer 是可以让我们很方便的做各种终端交互行为的一个库。
inquirer 主要提供了三个方法方便我们注册问题
- prompt(questions) => promise该方法就是 终端交互的核心方法,运行 prompt 方法即告诉终端启动 交互式命令界面。 - prompt 方法需要传入一个 questions 数组, questions 数组包含对象形式的各个 question. question 的具体结构字段含义在后文介绍。 - prompt 方法的返回值是一个 promise 对象,promise.then 接收的返回值是 answers 对象,answers 对象包含前面所有问题回答的数据结果。
#!/usr/bin/env node
const inquirer = require("inquirer")
function getUsername() {
return inquirer
.prompt([
{
type: "input",
name: "progress",
message: "请输入项目名称",
default: "progress",
filter(input) {
return input.trim()
},
validate(input) {
return input.length > 0
},
},
])
.then((answer) => {
console.log(answer)
})
}
function getFramework() {
return inquirer
.prompt([
{
type: "list",
name: "framework",
choices: [
"express",
new inquirer.Separator(),
"koa",
new inquirer.Separator(),
"egg",
],
message: "请选择你所使用的框架",
},
])
.then((answer) => {
console.log(answer)
})
}
function getSelect() {
return inquirer
.prompt([
{
type: "checkbox",
name: "userndasde",
choices: [
{ name: "pr", disabled: true },
{ name: "oa", checked: true },
"gg",
],
message: "需要的验证格式",
// default: ["oa"],
},
])
.then((answer) => {
console.log(answer)
})
}
async function init() {
await getSelect()
await getUsername()
await getFramework()
}
init()
2.4 ora and chalk(美化)
在用户输入答案之后,开始下载模板,这时候使用 ora
来提示用户正在下载中。
注意:注意版本不同引入方式不同,这里用ora
(版本5.4.1) ,chalk
(版本4.1.2)
const ora = require("ora")
const chalk = require("chalk")
const spinner = ora("Loading unicorns").start()
spinner.text = chalk.blue("下载中~~~~~~")
setTimeout(() => {
spinner.succeed(chalk.red("下载成功!"))
spinner.fail("下载失败!")
spinner.warn("警告!")
}, 2000)
2.5 figlet(镂空文字)
镂空文字调试器地址:地址
figle
t旨在完全实现JavaScript中的FIGfont
规范。它可以在浏览器和Node.js
中工作。
用法
figlet.text( description,{options},callback(err,data){}) 这个是异步的会被
参数
description:需要格式化的字符串
options:参数配置
Font
:字体,Default value:Standard
;horizontalLayout
:布局,Default value:default
; Values:{default
,full
,fitted
};verticalLayout
:垂直布局, Default value:default
; Values:{defalut
,full
,fitted
,controlled smushing
,universal smushing
};Width
:宽度;whitespaceBreak
:换行(Boolean); Default value:false
callback(err,data):回调
const figlet = require("figlet")
const chalk = require("chalk")
//简单函数
function handleAsync(params) {
const JAVASCRIPT = figlet.textSync(
"NODEJS",
{
font: "big",
horizontalLayout: "fitted",
verticalLayout: "controlled smushing",
width: 600,
whitespaceBreak: true,
},
function (err, data) {
if (err) {
console.log("Something went wrong...")
console.dir(err)
return
}
console.log(data)
}
)
console.log(chalk.blue.bold(JAVASCRIPT))
}
handleAsync()
总结
创建一个完整的脚手架
目录结构:
bin/index.js
#!/usr/bin/env node
console.log("adas");
require("../lib/commander/index.js")
lib/commonder/index.js
const program = require("commander")
const init = require('../inquirer/index');
const downloadFun = require("../core/download.js");
program.version('1.1.0')
function getFramwork (val) {
console.log(val);
}
const myhelp = function (program) {
program.option('-f --framwork <framork> [other...]', '设置框架', getFramwork)
}
const createProgress = function (program) {
program.command('create <progress> [other...]')
.alias('crt')
.description('创建项目')
.action((progress, arg) => {
init();
})
}
const downloadUrl = function (program) {
program.command('download <url> [...other]')
.description('下载内容')
.action((url, ...args) => {
console.log(args);
downloadFun(url, args[1].args[1])
})
}
myhelp(program);
downloadUrl(program);
createProgress(program)
program.parse(process.argv)
lib/core/action.js
(package.json重写)
const fs = require('fs');
const path = require("path");
const handlebars = require("handlebars");
function modifyPackageJson (options) {
let downloadPath = options.projectName;
const packagePath = path.join(downloadPath, 'package.json');
console.log(packagePath, "packagePath");
//判断是否存在package.json文件
if (fs.existsSync(packagePath)) {
let content = fs.readFileSync(packagePath).toString();
//判断是否选择了eslint
if (options.isIslint) {
let targetContent = JSON.parse(content);
content = JSON.stringify(targetContent);
targetContent.dependencies.eslint = "^1.0.0";
console.log("content", content);
}
//写入模板
const template = handlebars.compile(content);
const param = { name: options.projectName };
const result = template(param);
//重新写入package.json文件
fs.writeFileSync(packagePath, result);
console.log('modify package.json complate');
} else {
throw new Error('no package.json');
}
}
module.exports = modifyPackageJson
lib/core/download.js
const download = require('download-git-repo');
const ora = require("ora");
const chalk = require("chalk");
const figlet = require("figlet");
const modifyPackageJson = require("./action")
function handleAsync (params) {
const JAVASCRIPT = figlet.textSync('JAVASCRIPT', {
font: 'big',
horizontalLayout: 'fitted',
verticalLayout: 'controlled smushing',
width: 600,
whitespaceBreak: true
}, function (err, data) {
if (err) {
console.log('Something went wrong...');
console.dir(err);
return;
}
console.log(data);
});
console.log(chalk.blue.bold(JAVASCRIPT));
}
const downloadFun = (url, option) => {
const spinner = ora("Loading unicorns").start()
spinner.text = chalk.blue("下载中");
download(url, option.projectName, { clone: true }, function (err) {
if (err) {
spinner.fail("下载失败!");
handleAsync()
} else {
spinner.succeed(chalk.red("下载成功!"))
console.log(chalk.blue(`cd ${option.projectName}`))
console.log(chalk.red("npm install"))
console.log(chalk.yellow(`npm run dev`))
modifyPackageJson(option)
handleAsync()
}
})
}
module.exports = downloadFun;
inquire/index.js
注意frameworkConfig
写自己的gitlab
仓库地址
const inquirer = require("inquirer");
const downloadFun = require("../core/download.js");
const frameworkConfig = {
front: "https://gitlab.com/flippidippi/download-git-repo-fixture.git",
manager: "https://gitlab.com/flippidippi/download-git-repo-fixture.git"
}
const config = {};
function getFramework () {
return inquirer.prompt([
{
type: 'list',
name: 'framework',
choices: ["front", "manager"],
message: "请选择你所使用的框架"
}
]).then((answer) => {
return answer.framework;
})
}
function getProjectName () {
return inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: '项目名称',
filter (input) {
return input.trim();
},
}
]).then((answer) => {
console.log(answer, "FDsfs");
return answer.projectName;
})
}
function getIsEslint () {
return inquirer.prompt([
{
type: 'confirm',
name: 'isIslint',
message: '是否使用eslint校验格式?'
}
]).then((answer) => {
return answer.isIslint;
})
}
async function init () {
config.projectName = await getProjectName();
config.framework = await getFramework();
config.isIslint = await getIsEslint();
let url = config.framework == "front" ? frameworkConfig.front : frameworkConfig.manager;
downloadFun("direct:" + url, config);
}
module.exports = init;
以上就是为什么需要脚手架?详解node构建脚手架的步骤的详细内容,更多请关注编程网其它相关文章!