自动 import 工具
先推荐两个干货,关于正则的
regexr,regexper,前者验证正则是否和预期一样,后者以图的形式表达正则,有助于理解天书般的别人写的正则
作为一个前端打字员,一个经常遇到的场景就是在路由文件中引入模块,比如这样
在 router/index.js
中写入
import Vue from 'vue'
import Router from 'vue-router'
const About = () => import('../pages/About.vue')
const Home = () => import('../pages/Home.vue')
Vue.use(Router)
...
如果修改了模块的名字,增加了模块或者删除了模块,就需要重新修改这个路由文件
总是做这么机械的事情无异于消耗我这个前端打字员的寿命
不能忍,遂写个工具
整理思路如下
其中,监视目录下文件的变动依靠的是 node API 中fs.watch(filename[, options][, listener])
替换目标文件中引入模块的部分,则是通过正则来实现
在这里五星推荐一个验证正则是否正确的网站,regexr
代码实现
监视包含模块的目录
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啊
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。