小程序的基本使用
小程序文件类型
小程序主要提供了 4 种文件类型:
类型名称 | 作用 | 是否必须存在 |
---|---|---|
.wxml | 用于页面的布局结构,相当于网页中 .html 文件 | 是 |
.wxss | 用于页面的样式,相当于网页中的 .css 文件 | 否 |
.js | 用于页面的逻辑 | 是 |
.json | 用于页面的配置 | 否 |
文件作用
文件名 | 作用 | 是否必须存在 |
---|---|---|
app.js | 小程序入口(首先执行的文件) | 是 |
app.json | 小程序的全局配置 | 是 |
app.wxss | 小程序的全局样式 | 否 |
project.config.json | 小程序开发者工具配置 | 是(会自动创建) |
sitemap.json | 小程序搜索优化 | 否 |
要点
data
初始化页面中的数据setData
更新数据{{}}
插值语法可以实现数据的渲染bind:事件类型=事件回调
Page({ //index.js
// 约定格式,使用data定义数据
data: {
message: 'nihao!'
},
changeMessage() {
// this.setData修改数据
this.setData({
message: 'new!'
})
}
})
index.wxml
<view>{{message}}</view>
<button bind:tap="changeMessage" type="primary" size="mini">点我试试</button>
如何注册小程序事件监听?
Page({
处理函数() {}
})
<button bind:事件名=“处理函数”>
配置文件
分类:
全局 app.json
页面 page.json
全局配置
app.json 是当前小程序的全局配置,包括了:
- 小程序首页
- 界面表现
- 网络超时时间
- 底部 tab
- …等配置
全局配置pages
用于指定小程序由哪些页面组成。
- 每一项都对应一个页面的 路径(含文件名) 信息。
- 小程序中的每一个页面都必须在pages下登记一下。
- 文件名不需要写文件后缀,框架会自动去寻找对应位置的 .json, .js, .wxml, .wxss 四个文件进行处理。
是一个数组,每一项表示一个页面
{
pages:[
"pages/index/index",
"pages/logs/logs"
]
}
数组中的第一个元素表示小程序启动时默认打开的页面-主页
页面跳转
<navigator url="/pages/logs/logs">跳转到log页面</navigator>
新建页面(自动优先)
新建页面-手动
共三步:
- 新建空目录
- 新建页面(4个文件)
- 在pages下手动补充页面地址
要点:
- 页面都放在pages下,一个页面一个文件夹。
- 新建page时,不需要写文件后缀名。
新建页面-自动
在pages下补充一项 pages/page1/page1
,然后保存,则会自动添加一个页面。
app.json
{
pages:[
"pages/index/index",
"pages/logs/logs",
"pages/page1/page1"
]
}
小程序配置-全局配置—window
用于设置小程序的状态栏、导航条、标题、窗口背景色。
https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#window
常见配置:
小程序配置-全局配置—tabBar
tabBar
tabBar 定义小程序 tab 栏的表现,如下图即所谓的 tab 栏:
常见配置属性
属性 | 类型 | 默认值 | 是否必须 | 说明 |
---|---|---|---|---|
list | array | 无 | 是 | tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab |
color | 16 进制颜色 | 无 | 否 | tab 上的文字默认颜色,仅支持十六进制颜色 |
selectedColor | 16 进制颜色 | 无 | 否 | tab 上的文字选中时的颜色,仅支持十六进制颜色 |
backgroundColor | 16 进制颜色 | 无 | 否 | tab 的背景色,仅只持 16 进制颜色 |
borderStyle | string | black | 否 | tabbar 上边框的颜色, 仅支持 black / white |
position | string | bottom | 否 | tabBar 的位置,仅支持 bottom / top |
上述配置中 list 具体又包含以下内容:
属性 | 类型 | 默认值 | 是否必须 | 说明 |
---|---|---|---|---|
pagePath | string | 是 | 页面路径,必须在 pages 中先定义 | |
text | string | 是 | tab 上按钮文字 | |
iconPath | string | 否 | 图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片,当 position 为 top 时,不显示 icon | |
selectedIconPath | string | 否 | 选中时的图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片,当 position 为 top 时,不显示 icon |
参考代码(app.josn)
{
......
"tabBar": {
"color": "#999",
"selectedColor": "#e93b3d",
"backgroundColor": "#fff",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/tabbar/home-default.png",
"selectedIconPath": "static/tabbar/home-active.png"
},
{
"pagePath": "pages/logs/logs",
"text": "日志",
"iconPath": "static/tabbar/video-default.png",
"selectedIconPath": "static/tabbar/video-active.png"
},
{
"pagePath": "pages/index/demo",
"text": "示例",
"iconPath": "static/tabbar/face-default.png",
"selectedIconPath": "static/tabbar/face-active.png"
}
]
}
}
小程序配置-页面配置
- 页面配置只针对某个页面生效
-
- 如 index.json 是针对 index 页面生效,demo.json 只针对页面 demo 生效
- 不用写window字段
- 优先级比全局配置高
常用配置:
属性 | 类型 | 默认值 | 是否必须 | 说明 |
---|---|---|---|---|
navigationBarTitleText | string | 空白 | 否 | 导航栏标题文字内容 |
navigationBarTextStyle | string | black | 否 | 导航栏标题颜色,仅支持 black / white |
navigationBarBackgroundColor | 16 进制颜色 | #00000 | 否 | 导航栏背景颜色,如 #000000 |
navigationStyle | string | default | 否 | 导航栏样式,仅支持 default / custom |
enablePullDownRefresh | boolean | false | 否 | 是否开启全局的下拉刷新 |
小程序适配-响应式单位rpx
rpx ,responsive pixel:
在小程序中的单位rpx,它的特点是能够自动地适配置不同尺寸的手机屏幕。
原理:不管手机屏幕具体多宽,100%的屏幕宽度就是750rpx
100%屏幕的宽度 = 750rpx
- 所有的设备宽度都是750rpx
- 在实际使用中只需要将设计稿调整为 750px 宽,然后 1:1 的比例来写长度(单位使用 rpx),如:设计稿中某个区域(盒子)的大小为 18090px ,写成小程序的尺寸为 18090rpx。
注:上述的规则仅适用于设计稿宽度为 750px
rpx (responsive pixel):规定不管屏幕为多少px,100%的屏幕宽度就是750rpx
100%屏幕的宽度 = 750rpx
内置组件—navigator(跳转)
navigator 是小程序中的导航标签,类似以前web中的a标签。通过 url 来指定跳转的页面
- url: 页面路径
-
- 支持相对和绝对路径
- 路径为空会报错
- 还可以跳到其他小程序
- hover-class:点击态的样式
-
- none 禁用点击效果
- open-type:跳转方式
-
- navigate。默认值
- switchTab。跳转到tabbar页
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
url | string | 当前小程序内的跳转链接 | |
open-type | string | navigate | 跳转方式 |
target | String | self | 在哪个目标上发生跳转,默认当前小程序 |
内置组件-image(图片)
格式
<image
src="图片资源地址"
mode="图片裁剪,缩放方式"></image>
image组件是一个有默认大小(320*240)的盒子。
- src: 图片资源地址。相对地址,绝对地址(外网地址)。
- mode: 默认值为scaleToFill,用来设置图片裁剪、缩放的模式。
-
- scaleToFill。不保证缩放比,图片拉伸填满容器
- aspectFit。保证缩放比,使图片的长边显示出来
- aspectFill。保证缩放比,使图片的短边显示出啦
- lazy-load:默认为false,是否开启懒加载模式。(3屏)
内置组件—swiper(轮播图)
swiper可以理解为小程序内置的轮播图标签,可以让方便快速地实现轮播功能。
<swiper>
<swiper-item> 第1屏的内容 </swiper-item>
<swiper-item> 第2屏的内容 </swiper-item>
<swiper-item> 第3屏的内容 </swiper-item>
<swiper-item> 第4屏的内容 </swiper-item>
</swiper>
- swiper:滑块容器。内只能写swiper-item。它的默认高度是150px;
- swiper-item:滑块单元。它的大小是: 宽度 和 高度 为 100% * 100%;
表单相关
- 输入框:input
-
- password密码类型,placeholder占位文字
- 单选框:radio-group和radio
-
- value指定表单数据,checked选中状态
- 复选框:checked-group和checkbox
-
- value指定表单数据,checked选中状态
- 选择框:picker
-
- mode:指定不同类型的选择框
scroll-view(滚动)
``scroll-view 在页面中指定一个**可以滚动**的区域,并且这个可滚动的区域能够实现一些高级的交互,比如:
下拉刷新`等。
scroll-view
中嵌套任意需要滚动的内容,要求内容必须有溢出(scroll-view有固定的尺寸),垂直滚动时 scroll-view
必须要指定高度。
属性
scroll-x
属性是否允许水平方面滚动scroll-y
属性是否允许垂直方向滚动refresher-enable
属性是否开启下拉刷新的交互
小程序样式-全局样式
app.wxss
定义全局样式,该文件中的样式会在所有的页面生效。
注:page
在每个页面中都有,它是由小程序自动添加上的,相当于网页中的 body
标签。
小程序样式-静态资源
小程序中 .wxss
文件中不支持使用本地路径的资源,比如背景图片是不允许使用本地图片路径的,必须使用网络路径(https:// 或 http:// 开头)或者转换成 base64 编码。
小程序样式-字体图标(iconfont)
小程序中字体图片的使用与网页中基本上是一致的,唯一的区别是小程序的 .wxss
文件中不支持使用本地字体文件,我们使用 iconfont 平台提供的服务生成字体文件后,直接使用其线上的字体文件地址。
资源参考:https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4061819
小程序基本使用-请求数据并渲染
小程序模板语法-数据绑定
在js中定义数据
Page({
data: {
isOpen: true,
message: 'hello world!'
}
})
小程序的data是一个对象,不同于vue的data是一个函数
在模块中获取使用数据
小程序中使用 {{}}
实现数据与模板的绑定
- 内容绑定:
<view>{{ 属性名 }}</view>
- 属性绑定:
<input value="{{属性名}}" />
{{}} 内写的是表达式
简易双向绑定
-
小程序中提供了
model:value="{{数据名}}"
语法来实现双向的数据绑定 -
目前只能用在
input
和textarea
组件中。只能是一个单一字段的绑定,不能嵌套对象.否则出现以下报错:
[pages/index/index] Two-way binding does not support complex data paths currently. This two-way binding is ignored.
双向绑定目前不支持复杂的数据路径。这种双向绑定将被忽略。
小程序修改数据
格式:
<元素 bind:事件名="处理函数">
this.setData({
属性名1: 新值1,
属性名2: 新值2
})
this.setData({
"属性1.属性2": 值
})
模板语法—条件渲染
小程序中的条件渲染的方式有两种
1.wx:if
-
- 在小程序中,使用wx:if="{{条件}}"来判断是否需要渲染该代码块
- 也可以用wx:elif 和 wx:else来添加else判断
2.hidden
-
- 在小程序中,使用hidden="{{条件}}"也能控制元素的显示与隐藏
- 条件为true则隐藏,否则则显示
wx:if 与 hidden区别
1.区别
-
- wx:if是通过动态创建或移除元素来控制元素是否可见
- hidden 是通过样式(none/block)来控制元素是否可见
2.要点
- 如果一个标签频繁切换显示,建议使用 hidden。例如:折叠面板,抽屉面板等等
- 如果不频繁切换,建议使用wx:if,它有更好的初始化性能。
模板语法—列表渲染—基础
格式
<元素 wx:for="{{列表数据}}" >
<!-- wx:for 结构内可以使用两个变量(1)item:循环项(2)index:循环索引 -->
{{item}}, {{index}}
</元素>
手动指定索引名和当前项的变量名
<view wx:for="{{list}}" wx:for-item="value" wx:for-index="key">
{{key}}-{{value}}
</view>
模板语法-列表渲染-wx:key
wx:key 针对不同的数组类型有不同的写法
- 普通数组 wx:key=“*this”
- 数组对象 wx:key=“具有唯一性的某个属性名”
小程序内置API-网络请求
网络请求
调用 wx.request
能够在小程序中发起网络请求与后端接口进行数据的交互,其语法格式如下:
wx.request({
url: '这里是接口的地址',
method: '这里是请求的方法',
data: '请求时提交的数据',
header: {/* 请求头信息 */},
success: () => {/* 成功的回调 */},
fail: () => {/* 失败的回调 */},
complete: () => {/* 成功或失败的回调 */}
})
配置网络请求合法域名
域名必须是https
小程序内置api-界面交互
showLoading效果
配合网络请求来使用
wx.showLoading
显示 loading 提示框
title
文字提示内容mask
是否显示透明蒙层,防止触摸穿透
hideLoading
wx.hideLoading
隐藏 loading 提示框
showToast
wx.showToast
消息提示框(轻提示)
title
提示的标题mask
是否显示透明蒙层,防止触摸穿透duration
延迟时间(提示框显示多久,单位是毫秒)icon
指定图标,none
不使用图标
操作注意:
- 发请求之前,showLoading
- 请求结束之后(无论成败),hideLoading
- 数据渲染成功之后,showToast
微信小程序本地存储
wx.setStorageSync(key, value)
存入一个数据,复杂类型数据不需要JSON.stringify
处理wx.getStorageSync(key)
读取本地key数据,复杂类型数据不需要JSON.parse
处理wx.removeStorageSync(key)
删除本地key数据wx.clearStorageSync()
清空本地全部数据
微信小程序API的特征
API的用法分类三类:
- 异步的api
- 同步的api
- 支持promise的api
异步 API
绝大部分的 API 都是异步方式,通过回调函数获取 API 执行的结果
success
API 调用成功时执行的回调fail
API 调用失败时执行的回调complete
API 调用结束时执行的回调(无论成功或失败)
基本格式:
wx.api名称({success(res){ console.log(成功执行api的结果) }})
支持promise的api
部分异步的 API 也支持以 Promise 方式返回结果,此时可以配合 asyc/await
来使用。
例如:支持Promise格式的异步api有:
- wx.getSystemInfoSync()
- wx.getStorage
支持Promise格式的异步api有:
- wx.request()
同步 API
部分 API 支持以同步方式获取结果,这些 API 的名称都以 **Sync**
结尾。如
wx.getStorageSync
: 获取本地存储- wx.getSystemInfoSync: 获取系统信息
基本格式:
const result = wx.api名称()
事件处理-事件对象&传参
事件对象
bind:事件类型=事件回调 //回调函数第1个参数即为事件对象
事件回调传参
小程序的事件回调不支持传参数
因此要将模板中的数据传递到事件回调中就必须要通过事件对象来实现。
方式1:
-
补充参数:
<button bind:tap="eventHandler" **mark:属性名="值"**>点击我看看</button>
-
获取值:
eventHandler(ev){ console.log(**ev.mark.属性名**)
方式2:
-
补充参数 :
<button bind:tap="eventHandler" **data-属性名="值"** >点击我看看</button>
-
获取值:
eventHandler(ev){ console.log(**ev.target.dataset.属性名**) }
事件处理-组件事件
事件类型只属于某个组件,我们将其称为组件事件
前面介绍的 tap
事件可以在绝大部分组件是监听,我们可以将其理解为通用事件类型,然而也有事件类型只属于某个组件,我们将其称为组件事件。
组件不同,支持的事件也不同
scroll-view组件中的事件
bind:scrolltolower
当滚动内容到达底部或最右侧时触发bind:refresherrefresh
执行下拉操作时触发
另外,还有注意一个特别的属性
refresher-triggered
用它来控制下拉刷新状态
事件处理-表单组件中的事件
如何获取表单中,用户选择的值?
- input: 简易双向绑定
- radioGroup: 绑定change事件,在事件对象中detail.value拿到值
- checkboxGroup: 绑定change事件,在事件对象中detail.value拿到值
- picker: 绑定change事件,在事件对象中detail.value拿到值
change
表单数据发生改变时触发(input 不支持)
5.整体表单提交
- form:
submit事件
表单提交时触发,button
按钮必须指定form-type
属性
生命周期-页面生命周期
- 分类
-
- 应用生命周期
- 页面生命周期
- 组件生命周期
生命周期是一些名称固定,会自动执行的函数。
页面生命周期-基本使用
-
onLoad
在页面加载完成时执行,只会执行 1 次,常用于获取地址参数和网络请求 -
onReady页面初次渲染完成
-
onShow
在页面处于可见状态时执行,常用于动态更新数据或状态 -
onHide
在页面处于不见状态时执行,常用于销毁长时间运行的任务,如定时器页面生命周期-应用场景
onLoad(){ // 发起请求 }
onShow(){ // 动态更新数据或状态 }
onHide
在页面处于不见状态时执行,常用于销毁长时间运行的任务,如定时器
onReady
在页面初次渲染完成时执行,只会执行 1 次,常用于节点操作或动画交互等场景
生命周期-应用生命周期
app.js
onLaunch
小程序启动时执行1次,常用于获取场景值或者启动时的一些参数(如自定义分享)onShow
小程序前台运行时执行,常用于更新数据或状态onHide
小程序后台运行时执地,常用于销毁长时间运行的任务,如定时器。
// pages/lifetimes/index.js
Page({
// 小程序转发/分享
onShareAppMessage() {
return {
title: '小程序学习',
path: '/pages/index/index?test=测试数据',
imageUrl: '/static/images/cover.png'
}
}
})
小程序基础-分包加载&&自定义组件&&&项目全流程
小程序分包加载
小程序分包加载-为什么要分包加载
- 微信平台对小程序单个包的代码体积限制为 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 域名才能生效。
保存到本地实现
1.给按钮绑定事件,调用getImageInfo获取图片临时路径
2.调用saveImageToPhotosAlbum传入临时路径完成保存功能
// 保存图片
async saveQRCode() {
try {
// 读取图片信息
const { path } = await wx.getImageInfo({
src: this.data.url,
})
// 保存图片到相册
wx.saveImageToPhotosAlbum({ filePath: path })
} catch (err) {
wx.$toast('保存图片失败,稍后重试!')
}
},
ush({ 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](https://developers.weixin.qq.com/miniprogram/dev/component/button.html) 组件 open-type="share")或右上角菜单“转发”按钮的行为,并自定义转发内容。
**注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮**
```js
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-M8SkNwoO-1687267859406)]
保存到本地实现
1.给按钮绑定事件,调用getImageInfo获取图片临时路径
2.调用saveImageToPhotosAlbum传入临时路径完成保存功能
// 保存图片
async saveQRCode() {
try {
// 读取图片信息
const { path } = await wx.getImageInfo({
src: this.data.url,
})
// 保存图片到相册
wx.saveImageToPhotosAlbum({ filePath: path })
} catch (err) {
wx.$toast('保存图片失败,稍后重试!')
}
},