先推荐两个干货,关于正则的
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啊