原生微信小程序进阶笔记 项目知识点

534 阅读12分钟

小程序进阶

一、组件使用(vant)

vant组件库中组件使用。

官方文档:vant-contrib.gitee.io/vant-weapp/…

步骤

1.安装Vant组件库

 # 通过 npm 安装
 npm i @vant/weapp -S --production
 ​
 # 通过 yarn 安装
 yarn add @vant/weapp --production

2.去掉app.json中的"style": "v2"

 //app.json
 {
   ...
   //"style": "v2", //删掉
   ...
 }

3.修改project.config.json

 {
   ...
   "setting": {
     ...
     "packNpmManually": true,
     "packNpmRelationList": [
       {
         "packageJsonPath": "./package.json",
         "miniprogramNpmDistDir": "./miniprogram/"
       }
     ]
   }
 }

4.构建npm。开发者工具上 > "工具" > "构建npm"

5.去app.json或页面json中注册

 // 通过 npm 安装
 // app.json
 "usingComponents": {
     "van-cell": "@vant/weapp/cell/index",
     "van-cell-group": "@vant/weapp/cell-group/index"
 },

使用

 <van-cell-group inset>
   <van-cell size="large" title="北京富力家园" value="审核通过" />
   <van-cell title="房间号" value="1号楼1单元101室" border="{{ false }}" />
   <van-cell title="业主" value="续集号" border="{{ false }}" />
 </van-cell-group>

二、组件介绍(部分)

1.Cell单元格

介绍

单元格为列表中的单个展示项

使用

1.引入

 "usingComponents": {
   "van-cell": "@vant/weapp/cell/index",
   "van-cell-group": "@vant/weapp/cell-group/index"
 }

2.使用

 <van-cell-group>
   <van-cell title="单元格" value="内容" />
   <van-cell title="单元格" value="内容" label="描述信息" border="{{ false }}" />
 </van-cell-group>

2.SwipeCell滑动单元格

介绍

可以左右滑动来展示操作按钮的单元格组件。

1.引入

 "usingComponents": {
     "van-cell": "@vant/weapp/cell/index",
     "van-cell-group": "@vant/weapp/cell-group/index",
     "van-swipe-cell": "@vant/weapp/swipe-cell/index"
   },

2.使用

 <van-swipe-cell right-width="{{ 65 }}">
   <van-cell-group inset>
     <van-cell title="北京富力家园" value="审核通过" />
     <van-cell title="房间号" value="1号楼1单元101室" border="{{ false }}" />
     <van-cell title="业主" value="续集号" border="{{ false }}" />
   </van-cell-group>
   <view slot="right">删除</view>
 </van-swipe-cell>

三、样式覆盖

参考文档:vant-contrib.gitee.io/vant-weapp/…

需求

Vant 组件中的组件提供了非常整齐美观的样式,但是开发中在所难免需要对原有样式进行个修改,官方介绍了3种方式来覆盖原来的样式

1.强制覆盖

通过调试工具查找要修改样式的盒子,找到已定义的类名,然后强制覆盖原有的样式

 .van-swipe-cell__right {
   display: flex;
   justify-content: center;
   align-items: center;
   width: 65px;
   background: red;
   color: #fff;
 }

这种方式能生效,还需要组件的options里面设置了styleIsolation为apply-shared或shared

 Component({
   options: {
     styleIsolation: 'shared',
   },
 });

或者设置了addGlobalClass: true。(Vant的所有组件都设置了addGlobalClass: true)

2.使用外部样式类

Vant 大部分组件都支持 custom-class 来指定一个类名,通过这个类名来修改组件根节点的样式。

 <van-swipe-cell custom-class="my-swipe-cell" right-width="{{ 65 }}">
   <van-cell-group custom-class="my-cell-group" inset>
     <van-cell title="北京富力家园" value="审核通过" />
     <van-cell title="房间号" value="1号楼1单元101室" border="{{ false }}" />
     <van-cell title="业主" value="续集号" border="{{ false }}" />
   </van-cell-group>
   <view slot="right">删除</view>
 </van-swipe-cell>
 .my-swipe-cell {
   margin-bottom: 20rpx;
   margin-right: 30rpx;
 }
 .my-cell-group {
   margin-right: 0 !important; // 通过!important增加优先级
 }

3.使用 CSS 变量

css变量语法

 .box {
   --my-custom-color: pink; // 变量声明
   backgound-color: var(--my-custom-color); //变量使用
 }

变量声明

变量名前面要加两根连词线–,变量名大小写敏感

 .box{
   --main-color: #4d4e53;
   --main-bg: rgb(255, 255, 255);
   --logo-border-color: rebeccapurple;
   --header-height: 68px;
   --content-padding: 10px 20px;
   --base-color: var(--main-color);
 }

var函数

var()函数用于读取变量,可以使用第二个参数,表示变量的默认值。如果该变量不存在,就会使用这个默认值。

 .box {
   color: var(--main-color);
   height: var(--header-height);
 }
 ​
 .box {
   color: var(--main-color1, #000);
   height: var(--header-height, 80px);
 }

变量作用域

上述代码中定义的变量只能用在 .box 盒子及后代元素上,如果希望整个页面都能使用这个变量,可以这样定义:

 page { // 小程序上, 小程序上page是页面根元素,所以放到page下,所有其它元素都可以使用
   --my-custom-color: pink;
 }
 ​

四、开发准备

1.权限分配

团队开发小程序时一般不再使用个人的 AppID,而使用企业 AppID 时做为开发人员需要先申请权限,权限可以分成两种:

开发者权限:提供给开发者使用(程序员)

  • 提供开发者的微信号
  • 获取企业(团队)AppID

体验者权限:提供给测试、产品、客户等

  • 提供体验者的微信号
  • 扫码申请,由管理员审核通过

2.环境调整

程序原始的一套开发环境比较基础,小程序项目的规模变大后将不易管理,需要有针对性的对开发环境进行调整 ,主要有三个方面:

1.优化目录结构

将涉及业务的代码独立到单独的目录当中,非业务的文件和目录直接放在根目录中,调整后小程序运行会报错,需要修改 project.config.json 中的配置项:

 {
   "setting": {
     "packNpmManually": true,
     "packNpmRelationList": [
       {
         "miniprogramNpmDistDir": "业务代码所在目录",
         "packageJsonPath": "package.json的路径"
       }
     ]
   },
   "miniprogramRoot": "业务代码所在目录"
 }
 ​

2.启用 less/sass

通过 less/sass 可以更好的管理 css 样式,通过 project.config.json 可以启用对 less/sass 的支持。

 {
   "setting": {
     "useCompilerPlugins": ["sass"]
   }
 }
 ​

然后将 .wxss 文件后缀改换成 .scss 即可

参考文档:developers.weixin.qq.com/miniprogram…

3.配置好开发工具

选择自己擅长的 原生小程序开发者工具,vscode, hbuilderx等,配置好插件等等

五、组件封装

在小程序项目的开发过程中将一些通用的功能逻辑封装成方法能够提高开发的效率。

1.消息提示

将所有通用的工具方法封装到 utils/index.js中

 // utils/index.js
 const utils = {
   /**
    * 用户消息反馈
    * @param {string} title 文字提示的内容
    */
   toast(title = '数据加载失败...') {
     wx.showToast({
       title,
       mask: true,
       icon: 'none',
     })
   }
 }
 // 挂载到全局对象 wx
 wx.utils = utils
 // 模块导出
 export default utils
 ​

除了将封装的模块正常导出外,还可以挂载到全局对象 wx 上,这样在使用的时候会更方便一些。

 // 在入口中执行 utils.js
 import './utils/index.js'
 App({
   // ...
 })
 ​

2.网络请求

小程序 API wx.request 不支持返回 Promise、拦截器等功能,需要开发者进行二次封装,也可以使用第三方的封装好的模块。

wechat-http

官网代码示例:www.npmjs.com/package/wec…

 npm install wechat-http        
 安装完成后还必须要构建 npm后才可以使用。
 ​

其用法与 axios类似:

  • http.baseURL 配置接口基础路径
  • http.getGET 方法发起请求, 还有post put等方法
  • http.intercept 配置请求和响应拦截器
  • http 本身做为函数调用也能用于发起网络请求

新建 utils/http.js 文件

 // 导入 http 模块
 import http from 'wechat-http'
 // 基础路径
 http.baseURL = 'https://live-api.itheima.net'
 // 挂载到全局对象
 wx.http = http
 // 普通的模块导出
 export default http
 ​

以全局对象方式调用时需要在入口中执行 utils/http.js

 // 执行 uitls/http.js
 import './utils/http.js'
 App({
   // ...
 })
 ​

还有其他的例如 wx-https 这个npm包也可以

示例

 Page({
   onLoad() {
     // 获取公告列表数据
     this.getNotices()
   },
   // 调用公告列表接口
   async getNotices() {
   // 请求接口
   const res =  await wx.http.get('/announcement')
   console.log(res)
   }
 })
 ​

六、项目技术点

1.解析富文本

rich-text

在小程序中无法直接解析富文本中包含的 HTML 标签,必须通过内置的小程序组件 rich-text 才能解析富文本,其用法是将富文件的内容赋值给 nodes 属性

 <!-- rich-text 使用示例 -->
 <rich-text nodes="{{'<h1>这里的 h1 标签可以被小程序解析</h1>'}}"></rich-text>
 ​

2.登录检测

检测方法

前端通过本地或内存存储的 token 来判断用户的登录状态。

wx.getStorage

通过本地存储api

优点:简单。 缺点:读取本地存储速度较慢。

getApp()

将token存储到全局实例app中,然后判断app实例中是否有token。

优点:速度快。 缺点:实现复杂些。

步骤

1.先获取token

 async login() {
     // 发送登录请求前校验手机号和验证码格式
     if (!this.validateMobile() || !this.validateCode()) return
         
     // 发送登录请求
     const res = await wx.http.post('/login', { mobile: this.data.mobile, code: this.data.code })
     
     // 验证登录请求业务是否成功
     if (res.code !== 10000) {
       wx.utils.toast(res.message || '登录请求失败')
       return
     }
     // 调用存储token方法
   getApp().setToken(res.data.token)
     // 点击登录按钮后跳转回之前页面
     wx.reLaunch({
       url: this.route,
     })
   },
 ​

2.存储token

 App({
   // 全局appjs中数据存储在内存中,访问较快
   // 全局appjs结合本地存储一起使用存储token
   token:'',
   onLaunch(){
     this.getToken()
   },
   // 获取本地token,并存储到app全局上
   getToken(){
     wx.getStorage({
       key:'token',
       success:(res)=>{
         this.token = res.data
       }
     })
   },
    // 本地存储token
   setToken(token){
     this.token = 'Bearer ' + token
     wx.setStorage({
       key: 'token',
       data: 'Bearer ' + token
     })
   }
 })
 ​

3.统一登录检测

问题

小程序中不支持路由拦截,需要开发者自行封装路由拦截的功能。

解决方法

先封装一个组件。用组件将每个需要登录的页面包起来,登录检测的逻辑统一放到组件内部实现。

将需要登录的页面都放置一个登录检测组件,登录检测组件里只放置一个插槽,该插槽只在用户登录时才显示,然后用该组件将页面所有内容全包裹起来,相当于将页面所有内容都放置于插槽中,这样用户未登录时将看不到页面的任何内容,用户登录与否可放到组件的attached生命周期钩子里做判断。

步骤

组件封装

用组件将每个需要登录的页面包起来

1.创建

新建components文件夹,在文件夹中新建authorization文件夹,然后右键选择新建组件。

 <!--components/authorization/index.wxml-->
 <slot wx:if="{{isLogin}}"></slot>
 ​

2.注册

在app.json中全局注册。

 // app.json
 "usingComponents": {
     "authorization": "/components/authorization/index"
   },
 ​

3.使用

找一个需要登录的页面,用该组件将页面所有元素包裹起来

 <authorization>
   <!-- 原页面元素 -->
     ...
 </authorization>

未登录跳转

登录检测的逻辑统一放到组件内部实现,未登录跳转到登录页

 Component({
   properties: {},
   data: {
     isLogin: false,
   },
 ​
   lifetimes: {
     // 在组件实例进入页面节点树时执行
     // 这里不能用created钩子,因为created里面不能调用setData
     attached() {
       // 判断是否有token
       const isLogin = !!getApp().token
       this.setData({
         isLogin,
       })
       // 未登录就跳转到登录页 
       if (!isLogin) {
         wx.redirectTo({ url: '/pages/login/index' })
       }
     },
   },
   methods: {},
 })

!! 是一个逻辑非操作符,用于将值转换为布尔类型。两个叹号将值转换为其对应的布尔值。如果值本身为真,那么 !! 将其转换为 true;如果值为假,那么 !! 将其转换为 false

返回原来页面

为了用户更好的体验,当用户跳转到A页面,发现用户未登录而重定向到登录页,用户登录后,应该要跳回A页面

实现方法

在重定向到登录页之前,记录下当前页面的路径,将该路径作为参数传入登录页

示例

 Component({
   data: {
     isLogin: false,
   },
   lifetimes: {
     attached() {
       const isLogin = !!getApp().token
       this.setData({
         isLogin,
       })
       if (!isLogin) {
         // 获取当前页面栈
         const pages = getCurrentPages()
         // 获取当前页面栈中最后一个页面的路径
         const lastPage = pages[pages.length - 1]
         const route = lastPage.route
         // 跳转到登录页面,并携带原始页面路径作为参数,用于登录后返回原始页面
         wx.redirectTo({ url: `/pages/login/index?redirectUrl=/${route}` })
       }
     },
   },
 })

4.获取验证码(vant组件)

参考文档:vant-contrib.gitee.io/vant-weapp/…

短信验证倒计时的交互可以使用 Vant 的组件 van–count-down

  • time指定倒计时时长
  • change 监听时间的变化
  • use-slot 启用插槽(自定义倒计时内容)
 <van-count-down use-slot time="{{6000}}" bind:change="countDownChange">
   <text>{{timeData.seconds}}秒后重新获取</text>
 </van-count-down>
 Page({
   // 监听时间的变化
   countDownChange(ev) {
     this.setData({
       // 倒计时当前的时间值
       timeData: ev.detail,
     })
   },
 })

结合项目来使用 van-count-down 组件,当用户点击了获取验证码按钮后再启用倒计时组件,通过数据 countDownVisible 进行控制:

 Page({
   data: {
     // 初始状态不启用倒计时组件
     countDownVisible: false
   },
   getCode() {
     // 点击后启用倒计时组件
     this.setData({countDownVisible: true})
   }
 })
 <text bind:tap="getCode" wx:if="{{!countDownVisible}}">获取验证码</text>
 <van-count-down wx:else use-slot time="{{6000}}" bind:change="countDownChange">
   <text>{{timeData.seconds}}秒后重新获取</text>
 </van-count-down>

另外还要处理倒计时结束后的状态,此时需要将倒计时组件关闭:

 ...
 countDownChange(ev) {
     this.setData({
       timeData: ev.detail,
       // 倒时结束时关闭组件
       countDownVisible: ev.detail.minutes === 1 || ev.detail.seconds > 0,
     })
   },

5.表单校验

没有原生的类似element-ui的validate校验方法,需要自己写函数,和提示

 <van-field
   model:value="{{mobile}}"
   placeholder="请输入手机号码"
   type="number"
   use-button-slot
   placeholder-style="color: #979797"
 >
 Page({
   data: {
     countDownVisible: false,
     mobile: '',
   },
     
   verifyMobile() {
     // 宽松验证规则
     const reg = /1[3-9][0-9]{9}/
     // 正则验证
     const valid = reg.test(this.data.mobile.trim())
     // 验证未通过
     if (!valid) wx.utils.toast('请填写正确的手机号')
     // 返回验证结果
     return valid
   },
   async getCode() {
     // 校验未通过,直接结束函数流程
     if (!this.verifyMobile()) return
     // 发送请求,获取验证码
     const { code, data } = await wx.http.get('/code', { mobile: this.data.mobile.trim() })
     // 请求失败,结束函数流程并给出提示
     if (code !== 10000) return wx.utils.toast()
     // 请求成功提示
     wx.utils.toast('发送成功,请查收短信')
     // 开始倒计时
     this.setData({
       countDownVisible: true,
     })
   },
   // 倒计时回调
   countDownChange(ev) {
     this.setData({
       timeData: ev.detail,
       countDownVisible: ev.detail.minutes === 1 || ev.detail.seconds > 0,
     })
   },
 })

真实场景中,点击获取验证码后,是不会通过接口给你返回验证码的,太不安全了,而是直接给你手机发送短信

6.剪切板

setClipboardData

设置系统剪贴板的内容。调用成功后,会弹出 toast 提示"内容已复制",持续 1.5s

参考文档:developers.weixin.qq.com/miniprogram…

getClipboardData

获取系统剪贴板的内容

参考文档:developers.weixin.qq.com/miniprogram…

 // 将指定数据设置到剪贴板中
 wx.setClipboardData({
   // 要设置的剪贴板数据
   data: 'data',
   // 设置成功后的回调函数
   success(res) {
     // 获取当前剪贴板中的数据
     wx.getClipboardData({
       // 获取成功后的回调函数
       success(res) {
         console.log(res.data); 
       }
     });
   }
 });

示例

 <view bind:tap="copyCode">复制验证码到剪切板</view>
 // 定义变量保存验证码
 let secret_code = ''
 Page({
  ...
   async getCode() {
     ...
     // 发送请求,获取验证码
     const { code, data } = await wx.http.get('/code', { mobile: this.data.mobile.trim() })
     ...
     secret_code = data.code
   },
   // 复制验证码到剪切板
   copyCode() {
     wx.setClipboardData({
       data: secret_code,
     })
     this.viewCode()
   },
   // 获取剪切板内容
   viewCode(){
     wx.getClipboardData({
       success (res){
         console.log(res.data)
       }
     })
   },
   ...
 })

7.发送登录请求

示例步骤

1.收集并校验内容

 // 定义变量保存验证码
 let secret_code = ''
 Page({
   data: {
     countDownVisible: false,
     mobile: '',
     code: '',
   },
   async submitForm() {
     // 校验手机号和验证码
     if (!this.verifyMobile() || !this.verifyCode()) return
     // 其他操作
       ...
   },
   // 校验验证码规则
   verifyCode() {
     // 正则验证是否为六个数字
     const reg = /^\d{6}$/
     const valid = reg.test(this.data.code.trim())
     // 验证未通过
     if (!valid) wx.utils.toast('请检查验证码是否正确')
     // 返回验证结果
     return valid
   },
   // 校验手机号规则
   verifyMobile() {
     // 宽松验证规则
     const reg = /1[3-9][0-9]{9}/
     // 正则验证
     const valid = reg.test(this.data.mobile.trim())
     // 验证未通过
     if (!valid) wx.utils.toast('请填写正确的手机号')
     // 返回验证结果
     return valid
   },
   // 获取验证码
   async getCode() {
     ...
   },
   // 复制验证码到剪切板
   copyCode() {
     ...
     })
   },
 })

2.发送请求后登录

 ...
 async submitForm() {
     // 验证手机号验证码,未通过则介绍登录流程
     ...
     // 获取登录参数
     const { mobile, code } = this.data
     // 发送登录请求
     const res = await wx.http.post('/login', { mobile, code })
     // 校验数据是否合法
     if (res.code !== 10000) return wx.utils.toast('登录失败,请检查手机号验证码')
     // 存储token
     ...
   },
 ...

3.存储token

 ...
   async submitForm() {
     // 验证手机号验证码,未通过则介绍登录流程
     ...
     // 发送登录请求
     ...
     // 存储token
     const token = 'Bearer ' + res.data.token
     wx.setStorageSync('token', token)
     getApp().token = token
   },
 ...

8.重定向回访问页面

登录成功后,体验比较好的做法是重定向回用户想要访问的页面。兜底做法是重定向到首页。

参考代码

先要去修改权限组件 authrazation组件,地址重定向,登录成功后跳回到原来的页面。在 authoirzation 组件检测登录时获取当前页面栈实例,并在跳转到登录页面时在 URL 地址上拼凑参数

 // /components/authorization/index.js
 Component({
   // ...
   lifetimes: {
     attached() {
       const token = getApp().token
       this.setData({
         // 判断是否有token
         isLogin: !!token
       })
         // 如果未登录
       if(!this.data.isLogin){
         // 通过将页面栈中保留的路径进行传递 
         const pages = getCurrentPages()
         // 传递跳转来的路径
         const currentRoute = pages[pages.length -1].route
         wx.redirectTo({
           url: `/pages/login/index?route=/${currentRoute}`,
         })
       }
     }
   }
 })

然后登陆成功以后,在login组件里面接受参数,然后跳转

 // 定义变量保存验证码
 let secret_code = ''
 Page({
   route:'',
   data: {
     countDownVisible: false,
     mobile: '',
     code: ''
   },
   onLoad(options) {
     // 获取传递来的路由路径
     if(!options.route){
         // 如果没有路径就定向到首页
       this.route = '/pages/index/index'
       return
     }
     // 存储路由路径
     this.route = options.route
   },
   // 登录按钮
   async submitForm() {
     // 登录成功
     ...
     // 重定向回跳转
     wx.reLaunch({
       url: this.route,
     })
   },
 })

9.请求拦截器配置token

配置请求拦截器将用户的登录状态(token)通过自定义的头信息 Authorization 随接口调用时一起发送到服务端。

 import http from 'wechat-http'
 // 添加基地址
 http.baseURL = "基地址"
 //请求拦截器
 http.intercept.request = function (options) {
   const defaultOptions = {}
   // 如果应用的全局数据中存在 token,将其添加到默认选项的 Authorization 属性中
   if (getApp().token) {
     defaultOptions.Authorization = getApp().token
   }
   // 如果有有options.header就将默认值合并覆盖
   options.header = Object.assign({}, defaultOptions, options.header)
   return options
 }
 //响应拦截器
 ...
 // 挂载到全局对象
 wx.http = http

10.上传头像

获取用户选择的头像地址,通过 wx.uploadFile 将图片上传到服务端

注:该 API 不支持返回 Promise,调用该 API 时,需要提前在小程序管理后台添加服务器域名或者开发阶段在工具里面设置不校验合法域名

 // 用于更新头像的函数
 chooseAvatarUpdate(e) {
   // 使用微信的uploadFile API上传选择的头像图片
   wx.uploadFile({
     // 选择图片文件的路径
     filePath: e.detail.avatarUrl,
     // 请求中文件字段的名称
     name: 'file',
     // 文件上传的URL
     url: '',
     // 包含Authorization令牌的请求头
     header: {
       Authorization: getApp().token
     },
     // 请求的附加表单数据,指定类型为'avatar'
     formData: { type: 'avatar'},
     // 成功请求的回调函数
     success: (res) => {
       // 将响应数据解析为JSON格式
       const data = JSON.parse(res.data)
       // 检查业务代码是否表示成功(代码10000)
       if(data.code !== 10000) {
         // 显示包含错误消息或默认消息的提示消息
         wx.utils.toast(data.message || '上传图片业务失败')
         return
       }
       // 如果数据成功更新,更新当前页面上的图片以及app.js文件中的图片
       this.setData({
         avatar: data.data.url
       })
       app.userInfo.avatar = data.data.url
     },
     // 失败请求的回调函数
     fail(err) {
       // 将错误记录到控制台
       console.log('请求失败', err);
     }
   })
 }

11.refreshToken

场景介绍

在调用登录接口成功后会返回 tokenrefreshToken ,token 有效时间设置的比较短,refreshToken 有效时间设置的比较长,当 token 失效后 refreshToken 仍然有效,此时可以通过 refreshToken 来为 token 续期,所谓的续期就是调用后端提供的一个接口,然后把 refreshToken 发送给服务端,服务端重新返回新的 tokenrefreshToken

示例步骤

1.登录后储存刷新refreshToken和token

  // 登录操作
   async login() {
     // 其他操作
     ...
     // 发送登录请求
     ...
     //调用appjs中存储token的方法
     getApp().setToken(res.data.token,res.data.refreshToken)
     ...
     })
   },

2.在app.js中将refreshToken和token保存在本地

 // app.js
 ​
 App({
   // 全局appjs中数据存储在内存中,访问较快
   // 全局appjs结合本地存储一起使用存储token
   token: '',         // 用户访问接口的身份验证令牌
   refreshToken: '', // 用于刷新token的令牌
 ​
   onLaunch() {
     // 在小程序启动时调用,通常用于初始化操作
     this.getToken(); // 获取token
   },
   // 获取token
   getToken() {
     // 从本地存储中获取token,防止页面刷新导致app上的数据清空
     wx.getStorage({
       key: 'token',
       success: (res) => {
         this.token = res.data; // 将获取的token存储到全局app对象中
       }
     });
 ​
     // 从本地存储中获取刷新token
     this.refreshToken = wx.getStorageSync('refreshToken');
   },
 ​
   // 存储token
   setToken(token, refreshToken) {
     // 设置全局app对象中的token和refreshToken
     this.token = 'Bearer ' + token;
     this.refreshToken = 'Bearer ' + refreshToken;
     // 将token和refreshToken存储到本地存储中
     wx.setStorage({
       key: 'token',
       data: 'Bearer ' + token,
     });
     wx.setStorage({
       key: 'refreshToken',
       data: 'Bearer ' + refreshToken,
     });
   }
 });
 ​

3.针对响应拦截器401做特殊处理

 //响应拦截器
 http.intercept.response = async function (res) {
   // token失效,利用refreshToken去获取新token
     if (res.data.code === 401) {
     // 处理token失效
     }
   }
 }

4.发送刷新token请求

 // 响应拦截器
 http.intercept.response = async function (res) {
   // 判断是否为token失效的响应码(401)
   if (res.data.code === 401) {
     // 获取小程序全局实例
     const app = getApp();
     // 使用refreshToken去获取新的token
     const res1 = await http({
       url: '/refreshToken',
       method: 'POST',
       header: {
         Authorization: app.refreshToken,
       },
     });
     // 调用appjs里存储token方法,更新全局app对象中的token和refreshToken
     getApp().setToken(res1.data?.token, res1.data?.refreshToken);
   // 如果响应不是token失效的情况,则直接返回响应数据
   }
   // 脱壳
   return res.data;
 };

5.如果刷新refreshToken也失效,则直接跳转到登录页

 http.intercept.response = async function (res) {
   // token失效,利用refreshToken也失效去获取新token
     if (res.data.code === 401) {
     // 用于检查请求的URL是否包含字符串 "/refreshToken",如果有则refreshToken也失效
     if (res.config.url.includes('/refreshToken')) {
       // 失效跳转至首页
       wx.navigateTo({
         url: '/pages/login/index',
       })
       return
     }
     ...
   }
   // 脱壳
   return res.data
 }

6.无感刷新,重发401请求

 http.intercept.response = async function (res) {
   // token失效,利用refreshToken去获取新token
   ...
     //获取最新的token和refreshToken
     getApp().setToken(res1.data?.token, res1.data?.refreshToken)
     const res2 =await http(Object.assign(res.config, { header: { Authorization: getApp().token } }))
     // 返回重新请求的数据
     return res2
   }
   return res.data
 }

12.地理定位

getLocation

获取用户所在位置的经纬度。在小程序中调用这个接口时必须要在 app.json 中申请调用权限

 {
   "requiredPrivateInfos": [
     "getLocation"
   ],
   "permission": {
     "scope.userLocation": {
       "desc": "你的位置信息将用于小程序位置接口的效果展示"
     }
   },
 }
 Page({
   onLoad() {
     this.getLocation()
   },
   async getLocation() {
     const res = await wx.getLocation()
     console.log(res)
   },
 })

chooseLocation

获取用户指定位置的经纬度。在小程序中调用这个接口时必须要在 app.json 中申请调用权限

 {
   "requiredPrivateInfos": [
     "chooseLocation"
   ]
 }
 Page({
   onLoad() {
     this.getLocation()
   },
   async chooseLocation() {
     const res = await wx.chooseLocation()
     console.log(res)
   },
 })

13.腾讯位置服务

位置服务SDK

腾讯位置服务为微信小程序提供了基础的标点能力、线和圆的绘制接口等地图组件和位置展示、地图选点等地图API位置服务能力支持,使得开发者可以自由地实现自己的微信小程序产品。 在此基础上,腾讯位置服务微信小程序 JavaScript SDK (opens new window)是专为小程序开发者提供的LBS数据服务工具包,可以在小程序中调用腾讯位置服务的POI检索、关键词输入提示、地址解析、逆地址解析、行政区划和距离计算等数据服务,让您的小程序更强大!

使用步骤

按文档指示操作即可。文档地址(opens new window)

1.申请开发者密钥(key):申请密钥(opens new window)

2.开通webserviceAPI服务:控制台 ->应用管理 -> 我的应用 (opens new window)->添加key-> 勾选WebServiceAPI -> 保存(小程序SDK需要用到webserviceAPI的部分服务,所以使用该功能的KEY需要具备相应的权限)

3.下载微信小程序JavaScriptSDK,微信小程序JavaScriptSDK v1.1 (opens new window)JavaScriptSDK v1.2(opens new window)

4.安全域名设置,在小程序管理后台 (opens new window)-> 开发 -> 开发管理 -> 开发设置 -> “服务器域名” 中设置request合法域名,添加apis.map.qq.com

逆地址解析

逆地址解析-reverseGeocoder

本接口提供由坐标到坐标所在位置的文字描述的转换,输入坐标返回地理位置信息和附近poi列表 文档地址:lbs.qq.com/miniProgram…

二次封装腾讯位置服务的sdk

 // utils/qqmap.js
 // 导入腾讯位置服务 SDK
 import QQMapWX from '../libs/qqmap-wx-jssdk'
 // 实例化位置服务(使用个人申请的 key)
 export default new QQMapWX({
   key: '填写自已的 KEY',
 })
 <van-cell title="{{address}}" border="{{false}}">
 import qqMap from '../../../utils/qqmap';
 Page({
   onLoad() {
     this.getLocation();
   },
 ​
   // 获取地址名称的方法,根据经纬度反向解析地址
   getAddressName(latitude, longitude) {
     // 使用qqMap模块的reverseGeocoder方法进行逆地址解析
     qqMap.reverseGeocoder({
       // 将经纬度拼接为字符串并传入location参数
       location: [latitude, longitude].join(','),
       // 成功获取地址信息的回调函数
       success: (res) => {
         // 结构赋值,从逆地址解析结果中获取地址名称
         const {
           result: { address },
         } = res;
         // 将获取到的地址名称更新到页面数据中
         this.setData({
           address,
         });
       },
     });
   },
 ​
   // 选择位置的异步方法,使用小程序的chooseLocation API
   async chooseLocation() {
     // 调用小程序的chooseLocation方法,用户选择位置后返回位置信息
     const res = await wx.chooseLocation();
     // 打印选择的位置信息到控制台
     console.log(res);
   },
 ​
   // 获取位置信息的异步方法,使用小程序的getLocation API
   async getLocation() {
     // 调用小程序的getLocation方法,获取当前用户的经纬度信息
     const { latitude, longitude } = await wx.getLocation();
     // 调用获取地址名称的方法,根据经纬度更新页面数据
     this.getAddressName(latitude, longitude);
   },
 });

地点搜索

地点搜索,搜索周边poi(Point of Interest),比如:“酒店” “餐饮” “娱乐” “学校” 等等

文档地址:lbs.qq.com/miniProgram…

 import qqMap from '../../../utils/qqmap';
 Page({
   onLoad() {
     this.getLocation();
   },
   // 获取附近小区的异步方法
   async getNeighborhood(latitude, longitude) {
     // 使用qqMap模块的search方法进行附近小区搜索
     qqMap.search({
       keyword: '住宅小区',
       location: [latitude, longitude].join(','),
       page_size: 5,
       // 成功获取搜索结果的回调函数
       success: (res) => {
         // 处理返回的数据,将其转换为适用于setData的格式
         const neighborhood = res.data.map((item) => {
           return {
             id: item.id,
             title: item.title,
           };
         });
         // 将处理后的数据更新到页面数据中
         this.setData({
           neighborhood,
         });
       },
     });
   },
 })

14.图片收集

小程序没有input type="file"用于选择文件,要实现类似功能,用以下api: wx.chooseMedia (opens new window) 拍摄或从手机相册中选择图片或视频。 低版本请用wx.chooseImage

 // 小程序页面对象定义
 Page({
   // 页面初始数据
   data: {
     image: '',  // 存储图片
   },
   // 上传图片的函数
   updateImg(e) {
     const that = this;
     // 选择媒体文件(图片或视频)
     wx.chooseMedia({
       count: 9,                 // 最大选择数量
       mediaType: ['image'],    // 允许选择的媒体文件类型,此处仅为图片
       sourceType: ['album', 'camera'],  // 可选择的来源,包括相册和相机
       maxDuration: 30,          // 视频的最长拍摄时间
       camera: 'back',           // 默认使用后置摄像头
       success(res) {
         // 打印选择的媒体文件的临时文件路径
         console.log(res.tempFiles[0].tempFilePath);
         // 上传文件至服务器
         wx.uploadFile({
           url: 'https://live-api.itheima.net/upload',  // 上传接口地址
           filePath: res.tempFiles[0].tempFilePath,      // 要上传的文件的临时路径
           name: 'file',                                 // 上传文件的字段名
           header: {
             Authorization: getApp().token  // 携带用户授权信息(token)的请求头
           },
           success(res) {
             // 解析服务器返回的数据
             const tmp = JSON.parse(res.data);
             // 更新页面数据,显示上传的图片
             that.setData({
               [e.currentTarget.dataset.type]: tmp.data?.url  // 根据类型更新对应图片的地址
             });
           }
         });
       }
     });
   },
 ...
 })

15.附件收集

参考文档:vant-contrib.gitee.io/vant-weapp/…

示例步骤

1.给文件上传组件绑定事件after-read

 <van-uploader preview-size="100" bind:after-read="afterRead" file-list="{{ fileList }}" />

2.在事件回调中获取要上传的文件

 afterRead(ev) {
     // console.log(ev)
     // 获取文件临时路径
     const filePath = ev.detail.file.url
     // 上传文件
     wx.uploadFile({
       url: 'https://live-api.itheima.net/upload',
       header: {
         Authorization: getApp().token,
       },
       filePath,
       name: 'file',
       success: (res) => {
         // console.log(res)
         const data = JSON.parse(res.data)
         if (data.code !== 10000) return wx.$utils.toast('文件上传失败!')
         this.data.attachment.push(data.data)
         this.setData({
           attachment: this.data.attachment,
         })
       },
     })
   },

16.地图组件

显示标点

map组件提供了地图展示、交互、叠加点线面及文字等功能,同时支持个性化地图样式,可结合地图服务 API 实现更丰富功能。

参考文档:developers.weixin.qq.com/miniprogram…

常用属性

属性名效果
latitude地图中心的纬度
longitude地图中心的经度
scale地图初始的缩放比例
markers地图上的标记
polyline地图路线

示例代码:

 <map 
   style="width: 100%; height: 100%;" 
   scale="16" 
   latitude="{{latitude}}" 
   longitude="{{longitude}}"
   markers="{{markers}}">
 </map>
 Page({
   data: {
     dialogVisible: false,
     latitude: 维度,
     longitude: 经度,
     markers: [
       {
         id: 1,
         latitude: 坐标记的维度,
         longitude: 标记的维度,
         width: 标记的宽度,
         height: 标记的高度
         ...
       },
     ],
   }
 })

显示路径

示例

 <map 
   style="width: 100%; 
   height: 100%;" 
   scale="16" 
   latitude="{{latitude}}" 
   longitude="{{longitude}}"
   markers="{{markers}}" 
   polyline="{{polyline}}">
 </map>
 async getPolyline() {
     const markers = this.data.markers
     qqMap.direction({
       mode: 'bicycling', // 可选值:'driving'(驾车)、'walking'(步行)、'bicycling'(骑行),不填默认:'driving',可不填'
       from: [markers[0].latitude, markers[0].longitude].join(','),
       to: [markers[1].latitude, markers[1].longitude].join(','), // 切记纬度在前,经度在后
       success: (res) => {
         // console.log(res)
         let ret = res
         let coors = ret.result.routes[0].polyline
         let pl = []
         //坐标解压(返回的点串坐标,通过前向差分进行压缩)
         let kr = 1000000
         for (let i = 2; i < coors.length; i++) {
           coors[i] = Number(coors[i - 2]) + Number(coors[i]) / kr
         }
         //将解压后的坐标放入点串数组pl中
         for (let i = 0; i < coors.length; i += 2) {
           pl.push({ latitude: coors[i], longitude: coors[i + 1] })
         }
         this.setData({
           polyline: [
             {
               points: pl, // 路径上的点
               color: '#5591af', // 路径颜色
               width: 4, // 路径宽度
             },
           ],
         })
       },
     })
   },

17.保存到本地

saveImageToPhotosAlbum

保存图片到系统相册。

参考文档:developers.weixin.qq.com/miniprogram…

getImageInfo

获取图片信息。网络图片需先配置 download 域名才能生效。

参考文档:developers.weixin.qq.com/miniprogram…

downloadFile

下载文件资源到本地。客户端直接发起一个 HTTPS GET 请求,返回文件的本地临时路径 (本地路径),单次下载允许的最大文件为 200MB

参考文档:developers.weixin.qq.com/miniprogram…

参考代码

 async saveToPhotosAlbum() {
     const { path } = await wx.getImageInfo({
       src: this.data.url,
     })
 ​
     wx.saveImageToPhotosAlbum({
       filePath: path,
     })
     
     // downloadFile
     wx.downloadFile({
       url: this.data.url,
       success: (res) => {
         wx.saveImageToPhotosAlbum({
           filePath: res.tempFilePath,
           })
        },
      })
   },

注意:记得配置download的合法域名