高效编写微信小程序
微信小程序是一个工程,就和盖房子一样,打好了地基,才能保证后续工程师建立在可靠牢固的基础上。
笔者需要经常新建项目,每次都要重复“修改项目结构->从老项目中复制粘贴文件->删除一些老项目中代码”这样的过程,实在是…费心费力。
另一个痛点是:每次新建小程序页面要生成三个文件名相同的文件(.wxml、.wxss和.js),命令行太长(据微信同事:也可以在app.json的pages字段下添加新页面的路径,保存后也会生成对应的文件)。
目标
我们现在有两个目标:
根据通用模板新建项目
一键新建页面目录以及在目录中的三个文件:.wxml、.wxss和.js也可以直接在app.json的pages字段创建页面,保存后生成这三个文件。笔者没有采用这个方法的缘由一个是开始时不知道有此功能,另一个是不合平时的操作习惯,再者想到js文件初始化后,需要引入常用库,要插入代码片段,所以保留了这个功能。
这两个需求其实很简单,不需要GUI,所以我们可以做一个npm命令行工具。想象一下这个命令行用起来应该是什么样的呢:
~npminstallwxapp-g
~wxapp-imyapp&&cdmyapp
~wxapp-plist
复制代码
用流程图示意就是:
实现
正式开始之前,请先确认本地的开发环境,笔者的本地环境是:
~npm-v
3.10.10
~node-v
v6.9.4
复制代码
我们把问题分解为三步:
实现命令行工具,可以在任意目录直接运行
通过输入不同的命令行参数,以执行不同的功能
考虑项目模板的存放位置,是集成到工具中,还是和工具分开呢
不用担心,都很容易解决,我们一个个看。
命令行工具
package.json中有一个字段是bin:
{
...
"bin":{
"mywxapp":"./index.js"
}
}
复制代码
这个字段可以将开发者希望执行的脚本注册到环境变量(PATH)中,不同的key对应执行不同的脚本。也就是说现在,当我们直接在命令行中执行:
~mywxapp
复制代码
等价于在terminal中执行:
~/path/to/index.js
复制代码
第一个问题轻松解决,关于bin字段更多信息请参考npm文档中package.json一节。
命令行参数
执行index.js时,可以通过process.argv获取执行时的参数,但是要从参数数组中拆分出参数无疑很麻烦。不过,npm发展至今,处理命令行参数的库肯定存在,就是commander。简单好用易上手,那么第二个问题也解决啦。
项目模板的存放位置
考虑项目模板的存放位置,是集成到工具中,还是和工具分开呢?
笔者选择分开管理。在一个单独的模板代码仓库中管理模板内容,方便我们维护。目前的模板还比较简单(详见下文“模板详解”),只有标准目录结构,预期后面会加上自动化的部分(比如less->wxss),所以未来会改动比较频繁。
download-git-repo可以把给定地址的仓库内容拷贝到执行目录中。API简单,所以就是它了。
问题都解决了,现在就让我们看看伪代码(注意:伪码中没有考虑出错情况):
constmkdirp=require('mkdirp');
constdownload=require('download-git-repo');
//创建项目
functioninitProj(projName){
if(currentDir.exsits(projName))
console.warn(projName+'项目已经存在于当前目录中,请使用别的名字');
else
download('path/to/tmpl',currentDir+'/'+projName);
}
//注册页面
functionregisterPage(pagesName){
//读配置文件
readFile(configFile,function(data){
//将新建的所有页面都写入配置文件中
for(nameinpagesName){
data.pages.push('pages/'+name+'/'+name);
}
writeFile(configFile);
});
}
//创建页面
functioncreatePage(pages){
for(varindexinpages){
varpage=pages[index];
mkdirp(pagePath,function(){
createFile(page+'.wxml');
createFile(page+'.wxss');
createFile(page+'.js');
})
}
//将页面注册到app.json中
registerPage(pages);
}
复制代码
使用
在编写好了这个工具之后,只需要在本地全局使用的话:
npminstall-g
复制代码
在本地开发过程中,如果更新了开发版本的代码,需要更新同步到全局,这时候需要执行:
npmlink
复制代码
就会看到安装到环境变量中的工具目录地址已经和开发目录关联起来了:
~/Documents/kmokidd/cli-build$npmlink
/usr/local/bin/wxapp->/usr/local/lib/node_modules/@kmokidd/wxapp-generator/index.js
/usr/local/bin/node_modules/@kmokidd/wxapp-generator->/Users/kmokidd/Documents/kmokidd/cli-build/index.js
复制代码
使用起来是这样的:
模板和插件地址将附在参考资料一节中
发布npm插件
如果和笔者一样,希望在多个机器上使用这个工具,可以选择发布到npm官网上。发布步骤非常简单,基本上就是:
npmlogin
npmpublish
复制代码
过笔者考虑到,项目模板毕竟是因人而异的东西,所以选择了发布scopepackage,也就是在插件的package.json中的name字段使用@scopeName/wxapp-generator这样的值。
如果你也有类似的想法,并且也是个npm免费用户,那么发布的时候要执行:
npmpublish--accesspublic
复制代码
scope对使用没有任何影响,但是安装的时候要记得带上scopename执行:
npminstall@scopeName/wxapp-generator-g
复制代码
模板详解
一千个人中有一千种项目模板。根据业务/个人爱好不同,大家的项目模板可能也相去甚远。笔者自觉目前的模板用起来还不错,将在这一节介绍一下。以下是项目的文件结构:
wxapp
├──app.js
├──app.json
├──app.wxss
├──base-styles/
├──images/
├──pages/
│├──tmpl/
├──utils/
│├──view.js
│├──util.js
│├──polyfiil.js
└──└──Deferred.js
复制代码
之所以采用这样的结构,是希望尽可能解耦UI逻辑与业务逻辑。但是由于完全解耦是不可能的,基本思路是单纯的“变量分离”。通常UI的改变是通过class的切换或者内联样式的调整,所以笔者的思路,是将“要切换的class”或者“要调整的内联样式”作为变量,由于大部分情况下业务逻辑和UI变化是联动的,通过抽离出来的变量,实现在业务逻辑中简单直白地改变UI。
可能看到这里,读者会有些困惑,那让我们直接以「企鹅听书」为例,具象地看看笔者是怎么做的吧。听书的界面会出现变化的时以下两种场景:
一共有两种播放器:minibar和全屏的播放器,播放器的播放按钮有“播放”和“暂停”两种状态(图片)切换,这个可以通过class来控制。
当播放器进入全屏模式后,节目列表将被隐藏;点击箭头以后,节目列表将重新显示出来。
上文的文件结构中的view.js就是UI逻辑的代码。pages/目录中的js文件将通过import引用view.js,view.js中的接口分为“通用”和“页面使用”这两个类型:
module.exports={
//通用
general:{
hide:'hide',//变量分离在此
show:'show'
},
//播放器页面
playerView:{
class:{
listItemPlaying:'playing'
}
}
//其他页面如果也有需要,以页面为单位添加...
}
复制代码
如果未来出现更多UI变化的场景,可以再通过变量添加上去,比如pageView.id。
举个超级简单的例子(如下),模拟工作流程:
在wxss中定义好控制不同样式的class
将需要变化的class写到view.js中,并暴露接口
在wxml中的对应结构中绑定eventhandler
在对应的page.js里实现eventhandler的具体内容,也就是切换class的触发条件
老司机一看就知道是MVVC模式,这样分离也就是为了UI有独立的控制器,不至于和业务逻辑耦合严重,在页面开发的阶段就可以完成UI上的变化。从这个角度上看,小程序反而能给UI工程师更多控制UI逻辑的能力,确定好代码规范和接口。
总结
初始化一个项目是开始编码的第一步,值得多花一些时间找到合适团队合适自己的项目模板。