微信小程序(解密构架)
最近小程序被炒的火热。很多人应该已经尝试了微信小程序,我也抱着学习的态度,准备研究一下。研究之后感觉还可以,如果有h5和angular(或vue)基础的话,入门还是比较容易的。
小程序是一种不需要下载安装即可使用的应用,他实现了应用触手可及的梦想,用户扫一扫或者搜一下就可以打开应用。也提现了“用完即走”的里面,用户不用关心安装了太多了应用。应用将无处不在,随即使用,但又无需安装卸载。
小程序的特点
1.随后可得 用完即走
2.拥有离线能力
3.一次开发,多端兼容
4.操作体验好
安装环境、
首先需要先安装发开环境。
下载地址https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html?t=20161122
选择对应的系统(win32,win64,mac)安装
小程序框架解密
下载环境,我们只需要默认安装即可,然后用手机微信扫描环境安装成功之后弹出的二维码,新建一个项目,(没有AppID可以不填,只是有些功能会受限)就能进入到微信小程序开发的安装环境。就是如图所示。
开发者工具.jpg小程序基本架构解密
点击开发者工具左侧导航的“编辑”,我们可以看到这个项目,已经初始化并包含了一些简单的代码文件。最关键也是必不可少的,是 app.js、app.json、app.wxss 这三个。其中,.js后缀的是脚本文件,.json后缀的文件是配置文件,.wxss后缀的是样式表文件。微信小程序会读取这些文件,并生成小程序实例
小程序基本构架.png
app.json解析
app.json 是对整个小程序的全局配置。我们可以在这个文件中配置小程序是由哪些页面组成,配置小程序的窗口背景色,配置导航条样式,配置默认标题。注意该文件不可添加任何注释。
app.json.png
app.js解析
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
}
})
app.wxss解析
app.wxss 是整个小程序的公共样式表。我们可以在页面组件的 class 属性上直接使用 app.wxss 中声明的样式规则。
/**app.wxss**/
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}
创建页面
小程序中所有的页面都在Pages中,在新建项目的初始化代码中,有两个页面,一个是index,表示首页,一个是logs,表示日志页,我们可以用过新建来创建页面,但是一定要让这个页面的文件夹在Pages文件夹中,且这个页面的文件也要含有.wxml (表示页面结构的文件) .js(页面的 脚本文件) .wxss(页面的样式表)。
例如,下图新建了一个test页面,架构如图所示
1.jpg
解密框架
2.jpg显示我们创建的页面(逻辑层)
刚才创建了一个text页面,那么如何让我们创建的页面显示在小程序中。(我们先简单在text中写点东西)
当我们新建了一个text文件时,首先需要在app.json文件的pages下配置页面。注意,页面的配置是有顺序的,此案例中我们把text配置在了最前面,那么此时启动小程序先看到的就是text页面
小程序的模块化(逻辑层)
文件作用域
在 JavaScript 文件中声明的变量和函数只在该文件中有效;不同的文件中可以声明相同名字的变量和函数,不会互相影响。
通过全局函数 getApp()可以获取全局的应用实例,如果需要全局的数据可以在 App()中设置
这里举例子使用官方文档中的例子
模块化
我们可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口。
需要注意的是:
exports 是 module.exports 的一个引用,因此在模块里边随意更改 exports 的指向会造成未知的错误。所以我们更推荐开发者采用 module.exports 来暴露模块接口,除非你已经清晰知道这两者的关系。
小程序目前不支持直接引入 node_modules , 开发者需要使用到 node_modules 时候建议拷贝出相关的代码到小程序的目录中。
官方案例
5.jpg数据绑定(视图层)
内容
WXML 中的动态数据均来自对应 Page 的 data。
数据绑定使用 {{ }}(双大括号)将变量包起来
<!--text.wxml-->
<view class="text">{{msg}}</view>
//text.js
Page({
data:{
msg:"通过数据绑定显示在页面上"
}
})
在视图中的运行结果
6.jpg组件属性
<!--text.wxml-->
<view id="item-{{id}}"> </view>
//text.js
Page({
data: {
id: 0
}
})
属性控制
<!--text.wxml-->
<view wx:if="{{condition}}"> </view>
//text.js
Page({
data: {
condition: true
}
})
布尔类型
<!--text.wxml-->
<checkbox checked="{{false}}"> </checkbox>
特别注意:不要直接写 checked="false",其计算结果是一个字符串,转成 boolean 类型后代表真值。
运算
可以在 {{}} 内进行简单的运算,比如三目运算,和算术运算
三目运算
<!--text.wxml-->
<view hidden="{{flag ? true : false}}"> Hidden </view>
算术运算
<!--text.wxml-->
<view> {{a + b}} + {{c}} + d </view>
//text.js
Page({
data: {
a: 1,
b: 2,
c: 3
}
})
view中的内容为 3 + 3 + d
数据路径运算
<!--text.wxml-->
<view>{{object.key}} {{array[0]}}</view>
//text.js
Page({
data: {
object: {
key: 'Hello '
},
array: ['MINA']
}
})
组合
<!--text.wxml-->
<view wx:for="{{[zero, 1, 2, 3, 4]}}"> {{item}} </view>
//text.js
Page({
data: {
zero: 0
}
})
对象
<!--text.wxml-->
<template is="objectCombine" data="{{foo, bar}}"></template>
//text.js
Page({
data: {
foo: 'my-foo',
bar: 'my-bar'
}
}
最终组合成的对象是 {foo: 'my-foo', bar:'my-bar'}。
注意:上述方式可以随意组合,但是如有存在变量名相同的情况,后边的会覆盖前面
条件渲染
wx:if
用wx:if来判断是否需要渲染这段代码,如果满足条件,即可渲染该段代码,类似于js中的if语句。
下面这段代码,如果condition为true,则渲染这段代码
<view wx:if="{{condition}}"> True </view>
wx:elif 和 wx:else
类似于js中的if-else语句
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
block wx:if
因为 wx:if 是一个控制属性,需要将它添加到一个标签上。但是如果我们想一次性判断多个组件标签,我们可以使用一个 <block/> 标签将多个组件包装起来,并在上边使用 wx:if 控制属性。
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
注意: <block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
wx:if vs hidden
因为 wx:if 之中的模板也可能包含数据绑定,所有当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。
同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。
相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。
一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。
列表循环渲染
wx:for
在组件上使用wx:for控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
item代表list中的每一项
<!--text.wxml-->
<view wx:for="{{list}}">
{{item.name}}-----{{item.singer}}
</view>
//text.js
Page({
data:{
list:[
{name:"《说走就走》",singer:"周杰伦"},
{name:"《倒带》",singer:"蔡依林"},
{name:"《单车恋人》",singer:"后弦"},
{name:"《给我一首歌的时间》",singer:"周杰伦"},
{name:"《演员》",singer:"薛之谦"}
]
}
})
运行结果
7.jpg如果你想给页面加点样式,可以写在text.exss中
先给text.wxml添加一个class名称,比如叫abc ,然后用过class选择器在text.exss中书写样式
.abc{
padding: 10px;
margin-bottom: 10px;
border-bottom: 1px solid #ccc;
}
此时页面效果(index表示数组的下标,下标是从0开始的,也可以更改默认的item的名称,如下图所示)
8.jpgblock wx:for
类似block wx:if,也可以将wx:for用在<block/>标签上,以渲染一个包含多节点的结构块
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: </view>
<view> {{item}} </view>
</block>
wx:key
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 <input/> 中的输入内容,<switch/> 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。
wx:key 的值以两种形式提供
1.字符串,代表在 for 循环的 array 中 item 的某个 属性,该 属性 的值需要是列表中唯一的字符串或数字,且不能动态改变。
<!--text.wxml-->
<view class="abc" wx:key="{{name}}" wx:for="{{list}}" wx:for-item="newName">
{{index+1}}. {{newName.name}}-----{{newName.singer}}
</view>
2.保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字,如:
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
//text.js
Page({
data:{
array:[1,2,3,4,5,6,7,8]
}
})
<view wx:for="{{array}}" wx:key="*this">
{{item}}
</view>
模板
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。
定义模板
使用name属性,作为模板的名字。然后在<template/>内定义代码片段
<!--text.wxml-->
<template name="info">
<view>
<text>{{name}}</text>
<text>{{singer}}</text>
</view>
</template>
使用模板
使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入
<!--text.wxml-->
<template name="info">
<view>
<text>{{name}}</text>
<text>{{singer}}</text>
</view>
</template>
<template is="info" data="{{...item}}"></template>
//text.js
Page({
data:{
item:{name:"《说走就走》",singer:"周杰伦"}
}
})
也可以把模板单独放在一个文件里。需要时使用import或者include引用。
事件
什么事件?
事件是视图层到逻辑层的通讯方式。
事件可以将用户的行为反馈到逻辑层进行处理。
事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
事件对象可以携带额外信息,如 id, dataset, touches。
事件的使用方式
在数组中绑定一个事件处理函数
如bindtap,当用户点击该组件的时候会在该页面对应的Page中找到相应的事件处理函数。
<view id="tapTest" data-hi="WeChat" bindtap="tapName"> Click me! </view>
在相应的Page定义中写上相应的事件处理函数,参数是event
Page({
tapName: function(event) {
console.log(event)
}
})
可以看到log出来的信息大致如下
{
"type":"tap",
"timeStamp":895,
"target": {
"id": "tapTest",
"dataset": {
"hi":"WeChat"
}
},
"currentTarget": {
"id": "tapTest",
"dataset": {
"hi":"WeChat"
}
},
"detail": {
"x":53,
"y":14
},
"touches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}],
"changedTouches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}]
}
在打印的结果中,我们可以看到事件对象,上述事件与js中的点击事件类似。
其他的事件,如果想学的读者老爷可以在官方文档中查看。