小程序基础-分包加载&&自定义组件
小程序分包加载
小程序分包加载-为什么要分包加载
- 微信平台对小程序单个包的代码体积限制为 2M,超过 2M 的情况下可以采用分包来解决
- 即使小程序代码体积没有超过 2M 时也可以拆分成多个包来实现按需加载
- 配置文件能忽略的只有静态资源,代码无法被忽略
配置忽略文件
project.config.json
{
"description": "项目配置文件",
"packOptions": {
"ignore": [
{
"value": "static/uploads",
"type": "folder"
}
],
"include": []
},
type: 表示要忽略的资源类型
value: 表示具体要忽略的
小程序分包加载-使用分包配置
分类:
- 主包:
-
每个小程序必定含有一个主包。
-
默认启动页面、TabBar 页面,以及公共资源/JS 脚本必须放在主包;
2.分包 https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages.html
-
通过subPackages来配置
-
所有包的大小之和不超过20M
app.json
{
// 省略其他的...
"subPackages": [
{
"root": "subpkg_user", // 分包代码的目录,其实就是一个独立的文件夹
"pages": [
"pages/profile/profile"
]
},
{
"root": "subpkg_order", // 文件夹
"pages": [
"pages/order_list/index",
"pages/order_list/index"
]
}
]
}
注意: 写完分包之后,如果对应的文件夹和页面不存在,它会自动创建文件夹和页面。
小程序分包—预加载
https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/preload.html
在打开小程序启动的时候只下载主包代码,分包并不会下载,因此能够提升小程序启动时的打开速度,但是分包的代码只有在访问到分包的页面时才去下载,这样用户就需要有一定时间的等待(一般不太影响),通过分包预加载技术可以实现提前去下载分包的代码,这样分包页面的访问速度也会得到提升。
小程序通过 preloadRule 配置需要预加载的分包。
app.json
{ ......
"preloadRule": {
"页面地址,进入这个页面就需要预加载分包": { // pages/index/index
"network": "网络环境", // "wifi"
"packages": ["要预加载的包名"] //["goods_pkg"]
}
}, ......
} //当用户访问到 pages/index/index 时,在 wifi 网络前提下预先下载 goods_pkg 分包的代码。
- 指定某个页面路径做为 key,含义是当访问这个页面时会去预加载一个分包
- network 预加载分包的网络条件,可选值为 all、wifi,默认为 wifi
- packages 指定要预下载的分包名或根路径
配置完成之后,访问指定页面时,就会在控制台输出提示。
自定义组件—基本使用
创建组件
通常将项目中的组件都放在一个独立的目录下,并且一般就给这个文件夹取名为:components 。这个目录需要我们手动进行创建。
-
新建一个目录:components
-
在components上点击鼠标右键,选择「新建Component」
-
填入组件的名字。它会自动创建4个同名的文件。(这一点和创建页面是一样的)
组件和页面的结构区别:
- 组件的配置文件(.json文件)中,有一个配置项:component: true
- 组件的 .js 文件中调用 Component 函数,页面的.js文件中调用Page函数
注册组件
-
页面注册是在使用组件的(xxxx.json)中通过 usingComponents 进行注册,只能在当前页面中组件
-
全局注册是在 app.json 文件中通过 usingComponents 对自定义组件进行注册,可以在任意页面中使用
"usingComponents": {
"my-test": "/components/MyTest/index"
}
使用组件
在wxml中,直接通过标签的方式使用即可。
自定义组件—组件样式
- 组件中的样式不要使用标签选择器
- 组件中,样式默认是隔离的: 自定义组件的样式只受到自定义组件 wxss 的影响
- 通过对组件的配置,可以取消这个隔离的状态。
样式隔离注意点
-
app.wxss中的全局样式对组件无效
-
只有class选择器具有样式隔离效果,id选择器、属性选择器、标签选择器不受样式隔离的影响
建议:在组件和引用组件的页面中建议使用class选择器,不要使用id、属性、标签选择器
修改组件样式的隔离选项
默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。但有时,我们希望外界能够控制组件内部的样式,此时,可以通过在组件的.js文件中设置: options → addGlobalClass 为true
XX.js
Component({
options: {
addGlobalClass: true
}
})
在页面中设置的同类名的选择器就能作用于子组件内部的元素。但是,组件内的class选择器,不能影响页面的元素。
自定义组件—组件样式-外部样式类
组件希望接受外部传入的样式类。此时可以在 Component 中用 externalClasses 定义若干个外部样式类。
在开发组件时,主动暴露给组件使用者,修改组件内部样式
组件 custom-component.js
/* 组件 custom-component.js */
Component({
externalClasses: ['my-class']
});
组件 custom-component.wxml
<!-- 组件的wxml -->
<!-- 这里的my-class相当于一个占位符 -->
<view class="my-class">components/MyTest/index.wxml</view>
页面的 WXML
<!-- 页面的 WXML -->
<custom-component my-class="red-text" />
<custom-component my-class="large-text" />
页面的wxss
.red-text{ color: red; }
.large-text {font-size: 50px; }
外部样式类相当于用一个类名去当占位符,以便于在后期使用时替换成真实的类名,方便添加额外的样式。
参考:https://vant-contrib.gitee.io/vant-weapp/#/button#wai-bu-yang-shi-lei
自定义组件—数据方法
组件的典型结构
// borderImage.js
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
}
})
定义数据
在小程序中,用于组件模板渲染的私有数据,需要定义到data中
methods方法
在小程序的组件中,事件处理函数和自定义方法需要定义到methods中
自定义组件—组件插槽
单个插槽
在小程序中,默认情况下每个自定义组件中只允许使用一个插槽进行占位。
<!--components/MyTest2/index.wxml-->
<view>
<text>components/MyTest2/index.wxml</text>
<!-- 对于不确定的内容,可以使用slot进行占位,具体内容交给使用者确定 -->
<slot></slot>
</view>
使用组件
<my-test2>
<!-- 这里的内容将被放到组件中<slot>的位置 -->
<view>
这里是slot里的内容
</view>
</my-test2>
多插槽(具名插槽)
组价.js
Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多 slot 支持
},
// ... 省略其他
})
此时,可以在这个组件的 wxml 中使用多个 slot ,以不同的 name 来区分。
定义插槽
<view>
<text>components/MyTest2/index.wxml</text>
<!-- 对于不确定的内容,可以使用slot进行占位,具体内容交给使用者确定 -->
<!-- <slot></slot> -->
<slot name="before"></slot>
<view>
---------这里是分割线--------
</view>
<slot name="after"></slot>
</view>
使用组件
<my-test2>
<!-- 这里的内容将被放到组件中<slot>的位置 -->
<!-- <view>
这里是slot里的内容
</view> -->
<view slot="before">
这里是before slot里的内容
</view>
<view slot="after">
这里是after slot里的内容
</view>
</my-test2>
自定义组件—生命周期
组件生命周期-lifetimes
生命周期 | 参数 | 描述 |
---|---|---|
created | 无 | 在组件实例刚刚被创建时执行,此时还不能调用 setData,一般用于给组件的this添加一些自定义的属性字段 |
attached | 无 | 在组件实例进入页面节点树时执行,绝大多数初始化工作可以在这个时机进行,例如发请求获取初始数据 |
ready | 无 | 在组件在视图层布局完成后执行 |
moved | 无 | 在组件实例被移动到节点树另一个位置时执行 |
detached | 无 | 在组件实例被从页面节点树移除时执行,适合做一些清理工作 |
error | Object Error | 每当组件方法抛出错误时执行 |
生命周期函数要写在lifetimes里边
lifetimes: {
created() {
console.log('组件被created') // 这里使用setData不会引起视图的更新
this.setData({ msg: 'abc!' })
},
attached() {
this.setData({ msg: 'abcd' })
}
}
自定义组件-属性(父传子)
在小程序中,properties是组件的对外属性,用于接收外界传递到组件中的数据
父组件传入属性值
<my-test isOpen max="9" min="1" />
子组件.js中接收
Component({
properties: {
isOpen: Boolean,
min: Number, // 直接写类型
max: { // 写类型 + 初始值
type: Number,
value: 10 // value用于指定默认值
}
}
})
自定义组件-组件通讯-自定义事件triggerEvent(子传父)
Vant组件库
官方文档:https://vant-contrib.gitee.io/vant-weapp/#/quickstart
步骤一 通过 npm 安装
npm i @vant/weapp -S --production
步骤二 修改 app.json
将 app.json 中的 "style": "v2"
去除
步骤三 修改 project.config.json
开发者工具创建的项目,miniprogramRoot
默认为 miniprogram
,package.json
在其外部,npm 构建无法正常工作。
需要手动在 project.config.json
内添加如下配置,使开发者工具可以正确索引到 npm 依赖的位置。
{
...
"setting": {
...
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram/"
}
]
}
}
步骤四 构建 npm 包(重点)
开发者工具上 > “工具” > “构建npm”
使用
去app.json(全局注册)或页面.json(局部注册)中注册
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
在页面中使用
<van-button type="primary">按钮</van-button>
小程序开发环境-优化目录结构
项目的根目录
├── miniprogram // 项目相关的代码夹
├── node_modules // npm包目录
├── package.json
├── project.config.json
├── package-lock.json
└── sitemap.json
修改 project.config.json
中的配置项
{
// 省略其他......
"setting": {
// 省略其他......
"packNpmManually": true,
"packNpmRelationList": [
{
"miniprogramNpmDistDir": "./miniprogram",
"packageJsonPath": "package.json"
}
]
},
"miniprogramRoot": "miniprogram/"
}
启用 less/sass
通过 less/sass 可以更好的管理 css 样式,通过 project.config.json
可以启用对 less/sass 的支持。
{
"setting": {
"useCompilerPlugins": ["sass"]
}
}
然后将 .wxss
文件后缀改换成 .scss
即可。
启动项目
-
拉取代码
-
导入项目
使用小程序开发者工具导入【项目】的代码
3. 使用小程序开发者工具构建 npm
- 安装包:npm install
- 手动构建: 【工具】→【构建npm】
project.config.json的几个配置
{
"miniprogramRoot": "miniprogram/",
"setting": {
"useCompilerPlugins": ["sass"],
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram"
}
],
}
}
-
miniprogramRoot
项目的根目录为miniprogram
-
setting.useCompilerPlugins
启用了sass
支持 -
packNpmRelationList
指定了 npm 构建时所需的package.json
的位置以及构建后代码的生成位置
4. 改成自己的appid
这个项目中的appid是别人的,如果我们需要改成自己的。
基础封装-消息反馈
将所有通用的工具方法封装到 utils/utils.js 中
/**
* 用户消息反馈
* @param {string} title 文字提示的内容
*/
export const toast = (title = '数据加载失败...') => {
wx.showToast({
title,
mask: true,
icon: 'none',
})
}
// 挂载到全局对象 wx
wx.$toast = toast
app.js
// 在入口中执行 utils.js
import './utils/utils.js'
App({
// ...
})
使用
wx.$toast('//提示文字', "icon图标")
基础封装-网络请求
安装第三方的包-构建
- npm install wechat-http
- 安装完成后还必须要构建 npm后才可以使用
wechat-http用法与 axios
类似:
-
http.baseURL
配置接口基础路径 -
http.get
以GET
方法发起请求 -
http.post
以POST
方法发起请求 -
http.put
以PUT
方法发起请求 -
http.delete
以DELETE
方法发起请求 -
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({
// ...
})
配置响应拦截器
// 配置响应拦截器
http.intercept.response = function ({ data, config }) {
// 检测接口是否正常返回结果
if (data.code !== 10000) {
wx.$toast()
return Promise.reject(data)
}
// 只保留data数据,其它的都过滤掉
return data.data
}
跳转传参
点击公告列表后将公告的ID通过地址参数传递到公告详情页面,在公告详情页 onLoad 生命周期中读取到公告 ID,然后调用接口获取公告详情的数据。
van-count-down 组件(倒计时)的应用
<van-count-down use-slot time="{{6000}}" bind:change="countDownChange">
<text>{{timeData.seconds}}秒后重新获取</text>
</van-count-down>
time: 指定了倒计时多少毫秒
bind:change每隔一秒的回调,它会传出来当前的倒计时信息
countDownChange(ev) {
console.log(ev.detail)
this.setData({
timeData: ev.detail,
getCodeBtnVisible: ev.detail.minutes === 0 && ev.detail.seconds === 0,
})
},
表单验证插件使用
先在data中设置mobile,再在模板中进行双向绑定
model:value=“{{mobile}}”
- 安装 并 构建 表单验证码插件
wechat-validate
npm install wechat-validate
- 将插件导入到项目中
-
behaviors
将插件注入到页面中 -
rules
由插件提供的属性,用来定义数据验证的规则(类似于 Element UI) -
validate
由插件提供的方法,根据rules
的规则来对数据进行验证
// 导入表单验证插件
import validate from 'wechat-validate'
Page({
data: {
mobile: '' // 省略其他
},
behaviors: [validate], // 将插件注入到页面实例中
rules: {
mobile: [
{required: true, message: '请填写手机号码!'},
{pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号码!'}
]
},
getSMSCode() {
// 获取验证结果
const { valid, message } = this.validate('mobile')
// 如果验证不合法则不再执行后面的逻辑
if (!valid) return wx.$toast(message)
console.log('getCode')
this.setData({ getCodeBtnVisible: false })
},
})
保存token-跳转
- 在app.js中设置setToken方法,保存到本地存储
App({
// ...
setToken(key, token) {
// 将 token 记录在应用实例中
this[key] = token
// 将 token 存入本地
wx.setStorageSync(key, token)
}
})
2.点击登录 | 注册发送请求成功之后
const app = getApp() //小程序中获取全局的实例对象
app.setToken('token', res.token)
app.setToken('refreshToken', res.refreshToken)
// 跳转
const url = '/pages/profile/index'
wx.redirectTo({ url })
登录检测-鉴权组件
1.在根目录中创建 components
文件夹用来存放全局的组件,然后通过小程序开发者工具创建一个名为 authorization
的组件
2.接下来全局来注册这个组件,保证任何页面中都可以直接应用 authorization
组件
{
"usingComponents": {
"authorization": "/components/authorization/index"
},
}
3.到用户信息页面中应用 authorization
使用做为页面根节点
<authorization>
...
</authorization>
4.在authorization中补充插槽和状态
<!--components/authorization/index.wxml-->
<slot wx:if="{{isLogin}}"></slot>
data: {
isLogin: false
},
读取本地存储token
读取本地存储的 token
数据,用于判断是否曾登录过
// app.js
App({
......
getToken() {
// 将 token 数据记到应用实例中
// return this.token = wx.getStorageSync('token')
return this.token
}
})
在组件内读token并处理
data: {
isLogin: false
},
lifetimes: {
attached() {
const isLogin = !!getApp().getToken()
//const app = getApp() const isLogin = !!app.getToken()
this.setData({ isLogin })
if (!isLogin) {
wx.redirectTo({ url: '/pages/login/index' })
}
}
},
地址重定向,登录成功后跳回到原来的页面
在 authoirzation
组件检测登录时获取当前页面栈实例,并在跳转到登录页面时在 URL 地址上拼凑参数:
// /components/authorization/index.js
Component({
// ...
lifetimes: {
attached() {
// 获取登录状态
const isLogin = !!getApp().token
// 变更登录状态
this.setData({ isLogin })
// 获取页面栈
const pageStack = getCurrentPages()
// 获取页面路径
const currentPage = pageStack.pop()
// 未登录的情况下跳转到登录页面
if (!isLogin) {
wx.redirectTo({
url: '/pages/login/index?redirectURL=/' + currentPage.route,
})
}
},
},
})
用户管理-显示默认值
app.js中添加初始值
App({
globalData: {},
userInfo: { avatar: '', nickName: '微信用户1' }
}
在onLoad中加载值
data: {
avatar: '',
nickName: ''
},
onLoad() {
const app = getApp()
console.log(app.userInfo)
const { avatar, nickName } = app.userInfo
this.setData({ avatar, nickName })
// 用户未登录时不必请求
app.token && this.getUserProfile()
},
配置请求拦截器
将用户的登录状态通过自定义的头信息 Authorization
随接口调用时一起发送到服务端。
// 导入 wechat-http 模块
import http from 'wechat-http'
// 配置接口基础路径
http.baseURL = 'https://live-api.itheima.net'
// 配置请求拦截器
http.intercept.request = function (options) {
console.log('请求拦截器', options.header)
// 扩展头信息
const defaultHeader = {}
// 身份认证
const token = getApp().getToken()
if (token) {
defaultHeader.Authorization = 'Bearer ' + getApp().getToken()
}
// 与默认头信息合并
options.header = Object.assign({}, defaultHeader, options.header)
// 处理后的请求参数
return options
}
注:传递 token
时需要拼凑字符串前缀 "Bearer "
文件上传(例:更新用户头像)
获取用户选择的头像地址,通过 wx.uploadFile
将图片上传到服务端。
wx.uploadFile
的基本语法:
-
url
上传接口地址 -
filePath
待上传文件的临时路径(该路径只能用于小程序内部) -
name
接口接收上传文件的数据名称(由后端指定) -
formData
除上传文件外的其它数据 -
header
自定义头信息 -
success
上传成功的回调函数 -
fail
上传失败后的回调函数 -
complete
上传完成时的回调(无论成功或失败)
注:该 API 不支持返回 Promise,调用该 API 时,需要提前在小程序管理后台添加服务器域名。
<!-- pages/profile/index.wxml -->
<authorization>
<view class="profile">
<van-cell center title="头像">
<van-icon slot="right-icon" name="arrow" size="16" color="#c3c3c5" />
<button
class="button"
size="mini"
hover-class="none"
bind:chooseavatar="updateUserAvatar" //事件,事件名全部小写,A千万不要大写,不会触发
open-type="chooseAvatar"> //与上边事件对应
<image class="avatar" src="{{avatar}}"></image>
</button>
</van-cell>
...
</view>
</authorization>
// pages/profile/index.js
const pageStack = getCurrentPages()
Page({
...
// 更新用户头像
updateUserAvatar(ev.detail.avatarUrl) {
// 调用 API 上传文件
wx.uploadFile({
// 接口地址
url: wx.$http.baseURL + '/upload',
// 待上传的文件路径
filePath: avatar,
name: 'file',// wx.uploadFile 要求必传。
header: {
Authorization: 'Bearer ' + getApp().getToken() // 用户登录状态
},
formData: { // 是我们自己的接口文档的要求。可以不传,默认就是avatar
type: 'avatar'
},
success: (result) => {
console.log(JSON.parse(result.data))
const res = JSON.parse(result.data)
// console.log(res.data.url)
const avatar = res.data.url
// 1. 在页面上显示
this.setData({ avatar })
// 2. 更新全局数据
const app = getApp()
app.userInfo.avatar = avatar
// 3. 通过页面栈找到my/index页面,更新它的avatar信息
const pages = getCurrentPages()
// pages[0].data.nickName = nickName 直接修改数据不会让视图更新
// 调用setData更新
pages[0].setData({ avatar })
}
})
}
})
上述代码中通过 wx.http.baseURL
获取接口服务器地址,通过应用实例获取 token
。
refresh_token使用
-
token:
-
- 作用:在访问一些接口时,需要传入token,就是它。
- 有效期:2小时(安全)。
-
refresh_token
-
- 作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token。
- 有效期:14天。(最理想的情况下,一次登陆可以持续14天。)
1.用户在首次完成登录时会分别得到 token 和 refresh_token
2.当 token 失效后(例如2小时之后),调用接口A会返回 401 状态码(这是与后端约定好的规则)
3.检测状态码是否为 401**,如果是,则携带refreshToken去调用刷新token的接口
4.刷新 token 的接口后会返回新的 token 和 refreshToken
5.把401的接口A重新发送一遍
注意:
refresh_token也是有过期时间的,只不过一般会比token过期时间更长一些。这就是为啥如果某个应用我们天天打开,则不会提示我们登录,如果是有几周或更长时间去打开时,会再次要求我们登录。
refresh_token一个更常见的名字叫token无感刷新。
refreshToken功能-基本实现
// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
console.log(statusCode, data, config)
// console.log(statusCode) // http 响应状态码
// console.log(config) // 发起请求时的参数
if (data.code === 401) {
const app = getApp()
// 调用接口获取新的 token
const res = await http({
url: '/refreshToken',
method: 'POST',
header: {
Authorization: 'Bearer ' + app.getToken('refreshToken'),
}
})
app.setToken('token', res.token)
app.setToken('refreshToken', res.refreshToken)
// 获得新的token后需要重新发送刚刚未完成的请求
config = Object.assign(config, {
header: {
// 更新后的 token
Authorization: 'Bearer ' + res.token,
},
})
// 重新发请求
return http(config)
}
// 拦截器处理后的响应结果
if (data.code === 10000) {
return data.data
} else {
wx.$toast(data.message || '请求失败')
return Promise.reject(data.message)
}
}
refreshToken也过期的特殊处理
完整版响应拦截器
// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
console.log(statusCode, data, config)
// console.log(statusCode) // http 响应状态码
// console.log(config) // 发起请求时的参数
if (data.code === 401) {
++ if (config.url.includes('/refreshToken')) {
++ console.log('/refreshToken过期了')
++ // 获取当前页面的路径,保证登录成功后能跳回到原来页面
++ const pageStack = getCurrentPages()
++ const currentPage = pageStack.pop()
++ const redirectURL = currentPage.route
++ // 跳由跳转(登录页面)
++ wx.redirectTo({
++ url: '/pages/login/index?redirectURL=/' + redirectURL,
++ })
++ return Promise.reject('refreshToken也过期了,就只能重新登录了')
++ }
const app = getApp()
// 调用接口获取新的 token
const res = await http({
url: '/refreshToken',
method: 'POST',
header: {
Authorization: 'Bearer ' + app.getToken('refreshToken'),
}
})
app.setToken('token', res.token)
app.setToken('refreshToken', res.refreshToken)
config = Object.assign(config, {
header: {
// 更新后的 token
Authorization: 'Bearer ' + res.token,
},
})
// 重新发请求
return http(config)
}
// 拦截器处理后的响应结果
else if (data.code === 10000) {
return data.data
} else {
wx.$toast(data.message || '请求失败')
return Promise.reject(data.message)
}
}
腾讯位置服务-需要提前注册
文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/jsSdkOverview
使用步骤(共4步)
- 申请开发者密钥(key):申请密钥(地址:https://lbs.qq.com/dev/console/application/mine)
- 开通webserviceAPI服务:控制台 ->应用管理 -> 我的应用->添加key-> 勾选WebServiceAPI -> 保存(小程序SDK需要用到webserviceAPI的部分服务,所以使用该功能的KEY需要具备相应的权限)
3.下载微信小程序JavaScriptSDK,微信小程序JavaScriptSDK v1.1(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.1.zip) JavaScriptSDK v1.2(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.2.zip) js文件
4.安全域名设置,在小程序管理后台-> 开发 -> 开发管理 -> 开发设置 -> “服务器域名” 中设置request合法域名,添加https://apis.map.qq.com
地理定位-wx.getLocation
获取用户所在位置的经纬度。在小程序中调用这个接口时必须先在 app.json 中申请调用权限(开发环境可以省略)。
//app.json
{
"requiredPrivateInfos": [
++ "getLocation"
],
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
},
}
Page({
onLoad() {
this.getLocation()
},
async getLocation() {
const res = await wx.getLocation() // 要提前申请权限
console.log(res)
},
})
wx.getLocation返回的结果格式大致如下:
accuracy: 65
errMsg: "getLocation:ok"
horizontalAccuracy: 65
latitude: 30.88131
longitude: 114.37509
speed: -1
verticalAccuracy: 65
wx.getLocation 只能得到经纬度信息
逆地址解析-reverseGeocoder
由坐标 → 坐标所在位置的文字描述的转换,输入坐标返回地理位置信息和附近poi列表
文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodReverseGeocoder
1. 导入 QQMapWX 并设置好 key
2.在代码中补充getPoint方法:
- 调用接口把经纬度转换成对应位置的文字
- 保存文字到address
// 导入位置服务实例
import QQMap from '../../../utils/qqmap'
Page({ ......
onLoad() {
this.getLocation()
},
async getLocation() {
// 调用小程序API获取经纬度等信息
const { latitude, longitude } = await wx.getLocation() //获取用户经纬度
this.getPoint(latitude, longitude)
},
getPoint(latitude, longitude) {
// 逆地址解析(根据经纬度来获取地址)
QQMap.reverseGeocoder({
location: [latitude, longitude].join(','),
success: (result) => {
const address = res.address
this.setData({ address })
},
})
}
})
3.渲染页面
<van-cell-group border="{{false}}" title="当前地点">
<van-cell title="{{address}}" border="{{false}}"> //border="{{false}}"设置无边框样式
<text bind:tap="chooseLocation" class="enjoy-icon icon-locate">重新定位</text>
</van-cell>
</van-cell-group>
QQMap地点搜索—search
根据当前的定位,调用 QQMap.search() 找到周边的信息。
搜索周边poi(Point of Interest),比如:“酒店” “餐饮” “娱乐” “学校” 等等
文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodSearch
在小程序中调用这个接口时必须要在 app.json 中申请调用权限
//app.json
{
"requiredPrivateInfos": [
++ "chooseLocation"
]
}
// 选择新的位置
async chooseLocation() {
// 调用小程序 API 获取新的位置
const { latitude, longitude } = await wx.chooseLocation()
this.getPoint(latitude, longitude) // 获取新的位置经纬度
},
getPoint(latitude, longitude) {
wx.showLoading({
title: '正在加载...', // 显示loading提示
})
// 逆地址解析(根据经纬度来获取地址)
QQMap.reverseGeocoder({
location: [latitude, longitude].join(','),
success: ({ result: { address } }) => {
this.setData({ address })
},
})
QQMap.search({
keyword: '住宅小区', //搜索关键词
location: [latitude, longitude].join(','), //设置周边搜索中心点
page_size: 5, //只显示5条信息,不设置此项默认为10
success: (result) => { //success 是一个回调函数,表示搜索成功后的处理逻辑。
const points = result.data
this.setData({ points }) // 渲染数据
},
fail: (err) => { //fail 是一个回调函数,表示搜索失败后的处理逻辑。
console.log(err.message)
},
complete: () => {//complete 是一个回调函数,表示搜索结束后的处理逻辑(无论搜索成功还是失败)
wx.hideLoading() // 隐藏loading提示
},
})
},
重新定位-wx.chooseLocation
申请权限
获取用户指定位置的经纬度。在小程序中调用这个接口时必须要在 app.json 中申请调用权限
{
"requiredPrivateInfos": [
"chooseLocation"
]
}
示例代码:
// 选择新的位置
async chooseLocation() {
// 调用小程序 API 获取新的位置
const { latitude, longitude } = await wx.chooseLocation()
// 获取新的位置附近的小区
this.getPoint(latitude, longitude)
console.log('起点位置:', latitude, longitude)
},
getPoint(latitude, longitude) {
// 显示loading提示
wx.showLoading({
title: '正在加载...',
})
// 逆地址解析(根据经纬度来获取地址)
QQMap.reverseGeocoder({
location: [latitude, longitude].join(','),
success: ({ result: { address } }) => {
// console.log(address)
// 数据数据
this.setData({ address })
},
})
QQMap.search({
keyword: '住宅小区', //搜索关键词
location: [latitude, longitude].join(','), //设置周边搜索中心点
page_size: 5,
success: (result) => {
// console.log(result)
// 过滤掉多余的数据
const points = result.data.map(({ id, title, _distance }) => {
return { id, title, _distance }
})
// console.log(points)
// 渲染数据
this.setData({ points })
},
fail: (err) => {
console.log(err.message)
},
complete: () => {
// 隐藏loading提示
wx.hideLoading()
},
})
},
图片收集(收集身份证信息)
小程序没有input type="file"用于选择文件,要实现类似功能,用以下api:
wx.chooseMedia**:**拍摄或从手机相册中选择图片或视频。
低版本请用wx.chooseImage。
1.绑定事件选择身份证图片上传。
2.发送请求上传图片,拿到上传后的图片地址。
Page({
...
async uploadPicture(ev) {
// 获取图片临时地址
const res = await wx.chooseMedia({
count: 1,
mediaType: ['image'],
sizeType: ['compressed'],
})
const tempPath = res.tempFiles[0].tempFilePath
const type = ev.mark.type
// 上传图片到服务器
wx.uploadFile({
url: wx.$http.baseURL + '/upload',
filePath: tempPath,
name: 'file',
header: {
Authorization: 'Bearer ' + getApp().getToken(),
},
success: (res) => {
const res = JSON.parse(result.data)
console.log(res.data.url) // 上传成功的回调
this.setData({ [type]: res.data.url })
},
})
},
})
校验表单信息
获取了全部的表单数据后再对数据进行验证,说明如下:
- 房屋的信息是通过url地址获取的不需要验证
- 性别可以指定默认值也不需要验证
- 剩下的数据通过
wechat-validate
插件进行验证:
// house_pkg/pages/form/index.js
// 导入表单验证插件
import wxValidate from 'wechat-validate'
Page({
behaviors: [wxValidate],
data: {
point: '',
building: '',
room: '',
name: '',
gender: 1,
mobile: '',
idcardFrontUrl: '',
idcardBackUrl: '',
},
rules: {
name: [
{ required: true, message: '业主姓名不能为空!' },
{ pattern: /^[\u4e00-\u9fa5]{2,5}$/, message: '业主姓名只能为中文!' },
],
mobile: [
{ required: true, message: '业主手机号不能为空!' },
{ pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号!' },
],
idcardFrontUrl: [
{ required: true, message: '请上传身份证国徽面!' }
],
idcardBackUrl: [
{ required: true, message: '请上传身份证照片面!' }
],
},
})
表单收集—收集预约日期
1.时间选择控件:van-datetime-pickerhttps://vant-contrib.gitee.io/vant-weapp/#/datetime-picker
2.弹出层控件:van-popuphttps://vant-contrib.gitee.io/vant-weapp/#/popup
- 给选择日期单元绑定事件——显示选择日期弹层(van-popup)
2.给日期控件绑定确认事件confirm
3.在确认回调中获取时间戳
4.将时间戳格式化并显示
selectDate(ev) {
// console.log(ev)
this.setData({
currentDate: ev.detail,
appointment: wx.$utils.formatDate(ev.detail), // 格式化日期
})
this.closeDateLayer()
},
补充格式化日期的函数
formatDate(time) {
const d = new Date(time)
const year = d.getFullYear()
let month = d.getMonth() + 1 // 获取月份,月份从0开始,所以加1
let day = d.getDate()
month = month < 10 ? '0' + month : month
day = day < 10 ? '0' + day : day
return `${year}-${month}-${day}`
},
wxml
<van-cell title-width="100" title="预约日期" value-class="{{appointment && 'active-cell'}}" bind:click="openDateLayer"
is-link value="{{appointment || '请选择上门维修日期'}}" />
...省略
<van-popup bind:close="closeDateLayer" round show="{{ dateLayerVisible }}" position="bottom">
<van-datetime-picker bind:cancel="closeDateLayer" bind:confirm="selectDate" type="date" value="{{ currentDate }}"
min-date="{{ 1664582400000 }}" />
</van-popup>
加载更多
在scroll-view上添加 bindscrolltolower=“loadMore”
<scroll-view
bindscrolltolower="loadMore"
show-scrollbar="{{false}}" enhanced scroll-y>
<view class="repairs">
<view class="repairs-title">我的报修</view>
loadMore() {
// if(是否有更多的数据)
if (this.data.total <= this.data.list.length)
return
console.log('更多的数据')
// 把页码+1,发请求,请求回来的数据要 追加 到原数组
// [5] → [10]
this.data.page++
this.getList()
},
async getList() {
// 发请求
const { pageTotal, total, rows: list } = await wx.$http.get('/repair', { current: this.data.page, pageSize: this.data.pageSize })
console.log(list, pageTotal, total)
// 渲染数据
// 在原来的基础上添加数据
this.setData({ total, list: [...this.data.list, ...list] })
},
路线规划
路线规划是常见的一个功能,它用来在地图上展示两点间路线,要使用这个功能需要用到两部分的知识:
- 小程序提供的 map 组件用来在页面渲染地图
map | 微信开放文档https://developers.weixin.qq.com/miniprogram/dev/component/map.html)
- 腾讯位置服务计算两点路线的所有坐标点(经纬度)
首先来看小程序提供的地图组件 map
- latitude 指定地图中心点的纬度
- longitude 指定地图中心点的经功
- scale 指定地图初始的缩放比例,取值范围 3 - 20
- markers 地图上的标记点
- polyline 地图上的路线
latitude、longitude、scale 相对容易理解,重点来看一下 markers 的使用:
repqir_pkg/pages/detail/index.js
Page({
data: {
markers: [
{
id: 1,
latitude: 40.22077,
longitude: 116.23128,
width: 24,
height: 30,
},
{
id: 2,
latitude: 40.225857999999995,
longitude: 116.23246699999999,
iconPath: '/static/images/marker.png',
width: 40,
height: 40,
},
],
}
})
在定义标记点时每个标记点必须要指定 ID 属性,否则会有错误产生,通过 iconPath 可以自定义标记点的图片,width/height 定义标记点的大小尺寸。
polyline
polyline 用来在在图上展示两点间的行进路线,需要传递给它路线对应的坐标点(很多个点组成的线),获取这些坐标点需要通过位置服务计算得到。
计算两点间路线的坐标点需要用到位置服务的[路线规划]https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodDirection方法
repqir_pkg/pages/detail/index.js
// repqir_pkg/pages/detail/index.js
Page({
data: {},
onLoad() {
// 生成路线
this.getPolyline()
},
// 调用位置服务(路线规划)
getPolyline() {
qqMap.direction({
mode: 'bicycling',
from: '40.227978,116.22998',
to: '40.22077,116.23128',
success: ({ result }) => {
const coors = result.routes[0].polyline
const points = []
//坐标解压(返回的点串坐标,通过前向差分进行压缩)
for (let i = 2; i < coors.length; i++) {
coors[i] = Number(coors[i - 2]) + Number(coors[i]) / 1000000
}
// 获取经纬度
for (let i = 0; i < coors.length; i += 2) {
points.push({ latitude: coors[i], longitude: coors[i + 1] })
}
// 渲染数据
this.setData({
latitude: points[30].latitude,
longitude: points[30].longitude,
polyline: [
{points, color: '#5591af', width: 4},
],
})
},
})
},
})
计算出来的坐标点是经过压缩的,需要按着官方指定的方式对数据进行解压才可以获取真正的坐标点,并且为了适应小程序地图组件的用法,还需要对数据进行二次的加工。
关于数据的处理大家只需要参考文档来实现就可以,可以说之所这么操作是腾讯位置服务规订好的,做为开发者按着官方提从的方法来应用即可。
自定义分享
[onShareAppMessage]https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object
监听用户点击页面内转发按钮(button 组件 open-type=“share”)或右上角菜单“转发”按钮的行为,并自定义转发内容。
注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮
Page({
onLoad({ id, encryptedData }) {
this.getPassport(id)
this.getPassportShare(encryptedData)
},
// 获取访客详情(通行证)
async getPassport(id) {
// 检测是否存在 id
if (!id) return
// 调用接口
const passport = await wx.$http.get('/visitor/' + id)
// 渲染数据
this.setData({ ...passport })
}
async getPassportShare(encryptedData) {
// 检测是否存在 id
if (!encryptedData) return
// 调用接口
const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
// 渲染数据
this.setData({ passport })
},
onShareAppMessage() {
return {
title: '查看通行证',
path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
}
},
})
保存到本地api介绍
[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html
保存图片到系统相册。
[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html
获取图片信息。网络图片需先配置 download 域名才能生效。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UyIG4FD7-1687267141879)(C:\Users\ZhengKaiYa\Desktop\个人笔记\3\21.png)]
保存到本地实现
1.给按钮绑定事件,调用getImageInfo获取图片临时路径
2.调用saveImageToPhotosAlbum传入临时路径完成保存功能
// 保存图片
async saveQRCode() {
try {
// 读取图片信息
const { path } = await wx.getImageInfo({
src: this.data.url,
})
// 保存图片到相册
wx.saveImageToPhotosAlbum({ filePath: path })
} catch (err) {
wx.$toast('保存图片失败,稍后重试!')
}
},
gram/dev/component/button.html) 组件 open-type=“share”)或右上角菜单“转发”按钮的行为,并自定义转发内容。
注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮
Page({
onLoad({ id, encryptedData }) {
this.getPassport(id)
this.getPassportShare(encryptedData)
},
// 获取访客详情(通行证)
async getPassport(id) {
// 检测是否存在 id
if (!id) return
// 调用接口
const passport = await wx.$http.get('/visitor/' + id)
// 渲染数据
this.setData({ ...passport })
}
async getPassportShare(encryptedData) {
// 检测是否存在 id
if (!encryptedData) return
// 调用接口
const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
// 渲染数据
this.setData({ passport })
},
onShareAppMessage() {
return {
title: '查看通行证',
path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
}
},
})
保存到本地api介绍
[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html
保存图片到系统相册。
[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html
获取图片信息。网络图片需先配置 download 域名才能生效。
保存到本地实现
1.给按钮绑定事件,调用getImageInfo获取图片临时路径
2.调用saveImageToPhotosAlbum传入临时路径完成保存功能
// 保存图片
async saveQRCode() {
try {
// 读取图片信息
const { path } = await wx.getImageInfo({
src: this.data.url,
})
// 保存图片到相册
wx.saveImageToPhotosAlbum({ filePath: path })
} catch (err) {
wx.$toast('保存图片失败,稍后重试!')
}
},