HTTP
协议的状态管理由于HTTP
协议是一款基于短连接模型的协议,所以同一个客户端发送的请求是无状态的。即服务端没有将同一个客户端发送的多次请求当成一个整体来看待,也没有将同一个客户端涉及到的数据保存下来以后使用。这就是无状态协议的特点。
HTTP
协议状态的管理办法:
cookie
机制
cookie
信息,返回给客户端让客户端保存cookie
信息,将这些数据存入本地cookie
存储区。cookie
数据一起发送请求,这样,服务端就可以获取上一次请求所存储的信息,从而知道当前客户端的状态,实现http
的状态管理。cookie
不安全,无法存储敏感数据
session
机制
session
区域,并且为该用户分配一个SESSIONID
。在返回响应时,以cookie
形式发给客户端。cookie
,将SESSIONID
存起来。SESSIONID
一起发送请求,这样服务端就可以通过SESSIONID
找到以前存过的数据,从而获取用户的状态信息,完成http
状态管理。token
机制
https://pan.baidu.com/s/1B3YUiJnd3A2vOGKmkEu0_Q
2prs
下载对应版本的微信开发者工具。
https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
强烈建议申请新邮箱账号。
百度:网易邮箱。
https://mail.163.com/register/index.htm?from=163mail&utm_source=163mail
https://mp.weixin.qq.com
服务号:为企业和组织提供的进行用户管理和服务的账号类型。
订阅号:为企业、组织和个人提供的进行信息发布的账号类型。
小程序:为企业、组织或个人提供的可以达到与原生app
功能相近的应用程序。在微信内部运行,其优点在于小,无需下载安装包,用完就走。
在微信公众平台首页,注册小程序开发者账号。
安装微信开发者工具IDE
。
扫码登录后,点击+新建小程序项目。
填写基本信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uuNrJkkO-1648187335442)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\1645601263836.png)]
目录:不能有中文、不能有空格,最后一个目录得是空目录。
AppID
:下拉列表中可以选择AppID
。
后台管理网站 – 开发管理 – 开发设置 – 看到AppID
。
小程序项目中包含的文件类型:
.json
文件 配置文件
app.json
在项目的根目录下,定义项目的全局配置参数。
页面.json
在pages
文件夹下,定义单个页面的配置参数。
.wxml
文件
模板文件(类似于html
,定义页面结构。但是此处不能使用任何html
标签)
.wxss
文件 样式文件
app.wxss
项目的根目录下。定义全局样式。
页面.wxss
在pages
文件夹下。定义单个页面的样式。
.js
文件 脚本文件
app.js
项目根目录下。用于创建App
对象。小程序启动时调用,全局唯一。可以把一些共享数据、全局生命周期相关代码定义在这里。
页面.js
pages
文件夹下。每一个页面都会有一个js
文件。通过该js
文件来创建Page
对象来管理当前页面中的脚本代码。当需要显示某页面时就会创建Page
对象,用于初始化页面数据,声明事件处理函数,声明生命周期等脚本代码。
app.json
app.json
用于对小程序进行全局配置。
pages
配置项pages
配置项用于定义当前小程序包含哪些页面(index
)
"pages": [
"pages/index/index",
"pages/test/test"
],
新建配置项:"pages/test/test"
,将会在pages
目录下新增test
目录,test
目录下新增test
四件套。意味着项目又多了一个页面。如果将该配置写在数组的首位,那么test
将会作为项目的首页,启动时自动显示首页。
JSON
文件配置的语法JSON
文件不能写注释。JSON
对象的属性名也必须在双引号之间。window
配置项"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle": "black"
},
tabbar
配置项"tabBar": {
"color": "#333",
"selectedColor": "#f00",
"list": [{
"text": "电影",
"pagePath": "pages/index/index",
"iconPath": "/images/index_disable.png",
"selectedIconPath": "/images/index_enable.png"
},{
"text": "影院",
"pagePath": "pages/theatre/theatre",
"iconPath": "/images/theatre_disable.png",
"selectedIconPath": "/images/theatre_enable.png"
},{
"text": "我的",
"pagePath": "pages/me/me",
"iconPath": "/images/me_disable.png",
"selectedIconPath": "/images/me_enable.png"
}]
},
style
基础库 2.8.0 开始支持,低版本需做兼容处理。
微信客户端 7.0 开始,UI 界面进行了大改版。小程序也进行了基础组件的样式升级。app.json 中配置 "style": "v2"
可表明启用新版的组件样式。
本次改动涉及的组件有 button icon radio checkbox switch slider
。可前往小程序示例进行体验。
sitemapLocation
用于指明 sitemap.json 的位置;默认为 ‘sitemap.json’ 即在 app.json
同级目录下名字的 sitemap.json
文件。
sitemapLocation
的作用是定义一些通用的爬虫规则,指定小程序中那些页面被允许索引
注:sitemap
的索引提示是默认开启的,如需要关闭 sitemap
的索引提示,可在小程序项目配置文件 project.config.json
的 setting
中配置字段 checkSiteMap
为 false
app.wxss
app.wxss
用于定义全局样式。
app.js
app.js
在小程序项目根目录下。是小程序全局的初始化脚本。当小程序启动时,就会执行该文件,创建App
对象。 该文件仅执行一次,也就意味着App
对象全局单例(唯一)。App
对象用于定义整个应用程序的生命周期回调方法,全局共享的数据等内容。
小程序中wxml
用于定义页面内容,它由各式各样的组件构成,这些组件都是微信自定义的,原生html
标签不能用。
关于组件属性的使用
小程序中的组件若含有布尔类型的属性,无论设置为true
还是false
,小程序都会当做字符串来进行解释,都会被解释为true
。除非用空字符串""
。但是这么写有点野,推荐通过{{}}
引用js
脚本变量,为属性赋值。
小程序中组件属性的属性名,既可以使用驼峰命名法,也可以使用短横线命名法,两种属性名的命名习惯小程序都支持。
view
组件view
组件为视图容器组件(类似div
)。 其基本语法:
1.如果组件的属性为布尔类型,当我们直接设置属性值为true或false时,都会被当做true来看待(js中非空字符串都为true);除非使用空字符串“ ”,但这么写有点野
属性一般默认false,写上就是true,不写就是false
建议用以下方法:
hover-stop-propagation="{{false}}"
2.小程序组件的属性名既可以使用驼峰命名法,也可以使用短横线命名法,二者够可以正确设置属性
<view class="定义样式类名"
hover-class="点击view后应用的样式类名">view>
<view class="v2"
hover-class="v2-hover"
hover-start-time="按住多久设置点击态,单位毫秒"
hover-stay-time="松开多久取消点击态,单位毫秒1000"
hover-stop-propagation="阻止点击态向父级传播 值为布尔类型"
>
案例:
pages/testing/view/view
。view
组件。let f = true
1 f = 'true'
2 f = 'false'
3 f = 'abc'
4 f = ''
5 f = 1
6 f = -1
7 f = 0
8 f = undefined
9 f = null
if(f){
console.log('真...')
}
image
组件image
组件为图片组件,用于显示图片。支持GIF
、JPG
、PNG
、SVG
、WEBP
等图像格式。其语法如下:
<image src="文件路径(支持网络路径)"
lazy-load="是否采用图片懒加载模式"
mode="图像的裁切模式"
show-menu-by-long-press="是否显示长按菜单">image>
mode="aspectFill" //图片在容器居中全部显示,多余的切除
mode="aspectFit" //图片完全在容器中显示,其余空间留黑
mode="scaleToFill" //图片在容器铺满,会失真
案例:pages/testing/image/image
。
wxss
是小程序提供的一套样式语言,也会经过编译来渲染组件样式。wxss
具备了css
的大部分特性。并且对css
进行了扩展,新增了rpx
尺寸单位。
rpx
响应式像素使用rpx
作为尺寸单位来定义组件的宽高,可以根据屏幕的分辨率进行自动转换,在不同的屏幕下会转成不同的px
物理像素值。从而实现屏幕适配。
设计规定:无论任何设备,屏幕的宽度都是750rpx
。
由此可知iphone6
下原始宽度为375px
,用rpx
表示为750rpx
。意味着在iphone6
平台下,1px = 2rpx
。
设备 | rpx换算px (屏幕宽度/750) | px换算rpx (750/屏幕宽度) |
---|---|---|
iPhone5 | 1rpx = 0.42px | 1px = 2.34rpx |
iPhone6 | 1rpx = 0.5px | 1px = 2rpx |
iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx |
swiper
组件swiper
组件为轮播图组件,其语法为:
<swiper autoplay="是否自动播放"
indicator-dots="是否显示指示器"
circular="是否采用前后循环播放"
interval="多长时间换下一页"
duration="切换动画的持续时间"
....>
<swiper-item>swiper-item>
<swiper-item>swiper-item>
<swiper-item>swiper-item>
swiper>
案例:新建pages/testing/swiper/swiper
。
text
组件text
组件用于显示文本的组件,其语法:
<text user-select=""
decode=""
space="">文本text>
navigator
组件navigator
组件是页面链接组件,用于控制页面的跳转。其基本语法:
<navigator url="当前小程序内的路由地址"
open-type="跳转方式">
链接文本
navigator>
open-type
跳转方式有以下几种:
navigate
,默认的跳转方式,可以从当前页跳转到非tabbar
页面。跳转的过程将会保留当前页,新建目标页,而后跳转过去,称为保留跳转。navigateBack
,返回上一级页面。这种操作将会销毁当前页,从而显示上一页。可以配合属性delta
实现上n
页的跳转。switchTab
,字面理解为切换标签页(底部选项卡页面)。这种跳转方式用于跳转到tabbar
页面。一旦这么做,就会销毁所有非tabbar
页面。redirect
,这种跳转方式将会关闭当前页,跳转到非tabbar
的目标页面。这种方式也将创建新页面。reLaunch
,字面理解为: 重新启动应用。这种方式将会销毁所有页面。重新打开小程序中的某一个目标页面。案例:testing/a/a tesing/b/b testing/c/c
。
scroll-view
组件scrollview
组件用于实现可滚动的视图容器(支持水平、垂直滚动)。基本结构如下:
<scroll-view style="height:200px;"
scroll-x="是否允许水平方向滚动"
scroll-y="是否允许垂直方向滚动">
<view>....view>
<view>....view>
<view>....view>
...非常多....
scroll-view>
案例: pages/testing/scroll/scroll
。
scroll-view
组件scrollview
组件用于实现可滚动的视图容器(支持水平、垂直滚动)。基本结构如下:
<scroll-view style="height:200px;"
scroll-x="是否允许水平方向滚动"
scroll-y="是否允许垂直方向滚动">
<view>....view>
<view>....view>
<view>....view>
...非常多....
scroll-view>
案例: pages/testing/scroll/scroll
。
input
组件input
组件为输入框组件,其语法结构:
<input type="输入框的类型:text|number|idcard|digit"
placeholder="占位符内容"
value="文本框的值"
maxlength="可输入的最大长度"
password="是否是密码框"
bindinput="输入内容后,触发"
bindfocus="获取焦点时,触发"
bindblur="焦点失去时,触发">input>
案例:pages/testing/input/input
。
基于小程序的input
组件实现双向数据绑定,让文本框中值与data
中的某一个变量实现动态绑定。
wxml
:
<input model:value="{{name}}" type="text" />
输入的是:{{name}}
js
:
Page({
data: {
name: ''
}
})
而早期小程序的双向数据绑定需要借助于bindinput
事件来进行处理。一旦用户在文本框中更新了内容,就会触发该事件,在事件处理函数中获取文本框的值,然后更新data
。
wxml
<input bindinput="inputPwd" type="text"/>
{{pwd}}
js
Page({
data:{
pwd: ''
},
inputPwd(event){
let val = event.detail.value // 文本框的值
// 第一种更新data的方式:不会更新界面
this.data.pwd = val
// 第二种更新data的方式:可以自动更新界面中使用pwd的位置
this.setData({ pwd:val })
}
})
WXML
语法基础wxml
是一套标签语言,符合标签语言的相关语法,用于定义页面的结构、内容。在wxml
中经常需要呈现动态数据(动态文本、动态样式、动态属性等),而这些动态数据来源于对应js
文件中data
里声明的变量。就需要使用{{}}
来动态引用。大概有以下几类需求:
Page({
data:{
name: 'zs',
age: 15,
userInfo: {}
}
})
<view>{{name}}view>
<view>{{age}}view>
当需要动态设置组件的属性值时,就需要使用属性绑定:
Page({
data:{
url: '/images/1.jpg',
num: 1,
d: 'images'
}
})
<image src="{{url}}">image>
<image src="/images/{{num}}.jpg">image>
动态更新组件的wxss
样式:
Page({
data:{
className: 'blue',
c: 'red',
bw: 1
}
})
.red{ color:red; }
.blue{ color:blue; }
<text class="{{className}}">内容文本text>
<text style="color:{{c}}; border:{{bw}}px solid {{c}};">
内容文本
text>
基于小程序的提供的列表渲染的语法,可实现遍历数组中每个元素动态输出列表数据的需求。类似vue
中的v-for
。
data: {
foods: [
{id:1, name:'臭豆腐', price:18.0},
{id:2, name:'螺蛳粉', price:15.0},
{id:3, name:'鲱鱼罐头', price:66.0},
{id:4, name:'毛鸡蛋', price:5.0}
]
}
Vue
应如下遍历输出foods
:
<div v-for="(item,i) in foods" :key="item.id">
id: {{item.id}}
name: {{item.name}}
price: {{item.price}}
div>
微信小程序的语法,应如下遍历输出foods
:
<view wx:for="{{foods}}">
index: {{index}}
id: {{item.id}}
name: {{item.name}}
price: {{item.price}}
view>
设置后,发现控制台有一个警告,需要为wx:for
提供一个wx:key
。
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key
来指定列表中项目的唯一的标识符。
wx:key
的值以两种形式提供
*this
代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
如不提供 wx:key
,会报一个 warning
, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
<view wx:for="{{foods}}" wx:key="id">
序号:{{index}}
ID: {{item.id}}
菜名:{{item.name}}
价格:{{item.price}}
view>
wx:for
将会为这次遍历默认新增两个变量:item
,index
。这两个变量可以自定修改,语法如下:
<view wx:for="{{foods}}" wx:key="id"
wx:for-item="f" wx:for-index="i">
序号:{{i}}
ID: {{f.id}}
菜名:{{f.name}}
价格:{{f.price}}
view>
使用条件渲染可以动态处理是否渲染某一个元素,类似vue
中的v-if
。
data:{
islogin: true
}
<text wx:if="{{islogin}}">欢迎:XXXtext>
<text wx:else>登录 注册text>
常见写法有以下几种:
<text wx:if="{{条件表达式}}">xxxtext>
---------------------------------------------
<text wx:if="{{条件表达式}}">xxxtext>
<text wx:else>xxxtext>
---------------------------------------------
<text wx:if="{{条件表达式}}">xxxtext>
<text wx:elif="{{条件表达式}}">yyytext>
<text wx:elif="{{条件表达式}}">zzztext>
<text wx:elif="{{条件表达式}}">aaatext>
<text wx:else>bbbtext>
radio-group
组件radiogroup
组件为单选框组组件,包含一组单选按钮。基本结构:
<radio-group>
<radio value="M" checked color="blue">男radio>
<radio value="F" color="red">女radio>
radio-group>
radio-group
中的radio
只有一个可以被选中。
案例:testing/form/form
。
checkbox-group
组件复选框组件,其语法:
<checkbox-group>
<checkbox value="A" color="" checked>...checkbox>
<checkbox value="B" color="" checked>...checkbox>
<checkbox value="C" color="" checked>...checkbox>
checkbox-group>
事件是视图层到逻辑层的通讯方式,它可以将用户的行为反馈到逻辑层进行后续处理。
<scroll-view bindscrolltolower="">scroll-view>
<scroll-view bindscrolltoupper="">scroll-view>
<scroll-view bindscroll="">scroll-view>
<input bindinput=""/>
<image bindtap=""/>
微信小程序中,事件类型大致分为两大类:
WXML的冒泡事件列表:
类型 | 触发条件 |
---|---|
touchstart | 手指触摸动作开始 |
touchmove | 手指触摸后移动 |
touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 |
touchend | 手指触摸动作结束 |
tap | 手指触摸后马上离开 |
longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 |
longtap | 手指触摸后,超过350ms再离开(推荐使用longpress事件代替) |
transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 |
animationstart | 会在一个 WXSS animation 动画开始时触发 |
animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 |
animationend | 会在一个 WXSS animation 动画完成时触发 |
touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 |
<button bind事件名称="事件处理函数名称">按钮button>
<button bind:事件名称="事件处理函数名称">按钮button>
<button catch事件名称="事件处理函数名称">按钮button>
bind
的方式绑定事件可以应用于任何组件,而bind:
的方式不能应用于原生组件。
原生组件指由操作系统直接控制的组件。如:获取焦点后的
input
组件、video
组件、camera
组件等。原生组件的特点是由Android
或IOS
操作系统直接处理,并非微信自己实现的UI
或功能。
bind
方式与bind:
方式绑定的事件不能阻止事件冒泡,而catch
方式可以自动阻止事件冒泡。
无论组件绑定的是冒泡事件还是非冒泡事件,事件处理函数名称都严禁出现小括号:
<button bindtap="tapEvent">xxxbutton>
<input bindinput="inputEvent"/>
问题:如何完成事件参数的传递?
需求:
<view>
商品信息.....
<button bindtap="tapDel" data-i="0">删除button>
view>
<view>
商品信息.....
<button bindtap="tapDel" data-i="1">删除button>
view>
<view>
商品信息.....
<button bindtap="tapDel" data-i="2">删除button>
view>
data: {
items: [{购物项1}, {购物项2}, {购物项3}]
}
tapDel(event){
// event.target 获取触发事件的事件源对象 --> Button对象
// event.target.dataset 获取Button上封装了所有的data-*属性的对象
// event.target.dataset.i 获取button组件上的data-i属性值
let i = event.target.dataset.i
}
案例:购物车。
案例:吃饭睡觉打豆豆。
API
API
wx.showToast()
提示框
wx.showModal()
模态对话框
API
上述5中方法跳转的过程,与navigator
组件的5中opentype
一一对应,功能完全一致。
wx.navigateTo
跳转时的参数传递问题wx.navigateTo
可以保留当前页,新建目标页,跳转过去。不能跳转到tabbar
页面。在跳转的过程中可以传参,有两种传参的方案:
正向传参
假设A跳转到B,同时携带参数,A传参,B接收,这种方式为正向传参。
A页面:
wx.navigateTo({
url: '/pages/testing/b/b?id=10&name=张三&pwd=1234'
})
B页面:
Page({
data: {},
// 系统自动调用,options系统自动传入
// options封装了上一个页面传进来的参数,在此使用options形参接收
onLoad(options){
}
})
反向传参
假设A跳转到B,在B页面中进行操作的时候,将参数回传给A,这种方式为反向传参。
A页面,定义一个事件处理函数,接收B返回回来的数据:
wx.navigateTo({
url: 'xxx',
events: {
acceptCity(data){
console.log('接受到了回传回来的数据',data)
}
}
})
B页面处理完业务后,通过事件通道(EventChannel
)回传数据:
let ec = this.getOpenerEventChannel()
ec.emit('acceptCity', 回传的数据)
小程序页面的生命周期相关钩子方法需要在Page.js
中进行定义,基本结构如下:
Page({
/** 页面的初始数据 */
data: {
},
/** 生命周期函数--监听页面加载 */
onLoad: function (options) {
},
/** 生命周期函数--监听页面初次渲染完成 */
onReady: function ()
},
/** 生命周期函数--监听页面显示 */
onShow: function () {
},
/** 生命周期函数--监听页面隐藏 */
onHide: function () {
},
/** 生命周期函数--监听页面卸载 */
onUnload: function () {
},
})
小程序页面的生命周期相关钩子方法需要在Page.js
中进行定义,基本结构如下:
Page({
/** 页面的初始数据 */
data: {
},
/** 生命周期函数--监听页面加载 仅执行一次 */
onLoad: function (options) {
},
/** 生命周期函数--监听页面初次渲染完成 仅执行一次 */
onReady: function ()
},
/** 生命周期函数--监听页面显示 执行多次 */
onShow: function () {
},
/** 生命周期函数--监听页面隐藏 执行多次 */
onHide: function () {
},
/** 生命周期函数--监听页面卸载 仅执行一次 */
onUnload: function () {
},
})
整个微信小程序从启动到销毁也会涉及到生命周期,称为小程序应用的生命周期。涉及到的相关生命周期钩子方法需要在app.js
中进行定义:
// app.js
App({
onLaunch(){ /** 当应用冷启动时(无运行状态中启动),执行 */
},
onShow(){ /** 当小程序显示时执行 */
},
onHide(){ /** 当小程序隐藏到后台时执行 */
},
globalData: {
// 全局共享数据存储区
}
})
如果需要在页面中访问globalData
,操作方式如下:
向globalData
中存数据:
getApp().globalData.cityname = '北京'
getApp().globalData.userInfo = {id:1, name:xxx,...}
从globalData
中取数据:
getApp().globalData.cityname
getApp().globalData.userInfo.id
getApp().globalData.userInfo.name
API
小程序对于发送请求时的一些限制:
- 请求资源路径只支持
https
协议。- 必须使用域名,不能使用
IP
。域名必须经过ICP
备案。- 域名必须在小程序后台管理网站中注册。(登录管理后台,选择开发管理、开发设置、服务器域名配置,新增:
https://api.tedu.cn
)注册完毕的域名才可以在小程序中向该地址发送请求。
在浏览器中验证一个请求资源路径:
https://api.tedu.cn/index.php?cid=1
如果在公司网络访问不了,尝试修改DNS
:
打开资源管理器 – 右键 网络 – 更改适配器设置
右键所使用的网卡 – 属性 – 双击选择 TCP/IP V4
使用下方的DNS
服务器地址:114.114.114.114
– 确定 – 确定.
wx.request()
wx.request({
url: '',
data: '',
method: '',
header: '',
success: (res)=>{},
fail: (err)=>{},
completed: (com)=>{}
})
案例:点击按钮,发送请求
新建项目。
项目目录不准有空格、中文特殊字符。最后一个目录为空目录。
选择正确的appid
。
选择不适用云服务。
搭建项目的主体结构。
index
、theatre
、me
。项目细节调整。
在小程序目录里找到 project.config.json
,找到 setting
配置对象,将 checkSiteMap
设置为 false
。
实现思路
Index.js
中的onLoad
方法。在页面初次加载时发请求,获取热映类别下的首页电影列表数据。wx:for
,完成电影列表渲染。说明 | |
---|---|
接口地址 | https://api.tedu.cn/index.php |
请求方式 | GET |
请求参数 | cid : 类别ID 热映ID:1 待映ID:2 经典ID:3 offset : 读取记录时的起始下标位置 |
返回值 | 相应类别下的电影列表。 |
访问不同类别的首页数据:
https://api.tedu.cn/index.php?cid=1 热映电影列表 首页
https://api.tedu.cn/index.php?cid=2 待映电影列表 首页
https://api.tedu.cn/index.php?cid=3 经典电影列表 首页
访问不同类别的后续数据:
https://api.tedu.cn/index.php?cid=1&offset=20 热映电影列表
https://api.tedu.cn/index.php?cid=2&offset=20 待映电影列表
https://api.tedu.cn/index.php?cid=3&offset=20 经典电影列表
所以当前接口将会返回相应类别下的电影列表数据,返回从offset
位置开始向后读取20条电影信息组成的数组。
[{电影},{电影},{电影},{电影},{电影},{电影}......]
实现思路
cid
(1/2/3),向服务端发送请求,获取响应类别下的首页电影列表数据。需求:当列表滚动到底部后,加载当前类别的下一页数据。
实现思路
Page
中重写onReachBottom
)cid
类别id
, offset
起始位置。发送新的列表请求,访问下一页电影列表数据。loadData
方法// 该方法的作用:传递两个参数:cid,offset,帮忙发请求
// 返回查询得到的结果
loadData(cid, offset){
return new Promise((resolve, reject)=>{
wx.request({
url: 'https://api.tedu.cn/index.php',
method: 'GET',
data: {cid: cid, offset: offset},
success: (res)=>{
resolve(res.data)
}
})
})
}
onLoad(){
this.loadData(1, 0).then(movielist=>{
this.setData({movies: movielist})
})
}
tapNav(){
this.loadData(1, 0).then(movielist=>{
this.setData({movies: movielist})
})
}
onReachBottom(){
this.loadData(动态cid, 动态offset).then(movielist=>{
push.......
this.setData()
})
}
客户端向服务端发送第一次请求试图获取一组数据,当数据下载完毕后,客户端可以将这些数据存入客户端本地缓存中。当下次发送相同请求时,先去本地缓存中搜索,看以前有没有存过,如果有,则直接获取后加载显示;如果没有,再发请求。
所以缓存的机制并不复杂,关键是找对时机进行缓存的存储与读取。
微信小程序将html5
中的webstorage
封装了,提供了一些wxAPI
用于向storage
中存,从storage
中取。
一个项目如何实现缓存还需要注重这个项目的业务形态。不同的业务,本地缓存到底存多久需要思考讨论。最终设计一个比较合理的更新缓存的方案。
一般列表展示都会配套一个下拉刷新来更新列表、更新缓存。
有些应用,每次打开时(App.onLaunch()
)将数据缓存清空。
有些应用,更新的频率更高些,就需要在应用使用时,每隔一段时间更新数据缓存。
.json
配置文件中,开启当前页面的下拉刷新。Page
的onPullDownRefresh
方法,监听下拉刷新事件。获取小程序的位置,可以使用:
wx.getLocation({
type:'gcj02',
altitude: true,
isHighAccuracy: true,
success: (res)=>{ .... }
})
在app.json
中配置权限声明:
{
"pages": ["pages/index/index"],
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
}
}
如果希望获取当前城市名称等业务数据,就需要接入第三方位置服务。小程序天然配套腾讯位置服务。
打开腾旭位置服务的官方网站:https://lbs.qq.com
开发文档 =>
微信小程序JS SDK
申请开发者密钥(key):申请密钥
开通webserviceAPI
服务:控制台 ->应用管理 -> 我的应用 ->添加key-> 勾选WebServiceAPI
-> 保存
(小程序SDK
需要用到webserviceAPI
的部分服务,所以使用该功能的KEY
需要具备相应的权限)
下载微信小程序JavaScriptSDK
,微信小程序JavaScriptSDK v1.1 JavaScriptSDK v1.2
安全域名设置,在小程序管理后台 -> 开发 -> 开发管理 -> 开发设置 -> “服务器域名” 中设置request合法域名,添加https://apis.map.qq.com
小程序示例
// 引入SDK核心类,js文件根据自己业务,位置可自行放置
var QQMapWX = require('../../libs/qqmap-wx-jssdk.js');
var qqmapsdk = new QQMapWX({
key: '申请的key'
});
qqmapsdk.reverseGeocoder({
success: (res)=>{....}
})
需求:当点击某一个电影列表项后,跳转到电影详情页面,显示当前电影的详细信息。(需要通过电影ID
,获取电影详情) 。
实现思路
pages/movie/movie
。id
。id
参数,通过id
查询电影详细信息,渲染页面。ID
查询电影详情接口说明 | |
---|---|
接口地址 | https://api.tedu.cn/detail.php |
请求方式 | GET |
请求参数 | id: 电影ID |
返回结果 | object 类型,返回电影的详细数据。 |
cover: "https://p1.meituan.net/movie/f6ec2a022d3644ef493f881d359f65303190471.jpg@218w_300h_1e_1c"
description: "如果你喜欢的女孩,得了抑郁症,你该怎么办?辛唐(孙晨竣 饰)拥有通过声音给他人制造快乐的能力,但对同一人使用三次后,性命就会和此人绑定,只有对方开心,辛唐才能活命。偶然,辛唐救下准备自杀的同校网络红人吉择(章若楠 饰),两人借此绑定。吉择表面开朗,但实际患了抑郁症。辛唐最初为了活下去,费尽心思让吉择开心,而后续也真的投入深情。遗憾辛唐的秘密总会败露,而吉择暗黑的过往也在网络上被人揭开....愿爱情的温暖,能治愈抑郁的青春。"
director: [{…}]
moviename: "如果声音不记得"
movietype: "爱情/青春/奇幻"
score: "8.2"
showingon: "2020-12-04"
star: "章若楠/孙晨竣/王彦霖"
thumb: (29) ['', ''......]
图片懒加载。
添加mode
,防止图像比例失真。
点击图片后,全屏大图浏览器剧照列表。
wx.previewImage({
current: newUrls[i],
urls:newUrls
})
开发者可以使用腾讯提供的云服务器来开发微信小程序、小游戏的服务端程序。而无需搭建服务器。
云开发提供的基础能力有:
云数据库
云数据库是一个可以在小程序前端直接操作的云端数据库。它与mysql
不同,是一个json
类型的非关系型数据库。
云存储
云存储是微信云服务器提供的一块存储空间,可以让小程序前端通过响应API
代码直接针对云存储空间进行上传和下载。
云函数
云函数是一个在小程序端进行编写,而后通过开发工具部署到云服务器中,并且提供给小程序远程调用的函数。
打开开发工具,点击工具栏中的云开发按钮。
云数据库是一个可以在小程序前端直接操作的云端数据库。它与mysql
不同,是一个json
类型的非关系型数据库。
打开开发工具,点击工具栏中的云开发按钮。
云数据库是一个可以在小程序前端直接操作的云端数据库。它与mysql
不同,是一个json
类型的非关系型数据库。
mysql
存储数据的结构:
id | name | gender | school_id |
---|---|---|---|
1 | zs | m | 1 |
2 | ls | m | 2 |
3 | ww | f | 1 |
… | … | … | … |
school_id | name | loc | area |
---|---|---|---|
1 | 清华大学 | 五道口 | 1000 |
2 | 北京大学 | 中关村 | 850 |
… | … | … | … |
云数据库存储数据的结构:
[
{
"id":1,
"name":'zs',
"gender":'m',
"school": {
id: 1,
name: '清华大学',
loc: '五道口',
area: 1000
}
},{
"id":2,
"name":'ls',
"gender":'m',
"school": {
id: 1,
name: '清华大学',
loc: '五道口',
area: 1000
}
},{
"id":3,
"name":'ww',
"gender":'f',
"school": {
id: 2,
name: '北京大学',
loc: '中关村',
area: 850
}
},
]
描述一个云数据库存储的数据:
在云数据库中有一个集合,里面存储了3条记录(3条文档),每一条记录包含四个字段,他们有不同的数据类型,其中school字段又是一个对象,该对象中又包含4个属性,用于描述学校的基本信息。
https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/database/init.html
const db = wx.cloud.database({
// env: '云环境的ID (云开发控制台中复制过来)'
env: 'cloud2012-9gl0qd6g8dc72b1c'
})
db.collection('test').add({
data: {新增的对象},
success: (res)=>{
新增数据成功后的回调
}
})
上述代码可以方便的向test
集合中新增一条记录,我们发现,小程序将会自动为该记录分配唯一的_id
(主键ID
)。同时也会新增字段:_openid
。不同的用户插入数据将会有不同的_openid
,同一个用户添加的记录_openid
是一样的。_openid
字段标识了当前这一条记录属于谁(是哪一个用户创建的)。
小程序将会通过_openid
字段来确定用户对该集合中数据的访问权限。
db.collection('test').doc('记录的 _id').get({
success: (res)=>{
返回结果后执行回调方法。 res中就是返回的结果
}
})
xzyycloud
xzyy
中已经写好的内容,直接拽到新项目中,覆盖相关资源。comments.json
中的评论数据导入comments
集合中。xzyycloud
项目中,找到电影详情页,在onLoad
生命周期方法中,查询云数据库,获取当前电影的评论列表,遍历渲染。Collection
对象的常用方法方法 | 描述 |
---|---|
collection.doc() | 通过id查询一条记录 |
collection.where() | 添加筛选条件 |
collection.get() | 发送请求,查询云数据库 |
collection.add() | 添加数据 |
collection.skip(n) | 跳过前n条 |
collection.limit(n) | 向后查询n条 |
collection.orderby() | 排序 |
collection.remove() | 删除 |
collection.update() | 更新 |
db.collection('test')
.where({
price: _.gt(10)
})
.field({
name: true,
price: true,
})
.orderBy('price', 'desc')
.skip(1)
.limit(10)
.get()
假设我们需要查询进度大于 30% 的待办事项,那么传入对象表示全等匹配的方式就无法满足了,这时就需要用到查询指令。数据库 API
提供了大于、小于等多种查询指令,这些指令都暴露在 db.command
对象上。
API 提供了以下查询指令:
查询指令 | 说明 |
---|---|
eq | 等于 |
neq | 不等于 |
lt | 小于 |
lte | 小于或等于 |
gt | 大于 |
gte | 大于或等于 |
in | 字段值在给定数组中 |
nin | 字段值不在给定数组中 |
准备一个城市列表页面: pages/citylist/citylist
.
完善城市列表页中的列表显示内容。
当点击右侧导航时,控制scrollview
滚动到相应位置。
<scroll-view scroll-into-view="C">
<view id="A">view>
<view id="B">view>
<view id="C">view>
<view id="D">view>
....
scroll-view>
点击首页左上角城市时,跳转到citylist
页面,选择城市。
城市选择完毕后,后退到首页,并且将选中的城市名称回传回来,更新首页城市名。
选择城市后,将城市名称存入globalData
,然后navigateBack
。
在首页重写onShow
生命周期方法,将会在onShow
时区globalData
中获取城市名称,更新左上角。
准备一个城市列表页面: pages/citylist/citylist
.
完善城市列表页中的列表显示内容。
当点击右侧导航时,控制scrollview
滚动到相应位置。
<scroll-view scroll-into-view="C">
<view id="A">view>
<view id="B">view>
<view id="C">view>
<view id="D">view>
....
scroll-view>
点击首页左上角城市时,跳转到citylist
页面,选择城市。
城市选择完毕后,后退到首页,并且将选中的城市名称回传回来,更新首页城市名。
选择城市后,将城市名称存入globalData
,然后navigateBack
。
在首页重写onShow
生命周期方法,将会在onShow
时区globalData
中获取城市名称,更新左上角。
getLocation
进行定位。(需要把index.js
中封装好的getLocation
方法复制过来一份直接用)cityname
存入globalData
,更新citylist
页面顶部的定位城市名称。globalData
,返回上一页即可。onShow
是更新首页左上角,完成业务流程。onShow
生命周期方法,读取globalData.cityname
,更新左上角城市名。看文档:
https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodSearch
实现思路
qqmapsdk
的方法封装到app.js
中,通过globalData
暴露qqmapsdk
的引用。globalData
中读取qqmapsdk
即可。
index.js
中需要。citylist.js
中需要。theatre.js
中需要。qqmapsdk
调用search
方法,获取选中城市的影院列表。theatreList
存入data
,在页面中渲染显示该列表。km
,保留两位小数。event.target
与event.currentTarget
之间的区别<view class="v1" bindtap="tapV1">
<view class="v2">view>
view>
tapV1(event){
// 当用户点击了v2, 触发tapv1,那么:
event.target // 指的是v2,因为真正直接被点击的元素是v2
event.currentTarget // 指的是v1,因为bindtap绑定到了v1上
// 当用户点击了v1,触发tapv1,那么:
event.target // 指的是v1,因为真正直接被点击的元素是v1
event.currentTarget // 指的是v1,因为bindtap绑定到了v1上
}
从小程序基础库版本 1.6.3 开始,小程序支持简洁的组件化编程。所有自定义组件相关特性都需要基础库版本 1.6.3 或更高。
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
<my-button>my-button>
<my-button color="#36d"
text="按钮上的字"
round
bind:doubletap="doubletapEvent">
my-button>
doubletapEvent(){
console.log('么么哒..')
}
对components
右键,新建Component
, 起名字,新建组件。
编写组件.wxml 组件.wxss
。
在需要引用组件的页面中,通过自定义组件名使用该组件。
<my-button>my-button>
前提就是在该页面.json
,需要声明引入该组件:
{
"usingComponents": {
"my-button": "/components/mybutton/mybutton"
}
}
从小程序基础库版本 1.6.3 开始,小程序支持简洁的组件化编程。所有自定义组件相关特性都需要基础库版本 1.6.3 或更高。
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
<my-button>my-button>
<my-button color="#36d"
text="按钮上的字"
round
bind:doubletap="doubletapEvent">
my-button>
doubletapEvent(){
console.log('么么哒..')
}
对components
右键,新建Component
, 起名字,新建组件。
编写组件.wxml 组件.wxss
。
在需要引用组件的页面中,通过自定义组件名使用该组件。
<my-button>my-button>
前提就是在该页面.json
,需要声明引入该组件:
{
"usingComponents": {
"my-button": "/components/mybutton/mybutton"
}
}
Vant
组件库安装vant
。
# 进入项目根目录, xzyycloud文件夹下执行命令:
npm init # 输入命令后一路回车,将会自动创建package.json
npm i @vant/weapp -S --production
修改app.json
.
将 app.json
中的 "style": "v2"
去除,小程序的新版基础组件强行加上了许多样式,难以覆盖,不关闭将造成部分组件样式混乱。
修改project.config.json
。
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram/"
}
],
如上配置的目的,是希望小程序开发工具在构建npm
时,可以找到package.json
, 还需要定义编译后的输出目录(./miniprogram/
)。
在小程序开发工具中构建npm
。
一旦构建npm
成功,将会在miniprogram
目录下新增miniprogram_npm
文件夹,里面就是打包好的组件源码。接下来就可以直接引入,使用。
Vant
组件库中的Button
:引入
在页面的.json
文件中引入该组件:
{
"usingComponents": {
"my-button": "/components/mybutton/mybutton",
"van-button": "@vant/weapp/button/index"
}
}
使用
<van-button type="default">默认按钮van-button>
<van-button type="primary">主要按钮van-button>
<van-button type="info">信息按钮van-button>
<van-button type="warning">警告按钮van-button>
<van-button type="danger">危险按钮van-button>
显示失败的,清除编译缓存,重新编译多尝试。
小程序提供了一个API
方法,可以获取用户数据:wx.getUserProfile()
:
点击头像后,选择新图片。
获取选中图片的路径,替换头像路径。(userInfo.avatarUrl
)
选择图片的方法:
wx.chooseImage()
wx.getUserProfile()
的作用是让用户方便的提供个人微信昵称与头像。以此来证明当前用户的身份。而一个完整的微信登录业务,必然需要将这些用户信息存入自己家数据库中。(例如,存入mysql
中的user
表里)
id | _openid | name | phone | nickName | avatarUrl | gender | … |
---|---|---|---|---|---|---|---|
1 | ovoaabbc | xuming | 13333… | 徐铭 | https://xx.jpg | m | … |
2 | aabbcdefg | lisi | 144 | … | https://xxxx.jpg | f | … |
这样将微信提供的用户信息存入到自己家数据库后,可以方便的修改昵称、修改头像、新增数据,做表关联,维护用户收藏、与用户喜欢等等信息。
所以一个正确的微信登录业务如下:
users
。 保存用户信息。users
集合。当登录成功后,点击头像,选择本地图片,将头像更新为选中的图片临时路径既可以修改显示的头像。但是这种方式只是纯客户端版本的更新头像,无法持久化保存信息。意味着下次登录时,还会显示微信头像。这是有问题的。
真正的修改头像业务流程如下:
点击头像选择一张新图片,获取图片路径。
wx.chooseImage()
将该头像上传至服务器,存到服务器某个目录下,并且返回可以访问它的网络路径。
http://api.tedu.cn/avatar/23dfad6fas98df6sdf5sd8fads9f.jpg
获取访问路径后,将该路径更新到云数据库中users
集合里当前用户的avatarUrl
字段。
let db = wx.cloud.database()
db.collection('users').doc('_id').update({
data: {
avatarUrl : xxxxxx
}
})
一旦数据库中头像地址变了,下次登录时将会加载最新的头像路径,从而访问到上传的头像图片,完成业务。
云存储类似云盘,可以在小程序客户端方便的上传、下载文件。可以在云开发控制台中测试相应功能。
小程序提供了API
,方便的实现文件上传操作:
wx.cloud.uploadFile({
filePath: '本地文件路径',
cloudPath: '云存储服务端的目标文件路径',
success: (res)=>{}
})
思考:如果没有使用云数据库,使用的是自己家的mysql数据库存储用户数据,那么如何完成头像路径的更新?
没有使用小程序云数据库意味着什么?
注册用户时,向自建的users
表中添加一条新纪录。
insert into users(id, nickName, avatarUrl, gender, ) values(?,?,?,?)
注册业务看似简单,但是有个棘手的问题,注册之前得先判断users
表中是否包含当前用户的信息?
答案:通过每个用户每个应用唯一的_openid
来解决这个问题。
如果是自建数据库,需要维护用户id
(_openid
)。用户一旦进入小程序,就会被分配一个openid
。获取openid
的方式比较麻烦:
wx.login
方法,获取一个校验码。nodejs
接口,后端将会通过该校验码,访问腾讯服务器,换取openid
返回给客户端。云开发环境下提供了云函数,可以方便的获取用户的openid
。
云函数是一种在小程序端编写、定义,通过开发工具部署到云服务器中,在小程序端可以远程调用的函数。这种函数在云服务器中执行。所以云函数可以简单替代nodejs
后端接口。
体验云函数。