web前端|微信小程序

HTTP协议的状态管理
由于HTTP协议是一款基于短连接模型的协议,所以同一个客户端发送的请求是无状态的。即服务端没有将同一个客户端发送的多次请求当成一个整体来看待,也没有将同一个客户端涉及到的数据保存下来以后使用。这就是无状态协议的特点。
HTTP协议状态的管理办法:

  1. cookie机制
    1. 客户端发送第一次请求,服务端接收请求,处理请求。
    2. 服务端在响应数据包中添加cookie信息,返回给客户端让客户端保存
    3. 客户端接收到响应,解析到cookie信息,将这些数据存入本地cookie存储区。
    4. 客户端发送后续请求时,将会自动携带域名匹配的cookie数据一起发送请求,这样,服务端就可以获取上一次请求所存储的信息,从而知道当前客户端的状态,实现http的状态管理。
    cookie不安全,无法存储敏感数据
  2. session机制
    1. 客户端发送第一次请求,服务端接收请求,处理请求。
    2. 服务端将敏感数据存入session区域,并且为该用户分配一个SESSIONID。在返回响应时,以cookie形式发给客户端。
    3. 客户端接收cookie,将SESSIONID存起来。
    4. 当客户端发送下次请求时,将会自动带着SESSIONID一起发送请求,这样服务端就可以通过SESSIONID找到以前存过的数据,从而获取用户的状态信息,完成http状态管理。
  3. token机制
    https://pan.baidu.com/s/1B3YUiJnd3A2vOGKmkEu0_Q
    2prs
微信小程序
  1. 下载对应版本的微信开发者工具。
    https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

  2. 强烈建议申请新邮箱账号。
    百度:网易邮箱。
    https://mail.163.com/register/index.htm?from=163mail&utm_source=163mail

微信公众平台
https://mp.weixin.qq.com 服务号:为企业和组织提供的进行用户管理和服务的账号类型。
订阅号:为企业、组织和个人提供的进行信息发布的账号类型。
小程序:为企业、组织或个人提供的可以达到与原生app功能相近的应用程序。在微信内部运行,其优点在于小,无需下载安装包,用完就走。
小程序接入流程
在微信公众平台首页,注册小程序开发者账号。
  1. 注册账号。
  2. 验证邮箱。
  3. 填写主体信息(个人)。
  4. 注册成功。
创建小程序项目
安装微信开发者工具IDE
扫码登录后,点击+新建小程序项目。
填写基本信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uuNrJkkO-1648187335442)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\1645601263836.png)]
目录:不能有中文、不能有空格,最后一个目录得是空目录。
AppID:下拉列表中可以选择AppID
? 后台管理网站 – 开发管理 – 开发设置 – 看到AppID
小程序项目的文件结构
小程序项目中包含的文件类型:
  1. .json文件 配置文件
    app.json 在项目的根目录下,定义项目的全局配置参数。
    页面.jsonpages文件夹下,定义单个页面的配置参数。
  2. .wxml文件
    模板文件(类似于html,定义页面结构。但是此处不能使用任何html标签)
  3. .wxss文件 样式文件
    app.wxss 项目的根目录下。定义全局样式。
    页面.wxsspages文件夹下。定义单个页面的样式。
  4. .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文件配置的语法
  1. JSON文件不能写注释。
  2. 字符串必须在双引号之间。 JSON对象的属性名也必须在双引号之间。
  3. 数组或对象的最后一个成员后不能加逗号。
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.jsonsetting 中配置字段 checkSiteMapfalse
app.wxss
app.wxss用于定义全局样式。
app.js
app.js在小程序项目根目录下。是小程序全局的初始化脚本。当小程序启动时,就会执行该文件,创建App对象。 该文件仅执行一次,也就意味着App对象全局单例(唯一)。App对象用于定义整个应用程序的生命周期回调方法,全局共享的数据等内容。
微信小程序组件库 小程序中wxml用于定义页面内容,它由各式各样的组件构成,这些组件都是微信自定义的,原生html标签不能用。
关于组件属性的使用
  1. 小程序中的组件若含有布尔类型的属性,无论设置为true还是false,小程序都会当做字符串来进行解释,都会被解释为true。除非用空字符串""。但是这么写有点野,推荐通过{{}}引用js脚本变量,为属性赋值。

  2. 小程序中组件属性的属性名,既可以使用驼峰命名法,也可以使用短横线命名法,两种属性名的命名习惯小程序都支持。
view组件
view组件为视图容器组件(类似div)。 其基本语法:
1.如果组件的属性为布尔类型,当我们直接设置属性值为true或false时,都会被当做true来看待(js中非空字符串都为true);除非使用空字符串“ ”,但这么写有点野
属性一般默认false,写上就是true,不写就是false
建议用以下方法:
hover-stop-propagation="{{false}}"

2.小程序组件的属性名既可以使用驼峰命名法,也可以使用短横线命名法,二者够可以正确设置属性

案例:
  1. 新建页面:pages/testing/view/view
  2. 该页面中测试应用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 = nullif(f){ console.log('真...') }

image组件
image组件为图片组件,用于显示图片。支持GIFJPGPNGSVGWEBP等图像格式。其语法如下:
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组件为轮播图组件,其语法为:
autoplay="是否自动播放" indicator-dots="是否显示指示器" circular="是否采用前后循环播放" interval="多长时间换下一页" duration="切换动画的持续时间" ....> > > >

案例:新建pages/testing/swiper/swiper
text组件
text组件用于显示文本的组件,其语法:
文本

navigator组件
navigator组件是页面链接组件,用于控制页面的跳转。其基本语法:
链接文本

open-type跳转方式有以下几种:
  1. navigate ,默认的跳转方式,可以从当前页跳转到非tabbar页面。跳转的过程将会保留当前页,新建目标页,而后跳转过去,称为保留跳转。
  2. navigateBack,返回上一级页面。这种操作将会销毁当前页,从而显示上一页。可以配合属性delta实现上n页的跳转。
  3. switchTab ,字面理解为切换标签页(底部选项卡页面)。这种跳转方式用于跳转到tabbar页面。一旦这么做,就会销毁所有非tabbar页面。
  4. redirect,这种跳转方式将会关闭当前页,跳转到非tabbar的目标页面。这种方式也将创建新页面。
  5. reLaunch,字面理解为: 重新启动应用。这种方式将会销毁所有页面。重新打开小程序中的某一个目标页面。
案例:testing/a/a tesing/b/b testing/c/c
scroll-view组件
scrollview组件用于实现可滚动的视图容器(支持水平、垂直滚动)。基本结构如下:
style="height:200px; " scroll-x="是否允许水平方向滚动" scroll-y="是否允许垂直方向滚动"> .... .... .... ...非常多....

案例: pages/testing/scroll/scroll
scroll-view组件
scrollview组件用于实现可滚动的视图容器(支持水平、垂直滚动)。基本结构如下:
style="height:200px; " scroll-x="是否允许水平方向滚动" scroll-y="是否允许垂直方向滚动"> .... .... .... ...非常多....

案例: pages/testing/scroll/scroll
input组件
input组件为输入框组件,其语法结构:

案例:pages/testing/input/input
基于小程序的input组件实现双向数据绑定,让文本框中值与data中的某一个变量实现动态绑定。
简易双向数据绑定 wxml:
输入的是:{{name}}

js:
Page({ data: { name: '' } })

而早期小程序的双向数据绑定需要借助于bindinput事件来进行处理。一旦用户在文本框中更新了内容,就会触发该事件,在事件处理函数中获取文本框的值,然后更新data
标准的双向数据绑定 wxml
{{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: {} } })

{{name}} {{age}}

属性绑定 当需要动态设置组件的属性值时,就需要使用属性绑定:
Page({ data:{ url: '/images/1.jpg', num: 1, d: 'images' } })


样式绑定 动态更新组件的wxss样式:
Page({ data:{ className: 'blue', c: 'red', bw: 1 } })

.red{ color:red; } .blue{ color:blue; } 内容文本 内容文本

列表渲染 基于小程序的提供的列表渲染的语法,可实现遍历数组中每个元素动态输出列表数据的需求。类似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
id: {{item.id}} name: {{item.name}} price: {{item.price}}

微信小程序的语法,应如下遍历输出foods
index: {{index}} id: {{item.id}} name: {{item.name}} price: {{item.price}}

设置后,发现控制台有一个警告,需要为wx:for提供一个wx:key
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。
wx:key 的值以两种形式提供
  1. 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
  2. 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
序号:{{index}} ID: {{item.id}} 菜名:{{item.name}} 价格:{{item.price}}

wx:for将会为这次遍历默认新增两个变量:itemindex。这两个变量可以自定修改,语法如下:
序号:{{i}} ID: {{f.id}} 菜名:{{f.name}} 价格:{{f.price}}

条件渲染 使用条件渲染可以动态处理是否渲染某一个元素,类似vue中的v-if
data:{ islogin: true }

欢迎:XXX 登录 注册

常见写法有以下几种:
xxx --------------------------------------------- xxx xxx --------------------------------------------- xxx yyy zzz aaa bbb

小程序常用组件 radio-group组件
radiogroup组件为单选框组组件,包含一组单选按钮。基本结构:

radio-group中的radio只有一个可以被选中。
案例:testing/form/form
checkbox-group组件
复选框组件,其语法:
... ... ...

小程序的事件处理
事件是视图层到逻辑层的通讯方式,它可以将用户的行为反馈到逻辑层进行后续处理。
bindscrolltolower=""> bindscrolltoupper=""> bindscroll="">

小程序中的事件类型 微信小程序中,事件类型大致分为两大类:
  1. 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
  2. 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。
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 设备,重按时会触发
小程序事件的绑定方式

bind的方式绑定事件可以应用于任何组件,而bind:的方式不能应用于原生组件。
原生组件指由操作系统直接控制的组件。如:获取焦点后的input组件、video组件、camera组件等。原生组件的特点是由AndroidIOS操作系统直接处理,并非微信自己实现的UI或功能。
bind方式与bind:方式绑定的事件不能阻止事件冒泡,而catch方式可以自动阻止事件冒泡。
小程序中事件参数的传递 无论组件绑定的是冒泡事件还是非冒泡事件,事件处理函数名称都严禁出现小括号:

问题:如何完成事件参数的传递?
需求:
商品信息..... 商品信息..... 商品信息.....

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 }

案例:购物车。
案例:吃饭睡觉打豆豆。
  1. 查询并显示待办事项列表。
  2. 添加新的待办事项。
  3. 删除代办事项。
小程序API 小程序界面交互类API
wx.showToast() 提示框
wx.showModal() 模态对话框
小程序路由跳转相关API
  • wx.switchTab
  • wx.reLaunch
  • wx.redirectTo
  • wx.navigateTo
  • wx.navigateBack
上述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', 回传的数据)

小程序的生命周期
  1. 页面的生命周期
  2. 小程序应用的生命周期
页面的生命周期 小程序页面的生命周期相关钩子方法需要在Page.js中进行定义,基本结构如下:
Page({ /** 页面的初始数据 */ data: { },/** 生命周期函数--监听页面加载 */ onLoad: function (options) { },/** 生命周期函数--监听页面初次渲染完成 */ onReady: function () },/** 生命周期函数--监听页面显示 */ onShow: function () { },/** 生命周期函数--监听页面隐藏 */ onHide: function () { },/** 生命周期函数--监听页面卸载 */ onUnload: function () { }, })

小程序的生命周期
  1. 页面的生命周期
  2. 小程序应用的生命周期
页面的生命周期 小程序页面的生命周期相关钩子方法需要在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
小程序对于发送请求时的一些限制:
  1. 请求资源路径只支持 https协议。
  2. 必须使用域名,不能使用IP。域名必须经过ICP备案。
  3. 域名必须在小程序后台管理网站中注册。(登录管理后台,选择开发管理、开发设置、服务器域名配置,新增: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)=>{} })

案例:点击按钮,发送请求
项目案例: 初始化项目
  1. 新建项目。
    1. 项目目录不准有空格、中文特殊字符。最后一个目录为空目录。
    2. 选择正确的appid
    3. 选择不适用云服务。
  2. 搭建项目的主体结构。
    1. 准备3个页面:indextheatreme
    2. 拷贝所有相关资源,搭建底部选项卡的基本结构。
  3. 项目细节调整。
    在小程序目录里找到 project.config.json,找到 setting 配置对象,将 checkSiteMap 设置为 false
初始化首页时加载电影列表
实现思路
  1. 重写Index.js中的onLoad方法。在页面初次加载时发请求,获取热映类别下的首页电影列表数据。
  2. 通过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条电影信息组成的数组。
[{电影},{电影},{电影},{电影},{电影},{电影}......]

控制顶部导航的选中项
实现思路
  1. 当选择某一个顶部导航项后,将该导航项改为激活状态,其它导航项改为默认样式。
  2. 选择某一个顶部导航后,需要获取当前选中项的类别cid(1/2/3),向服务端发送请求,获取响应类别下的首页电影列表数据。
  3. 获取数据后,重新更新电影列表即可。
触底加载下一页
需求:当列表滚动到底部后,加载当前类别的下一页数据。
实现思路
  1. 监听列表滚动触底事件。(Page中重写onReachBottom
  2. 整理请求参数,cid类别idoffset起始位置。发送新的列表请求,访问下一页电影列表数据。
  3. 当加载到新数据后,把新电影列表追加到旧电影列表的末尾。
封装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())将数据缓存清空。
有些应用,更新的频率更高些,就需要在应用使用时,每隔一段时间更新数据缓存。
基于下拉刷新更新缓存
  1. 在页面的.json配置文件中,开启当前页面的下拉刷新。
  2. 重写PageonPullDownRefresh方法,监听下拉刷新事件。
  3. 发送请求,加载当前了类别的首页电影数据,更新列表、更新缓存。
小程序位置服务
获取小程序的位置,可以使用:
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
Hello world!
  1. 申请开发者密钥(key):申请密钥
  2. 开通webserviceAPI服务:控制台 ->应用管理 -> 我的应用 ->添加key-> 勾选WebServiceAPI -> 保存
    (小程序SDK需要用到webserviceAPI的部分服务,所以使用该功能的KEY需要具备相应的权限)
  3. 下载微信小程序JavaScriptSDK,微信小程序JavaScriptSDK v1.1 JavaScriptSDK v1.2
  4. 安全域名设置,在小程序管理后台 -> 开发 -> 开发管理 -> 开发设置 -> “服务器域名” 中设置request合法域名,添加https://apis.map.qq.com
  5. 小程序示例
    // 引入SDK核心类,js文件根据自己业务,位置可自行放置 var QQMapWX = require('../../libs/qqmap-wx-jssdk.js'); var qqmapsdk = new QQMapWX({ key: '申请的key' }); qqmapsdk.reverseGeocoder({ success: (res)=>{....} })

显示电影详情页
需求:当点击某一个电影列表项后,跳转到电影详情页面,显示当前电影的详细信息。(需要通过电影ID,获取电影详情) 。
实现思路
  1. 准备好电影详情页面。pages/movie/movie
  2. 点击电影列表项后,跳转到电影详情页,并且传递选中项的电影id
  3. 在详情页中获取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) ['', ''......]

完成电影详情的页面渲染
渲染基本信息 渲染演职人员列表 渲染剧照列表
  1. 图片懒加载。
  2. 添加mode,防止图像比例失真。
  3. 点击图片后,全屏大图浏览器剧照列表。
    wx.previewImage({ current: newUrls[i], urls:newUrls })

小程序云开发 概述
开发者可以使用腾讯提供的云服务器来开发微信小程序、小游戏的服务端程序。而无需搭建服务器。
云开发提供的基础能力有:
  1. 云数据库
    云数据库是一个可以在小程序前端直接操作的云端数据库。它与mysql不同,是一个json类型的非关系型数据库。
  2. 云存储
    云存储是微信云服务器提供的一块存储空间,可以让小程序前端通过响应API代码直接针对云存储空间进行上传和下载。
  3. 云函数
    云函数是一个在小程序端进行编写,而后通过开发工具部署到云服务器中,并且提供给小程序远程调用的函数。
开通云开发服务
打开开发工具,点击工具栏中的云开发按钮。
云数据库 云数据库是一个可以在小程序前端直接操作的云端数据库。它与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中就是返回的结果 } })

删除数据 修改数据 实现学子影院项目中的评论列表
  1. 创建一个新的云开发项目。xzyycloud
  2. xzyy中已经写好的内容,直接拽到新项目中,覆盖相关资源。
  3. comments.json中的评论数据导入comments集合中。
  4. 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 字段值不在给定数组中
动态选择首页左上角城市名称
  1. 准备一个城市列表页面: pages/citylist/citylist.
  2. 完善城市列表页中的列表显示内容。
    当点击右侧导航时,控制scrollview滚动到相应位置。
    scroll-into-view="C"> ....

  3. 点击首页左上角城市时,跳转到citylist页面,选择城市。
  4. 城市选择完毕后,后退到首页,并且将选中的城市名称回传回来,更新首页城市名。
    选择城市后,将城市名称存入globalData,然后navigateBack
    在首页重写onShow生命周期方法,将会在onShow时区globalData中获取城市名称,更新左上角。
动态选择首页左上角城市名称
  1. 准备一个城市列表页面: pages/citylist/citylist.
  2. 完善城市列表页中的列表显示内容。
    当点击右侧导航时,控制scrollview滚动到相应位置。
    scroll-into-view="C"> ....

  3. 点击首页左上角城市时,跳转到citylist页面,选择城市。
  4. 城市选择完毕后,后退到首页,并且将选中的城市名称回传回来,更新首页城市名。
    选择城市后,将城市名称存入globalData,然后navigateBack
    在首页重写onShow生命周期方法,将会在onShow时区globalData中获取城市名称,更新左上角。
实现城市列表页面的重新定位功能
  1. 当点击进入城市列表页后,需要重新调用getLocation进行定位。(需要把index.js中封装好的getLocation方法复制过来一份直接用)
  2. 获取到当前定位城市名后,将cityname存入globalData,更新citylist页面顶部的定位城市名称。
  3. 当点击顶部当前定位城市按钮时,需要将城市名称存入globalData,返回上一页即可。
  4. 首页将会在onShow是更新首页左上角,完成业务流程。
实现未授权状态下的定位业务
  1. 拒绝授权。
  2. 点击城市名,进入城市列表页。定位失败后提示重试。
  3. 点击重试弹出引导授权窗口。
  4. 授权成功后重新定位,完成业务。
实现影院模块业务
影院页面左上角城市与首页左上角城市实现联动
  1. 重写影院页面的onShow生命周期方法,读取globalData.cityname,更新左上角城市名。
看文档:
https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodSearch
实现思路
  1. 将获取qqmapsdk的方法封装到app.js中,通过globalData暴露qqmapsdk的引用。
  2. 什么时候需要,就直接从globalData中读取qqmapsdk即可。
    1. index.js中需要。
    2. citylist.js中需要。
    3. theatre.js中需要。
  3. 通过qqmapsdk调用search方法,获取选中城市的影院列表。
  4. theatreList存入data,在页面中渲染显示该列表。
  5. 细节处理:
    1. 若影院没有电话,则样式有问题。
    2. 距离应该显示km,保留两位小数。
    3. 如果不是当前城市的电影院,不会返回距离。
event.targetevent.currentTarget之间的区别

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 或更高。
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
doubletapEvent(){ console.log('么么哒..') }

自定义简单组件
  1. components右键,新建Component, 起名字,新建组件。
  2. 编写组件.wxml 组件.wxss
  3. 在需要引用组件的页面中,通过自定义组件名使用该组件。

  4. 前提就是在该页面.json,需要声明引入该组件:
    { "usingComponents": { "my-button": "/components/mybutton/mybutton" } }

自定义组件 从小程序基础库版本 1.6.3 开始,小程序支持简洁的组件化编程。所有自定义组件相关特性都需要基础库版本 1.6.3 或更高。
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
doubletapEvent(){ console.log('么么哒..') }

自定义简单组件
  1. components右键,新建Component, 起名字,新建组件。
  2. 编写组件.wxml 组件.wxss
  3. 在需要引用组件的页面中,通过自定义组件名使用该组件。

  4. 前提就是在该页面.json,需要声明引入该组件:
    { "usingComponents": { "my-button": "/components/mybutton/mybutton" } }

小程序Vant组件库
  1. 安装vant
    # 进入项目根目录, xzyycloud文件夹下执行命令: npm init# 输入命令后一路回车,将会自动创建package.json npm i @vant/weapp -S --production

  2. 修改app.json.
    app.json 中的 "style": "v2" 去除,小程序的新版基础组件强行加上了许多样式,难以覆盖,不关闭将造成部分组件样式混乱。
  3. 修改project.config.json
    "packNpmManually": true, "packNpmRelationList": [ { "packageJsonPath": "./package.json", "miniprogramNpmDistDir": "./miniprogram/" } ],

    如上配置的目的,是希望小程序开发工具在构建npm时,可以找到package.json, 还需要定义编译后的输出目录(./miniprogram/)。
  4. 在小程序开发工具中构建npm
    一旦构建npm成功,将会在miniprogram目录下新增miniprogram_npm文件夹,里面就是打包好的组件源码。接下来就可以直接引入,使用。
使用Vant组件库中的Button: 引入
在页面的.json文件中引入该组件:
{ "usingComponents": { "my-button": "/components/mybutton/mybutton", "van-button": "@vant/weapp/button/index" } }

使用
默认按钮 主要按钮 信息按钮 警告按钮 危险按钮

显示失败的,清除编译缓存,重新编译多尝试。
实现微信登录业务
  1. 当点击登录时,申请用户授予获取用户基本信息的权限。
  2. 一旦用户允许,则可以得到用户的昵称、头像的等基本数据。
  3. 更新界面(昵称、头像)。
小程序提供了一个API方法,可以获取用户数据:wx.getUserProfile()
实现重新选择图片,更新头像
  1. 点击头像后,选择新图片。
  2. 获取选中图片的路径,替换头像路径。(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
这样将微信提供的用户信息存入到自己家数据库后,可以方便的修改昵称、修改头像、新增数据,做表关联,维护用户收藏、与用户喜欢等等信息。
所以一个正确的微信登录业务如下:
  1. 通过微信登录,获取用户的基本信息。
  2. 通过用户的信息,判断,该用户在自己家数据库中是否已注册:
    1. 如果没有注册,则在自己家数据库中新增一条数据,保存该用户信息,显示界面即可。
    2. 如果已经注册过,则找到自己家数据库中最新的用户数据(有可能头像、昵称已经被改过),将自家数据库中的信息更新到界面。
借助云数据库,来完成上述流程。
  1. 在云开发控制台中新建一个集合:users。 保存用户信息。
  2. 完成用户登录业务。
    1. 查询云数据库,检索当前用户以前是否已经注册过信息。
    2. 如果没有找到数据,则执行注册业务。将用户信息存入云数据库users集合。
    3. 如果找到了数据,则执行登录业务,将这些用户最新的数据,更新到界面中。
实现修改头像业务
当登录成功后,点击头像,选择本地图片,将头像更新为选中的图片临时路径既可以修改显示的头像。但是这种方式只是纯客户端版本的更新头像,无法持久化保存信息。意味着下次登录时,还会显示微信头像。这是有问题的。
真正的修改头像业务流程如下:
  1. 点击头像选择一张新图片,获取图片路径。
    wx.chooseImage()

  2. 将该头像上传至服务器,存到服务器某个目录下,并且返回可以访问它的网络路径。
    http://api.tedu.cn/avatar/23dfad6fas98df6sdf5sd8fads9f.jpg

  3. 获取访问路径后,将该路径更新到云数据库中users集合里当前用户的avatarUrl字段。
    let db = wx.cloud.database() db.collection('users').doc('_id').update({ data: { avatarUrl : xxxxxx } })

  4. 一旦数据库中头像地址变了,下次登录时将会加载最新的头像路径,从而访问到上传的头像图片,完成业务。
云存储
云存储类似云盘,可以在小程序客户端方便的上传、下载文件。可以在云开发控制台中测试相应功能。
小程序提供了API,方便的实现文件上传操作:
wx.cloud.uploadFile({ filePath: '本地文件路径', cloudPath: '云存储服务端的目标文件路径', success: (res)=>{} })

思考:如果没有使用云数据库,使用的是自己家的mysql数据库存储用户数据,那么如何完成头像路径的更新?
没有使用小程序云数据库意味着什么?
  1. 注册用户时,向自建的users表中添加一条新纪录。
    insert into users(id, nickName, avatarUrl, gender, ) values(?,?,?,?)

  2. 注册业务看似简单,但是有个棘手的问题,注册之前得先判断users表中是否包含当前用户的信息?
    答案:通过每个用户每个应用唯一的_openid来解决这个问题。
如果是自建数据库,需要维护用户id_openid)。用户一旦进入小程序,就会被分配一个openid。获取openid的方式比较麻烦:
  1. 客户端需要先调用wx.login方法,获取一个校验码。
  2. 将校验码发给后端nodejs接口,后端将会通过该校验码,访问腾讯服务器,换取openid返回给客户端。
云开发环境下提供了云函数,可以方便的获取用户的openid
云函数
云函数是一种在小程序端编写、定义,通过开发工具部署到云服务器中,在小程序端可以远程调用的函数。这种函数在云服务器中执行。所以云函数可以简单替代nodejs后端接口。
【web前端|微信小程序】体验云函数。

    推荐阅读