使用node开发工作流脚本
之前本来打算学学shell写脚本的,后来发现用PHP或者node等语言都可以实现相关的功能,且逻辑描述更清晰。最近刚好项目需要写一个简化工作流的脚本,因此在此整理一下过去写node脚本的经验。
获取命令行参数
这种需求是比较常见的
console.log(process.argv)
然后执行命令node index.js -ids 1,2,3
[ '/usr/local/bin/node',
'/Users/Txm/Desktop/script-demo/build.js',
'-ids',
'1,2,3' ]
因此可以通过下面方式获取到命令行参数
var args = process.argv.splice(2);
console.log(args) // ['-ids','1,2,3' ]
npm scripts
在package.json
中添加脚本运行命令是一个比较常见的做法,甚至
npm i
npm run dev
npm run build
都成了开发的基本步骤了,下面是npm scripts
中有几个比较有用的特性,参考
钩子
比如下面的配置
{
"scripts": {
"build": "node index.js",
"prebuild": "node pre.js",
"postbuild": ""
},
}
使用npm run build
的时候,会自动先执行prebuild
内的逻辑 异步代码 程序会等待预先的钩子所有任务全部执行后才会进行下一步,包括所有所有异步任务
// pre.js
console.log('pre')
setTimeout(() => {
console.log('pre interval')
}, 2000);
// index.js
console.log('build')
// 输出
pre
pre interval
build
因此可以放心地通过钩子来完成一些必要的任务。
这里遇见的一个问题是:如何在钩子函数中传递实际脚本的参数。比如
npm run build -ids 1,2
对应的命令行参数只能在build任务中获取到,在prebuild
中是获取不到的,这样的限制是比较有局限。
目前没有查到更好的解决办法,暂通过shelljs
进行hack处理,其原理是通过在prebuild.js
中调用
shellJS.exec('node build.js')
PS:关于shelljs
在下面的常用工具章节会提到。
然后修改build
任务为node prebuild.js
来实现的,不过这种混淆了语义性,也不是一种很可取的做法,待我再研究一下。
环境变量
区分开发环境和生成环境也是十分常见的生产需求,这个实现起来也比较方便
NODE_ENV='production' node build.js
即在指令前设置NODE_ENV
变量,然后通过
let nodeEnv = process.env.NODE_ENV
就可以访问到对应的环境变量了。同理,在npm scripts
中也可以使用这种方法设置环境变量。
子进程
nodejs默认是单进程的,但可以通过child_process
这个模块实现多进程。参考
Node.js 的父进程与衍生的子进程之间会建立 stdin、stdout 和 stderr 的管道。 数据能以非阻塞的方式在管道中流通。 有些程序会在内部使用行缓冲 I/O,虽然这并不影响 Node.js,但发送到子进程的数据可能无法被立即使用。
这里不讨论如何实现多进程,在日常的脚本中,也可以通过child_process来实现一些有趣的功能,比如文件IO、FTP等功能。
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`输出:${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`错误:${data}`);
});
ls.on('close', (code) => {
console.log(`子进程退出码:${code}`);
});
上面这段代码是文档里面的~说实话我对这个模块也不是特别了解,正在摸索中...
常用工具库
node生态圈为我们提供了大量的工具,用于提高开发效率,下面是我目前使用到的一些工具,感觉挺好用的。
shelljs
有时候需要直接运行命令行指令,shelljs为我们提供了完美的解决方案。
在大多数情况下,使用exec
方法就可以解决大部分问题了
let shellJS = require('shelljs)
let command = `npm run build`
shellJS.exec(command)
如果需要连续执行多个指令,或者需要获得某条指令的返回结果,可以参阅shelljs的API文档,里面封装了诸如grep
、echo
等常用命令。
要想高效使用shelljs,应当学习linux指令而不是接口本身。
js-xlsx
如果脚本有处理excel表格的需求,那么js-xlsx是一个不错的选择。
下面是我封装的一个常用的工具函数。
let XLSX = require('js-xlsx')
let fs = require('fs')
module.exports = excelPath => {
const workbook = XLSX.readFile(excelPath)
const sheetNames = workbook.SheetNames
let worksheet = workbook.Sheets[sheetNames[0]]
let rows = XLSX.utils.sheet_to_json(worksheet)
let row = rows[0]
let header = Object.keys(row)
return {
getHeader() {
return header
},
getRows() {
return rows
},
// 保存数据,貌似库文件本身没有提供相关方法,因此需要自己封装
saveRows(rows, header, fileName = 'data.xlsx') {
let _headers = header || this.getHeader()
let headers = _headers
.map((v, i) => Object.assign({}, {v: v, position: String.fromCharCode(65 + i) + 1}))
.reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v}}), {});
let data = rows
.map((v, i) => _headers.map((k, j) => Object.assign({}, {
v: v[k],
position: String.fromCharCode(65 + j) + (i + 2)
})))
.reduce((prev, next) => prev.concat(next))
.reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v}}), {});
let output = Object.assign({}, headers, data);
let outputPos = Object.keys(output);
let ref = outputPos[0] + ':' + outputPos[outputPos.length - 1];
let wb = {
SheetNames: ['Sheet1'],
Sheets: {
'Sheet1': Object.assign({}, output, {'!ref': ref})
}
};
XLSX.writeFile(wb, fileName);
},
// 将表格数据导出为JSON
saveJson() {
fs.writeFile('./data.json', JSON.stringify(rows), 'utf-8', function (err) {
if (err) {
throw err
}
})
}
}
}
log4js
如果需要生成日志文件,那么log4js你值得拥有~ PS:貌似之前的写JavaScript的调试那篇博客中已经安利过一次了...
let log4js = require("log4js"),
logger = log4js.getLogger();
logger.level = "info";
logger.info("info wtf");
logger.debug("debug wtf"); // 由于debug的等级低于info,因此该调试代码不会输出
小结
刚到新公司熟悉开发环境,由于公司规模比较大,流程也比较多,因此通过脚本来运行日常工作任务,比如切换开发环境、修改host等功能,提高的工作效率是十分可观的。
虽然工作是写前端,但是没必要把自己约束在某一个固定的岗位上,反正都是写代码嘛,能用代码跑的就不要手动去处理了~
关于shell脚本和linux指令,还有很多需要学习的地方,任重而道远啊。
你要请我喝一杯奶茶?
版权声明:自由转载-非商用-保持署名和原文链接。
本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。