什么是脚手架,
百度百科
的解释是:脚手架是为了保证各施工过程顺利进行而搭设的工作平台。在前端工作中,我把它理解成一个工具集合,在这个工具集合的帮助下,我们可以专注自己的业务代码开发,而不用再花时间去处理一些经常要做的重复工作。
小猪配淇手上纹,我们都是打工人🤣。
使用三大金刚做前端开发的小伙伴想必都使用过各家的脚手架工具吧,不知道有没有手痒痒的想试着写一下的,啦啦啦,反正我看着作者的文章是手痒啦。之前一直想着学习
NodeJS
脚手架开发,可是每次都被自己的懒癌打败,最近终于有时(hua)间(shui),于是花了点时间写了个小
DEMO
,也就是文章里面的记事本工具,这个工具并没有涉及到
git
下载(
可以参考掘金其他文章
),至于如何发布npm包,可以看我的另外一篇文章
🍖手把手教你发布npm包
,本文更多的是展示一个脚手架该具备的工作流程以及常用工具的用途。废话不多说,我们开始吧!
代码注释写的比较清楚,期望每一位小伙伴都能看懂。当然,作者本身也只是一名初级前端,其中要是有啥讲的不对的,也请大佬们指出,拜谢🙈,当然,要是觉得写得还行,不妨给个三连哟。
初始化环境
2、项目依赖
NodeJS:
v12.18.3
3、工具库
chalk
:
修改输入输出的文本颜色,添加成功失败图标
commander
:
命令行辅助工具库,可以添加命令提示,也可以解析参数
inquirer
:
命令行交互,输入,选项等
minimis
":
命令行参数解析,可以用commander替代
ora
:
终端旋转器,美化进度
prettier:
代码格式化
dayjs:
时间格式化工具
4、项目结构
需要关注的文件
notepad-cli
├── bin
│ └── notepad-cli.js
├── doc
│ └── 1610262571511.txt
├── lib
│ ├── commands
│ │ ├── add.js
│ │ ├── del.js
│ │ ├── exit.js
│ │ ├── find.js
│ │ ├── list.js
│ │ └── open.js
│ ├── config
│ │ └── index.js
│ ├── service.js
│ └── utils
│ ├── file.js
│ └── utils.js
├── package.json
git clone https://github.com/taosiqi/notepad-cli.git
cd notepad-cli
npm link //链接到全局库
notepad-cli open
npm install -g notepad-cli 安装体验
1、创建项目
1、mkdir notepad-cli && cd notepad-cli
2、依据上文目录结构创建目录
3、安装上文提到的工具库
npm init
创建 package.json
,大概会得到一个这样的文件
"name": "notepad-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"keywords": [],
"author": "",
"license": "ISC"
添加 bin
命令
"bin": {
"notepad-cli": "bin/notepad-cli.js"
添加各项依赖之后长这样
"name": "notepad-cli",
"version": "1.0.0",
"description": "node笔记本",
"main": "lib/service.js",
"homepage": "https://github.com/taosiqi/notepad-cli",
"author": {
"name": "siqiJson"
"keywords": [
"notepad",
"cli"
"bin": {
"notepad-cli": "bin/notepad-cli.js"
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/taosiqi/notepad-cli"
"dependencies": {
"chalk": "^4.1.0",
"commander": "^6.2.1",
"dayjs": "^1.9.8",
"inquirer": "^7.3.3",
"minimist": "^1.2.5",
"ora": "^5.1.0",
"prettier": "^2.1.1"
2、简单实例
创建/bin/notepad-cli.js
文件
#!/usr/bin/env node
console.log('nodejs脚手架')
项目根目录键入npm link
,它可以把指定的执行文件链接到全局(相当于npm -g install xxx
),这样我们就可以全局使用刚刚搭建的脚手架工具啦。
npm link
报错可以先卸载脚手架npm -g uninstall notepad-cli
执行notepad-cli
将会执行/bin/notepad-cli.js
文件
F:\notepad-cli>notepad-cli
nodejs脚手架
下面基本就是代码加注释了,小伙伴们加油!
1、入口文件notepad-cli
#!/usr/bin/env node
const Service = require('../lib/service')
const service = new Service()
const rawArgv = process.argv.slice(2)
console.log(rawArgv)
const args = require('minimist')(rawArgv)
console.log(args)
const command = args._[0]
service.run(command, args, rawArgv)
2、命令入口service
执行service.run(command, args, rawArgv)
将会执行/lib/service.js
文件
const program = require('commander')
const { packageInfo, open } = require('./config')
const {readDoc}=require('./utils/file')
module.exports = class Service {
constructor() {
readDoc()
setupDefaultCommands()
registerCommands()
run(_id, _args = {}, rawArgv = []) {
program.parse(rawArgv, { from: 'user' })
const setupDefaultCommands = () => {
program.version(packageInfo.version, '-v, --version', '输出当前版本号')
program.helpOption('-h, --help', '获取帮助')
program.addHelpCommand(false)
const registerCommands = () => {
return program.command('open').description('打开记事本').alias('o').action(() => {
open.apply(program.args)
3、打开记事本命令 open
/lib/service.js
文件注册的自定义命令 open
,使用 notepad-cli open
执行。
const inquirer = require('inquirer')
const { readKey } = require('../utils/file')
async function init(commands) {
const promptList = {
type: 'list',
message: '欢迎使用思淇记事本,请选择需要操作的功能:',
name: 'tools',
choices: () => {
let promptList = []
for (const commandsKey in commands) {
promptList.push({
name: commands[commandsKey].description,
value: commandsKey
return promptList
return promptList
module.exports = {
description: '打开记事本',
apply: async () => {
let commands = await readKey()
let promptList = await init(commands)
await inquirer.prompt(promptList).then(async (answers) => {
await commands[answers.tools].apply()
const command = require(`${__dirname}/open.js`)
await command.apply()
4、增加记事本 add
const inquirer = require('inquirer')
const { succeed } = require('../utils/utils')
const { add } = require('../utils/file')
const dayjs = require('dayjs')
const promptList = [
type: 'input',
message: '请输入标题:',
name: 'title',
validate: (val) => {
return val !== ''
type: 'input',
message: '请输入内容:',
name: 'content',
validate: (val) => {
return val !== ''
module.exports = {
description: '添加记事',
apply: async () => {
await inquirer.prompt(promptList).then(async (answers) => {
const timestamp = new Date().getTime()
const data = {
...answers,
time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
timestamp: timestamp
add(data, timestamp)
succeed('添加成功')
return Promise.resolve()
5、删除记事本 del
const { del } = require('../utils/file')
const { succeed, info } = require('../utils/utils')
const inquirer = require('inquirer')
function init() {
const promptList = {
type: 'input',
message: '请输入需要删除的记事序列号:',
name: 'del',
validate: (val) => {
let value = parseInt(val)
return (
typeof value === 'number' &&
!isNaN(value) &&
value > 0 &&
value < articleList.length + 1
return promptList
function initLog() {
let log = ''
articleList.forEach((item, index) => {
log += `${index + 1}:${item.title} \n`
return log
module.exports = {
description: '删除记事',
apply: async () => {
let promptList = await init()
let log = await initLog()
info(log)
await inquirer.prompt(promptList).then(async (answers) => {
await del(answers['del'])
succeed('删除成功')
return Promise.resolve()
6、退出记事本 exit
const { succeed } = require('../utils/utils')
module.exports = {
description: '退出记事',
apply: async (env) => {
succeed('退出成功')
process.exit(1)
7、查找记事本 find
const inquirer = require('inquirer')
const { info, underline } = require('../utils/utils')
function init() {
const promptList = {
type: 'input',
message: '请输入记事文章关键词:',
name: 'find',
validate: (val) => {
return val != ''
return promptList
function filtrate(keyWord) {
const filtrateData = {
type: 'list',
message: '记事列表:',
name: 'tools',
choices: () => {
let promptList = []
const data = articleList.filter((item, index) => {
return (
item.title.indexOf(keyWord) != -1 ||
item.content.indexOf(keyWord) != -1
data.forEach((item, index) => {
promptList.push({
name: item.title + ' ' + item.time,
value: index
return promptList
return filtrateData
module.exports = {
description: '查找记事',
apply: async () => {
let promptList = await init()
let keyWord = ''
await inquirer.prompt(promptList).then((answers) => {
keyWord = answers.find
let filtrateData = await filtrate(keyWord)
if (filtrateData.choices().length !== 0) {
await inquirer.prompt(filtrateData).then((answers) => {
console.log(underline(articleList[answers.tools].content))
} else {
info('未搜索到内容')
8、记事本列表 list
const inquirer = require('inquirer')
const { info, underline } = require('../utils/utils')
function init() {
const promptList = {
type: 'list',
message: '记事列表:',
name: 'tools',
choices: () => {
let promptList = []
articleList.forEach((item, index) => {
promptList.push({
name: item.title + ' ' + item.time,
value: index
return promptList
return promptList
module.exports = {
description: '记事列表',
apply: async () => {
let promptList = await init()
if (promptList.choices().length !== 0) {
await inquirer.prompt(promptList).then((answers) => {
console.log(underline(articleList[answers.tools].content))
} else {
info('未搜索到内容')
9、配置文件 config/index
module.exports = {
packageInfo: require('../../package.json'),
open: require('../commands/open')
10、文件操作 file
const fs = require('fs')
const path = require('path')
const docPath = path.join(__dirname, `../../doc`)
const commandPath = path.join(__dirname, `../commands`)
module.exports = {
add: (data, timestamp) => {
fs.writeFile(
`${docPath}/${timestamp}.txt`,
JSON.stringify(data),
(error) => {
if (error) {
console.error('写入失败了')
} else {
articleList.push(data)
del: (name) => {
fs.unlinkSync(`${docPath}/${articleList[name - 1].timestamp}.txt`)
articleList.splice(name - 1, 1)
readKey: () => {
let commands = {}
fs.readdirSync(commandPath).forEach((paths) => {
const command = require(`${commandPath}/${paths}`)
if (Object.keys(command).length !== 0) {
let commandName = paths.slice(0, -3)
commands[commandName] = command
return commands
readDoc: () => {
global.articleList = []
fs.readdirSync(docPath).forEach((fileName) => {
fs.readFile(
`${docPath}/${fileName}`,
'utf-8',
function (err, data) {
if (err) {
console.error(err)
} else {
articleList.push(JSON.parse(data))
articleList.reverse()
11、文本提示util
const ora = require('ora')
const chalk = require('chalk')
module.exports = {
log: (message) => {
console.log(message)
succeed: (...message) => {
ora().succeed(chalk.greenBright.bold(message))
info: (...message) => {
ora().info(chalk.blueBright.bold(message))
error: (...message) => {
ora().fail(chalk.redBright.bold(message))
underline: (message) => {
return chalk.underline.blueBright.bold(message)
看完的小伙伴应该对node脚手架的开发有一点的了解啦,DEMO
没有用到难的东西(还是博主太菜 噗),主要是想让大家对脚手架开发有个基本的了解。如果还有不理解的小伙伴,可以把github
的案例下载下来跑一跑,最好可以仿着把他重写一遍,好记性不如烂笔头,相信你会有更深的理解。
看完的朋友可以动动手点个赞再走哦,你们的支持是对我最大的鼓励!!!
deploy-cli-service
NodeJS中文网