个人 github/blog,给个小星星咯~javascript
最近由于工做,须要写一个脚原本自动读取文件夹下的某个文件,把其中的内容写到另外一个新生成的文件中。由于这种场景仍是挺常见的,网络上也搜不到好的(手把手教学的)解决方案,这对于还没学过 node.js
的前端小白来讲,很不友好啊~前端
因而这篇文章就手把手教你写一个这样的脚本,都会尽可能解释清楚,保证你看了就会!java
假若有这么一个项目,其文件目录以下:node
|-- app1
|-- config.json
|-- index.js
|-- app2
|-- config.json
|-- index.js
|-- app3
|-- config.json
|-- index.js
|-- app4
|-- config.json
|-- index.js
|-- config.all.js
|-- package.json
复制代码
index.js
中的内容是啥与本文无关,只是作个样子,可是在每一个 app
文件夹中都有一个 config.json
文件,这就是咱们须要读的配置文件,如今咱们要作的就是写一个 node
脚本去监听当前这个目录的文件变更,而且实时地去读各个 app
下的配置文件,并写入 config.all.js
文件中。webpack
如今假设配置文件 config.json
内容大概以下:nginx
{
"name": "vortesnail",
"github": "github.com/vortesnail",
"age": "24",
"address": "earth",
"hobby": ["sing", "dance", "rap", "code"]
}
复制代码
各个 app
文件下的 config.json
内容可不一致,比较符合咱们的实际项目场景。git
由于用原生的 fs.watch
会有不少问题,而且有很大的局限,咱们采用第三方模块 chokidar 来进行文件的监听。github
npm install chokidar
复制代码
如今在根目录下建立咱们的脚本文件: auto-config.js
,固然,名字随你。
先引用咱们的第三方模块 chokidar
,以及 node 核心模块 fs
、 path
以及 process
。web
const chokidar = require('chokidar')
const fs = require('fs')
const path = require('path')
const process = require('process')
const PROJECT_PATH = process.cwd()
复制代码
PROJECT_PATH
表示当前目录路径。npm
这里须要注意咱们是 chokidar.watch('.', {}).on()
, .
表明当前跟路径,使用 PROJECT_PATH
会有问题,有知道的大佬能够评论交流一下!
const chokidar = require('chokidar')
const fs = require('fs')
const path = require('path')
const process = require('process')
const PROJECT_PATH = process.cwd()
chokidar.watch('.', {
persistent: true,
ignored: /(^|[\/\\])\..|auto-config.js|config.all.js|node_modules/,
depth: 1
}).on('all', (event, pathname) => {
console.log(event, pathname)
// do something later...
})
复制代码
fs.watch
同样,表示是否保护进程不退出持久监听,默认值为true。使用 fs.readdirSync(PROJECT_PATH)
可读取当前目录的文件列表,是数组形式,数组内容为每个文件或文件夹的名字。更新咱们的代码:
chokidar.watch('.', {
persistent: true,
ignored: /(^|[\/\\])\..|auto-config.js|config.all.js|node_modules/,
depth: 0
}).on('all', (event, pathname) => {
console.log(event, pathname)
- // do something later...
+ const rootFilenames = fs.readdirSync(PROJECT_PATH)
+ console.log(rootFilenames)
})
复制代码
如今已经能够在当前目录执行 node auto-config.js
来查看当前控制台的打印了,会发现循环打印了当前目录下的文件名字数组:
[
'app1',
'app2',
'app3',
'app4',
'auto-config.js',
'config.all.js',
'node_modules',
'package-lock.json',
'package.json'
]
复制代码
循环的缘由是 chokidar
第一次会监听当前目录全部文件的 add 事件,都有哪些事件详情可看这: event。
得到了当前目录的文件,咱们须要先筛选出文件夹,再对该文件夹(好比咱们的 app1
、 app2
文件夹)使用上面使用过的 fs.readdirSync([路径])
来获取配置文件所在目录的文件列表, [路径]
可经过字符串拼接获得。
chokidar.watch('.', {
// ...
}).on('all', (event, pathname) => {
console.log(event, pathname)
const rootFilenames = fs.readdirSync(PROJECT_PATH)
- console.log(rootFilenames)
+ rootFilenames.forEach(function(file) {
+ const newPath = path.join(PROJECT_PATH, `/${file}/`)
+ const subFilenanme = fs.readdirSync(newPath)
+ console.log(subFilenanme)
+ })
})
复制代码
可是如今会报错,由于对于 fs.readdirSync
来讲,若读取的当前路径为一个文件而不是一个文件夹,就会发生错误并终止程序的运行。故咱们须要对其作一个判断。
使用 fs.stat(path,callback)
,而不是 fs.statSync
,咱们能够处理错误发生后的一些操做。
callback
有两个参数: (err,stats),stats 是一个 fs.Stats 对象。stats.isDirectory()
可判断是不是文件夹。更新代码以下:
chokidar.watch('.', {
// ...
}).on('all', (event, pathname) => {
console.log(event, pathname)
const rootFilenames = fs.readdirSync(PROJECT_PATH)
rootFilenames.forEach(function(file) {
const newPath = path.join(PROJECT_PATH, `/${file}/`)
fs.stat(newPath, function(err, stats) {
if(err){
console.log(file + 'is not a directory...')
} else {
const isDir = stats.isDirectory() //是文件夹
if (isDir) {
const subFilenanmes = fs.readdirSync(newPath)
console.log(subFilenanmes)
}
}
})
})
})
复制代码
如今已经能够获取到子目录的文件列表了,接下来能够判断是否找到咱们须要读取的文件,而且读文件了。
咱们须要一个变量来存储读取到的值,这里咱们使用
let content = ''
复制代码
这里我只是简单的读取 .json
文件,并将其内容后添加一个 ,
并所有写入到新生成的 config.all.js
文件中。
添加代码以下:
chokidar.watch('.', {
persistent: true,
ignored: /(^|[\/\\])\..|auto-config.js|config.all.js|node_modules/,
depth: 0
}).on('all', (event, pathname) => {
console.log(event, pathname)
+ let content = ''
const rootFilenames = fs.readdirSync(PROJECT_PATH)
rootFilenames.forEach(function(file) {
const newPath = path.join(PROJECT_PATH, `/${file}/`)
fs.stat(newPath, function(err, stats) {
if(err){
console.log(file + 'is not a directory...')
} else {
const isDir = stats.isDirectory() //是文件夹
if (isDir) {
const subFilenanmes = fs.readdirSync(newPath)
- console.log(subFilenanmes)
+ subFilenanmes.forEach(function(file) {
+ if (file === 'config.json') {
+ const data = fs.readFileSync(path.join(newPath, file), 'utf-8') //读取文件内容
+ content += data + ',' + '\n'
+ }
+ fs.writeFileSync(path.join(PROJECT_PATH, 'config.all.js'), `module.exports={data: [${content}]}`)
+ })
}
}
})
})
+ console.log(`配置表 config.all.js 已自动生成...`)
})
复制代码
到目前为止,这个读写脚本就算完成了,你不信你执行 node auto-config.js
,再打开根目录下 config.all.js
文件看看,是否是把全部 app
目录下的 config.json
中的文件写入到里面了,并且你任意修改一下当前目录以及子目录的任一文件内容,都会从新生成配置表。
最后的的打印由于第一次监听会生成不少不少。。。这看起来太丑了,能够加一个防抖,只让它输出一次。
另外,还能够在适合的地方加一些提示,现放出完整代码:
const chokidar = require('chokidar')
const fs = require('fs')
const path = require('path')
const process = require('process')
const PROJECT_PATH = process.cwd()
chokidar.watch('.', {
persistent: true,
ignored: /(^|[\/\\])\..|auto-config.js|config.all.js|node_modules/,
depth: 0
}).on('all', (event, pathname) => {
console.log(event, pathname)
let content = ''
const rootFilenames = fs.readdirSync(PROJECT_PATH)
rootFilenames.forEach(function(file) {
const newPath = path.join(PROJECT_PATH, `/${file}/`)
fs.stat(newPath, function(err, stats) {
if(err){
console.log(file + 'is not a directory...')
} else {
const isDir = stats.isDirectory() //是文件夹
if (isDir) {
const subFilenanmes = fs.readdirSync(newPath)
subFilenanmes.forEach(function(file) {
if (file === 'config.json') {
const data = fs.readFileSync(path.join(newPath, file), 'utf-8') //读取文件内容
content += data + ',' + '\n'
}
fs.writeFileSync(path.join(PROJECT_PATH, 'config.all.js'), `module.exports={data: [${content}]}`)
})
}
}
})
})
success()
})
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
var args = arguments;
clearTimeout(timeout)
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
const success = debounce(() => {
console.log('配置表 config.all.js 已自动生成...')
}, 500)
复制代码
如今你再试试 node auto-config.js
,看看效果吧~
有的时候,咱们不只仅是只在项目中使用而已,咱们须要打包出一个脚本文件,丢到 nginx
环境中去,在那个根目录打开咱们的脚本,自动时时刻刻监听文件的变更,生成配置表,完全解放双手!
打包配置很简单,不要慌!
无非就是一些 webpack 打包须要的依赖而已,比较重要的是 node-loader
这个包。
npm install -D webpack webpack-cli node-loader
复制代码
根目录建立 webpack.auto.js
const path = require('path');
module.exports = {
target: "node",
entry: {
script: path.resolve(__dirname, "auto-config.js"),
},
output: {
publicPath: '',
filename: '[name].js',
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
test: /\.node$/,
use: 'node-loader'
}
],
},
node: {
fs: 'empty',
child_process: 'empty',
tls: 'empty',
net: 'empty'
},
};
复制代码
比较重要的地方就是 target: node
,以及入口文件要写对,由于 fsevents 中有 .node 文件,咱们须要对其处理,须要一个 node-loader
作识别转译。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
+ "build:auto": "webpack -p --progress --config webpack.auto.js"
},
复制代码
如今,你在控制台执行 npm run build:auto
,一个能够监听并读写的小脚本就这样完成了!尽情去使用吧。把打出来的包丢到任意目录,执行:
node auto-config.js
// 没权限的话须要要加 sudo
sudo node auto-config.js
复制代码
完美!
自我认为该写法还有很大改进地方,奈何本身水平有限,若是有大佬有更好的意见,很是但愿您能在评论区说出来,让更多像我这样“求知若渴”的同窗获得成长,感谢!🙏
推荐阅读:
这一次,完全理解 https 原理