Open vortesnail opened 4 years ago
我的 github/blog,给个小星星咯~
最近因为工作,需要写一个脚本来自动读取文件夹下的某个文件,把其中的内容写到另一个新生成的文件中。因为这种场景还是挺常见的,网络上也搜不到好的(手把手教学的)解决方案,这对于还没学过 node.js 的前端小白来说,很不友好啊~
node.js
于是这篇文章就手把手教你写一个这样的脚本,都会尽量解释清楚,保证你看了就会!
假如有这么一个项目,其文件目录如下:
|-- 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 文件中。
index.js
app
config.json
node
config.all.js
现在假设配置文件 config.json 内容大概如下:
{ "name": "vortesnail", "github": "github.com/vortesnail", "age": "24", "address": "earth", "hobby": ["sing", "dance", "rap", "code"] }
各个 app 文件下的 config.json 内容可不一致,比较符合我们的实际项目场景。
因为用原生的 fs.watch 会有很多问题,并且有很大的局限,我们采用第三方模块 chokidar 来进行文件的监听。
fs.watch
npm install chokidar
现在在根目录下创建我们的脚本文件: auto-config.js ,当然,名字随你。 先引用我们的第三方模块 chokidar ,以及 node 核心模块 fs 、 path 以及 process 。
auto-config.js
chokidar
fs
path
process
const chokidar = require('chokidar') const fs = require('fs') const path = require('path') const process = require('process') const PROJECT_PATH = process.cwd()
PROJECT_PATH 表示当前目录路径。
PROJECT_PATH
这里需要注意我们是 chokidar.watch('.', {}).on() , . 代表当前跟路径,使用 PROJECT_PATH 会有问题,有知道的大佬可以评论交流一下!
chokidar.watch('.', {}).on()
.
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.readdirSync(PROJECT_PATH) 可读取当前目录的文件列表,是数组形式,数组内容为每一个文件或文件夹的名字。更新我们的代码:
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 来查看当前控制台的打印了,会发现循环打印了当前目录下的文件名字数组:
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([路径]) 来获取配置文件所在目录的文件列表, [路径] 可通过字符串拼接得到。
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.readdirSync
使用 fs.stat(path,callback) ,而不是 fs.statSync ,我们可以处理错误发生后的一些操作。
fs.stat(path,callback)
fs.statSync
callback
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 文件中。
.json
,
添加代码如下:
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 环境中去,在那个根目录打开我们的脚本,自动时时刻刻监听文件的变动,生成配置表,彻底解放双手!
nginx
打包配置很简单,不要慌!
无非就是一些 webpack 打包需要的依赖而已,比较重要的是 node-loader 这个包。
node-loader
npm install -D webpack webpack-cli node-loader
根目录创建 webpack.auto.js
webpack.auto.js
const path = require('path'); module.exports = { target: "node", entry: { 'auto-config': 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 做识别转译。
target: node
package.json
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "build:auto": "webpack -p --progress --config webpack.auto.js" },
现在,你在控制台执行 npm run build:auto ,一个可以监听并读写的小脚本就这样完成了!尽情去使用吧。把打出来的包丢到任意目录,执行:
npm run build:auto
node auto-config.js // 没权限的话需要要加 sudo sudo node auto-config.js
完美!
自我认为该写法还有很大改进地方,奈何自己水平有限,如果有大佬有更好的意见,非常希望您能在评论区说出来,让更多像我这样“求知若渴”的同学得到成长,感谢!🙏
推荐阅读: 这一次,彻底理解 https 原理
前言
最近因为工作,需要写一个脚本来自动读取文件夹下的某个文件,把其中的内容写到另一个新生成的文件中。因为这种场景还是挺常见的,网络上也搜不到好的(手把手教学的)解决方案,这对于还没学过
node.js
的前端小白来说,很不友好啊~于是这篇文章就手把手教你写一个这样的脚本,都会尽量解释清楚,保证你看了就会!
场景举例
假如有这么一个项目,其文件目录如下:
index.js
中的内容是啥与本文无关,只是做个样子,但是在每个app
文件夹中都有一个config.json
文件,这就是我们需要读的配置文件,现在我们要做的就是写一个node
脚本去监听当前这个目录的文件变动,并且实时地去读各个app
下的配置文件,并写入config.all.js
文件中。现在假设配置文件
config.json
内容大概如下:各个
app
文件下的config.json
内容可不一致,比较符合我们的实际项目场景。脚本编写
安装 chokidar
因为用原生的
fs.watch
会有很多问题,并且有很大的局限,我们采用第三方模块 chokidar 来进行文件的监听。创建脚本文件
现在在根目录下创建我们的脚本文件:
auto-config.js
,当然,名字随你。 先引用我们的第三方模块chokidar
,以及 node 核心模块fs
、path
以及process
。PROJECT_PATH
表示当前目录路径。使用 chokidar.watch
这里需要注意我们是
chokidar.watch('.', {}).on()
,.
代表当前跟路径,使用PROJECT_PATH
会有问题,有知道的大佬可以评论交流一下!fs.watch
一样,表示是否保护进程不退出持久监听,默认值为 true。使用 fs.readdirSync
使用
fs.readdirSync(PROJECT_PATH)
可读取当前目录的文件列表,是数组形式,数组内容为每一个文件或文件夹的名字。更新我们的代码:现在已经可以在当前目录执行
node auto-config.js
来查看当前控制台的打印了,会发现循环打印了当前目录下的文件名字数组:循环的原因是
chokidar
第一次会监听当前目录所有文件的 add 事件,都有哪些事件详情可看这: event。循环遍历每个文件夹并获取子目录文件列表
获得了当前目录的文件,我们需要先筛选出文件夹,再对该文件夹(比如我们的
app1
、app2
文件夹)使用上面使用过的fs.readdirSync([路径])
来获取配置文件所在目录的文件列表,[路径]
可通过字符串拼接得到。但是现在会报错,因为对于
fs.readdirSync
来说,若读取的当前路径为一个文件而不是一个文件夹,就会发生错误并终止程序的运行。故我们需要对其做一个判断。读取文件状态 fs.stat
使用
fs.stat(path,callback)
,而不是fs.statSync
,我们可以处理错误发生后的一些操作。callback
有两个参数: (err,stats),stats 是一个 fs.Stats 对象。stats.isDirectory()
可判断是否是文件夹。更新代码如下:
现在已经可以获取到子目录的文件列表了,接下来可以判断是否找到我们需要读取的文件,并且读文件了。
使用 fs.readFileSync 与 fs.writeFileSync
我们需要一个变量来存储读取到的值,这里我们使用
这里我只是简单的读取
.json
文件,并将其内容后添加一个,
并全部写入到新生成的config.all.js
文件中。添加代码如下:
到目前为止,这个读写脚本就算完成了,你不信你执行
node auto-config.js
,再打开根目录下config.all.js
文件看看,是不是把所有app
目录下的config.json
中的文件写入到里面了,而且你任意修改一下当前目录以及子目录的任一文件内容,都会重新生成配置表。处理瑕疵
最后的的打印因为第一次监听会生成很多很多。。。这看起来太丑了,可以加一个防抖,只让它输出一次。 另外,还可以在适合的地方加一些提示,现放出完整代码:
现在你再试试
node auto-config.js
,看看效果吧~webpack 打包配置
有的时候,我们不仅仅是只在项目中使用而已,我们需要打包出一个脚本文件,丢到
nginx
环境中去,在那个根目录打开我们的脚本,自动时时刻刻监听文件的变动,生成配置表,彻底解放双手!打包配置很简单,不要慌!
安装必要插件
无非就是一些 webpack 打包需要的依赖而已,比较重要的是
node-loader
这个包。webpack 配置
根目录创建
webpack.auto.js
比较重要的地方就是
target: node
,以及入口文件要写对,因为 fsevents 中有 .node 文件,我们需要对其处理,需要一个node-loader
做识别转译。修改
package.json
打包
现在,你在控制台执行
npm run build:auto
,一个可以监听并读写的小脚本就这样完成了!尽情去使用吧。把打出来的包丢到任意目录,执行:完美!
结语
自我认为该写法还有很大改进地方,奈何自己水平有限,如果有大佬有更好的意见,非常希望您能在评论区说出来,让更多像我这样“求知若渴”的同学得到成长,感谢!🙏
推荐阅读: 这一次,彻底理解 https 原理