前言
虽然控制台程序本来就是没有界面的,打包成exe有点多余,但是如果想把写好的代码发给一个非程序员使用,可能还要教人家如何装node,如何运行…… 麻烦死了!
本文采用的方法是先用批处理包装,然后再将批处理打包成exe,批处理转exe的工具很多,我这里随便找了一个Bat To Exe Converter
来实现。
正式开始
目标
我们肯定希望生成的exe是这样的:
- 双击即可运行,临时文件不能生成在当前目录(这样看起来才像个独立的exe);
- 退出程序时自动删除临时文件(不然时间越久临时目录越大);
- 可以任意设置图标;
- 虽然文件输出到临时目录,但是工作目录最好是exe的启动目录;
上面1、2、3这三点都比较好实现,关键是第4点。
测试文件
我们写一个最简单的控制台程序,只为了验证当前目录,test.js
:
console.log('当前工作目录:' + process.cwd());
批处理封装
批处理可以设置窗口标题以及字体颜色等,所以我们用批处理再包装一层:
@echo off
node test.js
pause
假设以上文件在D:\test
下面,我们在E:\exe
下面执行call D:\test\test.bat
,很显然,由于批处理的默认当前目录在E盘,所以报错:
E:\exe>call D:\test\test.bat
module.js:471
throw err;
^
Error: Cannot find module 'E:\exe\test.js'
at Function.Module._resolveFilename (module.js:469:15)
at Function.Module._load (module.js:417:25)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
at bootstrap_node.js:509:3
所以我们把批处理改成这样(同时为了后面测试方便,我们多输出了2个参数):
@echo off
echo %cd%
echo %~dpf0
node %~dp0%test.js
pause
运行之后正常:
E:\exe>call D:\test\test.bat
E:\exe
D:\test\test.bat
当前工作目录:E:\exe
请按任意键继续. . .
尝试使用好压打包成exe
使用好压的自解压模式可以轻松将批处理打包成exe(下图为900多kb的gif,耐心等待加载):
只可惜打包之后,它的工作目录默认是解压的临时目录:
当前工作目录:C:\Users\Administrator\AppData\Local\Temp\HZ$D.816.1632
请按任意键继续. . .
尝试了一番还是没找到解决办法,所以只能放弃了,如果有人知道还烦请告知。
使用第三方工具
我使用了一个叫Bat To Exe Converter的工具:
配置好以上几步之后,点击左下角的编译就OK了:
为了对比,我们把双击生成的test.exe也移到E:\exe
下面测试:
可以发现,这个工具把%cd%
和%~dpf0
和普通方式对调了一下,工作目录是临时目录,而%0
却成了启动exe的路径,好吧,这样也没关系,我们这样改造一番就OK了:
@echo off
:: 将当前目录保存起来
set tempPath=%cd%
:: 定位到exe所在目录
%~d0
cd %~dp0
:: 执行临时目录的js文件
node %tempPath%\test.js
pause
至此,我们完美地解决了最开始说的第4点问题^_^。
实例:nginx配置文件监听工具
别整些没用的,我们来点实际的。实际工作中经常需要改动nginx配置文件,每次改完都需要reload一下感觉很麻烦,于是简单写了个配置文件监听工具,一改动就自动刷新,代码如下:
nginx-conf-watcher.js:
const fs = require('fs');
const path = require('path');
const {exec, execFile, spawn} = require('child_process');
var lastModifiedTime = Date.now();
var args = process.argv.splice(2); // 获取命令行后面的参数
// 被监听的文件或者文件夹名字,path.resolve获取其相当于node启动路径的绝对路径
// 允许通过 node nginx-conf-watcher.js folderName 来指定被监听文件夹的名字
var watchFile = path.resolve(args[0] || 'conf');
console.log('\n提示:请务必将本程序放在和nginx.exe同一目录下,否则不会正常工作!\n');
try {
// recursive 表示递归监听子文件夹的变化
fs.watch(watchFile, {recursive: true}, function(eventType, fileName) {
var now = Date.now();
// 连续2次触发间隔小于50毫秒时忽略
if(now - lastModifiedTime < 50) return;
// 只监听conf文件的变化
if(!fileName.endsWith('.conf')) return;
lastModifiedTime = now;
console.log(`\n检测到文件 ${fileName} 的变化,开始刷新nginx配置文件...`);
var reload = spawn('./nginx', ['-s', 'reload']);
reload.stdout.on('data', data => {});
reload.stderr.on('data', data => {
console.log('刷新nginx配置文件失败:');
console.log(`${data}`);
});
reload.on('exit', (code) => {
// 0 表示进程正常结束
if(code == 0) console.log('刷新nginx配置文件成功!')
});
});
console.log('nginx配置文件监听服务已启动!');
}
catch(e) {
console.log(`监听“${watchFile}”文件(夹)失败,可能是文件不存在!`);
}
nginx-conf-watcher.bat:
@echo off
setlocal enabledelayedexpansion
title nginx配置文件监听工具v1.0
color 0A
mode con: cols=75 lines=30
:: 保存解压的临时目录
set tempPath=%cd%
:: 定位到exe文件所在盘
%~d0
:: 定位到exe文件所在路径
cd %~dp0
node %tempPath%\nginx-conf-watcher.js
pause
最后打包运行效果:
文件效果:
工具下载地址:http://file.haoji.me/detail/5
附
早些时候写了一篇水文,和本文有点关系,把链接也挂这吧:关于nodejs的启动路径和当前路径问题