github地址:github.com/jinxuanzhen… 以为有用的朋友帮忙给项目一个star,谢谢javascript
接上文 《从0到1开发一个小程序cli脚手架(一)--建立页面/组件模版篇》html
上文你们应该大体学会了怎么搭建一个cli脚手架,包括实现了一个快速生成启动模版的功能,本质上做为脚手架应该能够作更多的事情,本篇文章会实现一些新的功能,例如:自动发布体验版,版本号控制,环境变量控制java
不知道你们有没有一天发屡次版本或者一天给多个小程序发版的经历,按照微信正常的发布流程,须要:node
其中全部的1,2步为手工修改config文件,第5步是确认手工修改config文件的正确性,毕竟人总会犯错,做者表示就干过线下环境发布到测试环境的事情,并且这是在作了第5步的状况下,很遗憾没有仔细核对webpack
为了避免再次发生一样的事情致使引咎辞职,那么有没有更好的方法呢 ?天然是有的,既然人不可靠,那么直接把它流程化天然就能够了git
最好阅读了上篇文章《从0到1开发一个小程序cli脚手架(一)--建立页面/组件模版篇》,并搭建了一个简单的demogithub
须要了解微信小程序提供的一些cli能力, 点击这里web
后续的不少流程上的实操代码为了缩短篇幅会以伪代码的形式来进行描述,强烈推荐先阅读上文,若是须要更详细的实操代码请去github仓库查看json
实际效果图:
gulp
是否是发现很是简单,事实也是如此,整个功能作下来也就60行代码 ~
项目结构分为入口文件,配置文件
- lib
- publish-weapp.js
- index.js
- config.js
- node_modules
- package.json
复制代码
config.js用来记录一些基础常量和默认项的配置,例如项目路径,执行路径等
module.exports = {
// 根目录
root: __dirname,
// 执行命令目录路径
dir_root: process.cwd(),
// 小程序项目路径
entry: './',
// 项目编译输出文件夹
output: './',
}
复制代码
在入口文件(index.js)处利用第三方库 commander 识别命令行参数,同时做为路由进行任务分发,
#!/usr/bin/env node
const version = require('./package').version; // 版本号
/* = package import -------------------------------------------------------------- */
const program = require('commander'); // 命令行解析
/* = task events -------------------------------------------------------------- */
const publishWeApp = require('./lib/publish-weapp'); // 发布体验版
program
.command('publish')
.description('发布微信小程序体验版')
.action((cmd, options) => publishWeApp();
/* = main entrance -------------------------------------------------------------- */
program.parse(process.argv)
复制代码
接下来是一个QA环节,这时候须要根据本身的实际须要安排本身须要的命令,能够回想一下要解决的问题:
大概得出这样一个队列:
function getQuestion({version, versionDesc} = {}) {
return [
// 肯定是否发布正式版
{
type: 'confirm',
name: 'isRelease',
message: '是否为正式发布版本?',
default: true
},
// 设置版本号
{
type: 'list',
name: 'version',
message: `设置上传的版本号 (当前版本号: ${version}):`,
default: 1,
choices: getVersionChoices(version),
filter(opts) {
if (opts === 'no change') {
return version;
}
return opts.split(': ')[1];
},
when(answer) {
return !!answer.isRelease
}
},
// 设置上传描述
{
type: 'input',
name: 'versionDesc',
message: `写一个简单的介绍来描述这个版本的改动过:`,
default: versionDesc
},
]
}
复制代码
最后得到的json对象,是这个样子:
{isRelease: true, version: '1.0.1', versionDesc: '这是一个体验版'}
复制代码
主要是由于体验版并不是彻底是发行版,公司内部测试的时候也是须要发布测试用体验版的,可是又涉及只有正式版本修改本地版本文件,那么就只能多此一问做为区分了
设置上述如图的 体验版上的版本号和描述信息,
这里有个case是只有第一个问题选择发布正式版才会让你设置版本号,测试版本使用默认版本号0.0.0,这也是区分体验版的一种方式
when(answer) {
return !!answer.isRelease
}
复制代码
这里只设置了三个问题:是否发布正式版,设置版本号,设置上传描述,固然你也能够设置本身想作的其余事情
翻了下小程序的文档,大概犹以下几个关键点:
小程序cli并不是全局安装,须要本身去索引路径,命令行工具所在位置:
macOS: <安装路径>/Contents/MacOS/cli
Windows: <安装路径>/cli.bat
mac的 安装路径 若是是默认安装的话,是/Applications/wechatwebdevtools.app/, 外加cli的位置是:
/Applications/wechatwebdevtools.app/Contents/Resources/app.nw/bin/cli
windows 的话做者表示没有这个环境,只能你们本身探索了
官方文档给了很是详细的描述:
# 上传路径 /Users/username/demo 下的项目,指定版本号为 1.0.0,版本备注为 initial release
cli -u 1.0.0@/Users/username/demo --upload-desc 'initial release'
# 上传并将代码包大小等信息存入 /Users/username/info.json
cli -u 1.0.0@/Users/username/demo --upload-desc 'initial release' --upload-info-output /Users/username/info.json
复制代码
基本流程:
获取cli -> 获取当前版本配置 -> 问题队列(获取上传信息)-> 执行上传(cli命令)-> 修改本地版本文件 -> 成功提示
// ./lib/publish-weapp.js 文件
module.exports = async function(userConf) {
// cli路径
const cli = `/Applications/wechatwebdevtools.app/Contents/Resources/app.nw/bin/cli`;
// 版本配置文件路径
const versionConfPath = Config.dir_root + '/xdk.version.json';
// 获取版本配置
const versionConf = require(versionConfPath);
// 开始执行问题队列 anser case: {isRelease: true, version: '1.0.1', versionDesc: '这是一个体验版'}
let answer = await inquirer.prompt(getQuestion(versionConf));
versionConf.version = answer.version || '0.0.0';
versionConf.versionDesc = answer.versionDesc;
//上传体验版
let res = spawn.sync(cli, ['-u', `${versionConf.version}@${Config.output}`, '--upload-desc', versionConf.versionDesc], { stdio: 'inherit' });
if (res.status !== 0) process.exit(1);
// 修改本地版本文件 (当为发行版时)
!!answer.isRelease && await rewriteLocalVersionFile(versionConfPath, versionConf);
// success tips
Log.success(`上传体验版成功, 登陆微信公众平台 https://mp.weixin.qq.com 获取体验版二维码`);
}
// 修改本地版本文件
function rewriteLocalVersionFile(filepath, versionConf) {
return new Promise((resolve, reject) => {
fs.writeFile(filepath, jsonFormat(versionConf), err => {
if(err){
Log.error(err);
process.exit(1);
}else {
resolve();
}
})
})
}
复制代码
注意这里须要在项目根目录下建立一个xdk.version.json的版本文件,详细配置说明见仓库
大体是这样的结构:
// xdk.version.json
{
"version": "0.12.2",
"versionDesc": "12.2版本"
}
复制代码
到这里基本就完成了上传的全部步骤,能够当前项目下键入xdk-cli publish
查看一下程序是否正常运行,注意上传出错记得检查是否处于登陆状态
能够看到在版本号环节使用的是list类型,而非input类型,这是由于手写版本号有写错的风险, 仍是让我作选择题吧 emmm...
最终效果:
就不在这里展开了,能够下, 搜索getVersionChoices
函数:
github.com/jinxuanzhen…
你会发现是运行cli命令就直接发布了,那么用gulp,webpack等工具项目由于须要上传dist目录而非src的缘由,须要先进行打包再进行发布: gulp -> xdk-cli publish ,将一个动做拆成了两个
遗忘了一个怎么办?纠结了
不知道你们对微信开发者工具的上传钩子还有没有印象,要实现的大概就是这么个东西,
一个上传前预处理的钩子
这个实现起来也很简单,首先给用户的配置文件(xdk.config.js)开一个可配置项:
// xdk.config.js
{
// 发布钩子
publishHook: {
async before(answer) {
this.spawnSync('gulp');
return Promise.resolve();
},
async after(answer) {
this.log('发布后的钩子执行了~');
return Promise.resolve();
}
}
}
复制代码
在publish-weapp.js中去识别钩子便可:
// 前置钩子函数
await userConf.publishHook.before.call(originPrototype, answer);
//上传体验版
let res = spawn.sync(cli, ['-u', `${versionConf.version}@${Config.output}`, '--upload-desc', versionConf.versionDesc], { stdio: 'inherit' });
// 后置钩子函数
await userConf.publishHook.after.call(originPrototype, answer);
复制代码
固然,你须要判断下钩子是否存在,类型判断,包括须要返回给用户配置里一些基本的方法和属性,例如:日志输出,命令行执行等
我这边以gulp为例,能够看到在publsih前先执行gulp
,后进行发布,最后log出一行提示信息
彻底符合预期
解决了自动上传的问题,接下来就要解决环境变量切换的问题了,这里还要借用下刚才写的钩子函数:
{
// 发布钩子
publishHook: {
async before(answer) {
this.spawnSync('gulp', [`--env=${answer.isRelease ? 'online' : 'stage'}`]);
return Promise.resolve();
},
async after(answer) {
this.log.success('发布后的钩子执行了~');
return Promise.resolve();
}
}
}
复制代码
以gulp为例,根据以前的回答的结果answer.isRelease,判断是否为使用正式环境
若是你没有使用编译工具,也能够提出一个env的config文件,使用fs模块直接重写该环境变量
下面是一串伪代码:
目录结构
- app (小程序项目)
- page
- app.json
...
- env.js
- xdk.config.js
- xdk.version.js
- envConf.js
// envConf.js
module.exports = {
['online']: {
appHost1: 'https://app.xxxxxxxxx.com',
appHost2: 'https://app.xxxxxxxxx.com',
},
['stage']: {
appHost1: 'https://stage.xxxxxxxxx.com',
appHost2: 'https://stage.xxxxxxxxx.com',
}
};
// xdk.config.js
publishHook: {
async before(answer) {
let config = require('envConf.js')[answer.isRelease ? 'online' : 'stage'];
fs.writeFile('./app/env.js', jsonFormat(jsonConf), (err) => {
if(err){
this.log.error('写入失败')
}else {
this.log.success('写入成功);
}
});
return Promise.resolve();
}
复制代码
打包工具的问题还有环境变量的问题,我都没有写在cli里面,是由于不一样项目之间对环境变量的写法,格式都不尽相同,具体要怎么作仍是要留给开发者本身来肯定吧,这样看起来更灵活一些
总之利用脚手架解决了从发布到上线的一连串问题,使得再也不担忧由于切换环境致使线上bug,也再也不担忧写版本号写错的问题,确认线上环境这个环境也变成了一个非强需求的事情
整个上线流程也只须要:xdk-cli publish -> 提交审核便可,并且整个代码也并不复杂,publish-weapp.js整个文件算上注释也就60行代码,何乐不为呢?
注:下篇会讲一下如何作自定义指令,帮助小伙伴能够更自由的适配不一样的项目~