微信小程序开发
- 开发环境搭建
- 应用架构解析
- 逻辑层
- 注册程序
- 注册页面
- 模块化
- API
- 视图层
- 数据绑定
- 条件渲染和列表渲染
- 模块
- 事件
- 引用
- WXSS
- 组件
之前觉得微信小程序已经不行了,似乎没有任何生机,很多人可能都不知道有小程序这回事吧,但是最近在做vue的项目,发现美团和饿了么的微信公众号都连接到了小程序,主要特点就是非常像原生app,可以发送到桌面,使用感受就如同原生app,感觉还是很舒服的,所以觉得有必要尝试一下微信小程序的开发。。。嗯,现在已经是凌晨3点了,小程序啊。
根据上述的目录,我们主要讨论一下开发环境的搭建、应用架构、逻辑层、视图层。
一、开发环境搭建
官网有工具的下载地址,我们直接点击下载即可,目前的最新版本是2017年5月26日更新的。 看来腾讯还是不会轻易放弃小程序的。 当然我们需要提前注册一下微信小程序的appid,然后登陆即可, 值得注意的是,如果我们使用的是教老版本的微信开发者工具,并且你已经绑定了微信公众号,那么就不能进入到小程序开发的界面。 进入之后,我们就可新建一个项目,使用微信小程序的快速启动工具构建一个具有基本架构的项目,就类似于vue的vue-cli一样。
我们进入之后,可以看到基本的框架如下所示:
通过这些文件,就生成了基本的小程序实例。
点击编辑即可进入编辑代码的界面,编辑完之后,我们可以通过点击左下角的编译按钮来编译,这样就可以看到修改代码之后的效果了!
二、 应用架构解析
我们看看最简单的小程序的目录结构:
- pages
- index
- index.js
- index.wxml
- index.wxss
- logs
- logs.js
- logs.json
- logs.wxml
- logs.wxss
- index
- utils
- util.js
- app.js
- app.json
- app.wxss
首先比较突出的就是 wxss 和 wxml了, 其实就相当于css和html, 另外还有json的使用,主要是来配置小程序的,如颜色等。 毫无疑问app.js就是入口文件了。 不难看出 pages 就表示多个页面了。 还有一个公用的工具函数 util.js。如下所示:
function formatTime(date) { var year = date.getFullYear() var month = date.getMonth() + 1 var day = date.getDate() var hour = date.getHours() var minute = date.getMinutes() var second = date.getSeconds() return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') } function formatNumber(n) { n = n.toString() return n[1] ? n : '0' + n } module.exports = { formatTime: formatTime }
这段代码也非常简单,就是把当前的时间用某种形式标准化。
每一个小程序页面都是由相同路径下同名的四个同名、不同后缀的文件所组成的。如这里的 index.wxml、index.wxss、index.js、index.json。
那么我们知道wxml是页面结构、wxss是页面的样式、js是逻辑代码、json这个配置文件呢? 实际上json文件也是样式的配置,只是控制的属于app的,不是页面的,如最上面的导航条等等。 并且如果我们在一个页面的json文件中如果设置了和app.json文件中相同的内容,那么就会覆盖掉app.json中相同的配置样式。
注意: json文件同js文件是完全不同的,json是一种非常严格的文件格式。
其中的index页即主页,log页即日志页。如下是默认的主页:
下面是默认的日志页:
这是我对小程序启动的一些基本的记录。
如果我们把log.json文件中添加下面的内容(原来是没有内容的,使用的设置都是app.json文件中的设置):
{ "backgroundTextStyle": "blue", "navigationBarBackgroundColor": "blue", "navigationBarTextStyle": "blue", "navigationBarTitleText": "查看启动日志" }
那么日志页就是这样的:
我们注意看一下 app.json中的内容:
{ "pages":[ "pages/index/index", "pages/logs/logs" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "red", "navigationBarTitleText": "WeChat", "navigationBarTextStyle":"red" } }
可以看到必须要在app.json中说明这个项目一共包含哪些页面,并且通过window的设置来告诉我们这是全局的。 注意:在index.json和log.json中最好不要使用这种全局的配置或压根就不要使用。
我们先说说单个页面吧。 微信小程序要求页面文件名和目录的名称必须一致。比如这里的logs目录对应的一堆logs开头的文件,所以这样我们在使用的时候就不需要在js中import了。
下面是js文件和模板文件:
//index.js //获取应用实例 var app = getApp() Page({ data: { motto: 'Hello World', userInfo: {} }, //事件处理函数 bindViewTap: function() { wx.navigateTo({ url: '../logs/logs' }) }, onLoad: function () { console.log('onLoad') var that = this //调用应用实例的方法获取全局数据 app.getUserInfo(function(userInfo){ //更新数据 that.setData({ userInfo:userInfo }) }) } })
这个js也比较容易看,就是在点击之后进入log页面,在load(生命周期的钩子函数)后开始使用app这个全局实例的getUserInfo全局方法来获取到本地的userInfo, 并使用setData来修改data里的内容,这里也用了var that = this的形式,因为this指向的时app。
<!--index.wxml--> <view class="container"> <view bindtap="bindViewTap" class="userinfo"> <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image> <text class="userinfo-nickname">{{userInfo.nickName}}</text> </view> <view class="usermotto"> <text class="user-motto">{{motto}}</text> </view> </view>
其中 bindViewTap就是绑定一个tap事件,当我们点击进入的时候就可以引导到:
wx.navigateTo({
url: '../logs/logs'
})
所以就进入了 logs 页面。
其中的 Page 是框架提供的全局方法。 和vue类似的mvvm框架一样,采用了数据绑定、事件绑定、生命周期、路由这些。 下面是一个logs.json文件:
{ "navigationBarTitleText": "查看启动日志" }
即json文件就是做一些基础的配置的。 因为小程序毕竟还是运行在微信上的, 所以这些配置文件log就是改一下标题栏颜色、文字之类的东西。
而通过getApp()我们可以得到app实例,这个实例中包含了一些全局的方法。
嗯,这个hello world级别的就是这么简单。
三、逻辑层
微信小程序的逻辑层就下面这些:
- 注册程序
- 注册页面
- 模块化
- API
1. 注册程序
注册程序是提供给我们的,是一个全局的App方法(传入一个对象), 这个作为应用的主入口,当然是写在app.js这个入口文件中的,可以监听一些应用生命周期的事件,然后配置一些全局变量。
//app.js App({ onLaunch: function () { //调用API从本地缓存中获取数据 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) }, getUserInfo:function(cb){ var that = this if(this.globalData.userInfo){ typeof cb == "function" && cb(this.globalData.userInfo) }else{ //调用登录接口 wx.login({ success: function () { wx.getUserInfo({ success: function (res) { that.globalData.userInfo = res.userInfo typeof cb == "function" && cb(that.globalData.userInfo) } }) } }) } }, globalData:{ userInfo:null } })
这里的onLaunch是一个生命周期的钩子,然后getUserInfo相当于一个全局的方法(使用getApp()获取实例以后就可以调用,在Index.js里的), 而globalData作为app的属性自然就担任了全局变量这个角色了, 然后还使用了本地存储的api,来提高用户体验。
2. 注册页面
Page方法就是用于注册页面的,页面也会有一些生命周期的钩子等。 另外,Page还有一个事件: 即onPullDownRefresh,即页面下拉刷新。
<view bindtap="viewTap"> click me </view>
Page({ viewTap: function() { console.log('view tap') } })
除了使用bind + event作为属性名还可以使用catch + event, 区别在于后者可以继续阻止冒泡。 事件绑定函数同样还有一个参数event, 但是event包含了该节点的星星所以很快就可以完成逻辑定制。
3. 模块化
目前的小程序并不支持node_modules的导入, 所以要用就可能复制过去,另外和webpack的模块化是类似的, 即小程序各个page的作用域是独立的,如果要对外暴露接口的话需要使用模块导出。
// common.js function sayHello(name) { console.log(`Hello ${name} !`) } function sayGoodbye(name) { console.log(`Goodbye ${name} !`) } module.exports.sayHello = sayHello // module.exports = { sayHello: sayHello }
这里要说的是: 微信小程序是完全支持es6语法的,不用担心兼容性的问题啦!
4. API
小程序的API都通过wx这一个全局对象来访问的, 和运行在浏览器的JavaScript类似,wx也提供了如http请求、本地存储、绘图这一类的api, 除此之外,还有一些更为本地化的api,比如拨打电话、重力感应、交互反馈、支付这样的api。 可以查询小程序api。其实真的是非常方便的。
四、视图层
微信小程序也是自制了一套标签,成为WXML(weixin Markup Language), 同样也是基于组件的。 与之配合的样式表称为 WXSS (weixin Style Sheet)。
1. 数据绑定
数据绑定采用Mustache语法,也就是双大括号。值的注意的是大括号里支持一些ES6的表达式,比如说展开语法和简化的对象属性写法
<template is="objectCombine" data="{{...obj1, ...obj2, e}}"></template>
Page({ data: { obj1: { a: 1, b: 2 }, obj2: { c: 3, d: 4 }, e: 5 } })
这样得到的结果就是: {a: 1, b: 2, c: 3, d: 4, e: 5}。
2. 条件渲染和列表渲染
条件渲染和列表渲染很简单,就像vue.js里两个指令v-if
和v-for
,小程序里用的是wx:if
和wx:for
,用两个简单的DEMO过一下
<!-- 条件渲染 --> <view wx:if="{{length > 5}}"> 1 </view> <view wx:elif="{{length > 2}}"> 2 </view> <view wx:else> 3 </view> <!-- 列表渲染 --> <view wx:for="{{array}}"> {{index}}: {{item.message}} </view>
3. 模板
即我们需要定义一个模板来复用。
<template name="msgItem"> <view> <text> {{index}}: {{msg}} </text> <text> Time: {{time}} </text> </view> </template> <template is="msgItem" data="{{...item}}"/>
4. 事件
前面简单提了一下事件的两种绑定方式,现在简单列一下wx支持的一些冒泡事件名称:
- touchstart 手指触摸动作开始
- touchmove 手指触摸后移动
- touchcancel 手指触摸动作被打断,如来电提醒,弹窗
- touchend 手指触摸动作结束
- tap 手指触摸后马上离开
- longtap 手指触摸后,超过350ms再离开
其余的事件都是不冒泡的,那些就因组件而异了,比如说<form />
的submit等。然后之前也提过,事件参数就只有一个event,所以要传值的时候data-属性就显得很重要了,因为事件对象可以获取到一个dataset
对象,对应都是就是各个自定义的data-属性。
5. 视图的引用
就像ejs里会有include一样,模板语言肯定是会有引用嵌套的功能的,wx里提供了两种方法,分别是import
和include
,见DMEO
<!-- item.wxml --> <template name="item"> <text>{{text}}</text> </template>
<import src="item.wxml"/> <template is="item" data="{{text: 'forbar'}}"/>
也可以是include,
<!-- index.wxml --> <include src="header.wxml"/> <view> body </view>
<!-- header.wxml --> <view> header </view>
6. wxss
继承了css的大部分特性。