個人 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 原理