git 创建webpack项目_从0到1开发一个小程序cli脚手架(一)创建页面/组件模版篇...

github地址:

https://github.com/jinxuanzheng01/xdk-cli

cli工具是什么?

在正文之前先大致描述下什么是cli工具,

cli工具英文名command-line interface,也就是命令行交互接口,比较典型的几个case例如,create-react-app,vue-cli,具体可以去百度一下,下面gif是小打卡目前用的一套自动化发布工具?

744d377c603d7f9e2358ed85d3a14ff3.gif

可以看到整个发布流程大致是以选择或默认项的形式实现,大致分析下面几步

  • 选择打包形式    开发模式/debug模式/发布模式

  • 设置版本号

  • 填写发布信息

  • 选择环境

  • 是否提交版本commit

是不是非常无脑?是不是再也不用担心线上发错环境了?有了它就算不同项目间,就算一天发n次版本还需要担心什么呢?

当然除了简单的发布功能还,还可以做很多的事情,比如创建page/component模版等一些更多有趣的事情

为了节约版面就不贴图了,具体可以看下仓库  https://github.com/jinxuanzheng01/xdk-cli(目前该工具是从小打卡现有的cli库中抽离的部分功能)

明确痛点

也就是我为什么要做这么一个工具,其实最开始我只是为了解决一个问题,就是在整个发布流程中需要人工去改动/确认发布环境和版本信息,大致可以想象下把线下环境发布到线上的尴尬处境

后续发现从cli角度触发,很多东西都变得简单了,大致列了下:

  • 环境变量切换(线上环境,线下环境)

  • 创建启动模版,包括页面,组件

  • 自动化发布

  • ...

准备工作

本文会以快速创建页面模版文件为例教你怎么快速撸一个属于自己的cli工具,

如果觉得自己做比较麻烦,可以clone下我的仓库自己改装下

需要了解的三方库

中间会用到一些第三方库

  • commander, 一个解析命令行命令和参数工具

  • inquirer,常用交互式命令行用户界面的集合

  • chalk,美化你的终端输出样式

  • fuzzy,字符串模糊匹配的插件,根据输入关键词进行模糊匹配

  • json-format,json美化/格式化工具

其他的一些小知识:比如path模块,fs模块,大家可以去node官网自行查看:https://nodejs.org/api/

搭建开发环境

创建一个空文件夹,并且npm初始化, 并且创建一个index.js页面,这个index.js将作为你整个包的入口文件

npm init -y

安装上述的三方包,当然也可以后续按需安装,这样更能清楚每个包是做什么的

 npm install @moyuyc/inquirer-autocomplete-prompt commander chalk commander fuzzy inquirer json-format --save

在package.json里添加bin字段, 将自定义的命令软连到全局环境,同时执行npm link创建链接,这里如果报错{code EACCES,errno:13,...},是因为权限不足,可以尝试sudo npm link

 
"bin": {    "cli-demo": "./index.js"  }

在入口文件,index.js 行首加入一行#!/usr/bin/env node指定当前脚本由node.js进行解析

#!/usr/bin/env node// 输出文本console.log('Hello World!!!');

这时可以在命令行中执行 cli-demo验收一下成果了

74b8f1d032a882db6d270a4f64340dfd.png

ok,可以看到当在全局状态下输入自定义命令时,正确运行了入口文件,也就意味着的开发玩具已经搭建完成

Let‘ Go

整理逻辑

以快速创建页面模版文件为例,就需要考虑需要哪些逻辑:

  • 设置页面名称

  • 找到已有模版文件

  • copy到项目中

  • 修改app.json

识别命令行

在刚才的Hello World!!!环节,已经可以正确识别cli-demo,但是需要在一个cli工具中集成更多功能,可能需要有不同的执行策略,以git为例:git clone, git status,git push,所以需要识别不同的命令和参数,

是时候就需要用到commander这个第三方包帮助解析命令行参数了,当然你也可以自己撸一个lib,本质上还是方便解析process.argv

index.js (本质上这个js就是一个路由)#!/usr/bin/env nodeconst version                       = require('./package').version;                 // 版本号/* = package import-------------------------------------------------------------- */const program                       = require('commander');                         // 命令行解析/* = task events-------------------------------------------------------------- */const createProgramFs               = require('./lib/create-program-fs');           // 创建项目文件/* = config-------------------------------------------------------------- */// 设置版本号program.version(version, '-v, --version');/* = deal receive command-------------------------------------------------------------- */program    .command('create')    .description('创建页面或组件')    .action((cmd, options) => createProgramFs(cmd));/* 后续可以根据不同的命令进行不同的处理,可以简单的理解为路由 */// program//     .command('build [cli]')//     .description('执行打包构建')//     .action((cmd, env) => callback);/* = main entrance-------------------------------------------------------------- */program.parse(process.argv)

这时候当键入cli-demo create时会自动执行createProgramFs

// createProgramFs.jsmodule.exports = function () {    console.log('Hi, create-program-fs.js');};

命令行输入``cli-demo create``

11272c3712b25b7a04dbe66b43e64e17.png

可以看到已经成功的开辟出了一块独立的业务模块,后续就只需要依据需求填补相应的内容即可

创建交互命令

收到执行命令,这个时候按第一张图,是需要开始一系列QA(当然你也可以不做交互式,直接配置命令行参数),

引入三方包 inquirer,来指定问题队列

const question = [      // 选择模式使用 page -> 创建页面 | component -> 创建组件    {        type: 'list',        name: 'mode',        message: '选择想要创建的模版',        choices: [            'page',            'component',        ]    },        // 设置名称    {        type: 'input',        name: 'name',        message: answer => `设置 ${answer.mode} 名称 (e.g: index):`,    },];module.exports = function() {        // 问题执行    inquirer.prompt(question).then(answers => {        console.log(answers);    });};

5f6c834778fc525b108e197162a4705f.gif

可以看到通过一系列QA交互,实际输出拿到的是一个json对象,第一步已完成

创建模版文件

创建一个存放模版文件的文件夹template,并准备好你希望的模版

8f2efeabcde65ba9d7d949b91bd53bf3.png

项目中创建模版文件

为了方便阅读,下面的代码,需要明确下面变量的定义,

Config.dir_root  = 命令行执行目录

Config.root  = cli项目根目录

Config.appRoot = 小程序项目路径

Config.template = 模版目录

这里有两个点,一个是执行路径的问题,另一个是分包的问题,具体如下:

执行路径

这里一定要弄明白__dirname, process.cwd()的区别,同时还有一些小程序是自己搭的gulp/webpack,可能小程序项目是在src目录下,一定要分清楚

  • __dirname:被执行js文件的绝对路径,一般在index.js执行时缓存起来作为项目的全局路径,比如找到template文件夹就会使用 `${__dirname}/template`

  • process.cwd():当前命令行运行时的工作目录,比如在/Users/xuan/Documents/cli-demo

  • 如果当前项目在src,或其他文件夹里怎么办?可以提供一个给用户项目中的配置文件,类似于gulpfile.js或是webpack.config.js的形式,内容例如(具体可以看git仓库)

module.exports = {    // 小程序路径    app: './src',    // 模版文件夹    template: './template'};

可以看到对象中app属性,可以指定你当前小程序项目的路径

分包

因为小程序的分包机制会导致页面实际路径与在主包的路径不相符,例如:

  • 主包:pages/index/index

  • 分包:pages/main_module/pages/habit_enlist/habit_enlist

解决这个问题一方面是要有页面创建要有一定的规范,统一格式,另一方面需要根据规则解析app.json,

上面的主包,分包路径差不多是我目前使用的规范

解析app.json

// 获取app.jsonfunction getAppJson() {    let appJsonRoot = path.join(Config.appRoot, '/app.json');    try {        return require(appJsonRoot);    }catch (e) {        Log.error(`未找到app.json, 请检查当前文件目录是否正确,path: ${appJsonRoot}`);        process.exit(1);            // 异常退出    }}// 解析app.jsonlet parseAppJson = () => {    // app Json 原文件    let appJson = __Data__.appJson = getAppJson();    // 获取主包页面    appJson.pages.forEach(path => __Data__.appPagesList[getPathSubSting(path)] = '');    // 获取分包,页面列表    appJson.subPackages.forEach(item => {        __Data__.appModuleList[getPathSubSting(item.root)] = item.root;        item.pages.forEach(path => __Data__.appPagesList[getPathSubSting(path)] = item.root);    });};// __Data__.appPagesList = 小程序全部页面// __Data__.appModuleList = 小程序全部分包页面// item结构 {util_module: 'pages/util_module/'},这么定义结构是为了方便后续取数question队列里,增加删选分包的选项    // 设置page所属module    {        type: 'autocomplete',        name: 'modulePath',        message: 'Set page ownership module',        choices: [],        suggestOnly: false,        source(answers, input) {            // none 代表放在主包            return Promise.resolve(fuzzy.filter(input, ['none', ...Object.keys(__Data__.appModuleList)]).map(el => el.original));        },        filter(input) {            if (input === 'none') {                return '';            }            return __Data__.appModuleList[input];        },        when(answer) {            return answer.mode === 'page';        }    }

autocomplete类型本质上是个列表,但是可以进行模糊查询,非常方便,像小打卡有接近30个分包的情况下效果尤为明显

9ab39139d19d1952cf7eeb8bfb39a3b0.gif

有了文件名,有了分包路径,有了可供copy的模版,接下来就很简单了,把模版文件塞进项目就可以了,下面是一串从仓库里copy的代码,利用async/await很方便的写出一维代码,基本上的流程:

获取路径 -> 校验 -> 获取文件信息 -> 复制文件 -> 修改app.json -> 输出结果信息

async function createPage(name, modulePath = '') {    // 获取模版文件路径    let templateRoot = path.join(Config.template, '/page');    if (!Util.checkFileIsExists(templateRoot)) {        Log.error(`未找到模版文件, 请检查当前文件目录是否正确,path: ${templateRoot}`);        return;    }        // 获取业务文件夹路径    let page_root = path.join(Config.appRoot, modulePath, '/pages', name);    // 查看文件夹是否存在    let isExists = await Util.checkFileIsExists(page_root);    if (isExists) {        Log.error(`当前页面已存在,请重新确认, path: ` + page_root);        return;    }    // 创建文件夹    await Util.createDir(page_root);    // 获取文件列表    let files = await Util.readDir(templateRoot);    // 复制文件    await Util.copyFilesArr(templateRoot, `${page_root}/${name}`, files);    // 填充app.json    await writePageAppJson(name, modulePath);    // 成功提示    Log.success(`createPage success, path: ` + page_root);}

扩展

一个基本的快速创建页面模版的cli工具就这样完成,但是有可能需要更多的一些功能

自定义模版

比如说每个项目的模版都有可能不太一样,很大程度上需要根据项目进行定制,这时候可能就需要前文提到的给用户开放config文件的插槽了

项目中的config:

// xdk.config.jsmodule.exports = {    // 小程序路径    app: './',    // 模版文件夹    template: './template'};// create-program-fs.jsmodule.exports = function() {         // 校验:当前是否存在配置文件    let customConfPath = `${Config.dir_root}/xdk.config.js`;    if (!Util.checkFileIsExists(customConfPath)) {        Log.error('当前项目尚未创建xdk.config.js文件');        return;    }    // 获取用户配置项    let {app, template = ''} = require(customConfPath);    // 小程序目录    Config.appRoot = path.resolve(path.join(Config.dir_root, app));    // 模版文件目录(默认使用cli提供的默认模版,当config文件有设置template路径时,使用自定义路径)    !!template && (Config.template = path.resolve(path.join(Config.dir_root, template))));        // 问题执行    inquirer.prompt(question).then(answers => {        console.log(answers);    });};

发布的npm仓库

目前从开发到调试本质上是在本地提供服务,利用npm link提供软连接到全局PATH,

其实也可以直接发到npm上,让其他使用的该cli的成员一建安装,比如``npm install -g xxxxxxx``

教程的话百度,google有很多,作者表示很懒,遇到问题下面留言吧。。

最后

可以看到整个功能逻辑相对于平时写的复杂的业务逻辑来说相对简单,主要是工具库的一些使用方面的东西,中间的难点可能就是node中概念性的一些东西,然而这些多看一下文档基本就可以解决,希望大家可以从本文中了解到如何快速搭建一个属于自己的cli工具

顺便预告下后续的话可能会更新一些如何利用cli工具做到自动化发布,版本号控制,环境变量切换,自动生成文档等一系列有趣的功能

65c3b0635ab28a80f1a861b6db171f15.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/548006.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nginx 502 Bad Gateway 错误的原因及解决方法

2019独角兽企业重金招聘Python工程师标准>>> 刚才在调试程序的时候,居然服务器502错误,昨天晚上也发生了,好像我没有做非常规的操作。 然后网上寻找了下答案, 把一些原因及解决方法汇总一下,以防生产环境下…

easyExcel实现导入数据库

目录标题需求easyExcel Maven 依赖过程效果需求 我们需要将excel的数据导入至数据库中。 easyExcel Maven 依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version></dep…

CentOS7 shell脚本安装jdk

1&#xff0c;系统环境 操作系统 CentOS Linux release 7.6.1810 (Core) 64位 下载 Java SE Development Kit 8u152&#xff0c;文件名&#xff1a;jdk-8u152-linux-x64.rpm&#xff0c;下载地址&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/ja…

如何解决在使用ElementUI时发现有些控件是英文的

如下图 解决 我们需要在入口文件 main.js 中将 ElementUI 的默认语言改为中文。 import locale from element-ui/lib/locale/lang/en // lang i18n 英文 import zhLocale from element-ui/lib/locale/lang/zh-CN // 中文// 选择elementUi 默认语言为中文 Vue.use(ElementUI, …

CentOS7 源码编译安装Python3 shell脚本

1&#xff0c;系统环境 操作系统 CentOS Linux release 7.6.1810 (Core) 64位 2&#xff0c;Linux的Python3安装后即集成了pip&#xff0c;无需重新独立安装pip&#xff0c;执行以下命令完成python3和pip3的安装 yum groupinstall -y "Development tools" # 安…

ElementUI+Java实现搜索提示列表

效果 实现流程 首先我们需要在后端获取数据&#xff0c;我们可以根据name属性去模糊查询&#xff0c;返回Map类型的列表 然后将它返回给前端。 controller ApiOperation("根据关键字查询讲师名列表")GetMapping("list/name/{key}")public ResultVo sele…

CentOS7 搭建Pulsar 消息队列环境,CentOS(Linux)部署Pulsar,亲测成功,以及Python操作Pulsar实例驱动

在 最佳开源数据库与数据分析平台奖 中&#xff0c;之前曾连续两年入选的 Kafka 意外滑铁卢落选&#xff0c;取而代之的是新兴项目 Pulsar&#xff0c;Bossie Awards中对 Pulsar 点评如下&#xff1a;“Pulsar 旨在取代 Apache Kafka 多年的主宰地位。Pulsar在很多情况下提供了…

SpringSecurity +Jwt 实现权限管理

目录标题原理架构图demo的项目结构JwtTokenUtilRestAuthenticationEntryPoint 和 RestfulAccessDeniedHandlerMyUserDetailsServiceJwtAuthenticationTokenFilterSecurityConfigControllerPostman 测试为了方便&#xff0c;未采用是从数据库读取的方式。工具类都是别人那偷的&a…

FreeMarker_模板引擎_代码自动生成器_源码下载

首先我们先来认识一下Freemarker 1.what is the FreeMarker? 你可以到freemarker的官网上去&#xff0c;那里有很详细的介绍&#xff1a;http://freemarker.org/ 这里大概说一下&#xff1a;FreeMarker是一个用Java语言编写的模板引擎&#xff0c;它基于模板来生成文本输出。 …

CentOS7 搭建Kafka消息队列环境,以及Python3操作Kafka Demo

Kafka适合什么样的场景? 它可以用于两大类别的应用: 构造实时流数据管道&#xff0c;它可以在系统或应用之间可靠地获取数据。 (相当于message queue)构建实时流式应用程序&#xff0c;对这些流数据进行转换或者影响。 (就是流处理&#xff0c;通过kafka stream topic和topi…

hive序列生成_常见的序列化框架及Protobuf原理

享学课堂作者&#xff1a;逐梦々少年转载请声明出处&#xff01;上次我们详细的学习了Java中的序列化机制&#xff0c;但是我们日常开发过程中&#xff0c;因为java的序列化机制的压缩效率问题&#xff0c;以及序列化大小带来的传输的效率问题&#xff0c;一般很少会使用原生的…

decode语句不能再嵌套_自学C++基础教程【流程控制语句】(for、while 、do while 语句 )...

for语句for语句是C语言所提供的一种功能广泛的循环语句。下图为for语句的标准形式&#xff1a;表达式1&#xff1a;通常用于给循环变量赋初值&#xff0c;一般是赋值表达式。表达式2&#xff1a;通常用于设立循环条件&#xff0c;一般为关系表达式或逻辑表达式。表达式3&#x…

CentOS 7 利用Docker搭建禅道系统

1&#xff0c;系统环境 a&#xff0c;操作系统 CentOS Linux release 7.6.1810 (Core) 64位 b&#xff0c;确保Docker环境已经安装&#xff0c;具体教程请看 CentOS 安装docker 禅道系统一键安装说明文档&#xff1a;http://www.zentao.net/book/zentaopmshelp/90.html …

centos7 docker删除端口映射_centos7安装docker,结合docker安装mysql,学习简单使用

需要快速安装centos7的可以结合上一遍文章vagrant结合virtualbox让你直接在cmd窗口操作linux系统centos7地址&#xff1a;https://www.toutiao.com/i6858180977164812811/?group_id6858180977164812811Docker先说一下个人理解&#xff1a;docker其实就是一个工具&#xff0c;镜…

MongoDB中关于64位整型存储解决方案

为什么80%的码农都做不了架构师&#xff1f;>>> 社区内一哥们smcboy 提出关于php中操作MongoDB存储整数问题&#xff0c;找到点资料花点时间翻译过来&#xff0c;是个很好的学习方式。红薯 那篇讨论我的修改回复&#xff0c;仍然没有更新可恶啊~&#xff01;&#…

切割图形_泉州泡沫景观字切割机厂家

泉州泡沫景观字切割机厂家 jz4rw0qv泉州泡沫景观字切割机厂家 巨源线条切割机同步带型结构合理、性能、精密度高、、操作简便、价格合理&#xff0c;比同行业同款机床更高&#xff0c;是原有同步带型泡沫切割机的替代产品。自动编程使用计算机利用配合切割机应用&#xff0c;只…

你的搜索其实很糟糕?

为什么80%的码农都做不了架构师&#xff1f;>>> 日期&#xff1a;2013-3-27 来源&#xff1a;GBin1.com 尽管你非常擅长搜索&#xff0c;但是很多时候搜索内容和你想要的并不吻合。事实上&#xff0c;用户体验专家Jakob Nielsen认为大多数人都非常的不擅长搜索。…

Element Tree型控件

效果 前端 <template><div class"app-container"><el-inputplaceholder"输入关键字进行过滤"、<! -- 双向绑定-- >v-model"filterText"></el-input><el-tree ref"tree":data"subjectList"…

快速根据注释生成接口文档网页工具——Apidoc的使用教程

环境&#xff1a; 操作系统 CentOS Linux release 7.6.1810 (Core) 64位 服务器环境 “腾讯云”服务器 1&#xff0c;安装Node.js的npm工具环境&#xff1a; 如有不懂&#xff0c;请看我的博客&#xff1a;CentOS7 源码编译安装NodeJS 最新版本 2&#xff0c;npm环境搭…

频段表_5G频段范围之:频段3.3GHz-4.2GHz (n77,n78)

本文版权归“5G通信(tongxin5g)”和5G哥所有&#xff0c;未经授权&#xff0c;请勿转载比起以前的移动通信网络&#xff0c;5G探索的新频谱范围包括&#xff1a;3.3GHz-4.2GHz&#xff0c;4.4GHz-5.0GHz&#xff0c;24.25-29.5 GHz今天主要看频段3.3GHz-4.2GHz在3GPP中&#xf…