先推薦兩個乾貨,關於正則的
regexr,regexper,前者驗證正則是否和預期同樣,後者以圖的形式表達正則,有助於理解天書般的別人寫的正則前端
做爲一個前端打字員,一個常常遇到的場景就是在路由文件中引入模塊,好比這樣vue
在 router/index.js
中寫入node
import Vue from 'vue' import Router from 'vue-router' const About = () => import('../pages/About.vue') const Home = () => import('../pages/Home.vue') Vue.use(Router) ...
若是修改了模塊的名字,增長了模塊或者刪除了模塊,就須要從新修改這個路由文件webpack
老是作這麼機械的事情無異於消耗我這個前端打字員的壽命git
不能忍,遂寫個工具github
其中,監視目錄下文件的變更依靠的是 node API 中fs.watch(filename[, options][, listener])
web
替換目標文件中引入模塊的部分,則是經過正則來實現vue-router
在這裏五星推薦一個驗證正則是否正確的網站,regexrjson
監視包含模塊的目錄工具
fs.watch(dir, { recursive: true // 目錄下子目錄也被監視 }, (event, filename) => { // event 是文件變更的類型,添加文件、刪除文件和修改文件名都是'rename' 事件 // filename 是變化的文件或目錄 if(event === 'rename'){ // 判斷文件變更類型 } })
當發生rename事件後,須要從新得到目錄下(from)全部的模塊,包括模塊名moduleName,模塊文件相對於引用模塊文件(to)的相對路徑modulePath,將它們存入變量modules中
實際項目中,模塊一般都是.vue文件,或者.jsx文件,所以只將這些做爲模塊,在路由文件中引用
另外有些模塊文件由於各類緣由,但願人工引入,而不被watch,這樣的文件存入excludeArr中
const _ = require('lodash') let excludeArr = [...] let modules = [] let extname = '.vue' let from = './src/pages' let to = './src/router/index.js"' const mapDir = d => { // 得到當前文件夾下的全部的文件夾和文件 const [dirs, files] = _(fs.readdirSync(d)).partition(p => fs.statSync(path.join(d, p)).isDirectory() ) // 映射文件夾 dirs.forEach(dir => { modules.concat(mapDir(path.join(d, dir))) }) // 映射文件 files.forEach(file => { // 文件後綴名 let filename = path.join(d, file) if (path.extname(file) === extname) { if (!excludeArr.includes(path.resolve(__dirname, filename))) { let moduleName = path.basename(file, extname) // 若存在 - if (moduleName.match('-')) { moduleName = moduleName.replace( /(-)(.{1})/, (match, p1, p2, offset, string) => p2.toUpperCase() ) } modules.push({ moduleName, modulePath: path.relative(path.dirname(to), filename) }) } } }) }
生成好新的待引入的模塊後,接下來就是在路由文件中,將對應的內容替換掉
因此須要讀寫文件以及正則替換
const regex = /\/\*\sautoImport(.*\n)*\/\*\sautoImport\s\*\//g let importStr = '' modules.forEach((m, index) => { importStr = importStr + fillTemplate(template, m.moduleName, m.modulePath) + (cache.length - 1 === index ? '' : '\n') }) fs.readFile(to, 'utf8', (err, data) => { if (err) return console.log(err) let result = '' if (data.match(regex)) { result = data.replace( regex, `/* autoImport */ ${importStr} /* autoImport */` ) } else { /* 首次插入在文件最後的import插入 */ result = data.replace( /(.*import.*)(\n)([^(import)])/, (match, p1, p2, p3, offset, string) => { return `${p1} /* autoImport */ ${importStr} /* autoImport */ ${p3}` } ) } fs.writeFile(to, result, 'utf8', err => { if (err) return console.log(err) }) })
其中/\/\*\sautoImport(.*\n)*\/\*\sautoImport\s\*\//g
是用於匹配兩段註釋/* autoImport */
及其中間的內容
import Vue from 'vue' import Router from 'vue-router' /* autoImport */ const About = () => import('../pages/About.vue') const Home = () => import('../pages/Home.vue') /* autoImport */ Vue.use(Router)
當第一次使用,沒有/* autoImport */
時,就須要在最後一個import後面,插入引入的模塊
data.replace( /(.*import.*)(\n)([^(import)])/, (match, p1, p2, p3, offset, string) => { return `${p1} /* autoImport */ ${importStr} /* autoImport */ ${p3}`
在這裏還能夠自定義了引入模塊的方式,例如懶加載,"const moduleName = () => import(modulePath)"
const template = "const moduleName = () => import(modulePath)" const fillTemplate = (template, moduleName, modulePath) => template .replace('moduleName', moduleName) .replace('modulePath', `'${modulePath}'`)
爲了工具的靈活性,把可配置項寫成json文件的形式
{ "extname": ".vue", "from": "src/pages", "to": "src/router/index.js", "template": "const moduleName = () => import(modulePath)", "exclude": [ "./src/pages/login.vue", "./src/pages/404.vue", "./src/pages/overall/**", "./src/pages/account-result/**" ] }
而後經過如下的方式來得到
const config = fs.readFileSync('./autoImport.json') const { extname, from, to, template, exclude } = JSON.parse(config)
下一步準備把這個工具寫成webpack的插件,名字我都起好了,AutoImportPlugin,先在github上佔了個坑,順手給顆星,不用改Bug
同時準備用更加成熟的模塊chokidar來代替原生的watch
工具備問題提issue啊