什么是 Nodejs Cli 利用?
简略来说就是在命令行能够应用 nodejs 来执行的利用,例如:vue-cli、creat-react-app、webpack-cli 等;在前端开发过程中咱们会用到很多的工具,这些工具在装置过后能够间接应用命令行执行;留神在全局装置和在我的项目装置不同。
// 全局装置,间接执行命令
> npm install webpack webpack-cli -g
> webpack
// 我的项目装置,须要借助 npx 执行
> npm install webpack webpack-cli --save-dev
> npx webpack
Nodejs Cli 利用的工作流程!
1、启动过程:
命令行执行命令 => 依据 package.json 中 bin 查问入口 => 执行入口 js 文件 cli.js
2、执行过程:
命令行执行 js 文件性能启动 => 命令行询问用户问题 => 联合问题答案 + 模板等文件 => 生成构造文件
Nodejs Cli 利用的入口文件:cli.js
1、入口文件门路 ,首先在 package.json 中增加 bin 字段
{
"name": "ncl",
"main": "index.js",
"bin": {"ncl": "./cli.js" // 入口文件,ncl 和 name 保持一致},
...
}
2、入口文件特定的文件头 ,在 cli.js 顶部输出
#!/usr/bin/env node
3、入口文件权限
// 如果是 Linux 或者 macOS 零碎下还须要批改此文件的读写权限为 755
// 具体就是通过 chmod 755 cli.js 实现批改
4、简略测试模块
npm link 能够将模块链接到全局,也能够链接到应用该模块的我的项目 node_modules 中;这样在开发模块的过程中,不必公布到 npm 也能够应用模块进行测试。
> npm link // 在自定义模块我的项目的目录执行,将模块连贯到全局
> ncl // 间接执行模块,应用模块名
Nodejs Cli 利用的示例
该示例的性能实现一个自定义的脚手架:在命令行询问用户一些简略的问题作为参数,而后主动生成一些我的项目文件。其中的文件能够通过模板生成,也能够传递数据到模板。
1、装置一些依赖模块
> npm install inquirer --save //nodejs 环境下,实现命令行的用户交互插件
> npm install ejs --save // 模板引擎
2、cli.js 中定义命令行询问用户问题
- inquire.prompt 进行命令行的用户询问操作
- inquirer.prompt 返回值为一个 promise 对象
- inquirer.prompt 的参数为一个数组
const inquirer = require('inquirer')
inquirer.prompt([
{
type: 'input',
name: 'name',
message: '请输出项目名称?'
}
])
.then(anwsers => {// anwsers: { name: "xxx"} //anwsers 返回一个后果对象
})
3、获取模板目录和指标生成目录
const path = require('path')
// 模板目录
// __dirname 获取以后执行代码文件的绝对路径
// tmplDir 为 templates 的绝对路径
const tmplDir = path.join(__dirname, 'templates')
// 目标目录
// process.cwd() 返回 Node.js 过程的当前工作目录。// process 参考 api 文档:http://nodejs.cn/api/process.html
const destDir = process.cwd()
4、模板引擎渲染模板
const ejs = require('ejs')
const path = require('path')
// 通过模板引擎渲染文件
// 参数 1:fileDir 为文件的绝对路径
// 参数 2:渲染模板所需变量,存在 anwsers 对象外面
// 参数 3:回调函数,result 为新文件
ejs.renderFile(fileDir, anwsers, (err, result) => {if (err) throw err
// 将后果写入指标文件门路
fs.writeFileSync(fileDestDir, result)
})
// 一个 package.json 作为模板的示例:{
"name": "<%= name %>",
"version": "<%= version %>",
"description": "<%= description %>",
"author": "<%= author %>",
"bin": "cli.js",
"scripts": {"test": "echo \"Error: no test specified\"&& exit 1"},
"license": "ISC"
}
5、读取目录下的文件
// 将模板下的文件全副转换到目标目录
// 参数 1:path
// 参数 2:回调函数,参数 files 为文件相对路径组成的数组
fs.readdir(tmplDir, (err, files) => {if (err) throw err
files.forEach(file => {
// 通过模板引擎渲染文件
// 解决 file
})
})
})
6、将文件写入门路
// 将后果写入指标文件门路
// 参数 1:文件的绝对路径
// 参数 2:文件内容
fs.writeFileSync(fileDestDir, result)
模板文件相干
这里是依据本人的需要将须要主动生成的文件放到模板目录下,没有变动的或者对立的文件就不须要应用模板引擎。
1、模板门路:templates;
2、通常将整顿好的我的项目构造整体拷贝到 templates 下 ,例如:vue 的示例源文件、lint 文件、package.json 等等;
测试模块执行
本地开发能够应用 npm link 关联模块目录和依赖此模块的我的项目 node_modules 目录;也能够公布到 npm 源上后间接装置应用模块。
1、关联模块:
> cd nodejs-cli-sample //Nodejs Cli 利用的目录
> npm link // 将模块连贯到全局
2、执行模块:
> cd nodejs-cli-demo // 在我的项目目录执行模块
> ncl // 间接执行模块,应用模块名(ncl 是我的项目 nodejs-cli-sample 的名称)
公布 Nodejs Cli 利用
1、能够间接应用 npm publish 公布到源上
> npm publish --registry=https://registry.xxxx
2、要思考到 npm 源是否有写权限,能够公布到本人公司的 npm 源上或者 yarn 源上
// 淘宝镜像源是只读的,publish 不下来
// 公布到 yarn 的镜像源之后,应用淘宝镜像源时能够手动同步放慢模块下载速度
yarn publish --registry https://registry.yarnpkg.com/
残缺示例代码
1、NodeJs Cli 利用 cli.js 入口文件
#!/usr/bin/env node
// Node CLI 利用入口文件必须要有这样的文件头
// 如果是 Linux 或者 macOS 零碎下还须要批改此文件的读写权限为 755
// 具体就是通过 chmod 755 cli.js 实现批改
const fs = require('fs') // 文件读写
const path = require('path') // 门路获取
const inquirer = require('inquirer') // 命令行用户交互
const ejs = require('ejs') // 模板引擎
// 脚手架的工作过程:启动 => 命令行询问用户问题 => 联合问题答案 + 模板 => 生成构造文件
inquirer.prompt([
{
type: 'input',
name: 'name',
message: '请输出项目名称 (\'\')'
},
{
type: 'input',
name: 'version',
message: '请输出我的项目版本号 (1.0.0)'
},
{
type: 'input',
name: 'description',
message: '请输出我的项目备注 (\'\')'
},
{
type: 'input',
name: 'author',
message: '请输出作者名称 (\'\')'
}
])
.then(anwsers => {// anwsers: { name: "xxx"} //anwsers 返回一个后果对象
// 模板目录绝对路径
const tmplDir = path.join(__dirname, 'templates')
// 目标目录
const destDir = process.cwd()
// 读取目录下所有文件
let readFiles = (dir) => {return new Promise((resolve, reject)=>{
// 参数 1:目录门路
// 参数 2:回调函数(谬误对象,files 为文件相对路径组成的数组)fs.readdir(dir, (err, files) => {if (err) reject(err)
resolve(files)
})
})
}
// 解决模板文件
let ejsRender = (file) => {return new Promise((resolve, reject)=>{
// 模板文件绝对路径
let dir = path.join(tmplDir, file)
// 参数 1:文件门路
// 参数 2:数据对象
// 参数 3:回调函数(谬误对象,渲染后的新文件)ejs.renderFile(dir, anwsers, (err, result) => {if (err) reject(err)
resolve(result)
})
})
}
// 1、先读取目录下所有文件
// 2、应用 ejs 渲染所有模板
// 3、再将新文件写到指标门路
readFiles(tmplDir).then((files)=>{
files.forEach(file => {ejsRender(file).then((result)=>{
// 指标文件绝对路径,file 其实是文件相对路径
let fileDestDir = path.join(destDir, file)
// 将后果写入指标文件门路
// 参数 1:文件绝对路径
// 参数 2:渲染后新文件
fs.writeFileSync(fileDestDir, result)
},throwError)
})
},throwError)
})
/**
* 谬误处理函数
* @param {* 谬误对象} error
*/
function throwError(error){throw error}
2、package.json 示例模板文件,应用的 ejs 模板引擎
{
"name": "<%= name %>",
"version": "<%= version %>",
"description": "<%= description %>",
"author": "<%= author %>",
"bin": "cli.js",
"scripts": {"test": "echo \"Error: no test specified\"&& exit 1"},
"license": "ISC"
}