实现原理
之前一直猜测微信小程序的实现是类似于 react-native 的技术,但事实上生成的并不是 native 的视图,而是 Html + CSS 的形式,最终呈现给用户就是 WebView。不过页面跳转还是基于 native 做的,拿 iOS 来说也就是一个 ViewController 包含了一个 WebView,然后通过 UINavigationController
来做页面的跳转,当然页面的切换用的是UITabBarController
。下图是京东购物的小程序的视图结构,可以看到导航栏用的是 UINavigationBar
、内容的呈现用的是 WKWebView
,还有底部的用于切换视图的控件 UITabBar
。这样的 natvie + webview 的实现形式确实做到了让开发变得简单又有相对好的用户体验。
如何上手
小程序的开发可以说是比较简单的,首先要掌握 JS、CSS 和 Html 基本知识,其次是必须认真的看开发文档,还有就是官方的演示DEMO。
显示一个列表
小程序没有类似于 iOS 上的 UITablView 的控件,想要显示列表必须在组件上用 wx:for
来绑定一个数组然后渲染。在 .js 文件中定义一个数组 items
1
2
3
4
5
Page({
data: {
items:[],
},
})
请求接口获取数据并赋值给 items
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Page({
data: {
items:[],
},
onLoad: function() {
var that = this;
wx.request({
url: Url,
method: 'GET',
success: function(res){
that.setData({
items: res.data.result,
})
},
})
},
})
到这里数据就准备好了,接下来就是如何把数据显示到列表上,在 .wxml 中例如:
1
2
3
4
5
6
<view class="contain">
<view wx:for="{{items}}" wx:key="{{item.id}}">
<text>{{item.text}}</text>
<text>{{index}}</text>
</view>
</view>
需要注意的是:<view wx:for="{{items}}">
这里的 view 事实上相当于 Cell,并不是包裹所有 Cell 的视图。item
是默认数组元素,index
是默认是索引,当然默认值是可以修改的,具体可以参考官方文档列表渲染。wx:key
是用于标识 Cell 的唯一性的,可以提高列表的渲染性能。
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
点击Cell
一般情况下点击事件我们使用 bintap
,所以在 Cell 上绑定一个点击事件就是这么写:
1
2
3
4
5
6
<view class="contain">
<view wx:for="{{items}}" wx:key="{{item.id}}" bindtap="cellAction">
<text>{{item.text}}</text>
<text>{{index}}</text>
</view>
</view>
然后在 .js 实现相应的方法:
1
2
3
4
5
Page({
cellAction(){
console.log('action')
}
})
这时候点击 Cell 就会在控制台打印出 action
页面跳转,传参
比如点击 Cell 要跳转到详情页 Detail(不要忘记在app.json中配置页面),cellAction 这么写:
1
2
3
4
5
6
7
Page({
cellAction(){
wx.navigateTo({
url: './detail',
})
}
})
这个例子显然不够典型,因为没有传参,在小程序里页面之前的传参可以说有三种方式:
- 路由形式
- 全局变量
- 本地缓存
- 路由形式:
1
2
3
4
5
6
7
8
9
10
Page({
cellAction(object){
var id = this.data.items[0].id;
var text = this.data.items[0].text;
console.log(id);
wx.navigateTo({
url: './detail?id=' + id + '&text=' + text,
})
},
})
比如最终 url = ./detail?id=1&text=测试
,相当于传了两个参数过去,一个id
一个text
,这时候在下一个页面获取数据:
1
2
3
4
5
6
7
8
9
// detail.js
Page({
onLoad:function(options){
var id = options.id;
var text = options.text;
console.log(id);
console.log(text);
},
})
注意:必须在onLoad
才可以获取到参数。
- 全局变量:
首先必须在app.js
定义一个全局的变量,比如:globalData
,然后定义一个parameter
属性用于储存参数。
1
2
3
4
5
6
7
8
9
//app.js
App({
onLaunch: function () {
},
globalData:{
parameter:null
}
})
然后设置参数,别忘记var app = getApp()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var app = getApp()
Page({
cellAction(object){
var id = this.data.items[0].id;
var text = this.data.items[0].text;
app.globalData.parameter = {
"id" : id,
"text" : text
}
wx.navigateTo({
url: './detail',
})
},
})
获取参数,同样的需要获取app
对象
1
2
3
4
5
6
7
8
9
10
// detail.js
var app = getApp()
Page({
onLoad:function(options){
var id = app.globalData.parameter.id;
var text = app.globalData.parameter.text;
console.log(id);
console.log(text);
},
})
- 本地缓存:
1
2
3
4
5
6
7
8
9
cellAction(object){
var id = this.data.items[0].id;
var text = this.data.items[0].text;
wx.setStorageSync("id",id);
wx.setStorageSync("text",text);
wx.navigateTo({
url: './detail',
})
},
获取数据:
1
2
3
4
5
6
7
8
9
10
// detail.js
var app = getApp()
Page({
onLoad:function(options){
var id = wx.getStorageSync("id");
var text = wx.getStorageSync("text");
console.log(id);
console.log(text);
},
})
上面的例子传入都是数组中第一条元素的数据,那么如何传入对应的数据呢?我们需要用 dataset
,官方文档对其定义:
在组件中可以定义数据,这些数据将会通过事件传递给 SERVICE。 书写方式: 以
data-
开头,多个单词由连字符-
链接,不能有大写(大写会自动转成小写)如data-element-type
,最终在 event.target.dataset 中会将连字符转成驼峰elementType
。
回到之前的例子,我们添加一个 data-index="{{index}}"
1
2
3
4
5
6
<view class="contain">
<view wx:for="{{items}}" wx:key="{{item.id}}" bindtap="cellAction" data-index="{{index}}">
<text>{{item.text}}</text>
<text>{{index}}</text>
</view>
</view>
之前说过了,index 是默认的索引对象,这时候在cellAction
方法里就可以获取到 index 了:
1
2
3
4
5
6
7
8
9
10
Page({
cellAction(object){
var index = object.currentTarget.dataset.index;
var id = this.data.items[index].id;
var text = this.data.items[index].text;
wx.navigateTo({
url: './payFail?id=' + id + '&text=' + text,
})
},
})
获取方法
1
var index = object.currentTarget.dataset.index;
动态改变样式
简单粗暴的方式是通过条件渲染来显示不用的组件,这样从视觉上来讲也能达到改变样式的效果:
1
2
<view class="class1" wx:if="{{length > 5}}"> 1 </view>
<view class="class3" wx:else> 3 </view>
这显然不够优雅,官方文档中提到样式还可以通过 sytle
来控制,而且style
可以接收动态样式。比如点击之后改变字体颜色:
定义一个字体颜色的变量
1
2
3
4
5
Page({
data: {
textColor: "red"
},
})
视图这边,当然要指定 textColor 用于显示字体颜色
1
<text bindtap="change" style="color: {{textColor}}" >12321</text>
点击执行change
方法
1
2
3
4
5
6
change(){
var color = "green"
this.setData({
textColor : color
})
},