为nuxt项目写一个面包屑cli工具实现自动生成页面与面包屑配置
公司项目的面包屑导航是使用element的面包屑组件,配合一份json配置文件来实现的,每次写新页面都需要去写json配置,非常麻烦,所以写一个面包屑cli,自动生成页面、自动配置面包屑数据,提高效率:rocket:
明确目标
- 提供init命令,在一个新项目中能够通过初始化生成面包屑相关文件
- 能够通过命令生成页面,并且自动配置面包屑json数据
- 按照项目原有需求,能够配置面包屑是否可点击跳转
- 按照项目原有需求,能够配置某路径下是否展示面包屑
- 支持仅配置而不生成文件,能够为已存在的页面生成配置
- 能够动态配置当前面包屑导航的数据
- ……(后续在使用中发现问题并优化)
实现分成两部分
- 面包屑实现
- cli命令实现
面包屑实现
- 在路由前置守卫beforEach中根据当前路径在配置文件中匹配到相应的数据
- 把这些配置存到vuex
- 在面包屑组件中根据vuex中的数据v-for循环渲染出面包屑
JSON配置文件
json配置文件是通过命令生成的,一个配置对象包含namepathclickableisShow属性
[
{
"name":"应用",//面包屑名称(在命令交互中输入)
"path":"/app",//面包屑对应路径(根据文件自动生成)
"clickable":true,//是否可点击跳转
"isShow":true//是否显示
},
{
"name":"应用详情",
"path":"/app/detail",
"clickable":true,//是否可点击跳转
"isShow":true//是否显示
}
]
匹配配置文件中的数据
比如按照上面的配置文件,进入/app/detail时,将会匹配到如下数据
[
{
"name":"应用",
"path":"/app",
"clickable":true,
"isShow":true
},
{
"name":"应用",
"path":"/app/detail",
"clickable":true,
"isShow":true
}
]
动态面包屑实现
有时候需要动态修改面包屑数据(比如动态路由),由于数据是存在vuex中的,所以修改起来非常方便,只需在vuex相关文件中提供mutation即可,这些mutation在数据中寻找相应的项,并改掉
exportconststate=()=>({
breadcrumbData:[]
})
exportconstmutations={
setBreadcrumb(state,breadcrumbData){
state.breadcrumbData=breadcrumbData
},
setBreadcrumbByName(state,{oldName,newName}){
letcurBreadcrumb=state.breadcrumbData.find(breadcrumb=>breadcrumb.name===oldName)
curBreadcrumb&&(curBreadcrumb.name=newName)
},
setBreadcrumbByPath(state,{path,name}){
letcurBreadcrumb=state.breadcrumbData.find(
breadcrumb=>breadcrumb.path===path
)
curBreadcrumb&&(curBreadcrumb.name=name)
}
}
根据路径匹配相应配置数据具体代码
importbreadcrumbsfrom'@/components/breadcrumb/breadcrumb.config.json'
functionpath2Arr(path){
returnpath.split('/').filter(p=>p)
}
functionmatchBreadcrumbData(matchPath){
returnpath2Arr(matchPath)
.map(path=>{
path=path.replace(/^:([^:?]+)(\?)?$/,(match,$1)=>{
return`_${$1}`
})
return'/'+path
})
.map((path,index,paths)=>{
//第0个不需拼接
if(index){
letresult=''
for(leti=0;i<=index;i++){
result+=paths[i]
}
returnresult
}
returnpath
})
.map(path=>{
constitem=breadcrumbs.find(bread=>bread.path===path)
if(item){
returnitem
}
return{
name:path.split('/').pop(),
path,
clickable:false,
isShow:true
}
})
}
exportdefault({app,store})=>{
app.router.beforeEach((to,from,next)=>{
consttoPathArr=path2Arr(to.path)
consttoPathArrLength=toPathArr.length
letmatchPath=''
//从matched中找出当前路径的路由配置
for(letmatchofto.matched){
constmatchPathArr=path2Arr(match.path)
if(matchPathArr.length===toPathArrLength){
matchPath=match.path
break
}
}
constbreadcrumbData=matchBreadcrumbData(matchPath)
store.commit('breadcrumb/setBreadcrumb',breadcrumbData)
next()
})
}
面包屑组件
面包屑组件中渲染匹配到的数据
{{item.name}}
cli命令实现
cli命令开发用到的相关库如下:这些就不细说了,基本上看下README就知道怎么用了
- commander:命令行工具
- boxen:在终端画一个框
- inquirer:命令行交互工具
- handlebar:模版引擎
目录结构
lib//存命令行文件 |--bcg.js template//存模版 |--breadcrumb//面包屑配置文件与组件,将生成在项目@/components中 |--breadcrumb.config.json |--index.vue |--braadcrumb.js//vuex相关文件,将生成在项目@/store中 |--new-page.vue//新文件模版,将生成在命令行输入的新路径中 |--route.js//路由前置守卫配置文件,将生成在@/plugins中 test//单元测试相关文件
node支持命令行,只需在package.json的bin字段中关联命令行执行文件
//执行bcg命令时,就会执行lib/bcg.js的代码
{
"bin":{
"bcg":"lib/bcg.js"
}
}
实现命令
实现一个init命令,生成相关面包屑文件(面包屑组件、json配置文件、前置守卫plugin、面包屑store)
bcginit
实现一个new命令生成文件,默认基础路径是 src/pages,带一个 -b选项,可用来修改基础路径
bcgnew-b
具体代码如下
#!/usr/bin/envnode
constpath=require('path')
constfs=require('fs-extra')
constboxen=require('boxen')
constinquirer=require('inquirer')
constcommander=require('commander')
constHandlebars=require('handlebars')
const{
createPathArr,
log,
errorLog,
successLog,
infoLog,
copyFile
}=require('./utils')
constVUE_SUFFIX='.vue'
constsource={
VUE_PAGE_PATH:path.resolve(__dirname,'../template/new-page.vue'),
BREADCRUMB_COMPONENT_PATH:path.resolve(__dirname,'../template/breadcrumb'),
PLUGIN_PATH:path.resolve(__dirname,'../template/route.js'),
STORE_PATH:path.resolve(__dirname,'../template/breadcrumb.js')
}
consttarget={
BREADCRUMB_COMPONENT_PATH:'src/components/breadcrumb',
BREADCRUMB_JSON_PATH:'src/components/breadcrumb/breadcrumb.config.json',
PLUGIN_PATH:'src/plugins/route.js',
STORE_PATH:'src/store/breadcrumb.js'
}
functioninitBreadCrumbs(){
try{
copyFile(source.BREADCRUMB_COMPONENT_PATH,target.BREADCRUMB_COMPONENT_PATH)
copyFile(source.PLUGIN_PATH,target.PLUGIN_PATH)
copyFile(source.STORE_PATH,target.STORE_PATH)
}catch(err){
throwerr
}
}
functiongenerateVueFile(newPagePath){
try{
if(fs.existsSync(newPagePath)){
log(errorLog(`${newPagePath}已存在`))
return
}
constfileName=path.basename(newPagePath).replace(VUE_SUFFIX,'')
constvuePage=fs.readFileSync(source.VUE_PAGE_PATH,'utf8')
consttemplate=Handlebars.compile(vuePage)
constresult=template({filename:fileName})
fs.outputFileSync(newPagePath,result)
log(successLog('\nvue页面生成成功咯\n'))
}catch(err){
throwerr
}
}
functionupdateConfiguration(filePath,{
clickable,
isShow
}={}){
try{
if(!fs.existsSync(target.BREADCRUMB_JSON_PATH)){
log(errorLog('面包屑配置文件不存在,配置失败咯,可通过bcginit生成相关文件'))
return
}
letpathArr=createPathArr(filePath)
constconfigurationArr=fs.readJsonSync(target.BREADCRUMB_JSON_PATH)
//如果已经有配置就过滤掉
pathArr=pathArr.filter(pathItem=>!configurationArr.some(configurationItem=>configurationItem.path===pathItem))
constquestions=pathArr.map(pathItem=>{
return{
type:'input',
name:pathItem,
message:`请输入${pathItem}的面包屑显示名称`,
default:pathItem
}
})
inquirer.prompt(questions).then(answers=>{
constpathArrLastIdx=pathArr.length-1
pathArr.forEach((pathItem,index)=>{
configurationArr.push({
clickable:index===pathArrLastIdx?clickable:false,
isShow:index===pathArrLastIdx?isShow:true,
name:answers[pathItem],
path:pathItem
})
})
fs.writeJsonSync(target.BREADCRUMB_JSON_PATH,configurationArr,{
spaces:2
})
log(successLog('\n生成面包屑配置成功咯'))
})
}catch(err){
log(errorLog('生成面包屑配置失败咯'))
throwerr
}
}
functiongenerating(newPagePath,filePath){
inquirer.prompt([
{
type:'confirm',
name:'clickable',
message:'是否可点击跳转?(默认yes)',
default:true
},
{
type:'confirm',
name:'isShow',
message:'是否展示面包屑?(默认yes)',
default:true
},
{
type:'confirm',
name:'onlyConfig',
message:'是否仅生成配置而不生成文件?(默认no)',
default:false
}
]).then(({clickable,isShow,onlyConfig})=>{
if(onlyConfig){
updateConfiguration(filePath,{clickable,isShow})
return
}
generateVueFile(newPagePath)
updateConfiguration(filePath,{clickable,isShow})
})
}
constprogram=newcommander.Command()
program
.command('init')
.description('初始化面包屑')
.action(initBreadCrumbs)
program
.version('0.1.0')
.command('new')
.description('生成页面并配置面包屑,默认基础路径为src/pages,可通过-b修改')
.option('-b,--basePath','修改基础路径(不要以/开头)')
.action((filePath,opts)=>{
filePath=filePath.endsWith(VUE_SUFFIX)?filePath:`${filePath}${VUE_SUFFIX}`
constbasePath=opts.basePath||'src/pages'
constnewPagePath=path.join(basePath,filePath)
log(
infoLog(
boxen(`即将配置${newPagePath}`,{
padding:1,
margin:1,
borderStyle:'round'
})
)
)
generating(newPagePath,filePath)
})
program.parse(process.argv)
if(!process.argv.slice(2)[0]){
program.help()
}
发布npm
开发完成后,发布到npm,具体方法就不细说了,发布后全局安装就能愉快的使用咯!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。