微信小程序开发-2(原生)-具体开发-1
数据绑定:
1.data 中定义数据(在页面对应的 .js 文件中,把数据定义到data 对象中)
2. 在WXML 中使用数据(把data 中的数据绑定到页面中渲染,使用Mustache 语法(双大括号)将变量包起来即可)
// index.js page({ data:{ info:'hello' } }) // index.wxml <view>{{info}}</view>
// Mustache 语法的主要应用场景如下(动态绑定与vue 种不同的是不用绑定v-bind(:),直接使用{{}}包起来就行,例如:<img src='{{url地址}}'></img>):
// 1. 绑定内容
// 2. 绑定属性
// 3. 运算(三元运算、算数运算等)
// 三元运算
// index.js
page({
data:{
randomNum:Math.random() * 10 // 生成10以内的随机数
}
})
// index.wxml
<view>{{randomNum >= 5 ? '大于或等于5' : '小于5'}}</view>
// 算数运算
// index.js
page({
data:{
randomNum:Math.random().toFixed(2) // 生成一个带两位小数的随机数
}
})
// index.wxml
<view>生成100以内的随机数:{{randomNum * 100}}</view>
事件绑定:
是渲染层到逻辑层的通讯方式,通过事件可以将用户在渲染层产生的行为,反馈到逻辑层进行业务的处理。
小程序中常用的事件:
当事件回调触发的时候,会收到一个事件对象event,它的详细属性如下:
// target 和currentTarget 区别 // target 是触发该事件的源头组件,而currentTarget 则是当前事件所绑定的组件 // 例:点击内部的按钮时,点击事件以冒泡的方式向外扩散,也会触发外层view 的tap 事件处理函数,此时对于外层 view 来说, // e.target 指向的是触发事件的源头组件,因此,e.target 是内部的按钮组件 // e.currentTarget 指向的是当前正在触发事件的那个组件,因此,e.currentTarget 是当前的view 组件 <view bindtap='outerHandler'> <button type='primary'>按钮</button> </view>
// bindtap 语法 // 在小程序中,不存在HTML 中的onclick 鼠标点击事件,而是通过tap 事件来响应用户的触摸行为 // 1. 通过bindtap 可以为组件绑定tap 触摸事件 <button type='primary' bindtap='btnTapHandler'></button> // 2. 在页面的 .js 文件中定义对用的事件处理函数,事件参数通过形参 event(一般简写成 e)来接收 page({ batTapHandler(e){ console.log(e) // 打印事件参数对象 e } })
// 在事件处理函数中为 data 中的数据赋值 // 通过调用 this.setData(dataObject) 方法,可以给页面 data 中的数据重新赋值 // 页面的 html 文件 <button type='primary' bindtap='changeCount'>+1</button> // 页面的 .js 文件 page({ data:{ count:0 } }) changeCount(){ // 修改 count 值 this.setData({ count:this.data.count +1 }) }
// 事件传参(小程序中的事件传参比较特殊,不能在绑定事件的同时为事件处理函数传递参数) // 例如vue中的绑定事件后面跟着小括号内写入传参值,这种就不可行 // 因为小程序会把 bindtap 的属性值,统一当作事件名称来处理,相当于要调用一个名称 btnHandler(123) 的事件处理函数 // 可以为组件提供 data-* 自定义属性传参,其中 * 代表的是参数的名字 // 例:下面传递一个名为 info 的参数值 为2 // info 会被解析为参数的名字,数值2 会被解析为参数的值 <button bindtap='btnHandler' data-info='{{2}}'>传递参数 info</button> // 在事件处理函数中,通过event.target.dataset.参数名 获取具体参数的值 btnHandler(event){ console.log(event.target.dataset) // dataset 是一个对象包含了所有通过 data-* 传递过来的参数项 console.log(event.target.dataset.info) // 通过dataset 可以访问到具体参数的值 }
// bindinput (小程序中通过input 事件来影响文本框的输入事件) // 通过bindinput 可以为文本框绑定输入事件 <input bindinput='inputHandler'></input> // 页面的.js 文件中定义事件处理函数 inputHandler(e){ console.log(e.detail.value) // e.detail.value 是变化后文本框最新的值 }
// 实现文本框和data 中间的数据同步 // 1. 定义数据==》2. 渲染结构==》3. 美化样式==》4. 绑定input 事件处理函数 //.js 文件中 page({ data:{ msg:'hello! ' } }) iptHandler(e){ // 重新赋值 this.setData({ msg:e.detail.value // 通过e.detail.value 获取到文本框最新的值 }) } // .wxml 文件中 <input value='{{msg}}' bindinput='iptHandler'></input> // .wxss 文件中 input{ border:1rpx solid #eee; padding:5rpx; margin:5rpx; border-radius:3rpx; }
// 条件渲染 wx:if // 小程序中使用 wx:if="{{condition}}" 来判断是否需要渲染该代码块,例: <view wx:if="{{condition}}">true</view> // 也可以用wx:elif 和 wx:else 来添加else 判断,例: <view wx:if="{{type === 1}}">男</view> <view wx:elif="{{type === 2}}">女</view> <view wx:else="{{type === 3}}">保密</view> // 结合 <block> 使用 wx:if // 如果要一次性控制多个组件的展示与隐藏,可以使用一个<block></block> 标签将多个组件包装起来,并在<block> 标签上使用 wx:if 控制属性,例: // 注意:<block> 不是一个组件,它只是一个包裹性质的容器,不会在页面中做任何渲染 <block wx:if="{{true}}"> <view>显示值1</view> <view>显示值2</view> </block>
// 条件渲染 hidden // 小程序中直接使用hidden="{{condition}}" 也能控制元素的显示与隐藏,例: <view hidden="{{condition}}">条件为 true 隐藏,条件为false 显示</view>
// wx:if 与 hidden : // 1. 运行方式不同 // 1-1. wx:if 以动态创建和移除元素的方式,控制元素的展示与隐藏 // 1-2. hidden 以切换样式的方式(display:none/block)控制元素的显示与隐藏 // 2. 使用建议 // 2-1. 频繁使用时,建议使用hidden // 2-2. 控制条件复杂时,建议使用 wx:if 搭配wx:elif、wx:else 进行展示与隐藏的切换
// 列表渲染 wx:for(通过 wx:for 可根据指定的数组,循环渲染重复的组件结构) // 默认情况下,当前循环项的索引用index 表示,当前循环项用item 表示 <view wx:for="{{array}}">索引时:{{index}},当前值是:{{item}}</view> // 可以手动指定索引和当前想的变量名 // 使用wx:for-index 可以指定当前循环项的索引的变量名 // 使用wx:for-item 可以指定当前想的变量名 <view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">索引时:{{idx}},当前项是:{{itemName}}</view> // wx:key 的使用(类似于vue 列表渲染中的:key ,小程序在列表渲染时也建议为渲染的列表项指定唯一的key 值,从而提高渲染的效率) // data 数据 data:{ userList:[ {id:1,name:'小红'}, {id:2,name:'小黄'}, {id:3,name:'小白'}, ] } // wxml 结构 <view wx:for="{{userList}}" wx:key='id'>{{item.name}}</view>
下面开始介绍下WXSS 模板样式:
WXSS(WeiXin style Sheets)是一套样式语言,用于美化WXML 的组件样式,类似于网页开发中的css
WXSS 和 CSS 的关系:
WXSS 具有CSS 大部分特性,同时,WXSS 还对CSS 进行了扩充以及修改,以适应微信小程序的开发,与CSS 相比,WXSS 扩展的特性有(1. rpx 尺寸单位;2. @import 样式导入)
// rpx(responseive pixel) 是微信小程序独有的,用来解决屏适配的尺寸单位 // rpx 的实现原理: // 鉴于不同设备屏幕的大小不同,为了实现屏幕的自动适配,rpx 把所有设备的屏幕在宽度上等分位750 份(即:当前屏幕的总看宽度为750rpx) // 在较小的设备上,1rpx 所代表的宽度较小 // 在较大的设备上,1rpx 所代表的宽度较大 // 小程序在不同设备上运行的时候,会自动把rpx 样式单位换算成对应的像素单位来渲染,从而实现屏幕适配 // rpx 和 px 之间的单位换算 // 在iPhone6 上,屏幕宽度为375px,共有750个物理像素,等分为750rpx则: // 750rpx = 375px =750物理像素 // 1rpx = 0.5px = 1 物理像素 // 官方建议:开发小程序时,设计师可以用iPhone6 作为视觉稿的标准
// 样式导入(使用wxss 提供的@import 语法,可以导入外联的样式表) // @import 后跟需要导入的外联样式表的相对路径,用 ; 表示语句结束 // commpon.wxss(公共样式文件) .small{ padding:5px; } // index.wxss(页面的样式文件) @import "commpon.wxss"; .middle{ padding:15px; }
全局样式:定义在app.wxss 中的样式为全局样式,作用于每一个页面
局部样式:在页面的.wxss 文件中定义的样式为局部样式,只作用于当前页面
注意:
1. 当局部样式和全局样式冲突时,根据就近原则,局部样式会覆盖全局样式
2. 当局部样式的权重大于或等于全局样式的权重时,才会覆盖全局的样式
全局配置
全局配置文件及常用的配置项(小程序根目录下的app.json文件是小程序的全局配置文件,常用的配置项如下):
1. pages(记录当前小程序所有页面的存放路径)
2. window(全局设置小程序窗口的外观)
3. tabBar(设置小程序底部的tabBar效果)
4. style(是否启用新版的组件样式)
之前已经说过pages和style的配置,这里重点说下window和tabBar
window配置项(如下图所示,window配置的主要是红色导航栏区域):
window 节点常用的配置项:
// 设置导航栏的标题(例:把顶部导航条上的标题从默认的 WeChat 改为 某某商城) // app.json==》window==>navigationBarTitleText // 设置导航栏的背景色 // app.json==》window==》navigationBARBackgroundColor // 设置导航栏的标题颜色(可选值只有black 和 white) // app.json==》window==》navigationBarTextStyle // 全局开启下拉刷新功能(在app.json中启用下拉刷新功能,会作用于每个小程序页面) // 下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为 // app.json==》window==》enablePullDownRefresh(设置为true) // 注意:模拟器的效果不能真正100%还原手机的使用效果,类似下拉功能这种还是要用真机测试 // 设置下拉刷新时窗口的背景颜色(当全局开启下拉刷新功能之后,默认的窗口背景为白色) // app.json==》window==》为backgroundColor指定16进制的颜色值 // 设置下拉刷新时loading 的样式(全局开启下拉刷新功能后,默认窗口的loading样式(三个小圆点)为白色) // app.json==》window==》为backgroundTextStyle指定dark值(可选值只有light 和dark) // 设置上拉触底的距离 // 是指移动端的专有名词,通过手指在屏幕上的上拉滑动操作,从而加载更多数据的行为 // app.json==》window==》为onReachBottomDistance 设置新的数值 // 注意:默认距离值为50px,如果没有特殊需求,建议使用默认值即可(意思就是在右侧的滚动条距离页面底部不足50px的时候就自动触发加载下一页的行为)
tabBar配置项(tabBar是移动端应用常见的页面效果,用于实现多页面的快速切换,如下图所示,小程序通常tabBar分为两种 == 底部tabBar;顶部tabBar):
注意:
1. tabBar 中只能配置最少2个,最多5个tab页签
2. 当渲染顶部tabBar 时,不显示icon ,只显示文本
tabBar 的6个组成部分:
在app.json 中配置tabBar,节点的配置项如下:
其中,每个tab 项的配置选项如下:
实现tabBar的配置步骤:拷贝图标资源==》新建3个对应的tab页面==》配置tabBar选项
注意:tabBar的页面必须放在pages的最前面
页面配置文件的作用:小程序中,每个页面都有自己的.json 配置文件,用来对当前页面的窗口外观、页面效果等进行配置。
页面配置和全局配置的关系:
1. 小程序中,app.json中的window 节点,可以全局配置小程序中么个页面的窗口表现。
2. 如果某些小程序页面想要拥有特殊的窗口表现,此时,页面级别的.json 配置文件就可以实现这种需求。
注意:当页面配置与全局配置冲突时,根据就近原则最终的效果以页面配置为准
页面配置中常用的配置项:
网络数据请求:
小程序中网络数据请求的限制(出于安全性方面的考虑,小程序官方对数据接口的请求做出了如下两个限制):
1. 只能请求HTTPS 类型的接口
2. 必须将接口的域名添加到信任列表中
如何配置 request 合法域名(假设自己的微信小程序中,希望请求https://www.*****/ 域名下的接口):
配置步骤:登录微信小程序管理后台==》开发==》开发设置==》服务器域名==》修改request 合法域名
注意:
1. 域名只支持https 协议
2. 域名不能使用 IP 地址或 localhost
3. 域名必须经过ICP 备案
4. 服务器域名一个月内最多可申请5次修改
// 发起get 请求(调用微信小程序提供的 wx.request() 方法,可以发起get 数据请求) // 页面的 .wxml 文件 <button bindtap='getInfo'>发起get 请求</button> // 页面的 .js 文件 getInfo(){ wx.request({ url:'http://www.escook.cn/api/get', // 请求的接口地址,必须基于https 协议并且已经配置过 method:'GET', // 请求的方式 data:{ // 发送到服务器的数据 name:'tom', age:22 }, success:(res) => { // 请求成功之后的回调函数 console.log(res) } }) } // 发起post 请求(调用微信小程序提供的 wx.request() 方法,可以发起post 数据请求) // 页面的.wxml 文件 <button bindtap='postInfo'>发起post 请求</button> // 页面的.js 文件 postInfo(){ wx.request({ url:'https://www.escook.cn/api/post', method:'POST', data:{ name:'tom', age:33 }, success:(res)=>{ console.log(res) } }) }
// 在页面刚加载时请求数据
// 需要在页面刚加载的时候,自动请求一些初始化的数据,此时需要在页面的 onLoad 事件中调用获取数据的函数
// 页面的 .js 文件中(找到onLoad事件)
onLoad:function(){
this.getInfo()
this.postInfo()
}
跳过 request 合法域名校验:
如果后端仅仅提供了 http 协议的接口,暂时没有提供 https 协议的接口,此时为了不耽误开发进度,可以在微信开发者工具中,临时开启(开发环境不校验请求域名、TLS 版本及HTTPS证书 几个选项),跳过request 合法域名的校验
注意:跳过 request 合法域名校验的选项,仅限在开发与调试阶段使用
关于跨域 和 Ajax :
1. 跨域问题只存在于基于浏览器的web 开发中。由于小程序的宿主环境不是浏览器,而是微信客户端,所以小程序中不存在跨域问题
2. Ajax 技术的核心是依赖于浏览器中的 XMLHttpRequest 这个对象,由于小程序的宿主环境是微信客户端,所以小程序中不能叫做"发起Ajax 请求",而叫"发起网络数据请求"
下面介绍下小程序中的视图与逻辑:
什么是页面导航:页面导航指的是页面之前的相互跳转。
例如:浏览器中实现页面导航的方式:1.<a> 链接 2. location.href
小程序中实现页面导航的两种方式:
1. 声明式导航( 在页面声明一个<navigator> 导航组件,通过点击<navigator> 组件实现页面跳转)
2. 编程式导航(调用小程序的导航API ,实现页面的跳转)
// 声明式导航
// 导航到 tabBar 页面(tabBar页面指的是被配置为 tabBar 的页面) // 在使用 <navigator> 组件跳转到指定的tabBar 页面时,需要指定url 属性和 open-type 属性 // url 表示要跳转的页面的地址,必须 / 开头 // open-type 表示跳转的方式,必须为 switchTab // 单击页面里的文本跳转到tabBar 上对应的导航页面 <navigator url='/pages/message/message' open-type='switchTab'>导航到消息页面</navigator> // 导航到非tabBar 页面(非 tabBar 页面指的是没有被配置为tabBar的页面) // 在使用<navigator> 组件跳转到普通的非 tabBar 页面时,则需要指定url 属性和 open-type 属性 // url 表示要跳转的页面的地址,必须 / 开头 // pen-type 表示跳转的方式,必须为 navigate // 注意:为了简便,在导航到非tabBar 页面时,open-type=‘navigate’ 属性可以省略 // 点击页面里的文本跳转到对应的非tabBar 的普通页面 <navigator url='/pages/info/info' open-type='navigate'>导航到info页面</navigator> // 后退导航(如果要后退到上一页面或多级页面,则需要指定open-type 属性和 dalta 属性) // open-type 的值必须是navigateBack,表示要进行后退导航 // delta 的值必须是数字,表示要后退的层级 // 注意:为了简便,如果只是后退到上一页面,则可以省略 delta 属性,因为其默认值就是1 // 点击页面里的文本,后退到上一个页面 <navigator open-type='navigateBack' delta=''1>返回上一级</navigator>
// 声明式导航传参 // navigate 组件的 url 属性用来指定将要跳转到的页面的路径,同时,路径的后面还可以携带参数 // 1. 参数与路径之间使用 ?分隔 // 2. 参数键与参数值用 = 相连 // 3. 不同参数用 & 分隔 <navigator url='/pages/info/info?name=tom&age=20'>跳转到熬info页面时的传参</navigator>
编程式导航
导航到tabBar 页面(调用 wx.switchTab(Object,Object) 方法,可以跳转到tabBar 页面,其中 Object 参数对象的属性列表如下:)
// 导航到tabBar 页面 // 页面结构 <button bindtap='gotoMessage'>跳转到消息页面</button> // 通过编程式导航,跳转到 message 页面 gotoMessage(){ wx.switchTab({ url:'pages/message/message' }) }
导航到非 tabBar 页面(调用 wx.navigateTo(Object,Object) 方法,可以跳转到非tabBar的页面,其中Object 参数对象的属性列表如下:)
// 导航到非 tabBar 页面 // 页面结构 <button bindtap='gotoInfo'>跳转到info页面</button> // 通过编程式导航,跳转到info 页面 gotoInfo(){ wx.navigateTo({ url:'/pages/info/info' }) }
后退导航(调用 wx.navigateBack(Object,Object) 方法,可以返回上一页面或多级页面,其中Object 参数对象可选的属性列表如下:)
// 后退导航 // 页面结构 <button bindtap='gotoBack'>后退</button> // 编程式导航,后退到上一页面 gotoBack(){ wx.navigateBack() }
// 编程式导航传参 // 调用 wx.navigateTo(Object,Object) 方法跳转页面时,也可以携带参数 // 页面结构 <button bindtap='gotoInfo2'>跳转到info页面</button> // 通过编程式导航,跳转到info 页面,并携带参数 gotoInfo2(){ wx.navigateTo({ url:'/pages/info/info?name=tom&age=10' }) }
// 在onLoad 中接收导航参数 // 通过声明式导航传参或编程式导航传参所携带的参数,可以直接在onLoad 事件中直接获取到 onLoad:function(options){ console.log(options) // options 就是导航传递过来的参数对象 }
页面事件(下拉刷新)
下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为
启用下拉刷新有两种方式:
1. 全局开启下拉刷新(在app.json 的window 节点中,将enablePullDownRefresh 设置为true)
2. 局部开启下拉刷新(在页面的.json 配置文件中,将enablePullDownRefresh 设置为true)
实际开发中,推荐使用第二种方式为需要的页面单独开启下拉刷新的效果
配置下拉刷新窗口的样式:
在全局或页面的.json 配置文件汇总,通过 backgroundColor 和 backgroundTextStyle 来配置下拉刷新窗口的样式
backgroundColor 用来配置下拉刷新窗口的背景颜色,仅支持16进制的颜色值
backgroundTextStyle 用来配置下拉刷新loading 的样式,仅支持dark 和light
监听页面的下拉刷新事件:
在页面的.js 文件中,通过 onPullDownRefresh() 函数即可监听当前页面的下拉刷新事件
// 页面中的 .js 文件 onPullDownRefresh:function(){ console.log('触发了页面的下拉刷新...') }
停止下拉刷新的效果:
当处理完下拉刷新后,下拉刷新的loading 效果会一直显示,不会主动消失,所以需要手动隐藏loading 效果,此时,调用 wx.stopPullDownRefresh() 可以停止当前页面的下拉刷新
// 页面的.js 文件
onPullDownRefresh:function(){ this.setData({ count:0 }) wx.stopPullDownRefresh() // 当数据重置成功后,调用此函数关闭下拉刷新的效果 }
上拉触底事件
什么是上拉触底:是移动端的专有名词,通过手指在屏幕上的上拉滑动操作,从而加载更多数据的行为
监听页面的上拉触底事件(在页面 .js 文件中,通过 onReachBottom() 函数即可监听当前页面的上拉触底事件)
监听事件是可以重复发起的,实际项目中可以做节流处理,同一时间内放置重复下拉刷新,当上一个没有完成时,禁止再次请求
配置上拉触底距离(这个指的是触发上拉触底事件时,滚动条距离页面底部的距离。可以在去全局或页面的 .json 配置文件中,通过onReachBottomDistance 属性来配置上拉触底的距离)
// 上拉触底刷新(节流)案例 // .wxml <view wx:for="{{colorList}}" wx:key='index' class="num-item" style="background-color:rgba({{item}})"> {{item}} </view> // .wxss .num-item{ border: 1rpx solid #efefef; border-radius: 8rpx; line-height: 200rpx; margin: 15rpx; text-align: center; text-align: center; text-shadow: 0rpx 0rpx 5rpx #fff; box-shadow: 1rpx 1rpx 6rpx #aaa; } // .js data: { colorList:[], // 列表值 isLoading:false, // 定义节流阀 }, getColors(){ // 调接口获取列表 this.setData({ // 开启节流阀 isLoading:true }) wx.showLoading({ // 上拉触底时展示loading效果 title: '数据加载中....', }) wx.request({ // 调接口获取列表 url: 'https://www.escook.cn/api/color', method:'get', success:({data:res})=> { this.setData({ colorList:[...this.data.colorList,...res.data] }) console.log(this.data.colorList,'getColors') }, complete:()=>{ // 不论结果成功还是失败隐藏loading效果 wx.hideLoading() this.setData({ isLoading:false }) } }) }, onLoad: function (options) { this.getColors() }, onReachBottom: function () { // 页面上拉触底事件的处理函数 if(this.data.isLoading) return this.getColors() },
每次保存代码后自动编译后的页面跳转到指定页面: