目录:
1 网络请求API和封装
2 展示弹窗和页面分享
3 设备信息和位置信息
4 小程序Storage存储
5 页面跳转和数据传递
6 小程序登录流程演练
小程序的网络请求,不管是post还是get的请求的数据都是写在data里面的。
网络请求一般写在onLoad()的页面生命周期里面。
网络请求原生的写法是使用回调函数获取的数据,会有回调地狱,所以我们需要使用promise的写法。
回调地狱就是下图,回调里面有很多回调
注意:微信小程序在进行网络请求的时候,在测试阶段我们可以在详情的本地设置开启不校验合法域名和web-view。。。的选项就可以直接访问网址内容了。
但是需要上线小程序的时候,我们需要把这个获取data的网址在微信小程序登录后台-》开发管理-》开发设置-》服务器域名 才能正常使用网络请求。原因是小程序需要验证该网址是否发送的是合法的数据。具体配置方法最下方ppt有
在使用网络请求之前,我们可以封装好request的代码,步骤是创建个server文件,在index.js里面写入我们需要的代码。
// 封装成函数 export function hyRequest(options) { return new Promise((resolve, reject) => { wx.request({ ...options, success: (res) => { resolve(res.data) }, fail: reject }) }) } // 封装成类 -> 实例 class HYRequest { constructor(baseURL) { this.baseURL = baseURL } request(options) { const { url } = options return new Promise((resolve, reject) => { wx.request({ ...options, url: this.baseURL + url, success: (res) => { resolve(res.data) }, fail: (err) => { console.log("err:", err); } }) }) } get(options) { return this.request({ ...options, method: "get" }) } post(options) { return this.request({ ...options, method: "post" }) } } export const hyReqInstance = new HYRequest("http://codercba.com:1888/api") export const hyLoginReqInstance = new HYRequest("http://123.207.32.32:3000")
如果使用的是await/acyna的方法来写网络请求,那么在await之后的js代码都不会运行。建议还是使用promise方法写网络请求。就是在生命周期onLoad里面写网络请求。
还可以使用异步函数的方法写请求,即在onLoad生命周期之外写await/acyna的网络请求。在onload生命周期里面直接调用该异步函数不会影响后续代码运行。
网络请求封装成类,扩展性更强。
使用网络请求的页面js逻辑:
// pages/09_learn_api/index.js import { hyRequest, hyReqInstance } from "../../service/index" Page({ data: { allCities: {}, houselist: [], currentPage: 1 }, async onLoad() { // 1.网络请求基本使用 // wx.request({ // url: "http://codercba.com:1888/api/city/all", // success: (res) => { // const data = res.data.data // this.setData({ allCities: data }) // }, // fail: (err) => { // console.log("err:", err); // } // }) // wx.request({ // url: 'http://codercba.com:1888/api/home/houselist', // data: { // page: 1 // }, // success: (res) => { // const data = res.data.data // this.setData({ houselist: data }) // } // }) // 2.使用封装的函数 // hyRequest({ // url: "http://codercba.com:1888/api/city/all" // }).then(res => { // this.setData({ allCities: res.data }) // }) // hyRequest({ // url: "http://codercba.com:1888/api/home/houselist", // data: { // page: 1 // } // }).then(res => { // this.setData({ houselist: res.data }) // }) // 3.await/async // const cityRes = await hyRequest({ // url: "http://codercba.com:1888/api/city/all" // }) // this.setData({ allCities: cityRes.data }) // const houseRes = await hyRequest({ // url: "http://codercba.com:1888/api/home/houselist", // data: { page: 1 } // }) // this.setData({ houselist: houseRes.data }) // 4.将请求封装到一个单独函数中(推荐) this.getCityData() this.getHouselistData() // 5.使用类的实例发送请求 hyReqInstance.get({ url: "/city/all" }).then(res => { console.log(res); }) }, async getCityData() { const cityRes = await hyRequest({ url: "http://codercba.com:1888/api/city/all" }) this.setData({ allCities: cityRes.data }) }, // 下面的函数是下拉加载更多。 async getHouselistData() { const houseRes = await hyRequest({ url: "http://codercba.com:1888/api/home/houselist", data: { page: this.data.currentPage } }) const finalHouseList = [...this.data.houselist, ...houseRes.data] this.setData({ houselist: finalHouseList }) // 思考: 为什么这里不需要setData? // 因为这里的数据不需要浏览器来响应式更新 this.data.currentPage++ }, //触底时触发函数 onReachBottom() { this.getHouselistData() } })
wxml:
<!--pages/09_learn_api/index.wxml--> <view class="house-list"> <block wx:for="{{houselist}}" wx:key="abc"> <view class="item"> <image src="{{item.data.image.url}}"></image> <view class="title">{{item.data.houseName}}</view> </view> </block> </view>
4种常见弹窗,文档最下方PPT有具体的参数配置。
wx.showToast(Object object) | 微信开放文档 (qq.com)https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showToast.htmlwx.showModal(Object object) | 微信开放文档 (qq.com)https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showModal.htmlwx.showActionSheet(Object object) | 微信开放文档 (qq.com)https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showActionSheet.html
关于分享功能的配置:
获取用户设备信息:
设备信息里面有用户屏幕大小信息,
获取用户的位置:
1、先调用api
wx.getLocation(Object object) | 微信开放文档 (qq.com)https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.getLocation.html
这里需要用户授权才能获取。
2、获取用户授权的提示需要在app.json里面配置permission
全局配置 | 微信开放文档 (qq.com)https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#permission
数据缓存:
分为同步和异步两种,同步的就是必须执行完一行才能在执行下下一行的代码;异步是不会影响后续代码执行,可以先执行后续的内容。
存储数据的时候可以设置加密选项,在取数据的时候也相应的需要使用加密选项才能解密获取具体数据。加密是有基础库最低版本限制,
wx.setStorageSync(string key, any data) | 微信开放文档 (qq.com)https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.setStorageSync.html上述api的使用示例:
js文件:
// pages/10_learn_api/index.js Page({ // 1.弹窗相关的API onShowToast() { wx.showToast({ title: '购买失败!', icon: "error", duration: 5000, mask: true, success: (res) => { console.log("res:", res); }, fail: (err) => { console.log("err:", err); } }) // wx.showLoading({ // title: "加载中ing" // }) }, onShowModal() { wx.showModal({ title: "确定购买吗?", content: "确定购买的话, 请确定您的微信有钱!", confirmColor: "#f00", cancelColor: "#0f0", success: (res) => { if (res.cancel) { console.log("用户点击取消"); } else if (res.confirm) { console.log("用户点击了确定"); } } }) }, onShowAction() { wx.showActionSheet({ itemList: ["衣服", "裤子", "鞋子"], success: (res) => { console.log(res.tapIndex); }, fail: (err) => { console.log("err:", err); } }) }, // 2.分享功能 onShareAppMessage() { return { title: "旅途的内容", path: "/pages/favor/favor", imageUrl: "/assets/nhlt.jpg" } }, // 3.获取用户的设备信息 onGetSystemInfo() { // 1.获取手机的基本信息 // wx.getSystemInfo({ // success: (res) => { // console.log(res); // } // }) // 2.获取当前的位置信息 wx.getLocation({ success: (res) => { console.log("res:", res); } }) }, // 4.本地存储方式 onLocalStorage() { // 1.存储一些键值对 // wx.setStorageSync('name', "why") // wx.setStorageSync('age', 18) // wx.setStorageSync('friends', ["abc", "cba", "nba"]) // 2.获取storage中内容 // const name = wx.getStorageSync('name') // const age = wx.getStorageSync('age') // const friends = wx.getStorageSync('friends') // console.log(name, age, friends); // 3.删除storage中内容 // wx.removeStorageSync('name') // 4.清空storage中内容 // wx.clearStorageSync() // 异步操作 wx.setStorage({ key: "books", data: "哈哈哈", // 加密操作 encrypt: true, success: (res) => { wx.getStorage({ key: "books", encrypt: true, success: (res) => { console.log(res); } }) } }) console.log("-------"); } })
wxml:
<!--pages/10_learn_api/index.wxml--> <text>pages/10_learn_api/index.wxml</text> <!-- 1.展示弹窗 --> <view> <button size="mini" bindtap="onShowToast">showToast</button> <button size="mini" bindtap="onShowModal">showModal</button> <button size="mini" bindtap="onShowAction">showAction</button> </view> <!-- 3.设备信息 --> <button bindtap="onGetSystemInfo">设备信息</button> <!-- 4.本地存储 --> <button bindtap="onLocalStorage">本地存储</button>
页面跳转:
页面跳转需要传递参数和跳转到的页面需要接收数据;返回之前页面的方法,返回之前页面并且传递参数过去。
难点:返回之前页面并且传递参数过去。
第一种方法是通过页面栈的方法获取上一个页面的实例,然后取修改data里面的参数即可。这个代码是可以写在页面被卸载的时候执行的。因为如果你只通过按钮 返回 才能返回上一个页面和执行传递参数,页面导航的箭头 < 也是返回上一个页面,但是不会执行传递参数的代码。这时候把返回上一个页面并传递参数的方法写在生命周期卸载页面时执行最好。
第二种方法是通过给跳转之前的页面写一个事件频道event,跳转到的页面需要传递参数回跳转之前的页面需要通过获取事件event来传递。具体看下图:
跳转之前的页面js:
// pages/11_learn_nav/index.js Page({ data: { name: "kobe", age: 30, message: "哈哈哈" }, onNavTap() { const name = this.data.name const age = this.data.age // 页面导航操作 wx.navigateTo({ // 跳转的过程, 传递一些参数过去 url: `/pages2/detail/detail?name=${name}&age=${age}`, events: { backEvent(data) { console.log("back:", data); }, coderwhy(data) { console.log("why:", data); } } }) } })
跳转之前的页面wxml:
<!--pages/11_learn_nav/index.wxml--> <button bindtap="onNavTap">跳转</button> <navigator class="nav" url="/pages2/detail/detail?name=why&age=18">跳转</navigator> <view>{{message}}</view>
跳转到的页面js:
// pages2/detail/detail.js Page({ data: { name: "", age: 0 }, // 从别的页面跳转过来的数据需要从生命周期onLoad里面获取 onLoad(options) { console.log(options); const name = options.name const age = options.age this.setData({ name, age }) // const eventChannel = this.getOpenerEventChannel() }, onBackTap() { // 1.返回导航 wx.navigateBack() // 2.方式一: 给上一级的页面传递数据 // 2.1. 获取到上一个页面的实例 // const pages = getCurrentPages() // const prePage = pages[pages.length-2] // // 2.2.通过setData给上一个页面设置数据 // prePage.setData({ message: "呵呵呵" }) // 3.方式二: 回调events的函数 // 3.1. 拿到eventChannel const eventChannel = this.getOpenerEventChannel() // 3.2. 通过eventChannel回调函数 eventChannel.emit("backEvent", { name: "back", age: 111 }) eventChannel.emit("coderwhy", { name: "why", age: 10 }) }, onUnload() { // // 2.1. 获取到上一个页面的实例 // const pages = getCurrentPages() // const prePage = pages[pages.length-2] // // 2.2.通过setData给上一个页面设置数据 // prePage.setData({ message: "呵呵呵" }) } })
跳转到的页面wxml:
<!--pages2/detail/detail.wxml--> <text>pages2/detail/detail.wxml</text> <view>name: {{name}}, age: {{age}}</view> <button size="mini" type="primary" bindtap="onBackTap">返回</button> <navigator open-type="navigateBack">返回</navigator>
小程序的登录解析:
静默登录:
由于小程序是在微信上的,所以没有账号密码的登录过程。
这里是通过openID来知道用户即使是使用不同的手机进行登录小程序也是同一个人。但是现在小程序不提供这个了。
unionID是用来判断不同微信产品之间用户是同一个人。
前端的任务就是通过api login获取code,等后端发token然后保存起来,在之后操作中发送token就好了。
登录页面的js:
import { getCode } from "../../service/login"; import { hyLoginReqInstance } from "../../service/index" // pages/12_learn_login/index.js Page({ // onLoad登录的流程 async onLoad() { // 1.获取token, 判断token是否有值 const token = wx.getStorageSync('token') || "" // 2.判断token是否过期 const res = await hyLoginReqInstance.post({ url: "/auth", header: { token: token } }) // console.log(res); // 2.如果token有值 if (token && res.message === "已登录") { console.log("请求其他的数据"); } else { this.handleLogin() } }, async handleLogin() { // 1.获取code const code = await getCode() // 2.使用code换取token const res = await hyLoginReqInstance.post({ url: "/login", data: { code } }) // 3.保存token wx.setStorageSync('token', res.token) } // handleLogin() { // // 1.获取code // wx.login({ // success: (res) => { // const code = res.code // // 2.将这个code发送自己的服务器(后端) // wx.request({ // url: "http://123.207.32.32:3000/login", // data: { // code // }, // method: "post", // success: (res) => { // const token = res.data.token // wx.setStorageSync('token', token) // } // }) // } // }) // } })
login.js文件内容:
export function getCode() { return new Promise((resolve, reject) => { wx.login({ success: (res) => { resolve(res.code) } }) }) }
server文件夹下的index.js内容:
// 封装成函数 export function hyRequest(options) { return new Promise((resolve, reject) => { wx.request({ ...options, success: (res) => { resolve(res.data) }, fail: reject }) }) } // 封装成类 -> 实例 class HYRequest { constructor(baseURL) { this.baseURL = baseURL } request(options) { const { url } = options return new Promise((resolve, reject) => { wx.request({ ...options, url: this.baseURL + url, success: (res) => { resolve(res.data) }, fail: (err) => { console.log("err:", err); } }) }) } get(options) { return this.request({ ...options, method: "get" }) } post(options) { return this.request({ ...options, method: "post" }) } } export const hyReqInstance = new HYRequest("http://codercba.com:1888/api") export const hyLoginReqInstance = new HYRequest("http://123.207.32.32:3000")