原生小程序 - 小程序系统API

1,213 阅读9分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情

网络请求

微信提供了专属的API接口,用于网络请求: wx.request(Object object)

image.png

Page({
  // 一般在onLoad中发送一些网络数据
  onLoad() {
    // 发送网络请求 可以使用微信内置api -> wx.request
    // 参数为一个配置对象,配置对象中的url是必传的
    // url必须是在微信后台白名单中的地址,或者在测试的时候关闭地址校验
    wx.request({
      url: 'http://api.example.com/api/home/houselist',
      // data用于设置参数
      // 无论是post请求的参数还是get请求的参数都设置在data配置项中
      data: {
        page: 1
      },
      // 成功时候的回调
      // 写成箭头函数是因为方面内部在调用this.setData的时候可以使用正确的this
      success:  res => {
        console.log(res.data.data)
      },
      fail: err => console.log(err)
    })
  }
})

简单封装

import { BASE_URL, TIME_OUT } from './env'

class API {
  #BASE_URL = ''
  #TIME_OUT = 10000

  constructor(BASE_URL, TIME_OUT) {
    this.#BASE_URL = BASE_URL
    this.#TIME_OUT = TIME_OUT
  }

  // 传入options是为了方便进行更多的配置,例如 header
  request(url, data = {},  options = { method: 'get' }) {
    return new Promise((resolve, reject) => {
      wx.request({
        url: new URL(url, this.#BASE_URL).href,
        timeout: this.#TIME_OUT
        data,
        ...options,
        success:  res => {
          resolve(res.data)
        },
        fail: reject
      })
    })
  }

  get(url, query, options = {}) {
    return this.request(url, query, {
      ...options,
      method: 'get'
    })
  }

  post(url, data, options = {}) {
    return this.request(url, data, {
      ...options,
      method: 'post'
    })
  }
}

export default new API(BASE_URL)

使用

// 小程序在引入外部模块的时候,不支持以/开头的绝对路径
// 也不支持自动查找index文件,只支持省略后缀名
import api from '../../utils/api'

Page({
  data: {
    homeList: [],
    page: 1
  },

  onLoad() {
   this.fetchList()
  },

  onReachBottom() {
    this.data.page += 1
    this.fetchList()
  },

  async fetchList() {
    try {
      const res = await api.get('/home/houselist', { page: this.data.page ?? 1 })
      this.setData({
        homeList: [...this.data.homeList, ...res.data]
      })
    } catch(e) {
      console.error(e.message)
    }
  }
})

域名配置

每个微信小程序需要事先设置通讯域名,小程序只可以跟指定的域名进行网络通信

服务器域名请在 「小程序后台 - 开发 - 开发设置 - 服务器域名」 中进行配置,配置时需要注意

  1. 域名只支持 https (wx.request、wx.uploadFile、wx.downloadFile) 和 wss (wx.connectSocket) 协议
  2. 域名不能使用 IP 地址(小程序的局域网 IP 除外)或 localhost, 且域名必须经过 ICP 备案
  3. 可以配置端口, 但请求的时候必须携带端口号,如果没有配置端口,则在请求的时候,不可以配置端口号,即使是默认的也不可以

弹窗

小程序中展示弹窗有四种方式: showToast、showModal、showLoading、showActionSheet

showToast

wx.showToast({
  title: 'toast',
  icon: "success",
  duration: 3000
})

showLoading的功能和showToast是类似的,其相当于设置了icon的值为loading的toast

只不过showModal的关闭是通过duration属性,而showLoading需主动调用 wx.hideLoading 才能关闭提示框

showModal

wx.showModal({
  title: '我是title',
  content: '我是内容',
  success: res => {
    if (res.cancel) {
      console.log('用户退出了弹框')
    }

    if (res.confirm) {
      console.log('用户点击了确定')
    }
  },
  fail: err => console.log(err)
})

showActionSheet

wx.showActionSheet({
  itemList: ['从相册中选取', '拍照'],
  // 具体点击了那一项  可以通过res.tapindex来获取
  success: res =>  console.log(res),
  fail: err => console.log(err)
})

分享

分享是小程序扩散的一种重要方式,小程序中有两种分享方式:

  1. 点击右上角的菜单按钮,之后点击转发
  2. 点击某一个按钮,直接转发

监听用户点击页面内转发按钮(button 组件 open-type="share")或右上角菜单“转发”按钮的时

可以通过 onShareAppMessage方法自定义转发内容,此事件处理函数需要 return 一个 Object,用于自定义转发内容

image.png

Page({
  onShareAppMessage() {
    return {
      title: '设置分享的标题,默认是小程序名',
      path: '/pages/profile/profile',
      imgUrl: 'https://i1.hdslb.com/bfs/face/7c409a397356632362171fd6a25a8ed44f064e40.jpg'
    }
  }
})

获取信息

获取设备信息

我们可以通过getSystemInfo方法获取用户当前设备信息,用于收集设备信息或者进行一些适配工作

wx.getSystemInfo({
  // res.screenHeight --- 手机的屏幕高度
  // res.windowHeight -- 小程序可视窗口的高度
  // 小程序的可视窗口 = 屏幕高度 - 导航栏高度 - 状态栏高度
  success: res => console.log(res),
})

获取地址位置信息

可以通过getLocation方法,获取用户的位置信息,以方便给用户提供相关的服务

对于用户的关键信息,需要获取用户授权后才能获得

App.json

{
  "permission": {
    "scope.userLocation": {
      // 向用户申请授权时候的描述信息
      // 授权是授权给appid的,一个小程序对应着一个appid
      // 也就意味着,用户只要授权一次即可,下次再进小程序就都不需要再次授权
      "desc": "获取用户地址位置"
    }
  }
}

page.js

wx.getLocation({
  // 只有用户授权后,小程序才可以获取用户地理位置
  success: res => console.log(res),
})

设置页面title

// 动态设置NavigationBar的title
wx.setNavigationBarTitle({
  title: '测试',
})

存储

在开发中,某些常见我们需要将一部分数据存储在本地:比如token、用户信息等, 小程序提供了专门的Storage用于进行本地存储

同步存取数据的方法:

  1. wx.setStorageSync(string key, any data)

  2. wx.getStorageSync(string key)

  3. wx.removeStorageSync(string key)

  4. wx.clearStorageSync()

// 小程序中的storage 的key值必须是字符串
// 但是小程序中的value值是any类型,也就是任意类型都可以被存储

// 添加
wx.setStorageSync('name', 'Klaus')

// 获取
console.log(wx.getStorageSync('name'))

// 移除具体某一项
wx.removeStorageSync('name')

// 清空storage
wx.clearStorageSync()

异步存储数据的方法:

  1. wx.setStorage(Object object)
  2. wx.getStorage(Object object)
  3. wx.removeStorage(Object object)
  4. wx.clearStorage(Object object)

所有的异步操作获取值都需要通过success属性监听获取,错误信息也需要通过fail属性来进行监听

自小程序V2.21.3开始,在异步存储数据API中可以对存储的数据进行加密操作,同步存储数据方法不可以

// 存储
wx.setStorage({
  key: 'name', // key
  data: 'Klaus', // value
  encrypt: true, // 对存储的数据进行加密
  success: () => {
    // 获取
    wx.getStorage({
      key: 'name', // key
      // 如果存储的数据是加密的,那么获取数据的时候 也必须开启encrypt 已进行正确的加密和解密操作
      // 如果存储的数据没有加密,获取数据的时候,是不可以开启encrypt的, 因为此时对明文数据进行解密操作会失败
      encrypt: true,
      success: res => console.log(res),
      fail: err => console.log(err)
    })
  }
})

跳转

界面的跳转有两种方式:通过navigator组件 和 通过wx的API跳转

在小程序中 和浏览器类似,每开启一个新的页面,会在小程序的页面栈中压入一个新的页面,以便于进行页面的前进和回退

api方式进行页面跳转

方法说明
wx.switchTab跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
wx.reLaunch关闭所有页面,打开到应用内的某个页面
即清空页面栈,并加入当前页
wx.redirectto关闭当前页面,跳转到应用内的某个页面
相当于router.replace
wx.navigateTo保留当前页面,跳转到应用内的某个页面
但是不能跳到 tabbar 页面
wx.navigateBack关闭当前页面,返回上一页面或多级页面

navigateTo

当前页面

wx.navigateTo({
  // 如果我们希望向另一个页面传递数据的时候
  // 可以通过query参数的形式进行传递
  url: '/pages/shop/shop?name=Klaus&age=23',
})

跳转过去的页面

Page({
  // 在前一个页面传入的参数可以通过onLoad方法的参数获取到
  onLoad(option) {
    console.log(option.query)
  }
})

navigateBack

// navigateBack方法可以不传配置对象
// 在navigateBack方法的配置参数中可以设置delta属性来控制页面回退数
// delta的默认值为1, 也就会回退一个页面
// delta的值为0或者负数的时候,会默认失效,采用默认值
wx.navigateBack()

数据传递

很多时候在在界面跳转过程中我们需要相互传递一些数据

  • 首页 -> 详情页:使用URL中的query字段
  • 详情页 -> 首页:在详情页内部拿到首页的页面对象,直接修改数据

image.png

详情页 -> 首页 数据传递方式一

Page({
  // 在页面卸载事件中进行数据传递是为了保证
  // 无论是通过按钮点击返回 还是通过小程序导航栏自带返回按钮返回
  // 都可以向对应组件传递对应数据
  onUnload() {
    // getCurrentPages方法可以获取到这个页面调用栈中所有的页面
    const pages = getCurrentPages()

    // 拿到上一个页面数据
    const prePage = pages[pages.length - 2]

    // 向上一个页面传递所需要的数据
    prePage.setData({
      msg: {
        name: 'Klaus',
        age: 23
      }
    })
  }
})

详情页 -> 首页 数据传递方式二

早期数据的传递方式只能通过上述的方式来进行,在小程序基础库 2.7.3 开始支持events参数,也可以用于数据的传递

首页

Page({
  data: {
    msg: {}
  },

  handleTap() {
    wx.navigateTo({
      url: '/pages/shop/shop',
      // 通过events属性添加对应的事件 -- 有点类似于事件总线
      events: {
        // 使用箭头函数,以便于内部可以获取正确的this
        customEvent: payload => {
          this.setData({
            msg: payload
          })
        },

        triggerEvent: payload => {
          console.log(payload)
        }
      },

      success(res) {
        // 主动向下一个页面传递对应的数据
        res.eventChannel.emit('triggerEvent', {
          name: 'triggerEvent'
        })
      }
    })
  }
})

详情页

Page({
  onLoad() {
    const events = this.getOpenerEventChannel()

    // 监听对应的页面,以获取上一个页面传递过来的数据
    events.on('triggerEvent', res => console.log(res))
  },

  onUnload() {
    // 获取上一个页面的事件总线
    const events = this.getOpenerEventChannel()

    // 触发自定义事件
    events.emit('customEvent', {
      name: 'Klaus',
      age: 23
    })
  }
})

naviagtor组件

navigator组件主要就是用于界面的跳转的,也可以跳转到其他小程序中

<!--
  navigator 是一个块级组件
  url属性 决定了需要跳转到哪里
  open-type 跳转方式 和 api中四种小程序跳转方式是一一对应的
  open-type 的默认值是 navigate
-->
<navigator url="/pages/shop/shop?name=Klaus&age=23">前进</navigator>

<navigator open-type="navigateBack">返回</navigator>

登录

为了增加用户的粘性和产品的停留时间,我们往往需要用户在应用中进行登录

而小程序是依赖于微信这个宿主环境的,所以我们可以借助微信已经登录的环境

实现静默登录,也就是什么操作都不执行,直接实现登录行为

在小程序中,可以通过openid和unionid来识别用户身份

分类说明
openid用户针对每个公众号或小程序等应用会产生一个安全的OpenID
在小程序中,openID是小程序的普通用户的一个唯一的标识,只针对当前的小程序有效
在公众号中openID是公众号的普通用户的一个唯一的标识,只针对当前的公众号有效
unionid同一个微信开放平台帐号下,用户的 UnionID 是唯一的
同一用户,对同一个微信开放平台下的不同应用,unionid是相同的
如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性

多平台账号共享

如果希望用户在小程序中登录后进行的操作,可以在网页端或移动端同时进行同步

就希望其它平台的账号程序和小程序中的账号实现关联绑定

实现方式是账号绑定或手机号绑定

但无论那种实现方式 都需要用户在小程序静默登录后,在手动输入账号密码或手机号短信进行登录

以将多平台账号进行关联

image.png

getCode

export function getCode() {
 return new Promise((resolve, reject) => {
     // 通过调用wx.login方法可以获取到对应的code
     wx.login({
       timeout: 10000,
       success(res) {
        resolve(res.code)
       },
       fail(err) {
         reject(err)
       }
     })
 })
}

业务逻辑

import { loginApi } from './utils/api'
import { getCode } from './utils/login'

App({
  // 如果在进入小程序的时候就需要登录的话,可以在onShow生命周期中完成
  // 如果需要在某一个组件中触发对应登录流程的时候,可以在组件的onLoad生命周期中完成
  async onShow() {
    const token = await this.login()
    console.log(token)
  },

  // /auth
  async login() {
    const loginToken = wx.getStorageSync('token')

    if (loginToken && await this.checkTokenAuth(loginToken)) {
      return loginToken
    }

    const code = await getCode()

    const { token } = await loginApi.post('/login', {
      code
    })

    wx.setStorageSync('token', token)
    return token
  },

  async checkTokenAuth(token) {
    const { message } = await loginApi.post('/auth', {}, {
      header: {
        token
      }
    })

    return message === '已登录'
  }
})