搭建文件
mkdir vue-auto-router-cli
cd vue-auto-router-cli
npm init -y
npm i commander download-git-repo ora handlebars figlet clear chalk open -s
download-git-repo
作用是从传入的连接中下载并提取一个 git 存储库(如:GitHub,GitLab,Bitbucket),更详细的在这ora
可以理解为 loading 吧handlebars
可以理解为将数据和视图分离的一个插件,figlet
使你的 log 更炫酷?!clear
清里 cmd 面板了。chalk
例子:chalk.blue(‘Hello world!’);明白作用了吧。open
字面意思。
文件目录
vue-auto-router-cli
├──bin #程序执行入口
├────mycli.js #整个程序入口文件
├──node_modules
├──package-lock.json
├──package.json
mycli.js
#!/usr/bin/env node
// 以sheel 脚本的形式来执行
// 执行解释器类型是node。使用node去执行
// 程序执行入口bin
// 整个程序入口文件mycli
console.log('cli.....')
修改package.json
"bin":{
"mycli" : "./bin/mycli.js"
},
npm link
在项目目录下执行,相当于把入口文件链接到全局
当在其他文件下执行,mycli 这个指令,都会打印出来./bin/mycli.js
文件里面的内容
mycli.js
// 定制命令行界面的介绍
// 比如我们执行 vue cli 的时候,出来的命令行界面
const program = require('commander')
// -v 时候 的版本号。我们直接用package上的version字段
program.version(require('../package.json').version)
// 创建,命令,命令行定制
// <name> 代表参数
// 策略模式开发
program
.command('init <name>')
.description('init project') //描写
.action(name=>{ // 做的事情
console.log('init'+ name)
}
)
program.parse(process.argv)// 现在进程的argv,最后一行是固定的
打印process.argv的结果
process.argv [ '/Users/chensi/nvm/versions/node/v8.16.0/bin/node',
'/Users/chensi/nvm/versions/node/v8.16.0/bin/mycli',
'init',
'abc' ]
实现init函数的功能
下载代码
下载依赖
目录结构
根目录下:
├──lib
├────download.js
├────init.js
lib/download.js
`const {promisify} =require('util')`
// 实现一个clone函数,远程去拉取
// 是一个异步函数
module.exports.clone = async function (repo,desc) {
// repo 从哪拉
// desc 拉到哪里去
const download = promisify(require('download-git-repo'))
//进度条
const ora = require('ora')
// 进度条内容
const process = ora(`下载....${repo}`)
// 开启进度条
process.start()
// 开始下载
await download(repo,desc)
//显示完成
process.succeed()
}
lib/init.js
// 打印欢迎界面
const {clone} =require('./download')
const {promisify} = require('util')
const figlet = promisify(require('figlet'))
const clear = require('clear')
const chalk = require('chalk')
const log=content=>console.log(chalk.green(content))
module.exports = async name=>{
//清屏幕
clear()
// 打印
const data= await figlet('mycli-ohh Welocome')
log(data)
}
bin/mycli.js
修改命令的执行结果
.action(require('../lib/init')
下载代码
bin/mycli.js
下载vue框架
module.exports = async name=>{
//清屏幕
clear()
// 打印
const data= await figlet('mycli-ohh Welocome')
log(data)
// 克隆
log('🚀创建项目:'+name) // name是传进来的参数,创建的项目名称
// 当我们去执行 myclie init abc 时,相当于把项目克隆下来,放在abc文件里面
// 别人在vue-cli的工具上,又添加了渲染模板;如果是真是的项目开发,可以直接去vue的git地址拉取
await clone('https://github.com/vuejs/vue-cli.git', name)
}
在执行
mycli init abc
下载成功
在目录下创建了abc这个文件
安装依赖
安装依赖,就是执行npm install 方法
使用spawn 方法:通过生成一个子进程,去执行指定的命令child_process.spawn(command[, args][, options])
// 打印欢迎界面
const {clone} =require('./download')
const {promisify} = require('util')
const figlet = promisify(require('figlet'))
const clear = require('clear')
const chalk = require('chalk')
const log=content=>console.log(chalk.green(content))
// -- 改造spawn
// ------两步操作:
// --------- 1.对接输出流
// --------- 2.promisify,变成promise函数,会方便些,原生的promisify是一个回调函数
const spawn =async(...args)=>{
// 原生的spawn
const {spawn} = require('child_process')
// 封装 promise 方法;返回执行承诺
return new Promise(resolve=>{ // 回调 resolve
// 1. 定义一个线程 , 子进程
// --------- 执行spwan 方法,使用的子进程
// --------- 如果执行参数放在args里面,返回一个子进程
const proc = spawn(...args)
//2.子、主进程对接
// --------- 子进程执行的时候,是有一个输出流,也是一个流,
// --------- 希望把子进程执行的流和主进程的流,进行对接
// 对接的好处是,子进程的流都可以在主进程看到
proc.stdout.pipe(process.stdout) // stdout 正常流
proc.stderr.pipe(process.stderr) // stderr 异常流
// 3.回调
// --------- 当某一个指令完成的时候,会执行close,比如执行npm install,之后就会执行这个close
// --------- close执行,之后返回执行承诺
proc.on('close',()=>{
resolve()
})
})
}
module.exports = async name=>{
//清屏幕
clear()
// 打印
const data= await figlet('mycli-ohh Welocome')
log(data)
// 克隆
log('🚀创建项目:'+name) // name是传进来的参数,创建的项目名称
// 当我们去执行 myclie init abc 时,相当于把项目克隆下来,放在abc文件里面
await clone('github:su37josephxia/vue-template', name)
// 安装依赖 执行npm install。 子进程的方式去执行,使用spwan来执行
// 需要对spawn 进行一个小小的改造;比如安装了什么包,输出可以看到
log('安装依赖')
// npm是命令 npm后面接的都是参数 install是参数。所以后面的参数是以数组的形式去表示
await spawn('npm', ['install'],{cwd: `./${name}`})
// {cwd: `./${name}`} 要求在某一个文件夹下面执行
// 当我们在执行 ohh init abc; 但是我们实际上是想在abc这个文件下去执行npm install
// 日志
log(chalk.green(`
安装完成:
To get Start:
================
cd ${name}
npm run serve
================
`))
}
启动项目
利用open函数,启动项目
const open= require('open')
// 打开浏览器
module.exports = async name=>{
..........
// 在安装完成之后
open(`http://localhost:8080`);
await spawn('npm', ['run', 'serve'], {cwd:`./${name}`})
}
约定路由功能
目的:
当我们在views文件夹中添加新的 vue文件时,
router.js,也需要新增加载路径
App.vue 中也需要添加菜单
新加一个视图有两个地方需要变动,router.js需要加一个项目,App.vue也需要加一项。
接下来需要做的就是让他自动产生,类似于umi的命令
步骤:
1.首先用fs读取views里面的文件,根据文件的列表,自动去生成新的router.js里面新的代码。简单的说就是字符串拼接。复杂一些,可以利用模版工具进行渲染。类似组装vue代码。
- 利用模板自动渲染;事先设置两个template模板,App.vue 和 router.js;这两个代码模板。模板-router.js里面进行拼装
- 把渲染好的代码放在指定位置即可
abc/template/App.vue.hbs
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link>
{{#each list}}
| <router-link to="/{{name}}">{{name}}</router-link>
{{/each}}
</div>
<router-view/>
</div>
</template>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
abc/template/router.js.hbs
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{{#each list}}
{
path: '/{{name}}',
name: '{{name}}',
component: () => import('./views/{{file}}')
},
{{/each}}
]
})
lib/refresh.js
const fs = require('fs')
// Handlebars 是一种简单的 模板语言。
// 它使用模板和输入对象来生成 HTML 或其他文本格式。Handlebars 模板看起来像常规的文本,但是它带有嵌入式的 Handlebars 表达式 。
const handlebars = require('handlebars')
// 粉笔
const chalk = require('chalk')
module.exports = async()=>{
// 获取列表
const list =fs.readdirS~~~~ync('./src/views')
.filter(v=>v!=='Home.vue')
.map(v=>({
name:v.replace('.vue', '').toLowerCase(),
file:v
}))
// 生成路由
compile({list}, './src/router.js', './template/router.js.hbs')
//生成菜单
compile({list}, './src/App.vue', './template/App.vue.hbs')
/**
* 编译模板
* @param {*} meta 数据
* @param {*} filePath 目标文件
* @param {*} templatePath 模板文件 通过什么去编译
*/
function compile(meta, filePath,templatePath){
// 判断模板是否 存在
// 读成一个字符串
const content = fs.readFileSync(templatePath).toString()
//-柯理化方法;两层
// -------1.生成编译 输出 生成编译函数(生成代码的代码函数),类似于vue 的compile生成的是render函数;
// -------2. meta 真正的数据
const result= handlebars.compile(content)(meta)
// 写入
fs.writeFileSync(filePath, result)
console.log(chalk.green(`🚀 ${filePath} 创建成功`))
}
}
bin/mycli.js
#!/usr/bin/env node
// 以sheel 脚本的形式来执行
// 执行解释器类型是node。使用node去执行
// 程序执行入口bin
// 整个程序入口文件mycli
// console.log('cli.....')
// 定制命令行界面的介绍
// 比如我们执行 vue cli 的时候,出来的命令行界面
const program = require('commander')
// -v 时候 的版本号。我们直接用package上的version字段
program.version(require('../package.json').version)
// 创建,命令,命令行定制
// <name> 代表参数
program
.command('init <name>')
.description('init project') //描写
.action(require('../lib/init')
)
program
.command('refresh')
.description('refresh routers..')
.action(require('../lib/refresh'))
console.log('process.argv', process.argv)
program.parse(process.argv)// 现在进程的argv,最后一行是固定的
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。