Windows PowerShell

版权所有 (C) Microsoft Corporation。保留所有权利。

尝试新的跨平台 PowerShell https://aka.ms/pscore6

PS C:\Users\admin> hello

hello world在使用vue-cli的过程中,常用的webpack模板只为我们提供最基础的内容,但每次需要新建一个项目的时候就需要把之前项目的一些配置都搬过来,这样就造成挺大的不方便,如果是作为一个团队,那么维护一个通用的模板,我认为是挺有必要的。

前置知识-可执行模块

npm上边不仅仅存在一些用来打包、引用的第三方模块,还有很多优秀的工具(包括部分打包工具),他们与上边提到的模块的区别在于,使用npm install XXX以后,然后就可以通过命令行直接运行了,如各种脚手架工具、webpack命令等等。

举个例子

hello/packge.json

...
"bin": {
"hello": "./bin/index.js"
}
...

hello/bin/index.js配置

#!/usr/bin/env node,用于指明该脚本文件要使用node来执行。同时也解决了不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件。

还可以指定为 #!/usr/bin/env bash

#!/usr/bin/env node
console.log("hello world");

发包之后,npm install demo  -g。

打开cmd,输入hello按下回车

Windows PowerShell

版权所有 (C) Microsoft Corporation。保留所有权利。

尝试新的跨平台 PowerShell https://aka.ms/pscore6

PS C:\Users\admin> hello

hello world

npm link的使用

在开发阶段,为了验证效果,不可能没改一次都要上传发版。

我们可以npm lin命令 创建全局模块一个连接,指向当前目录

这样每次改动 就会同步到全局

npm link用来在本地项目和本地npm模块之间建立连接,可以在本地进行模块测试

具体用法:

  1. 项目和模块在同一个目录下,可以使用相对路径

    npm link ../module

  2. 项目和模块不在同一个目录下

    cd到模块目录,npm link,进行全局link

    cd到项目目录,npm link 模块名(package.json中的name)

  3. 解除link

    解除项目和模块link,项目目录下,npm unlink 模块名

    解除模块全局link,模块目录下,npm unlink 模块名

前置知识-inquirerJs

它是一个一个NodeJs交互式命令行工具。

力争成为Node.js的易于嵌入且美观的命令行界面。

提供用户界面和查询会话流。

#!/usr/bin/env node
const inquirer = require('inquirer'); inquirer
.prompt([{
type: "input",
name: "name",
message: "项目名称:",
default: "hello-word"
}])
.then(answers => {
console.log(answers);
})

PS C:\Users\admin> hello

? 项目名称: 测试

前置知识-shelljs

点此进入官方文档

它是Node下的脚本语言解析器,具有丰富且强大的底层操作(Windows/Linux/OS X)权限。

用js代码中编写shell命令实现功能。

它的底层实现是利用‘Nodejs下引入模块child_process实现调用shell’

child_process模块是nodejs的一个子进程模块,可以用来创建一个子进程,并执行一些任务,比如shell命令

原生执行shell

const childProcess = require('child_process');
childProcess.exec('dir', (err, sto)=> {
console.log(err);
console.log(sto);
})

PS C:\Users\admin\Desktop\hello> node .\bin\index.js

C:\Users\admin\Desktop\hello

2021/01/25  14:48

.

2021/01/25  14:48              ..

2021/01/25  14:29              bin

2021/01/25  14:48              node_modules

2021/01/25  14:29                68 package-lock.json

2021/01/25  14:48               315 package.json

2021/01/25  14:48            10,172 yarn.lock

shelljs执行shell

const shell = require('shelljs');
shell.exec('dir');

效果和原生一样

不过为了抹除平台差异,shell自己也封装了一些方法,比如ls

const shell = require('shelljs');
shell.ls('-l', 'C://Users/admin/Desktop/hello').forEach((file)=>{
console.log(file.name);
})

前置知识-chalkJs

一般情况下我们给log加颜色,写起来有点费劲。比如说

console.log("%c%s", "color: deeppink; font-size: 18px;", "hello World")



使用三方包

const chalk = require('chalk');
console.log(chalk.blue('Hello world!'));

思路

我们先创建一个通用模板

模板里某个文件夹放一些备用资源

通过inquirerJs来收集用户的输入,

然后通过shelljs来下载模板

通过手机用户的信息来修改模板内容。

修改的内容大致有以下信息:

全局替换用户输入的项目名字

根据用户选择的三方包,来动态修改packge.json和src相关信息

代码

首先创建一个项目,结构如下

配置入口文件

packge.json

"bin": {
"aegis": "./bin/index.js"
}

主程序

index.js如下

#!/usr/bin/env node

const inquirer = require('inquirer');
const shell = require('shelljs');
const fs = require('fs');
const path = require('path');
const chalk = require('chalk'); //当前cli的名称
console.log(chalk.yellowBright.bold('---------------------------------------\n 欢迎使用aegis-cli \n---------------------------------------')); const qs = [
{
type: "input",
name: "name",
message: "项目名称:",
default: "hello-word"
},
{
type: 'list',
name: 'type',
message: '创建项目类型:',
choices: [
"pc",
"mobile"
]
}, {
type: "checkbox",
message: "选择依赖库:",
name: "lib",
choices: [
{
name: "vuex"
},
{
name: "vue-router"
}
]
}, {
type: "confirm",
message: "是否需要我帮你安装依赖(可能会比较慢)?",
name: "autoInstall",
default: false
}]; inquirer
.prompt(qs)
.then(answers => {
console.log(answers); // shell.exec(`git clone ${tempUrl}`,
console.log(chalk.greenBright(
'-----项目信息-----'+'\n'+
'名称:' + answers.name + '\n'+
'类型:' + answers.type + '\n'+
'三方依赖库:' + answers.lib + '\n'+
'自动安装依赖:' + answers.autoInstall
)); inquirer
.prompt([
{
type: "confirm",
message: "您的项目配置如上,是否开始生成项目?",
name: "start",
default: true
}
])
.then(answers2 => {
if(!answers2.start){
return false;
}
const tempName = answers.type === 'pc' ? 'aegis-pc-template' : 'aegis-pc-template';
const tempUrl = `http://git2.aegis-info.com/fe-resources/${tempName}.git`;
const tempPath = path.resolve(`./${tempName}`);
const packgePath = `${tempPath}/package.json`;
const readMePath = `${tempPath}/README.md`;
const rootPath = path.resolve('./');
const finalProjectPath = path.resolve('./' + answers.name); shell.rm('-rf', tempPath);
shell.rm('-rf', finalProjectPath);
shell.exec(`git clone ${tempUrl}`, null, () => {
// 处理pc或移动端的库
const handleUi = () => {
const data = fs.readFileSync(path.resolve(tempPath + '/src/main.ts'), 'utf8').split('\n')
if (answers.type === 'pc') {
data.splice(2, 0, `import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);`)
// 添ui依赖
shell.sed('-i', `"dependencies": {`, `"dependencies": {
"element-ui": "^2.15.0",`, packgePath);
} else {
data.splice(2, 0, `import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);`)
// 添ui依赖
shell.sed('-i', `"dependencies": {`, `"dependencies": {
"vant": "^2.9.3",`, packgePath);
}
fs.writeFileSync(path.resolve(tempPath + '/src/main.ts'), data.join('\n'), 'utf8');
} // 如果选择了vuex
const handleVuex = () => {
shell.mv(path.resolve(tempPath + '/multiple-version/store'), path.resolve(tempPath + '/src'));
// shell.sed('-i', 'render: h => h(App)', 'render: h => h(App) store', path.resolve(tempPath+'/src/main.ts'));
const data = fs.readFileSync(path.resolve(tempPath + '/src/main.ts'), 'utf8').split('\n')
data.splice(2, 0, `import store from './store'`)
data.splice(8, 0, ` store`)
fs.writeFileSync(path.resolve(tempPath + '/src/main.ts'), data.join('\n'), 'utf8');
} // 如果选择了router
const handleRouter = () => {
shell.mv(path.resolve(tempPath + '/multiple-version/router'), path.resolve(tempPath + '/src'));
shell.mv(path.resolve(tempPath + '/multiple-version/views'), path.resolve(tempPath + '/src'));
shell.mv(path.resolve(tempPath + '/multiple-version/App.vue'), path.resolve(tempPath + '/src'));
const data = fs.readFileSync(path.resolve(tempPath + '/src/main.ts'), 'utf8').split('\n')
data.splice(2, 0, `import router from './router'`)
data.splice(8, 0, ` router`)
fs.writeFileSync(path.resolve(tempPath + '/src/main.ts'), data.join('\n'), 'utf8');
} handleUi();
if (answers.lib.indexOf('vuex') > -1) {
handleVuex()
}
if (answers.lib.indexOf('vue-router') > -1) {
handleRouter()
} // 修改项目名字
shell.sed('-i', tempName, answers.name, packgePath);
shell.sed('-i', tempName, answers.name, readMePath);
shell.mkdir('-p', answers.name);
shell.mv(path.resolve(tempPath + '/*'), path.resolve(answers.name));
shell.mv(path.resolve(tempPath + '/.[^.]*'), path.resolve(answers.name));
shell.rm('-rf', tempPath); // 删除模板备选
shell.rm('-rf', path.resolve(`${finalProjectPath}/multiple-version`));
shell.rm('-rf', path.resolve(`${finalProjectPath}/.git`));
shell.rm('-rf', path.resolve(`${finalProjectPath}/.git`)); // 帮助安装项目依赖
if (answers.autoInstall) {
console.log(8888);
shell.exec(`cd ${finalProjectPath} && yarn`);
} })
}) });

helper.js

var fs = require('fs');
var path = require('path'); //往固定的行写入数据
const writeFileToLine = (value)=>{
let basePath = path.resolve('./');
let data = fs.readFileSync(basePath+'/template.appcache', 'utf8').split(/\r\n|\n|\r/gm); //readFileSync的第一个参数是文件名
data.splice(data.length - 5, 0, ...value);
fs.writeFileSync('./manifest.appcache', data.join('\r\n'))
} module.exports = {
writeFileToLine
}

总结

实现本身并不难,

重点在于要理解所需,然后逐步实现

对于用户而言操作

对于cli而言

使用 CLI 命令,可以让开发人员更编写项目更简单的创建工具,简单来说可以为公司做一个自定义代码生成器。

在次之前,在使用vue-cli的过程中,cli只为我们提供最基础的内容,但每次需要新建一个项目的时候就需要把之前项目的一些配置都搬过来,这样就造成挺大的不方便,如果是作为一个团队,那么维护一个通用的模板,是挺有必要的。

其它

可以使用commanderJs来实现 直接命令行参数交互,这样就可以实现

vue create xxx

但是这个不是必须的

前端自定义cli的更多相关文章

  1. 为你的AliOS Things应用增加自定义cli命令

    摘要: 怎么才能在RTOS系统中,通过 串口shell控制LED的开关. 在日常嵌入式开发中,我们经常会用串口命令来使设备进入某种特定的状态,或执行某个特定的操作.如系统自检,模拟运行,或者进入手动模 ...

  2. Django前端获取后端数据之前端自定义函数

    在写网站的时候遇到了一个问题: Django在后端向前端传数据时,多数会使用dict字典来传送多个数据,但前端只能遍历,没有一个用key取到value值的方法可以直接使用 如果作为一个list传递到前 ...

  3. Django中利用filter与simple_tag为前端自定义函数的实现方法

    转自:http://www.jb51.net/article/116303.htm 前言 Django的模板引擎提供了一般性的功能函数,通过前端可以实现多数的代码逻辑功能,这里称之为一般性,是因为它仅 ...

  4. 前端 自定义format函数

    为字符串创建format方法,用于字符串格式化  {# 前端没有字符串占位符%s的替代方法,以下是自定义字符串替换的方法,以后前端拓展方法都可以使用下面的形式 #} String.prototype. ...

  5. [前端][自定义DOM事件]不使用setTimeout实现双击事件或n击事件

    使用setTimeout实现双击事件 例如,这样: let div = document.getElementById("div"); doubleClick(div, funct ...

  6. 前端——Vue CLI 3.x搭建Vue项目

    一.Node安装 windows 1. Node.js (>=8.9, 推荐8.11.0+) Node官网下载 .msi 文件,按步骤下载安装即可. 安装完之后在cmd中输入 node -v,若 ...

  7. 前端 | 自定义组件 v-model:Vue 如何实现双向绑定

    v-model 是 Vue 中一个常用的指令,常用于表单中的数据绑定.如下基本用法想必大家都很熟悉,data 中的 checked 属性的值就会随着多选框的状态实时变化. <el-checkbo ...

  8. 前端自定义format函数,做字符串格式化功能

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. ueditor 百度编辑器图片上传 接 node.js 及一些前端自定义

    百度编辑器 用node.js 做服务端 demo 大神已整理的 记录一下 以作参考 https://github.com/netpi/ueditor 1. 前端图片工具栏上传input file在这里 ...

  10. easyUI 之datagrid 在前端自定义排序

    首先先来看一下,直接从后台读取数据并展示到前端的列表,后端传回的数据是“按商品ID倒序排列” 前端源代码 $('#good_tables').datagrid({ nowrap: true, auto ...

随机推荐

  1. 重磅!微信官方恢复了个人红包封面的制作入口,限时开放!!.md

    前两天微信开放了个人红包封面,引起了大家欢呼雀跃~ 可惜--没几个小时,因为一个不可描述的原因,官方小程序下架了-- 但是, 现在好消息来啦, 官方又恢复个人红包封面制作啦~ 本文教你如何制作红包封面 ...

  2. CLI命令行应用

    前言 针对golang这门高级语言,主要想了解它的语言特性还有服务器建站还有微服务搭建方面的用途,以下都可以算是使用记录. 一.命令行应用的标准库实现 很多语言都有针对命令行参数的功能包,比如pyth ...

  3. Python科学计算系列9—逻辑代数

    1.基本定理的验证 代码如下: from sympy import * A, B, C = symbols('A B C') # 重叠律 # A·A=A A+A=A print(to_cnf(A | ...

  4. Java连接Redis常用操作

    1.去重 package Data; import redis.clients.jedis.Jedis; public class TestRedisUniq { public static Jedi ...

  5. MySQL插入异常:SQL state [HY000]; error code [1366]-----(utf8mb4)

    发现爬虫软件,爬取数据不及时,查询服务器日志发现异常: SQL state [HY000]; error code [1366] java.sql.SQLException: Incorrect st ...

  6. Sentinel源码—8.限流算法和设计模式总结

    大纲 1.关于限流的概述 2.高并发下的四大限流算法原理及实现 3.Sentinel使用的设计模式总结 1.关于限流的概述 保护高并发系统的三把利器:缓存.降级和限流.限流就是通过限制请求的流量以达到 ...

  7. GitLab CI/CD 的配置文件 .gitlab-ci.yml 简介

    〇.前言 .gitlab-ci.yml 文件主要用于项目的自动化部署配置,自动化可以大大提升团队效率,但同时这个文件的内容也比较复杂,弄清楚也并非易事,本文将对此文件的内容进行简单介绍,供参考. 另外 ...

  8. K8s新手系列之Pod的重启策略

    概述 K8s中Pod的重启策略具有确保服务连续性.保证任务完整性.提升资源利用效率.便于故障排查的作用 Pod的重启策略可以根据restartPolicy字段定义. 重启策略适用于pod对象中的所有容 ...

  9. Spring编程式事务控制

    目录 Spring编程式事务控制 代码实现 测试 Spring编程式事务控制 实际中很少使用 代码实现 pom.xml <?xml version="1.0" encodin ...

  10. 操作系统综合题之“用记录型信号量机制的wait和signal操作来解决了由北向南和由南向北过河人的同步问题(独木桥问题-代码补充)”

    1.问题:一条哦东西走向河流上,有一根南北走向的独木桥,要想过河只能通过这根独木桥.只要人们朝着相同的方向过独木桥,同一时刻允许有多个人可以通过.如果在相反的方向上同时有两个人过独木桥则会发生死锁.如 ...