微信小程序开发
- 自学方向
- 小程序基础知识
- 小程序项目
自学方向
自学视频:https://www.bilibili.com/video/BV1WJ41197sD
官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/config.html#全局配置
小程序基础知识
一、小程序的结构目录
1、小程序文件结构与传统web对比
2、基本项目目录
3、全局配置 app,json
3.1、pages 添加页面文件
3.2、window
3.3、tabBar
4、页面配置 pages.json
二、模板语法
1、text
相当于 web 中的 span 标签。行内元素,不会换行
2、view
相当于 web 中的 div 标签,块级标签,会换行
3、checkbox 复选框
4、数据绑定
WXML
<!--
1 text 相当于以前web中的 span标签 行内元素 不会换行
2 view 相当于以前web中的 div标签 块级元素 会换行
3 checkbox 以前的复选框标签
-->
<!-- <text>1</text>
<text>2</text>
<view>1</view>
<view>2</view> -->
<!-- 1 字符串 -->
<view> {{msg}} </view>
<!-- 2 数字类型 -->
<view>{{num}}</view>
<!-- 3 bool类型 -->
<view> 是否是伪娘: {{isGirl}} </view>
<!-- 4 object类型 -->
<view>{{person.age}}</view>
<view>{{person.height}}</view>
<view>{{person.weight}}</view>
<view>{{person.name}}</view>
<!-- 5 在标签的属性中使用 -->
<view data-num="{{num}}"> 自定义属性</view>
<!--
6 使用bool类型充当属性 checked
1 字符串和 花括号之间一定不要存在空格 否则会导致识别失败
以下写法就是错误的示范
<checkbox checked=" {{isChecked}}"> </checkbox>
-->
<view>
<checkbox checked="{{isChecked}}"> </checkbox>
</view>
JS
Page({
data: {
msg: "hello mina",
num: 10000,
isGirl: false,
person: {
age: 74,
height: 145,
weight: 200,
name: "富婆"
}
}
});
注意:字符串与花括号之间不能存在空格
例如:
<checkbox checked=" {{ischecked}}"></checkbox>
5、模板表达式
WXML
<!--
7 运算 => 表达式
1 可以在花括号中 加入 表达式 -- “语句”
2 表达式
指的是一些简单 运算 数字运算 字符串 拼接 逻辑运算。。
1 数字的加减。。
2 字符串拼接
3 三元表达式
3 语句
1 复杂的代码段
1 if else
2 switch
3 do while 。。。。
4 for 。。。
-->
<view>{{1+1}}</view>
<view>{{'1'+'1'}}</view>
<view>{{ 11%2===0 ? '偶数' : '奇数' }}</view>
三、列表渲染
1、列表循环
1.1、遍历数组
JS
Page({
data: {
msg: "hello mina",
num: 10000,
isGirl: false,
person: {
age: 74,
height: 145,
weight: 200,
name: "富婆"
},
isChecked:false,
list:[
{
id:0,
name:"猪八戒"
},
{
id:1,
name:"天蓬元帅"
},
{
id:2,
name:"悟能"
}
]
}
});
WXML
<!--
8 列表循环
1 wx:for="{{数组或者对象}}" wx:for-item="循环项的名称" wx:for-index="循环项的索引"
2 wx:key="唯一的值" 用来提高列表渲染的性能
1 wx:key 绑定一个普通的字符串的时候 那么这个字符串名称 肯定是 循环数组 中的 对象的 唯一属性
2 wx:key ="*this" 就表示 你的数组 是一个普通的数组 *this 表示是 循环项
[1,2,3,44,5]
["1","222","adfdf"]
3 当出现 数组的嵌套循环的时候 尤其要注意 以下绑定的名称 不要重名
wx:for-item="item" wx:for-index="index"
4 默认情况下 我们 不写
wx:for-item="item" wx:for-index="index"
小程序也会把 循环项的名称 和 索引的名称 item 和 index
只有一层循环的话 (wx:for-item="item" wx:for-index="index") 可以省略
-->
<view>
<view
wx:for="{{list}}"
wx:for-item="item"
wx:for-index="index"
wx:key="id"
>
索引:{{index}}
--
值:{{item.name}}
</view>
</view>
1.2、遍历对象
WXML
<!--
9 对象循环
1 wx:for="{{对象}}" wx:for-item="对象的值" wx:for-index="对象的属性"
2 循环对象的时候 最好把 item和index的名称都修改一下
wx:for-item="value" wx:for-index="key"
-->
<view>
<view>对象循环</view>
<view
wx:for="{{person}}"
wx:for-item="value"
wx:for-index="key"
wx:key="age"
>
属性:{{key}}
--
值:{{value}}
</view>
</view>
2、block标签
WXML
<!--
10 block
1 占位符的标签
2 写代码的时候 可以看到这标签存在
3 页面渲染 小程序会把它移除掉
-->
<view>
<block
wx:for="{{list}}"
wx:for-item="item"
wx:for-index="index"
wx:key="id"
class="my_list"
>
索引:{{index}}
--
值:{{item.name}}
</block>
</view>
四、条件渲染
1、wx:if
WXML
<!--
11 条件渲染
1 wx:if="{{true/false}}"
1 if , else , if else
wx:if
wx:elif
wx:else
2 hidden
1 在标签上直接加入属性 hidden
2 hidden="{{true}}"
3 什么场景下用哪个
1 当标签不是频繁的切换显示 优先使用 wx:if
直接把标签从 页面结构给移除掉
2 当标签频繁的切换显示的时候 优先使用 hidden
通过添加样式的方式来切换显示
hidden 属性 不要和 样式 display一起使用
-->
<view>
<view>条件渲染</view>
<view wx:if="{{true}}">显示</view>
<view wx:if="{{false}}">隐藏</view>
<view wx:if="{{flase}}">1</view>
<view wx:elif="{{flase}}">2 </view>
<view wx:else> 3 </view>
<view>---------------</view>
<view hidden >hidden1</view>
<view hidden="{{false}}" >hidden2</view>
<view>-----000-------</view>
<view wx:if="{{false}}">wx:if</view>
<view hidden style="display: flex;" >hidden</view>
</view>
2、hidden
五、事件绑定
1、bindinput 监听文本框发生变化
1.1、实现双向绑定功能
WXML
<!--
1 需要给input标签绑定 input事件
绑定关键字 bindinput
2 如何获取 输入框的值
通过事件源对象来获取
e.detail.value
3 把输入框的值 赋值到 data当中
不能直接
1 this.data.num=e.detail.value
2 this.num=e.detail.value
正确的写法
this.setData({
num:e.detail.value
})
-->
<input type="text" bindinput="handleInput" />
<view>
{{num}}
</view>
JS
Page({
data: {
num: 0
},
// 输入框的input事件的执行逻辑
handleInput(e) {
// console.log(e.detail.value );
this.setData({
num: e.detail.value
})
}
})
2、bindtap 点击事件
注意:无法在小程序事件中传参,需要使用自定义属性
WXML
<!--
4 需要加入一个点击事件
1 bindtap
2 无法在小程序当中的 事件中 直接传参
3 通过自定义属性的方式来传递参数
4 事件源中获取 自定义属性
-->
<button bindtap="handletap" data-operation="{{1}}" >+</button>
<button bindtap="handletap" data-operation="{{-1}}">-</button>
<view>
{{num}}
</view>
JS
// 加 减 按钮的事件
handletap(e) {
// console.log(e);
// 1 获取自定义属性 operation
const operation = e.currentTarget.dataset.operation;
this.setData({
num: this.data.num + operation
})
}
六、样式 WXSS
小程序不需要主动引入样式文件(link)
1、尺寸单位
1.1、rpx
2、样式导入
3、选择器
微信小程序 WXSS 不支持通配符 " * "
4、less
七、常见组件(标签)
1、view
2、text
3、image
WXML
<!--
image 图片标签
1 src 指定要加载的图片的路径
图片存在默认的宽度和高度 320 * 240 原图大小是 200 * 100
2 mode 决定 图片内容 如何 和 图片标签 宽高 做适配
1 scaleToFill 默认值 不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素
2 aspectFit 保持宽高比 确保图片的长边 显示出来 页面轮播图 常用
3 aspectFill 保持纵横比缩放图片,只保证图片的 短 边能完全显示出来。 少用
4 widthFix 以前web的图片的 宽度指定了之后 高度 会自己按比例来调整 常用
5 bottom。。 类似以前的backgroud-position
3 小程序当中的图片 直接就支持 懒加载 lazy-load
1 lazy-load 会自己判断 当 图片 出现在 视口 上下 三屏的高度 之内的时候 自己开始加载图片
-->
<image mode="bottom" lazy-load src="https://tva2.sinaimg.cn/large/007DFXDhgy1g51jlzfb4lj305k02s0sp.jpg" />
八、swiper 轮播图
1、swiper标签与swiper-item
2、swiper属性
WXML
<!--
1 轮播图外层容器 swiper
2 每一个轮播项 swiper-item
3 swiper标签 存在默认样式
1 width 100%
2 height 150px image 存在默认宽度和高度 320 * 240
3 swiper 高度 无法实现由内容撑开
4 先找出来 原图的宽度和高度 等比例 给swiper 定 宽度和高度
原图的宽度和高度 1125 * 352 px
swiper 宽度 / swiper 高度 = 原图的宽度 / 原图的高度
swiper 高度 = swiper 宽度 * 原图的高度 / 原图的宽度
height: 100vw * 352 / 1125
5 autoplay 自动轮播
6 interval 修改轮播时间
7 circular 衔接轮播
8 indicator-dots 显示 指示器 分页器 索引器
9 indicator-color 指示器的未选择的颜色
10 indicator-active-color 选中的时候的指示器的颜色
-->
<swiper autoplay interval="1000" circular indicator-dots indicator-color="#0094ff" indicator-active-color="#ff0094">
<swiper-item> <image mode="widthFix" src="//gw.alicdn.com/imgextra/i1/44/O1CN013zKZP11CCByG5bAeF_!!44-0-lubanu.jpg" /> </swiper-item>
<swiper-item> <image mode="widthFix" src="//aecpm.alicdn.com/simba/img/TB1CWf9KpXXXXbuXpXXSutbFXXX.jpg_q50.jpg" /> </swiper-item>
<swiper-item> <image mode="widthFix" src="//gw.alicdn.com/imgextra/i2/37/O1CN01syHZxs1C8zCFJj97b_!!37-0-lubanu.jpg" /> </swiper-item>
</swiper>
WXSS
swiper {
width: 100%;
/* height: calc(100vw * 352 / 1125); */
height: 31.28vw;
}
image {
width: 100%;
}
九、navigator导航标签
类似a标签 超链接标签
WXML
<!--
导航组件 navigator
0 块级元素 默认会换行 可以直接加宽度和高度
1 url 要跳转的页面路径 绝对路径 相对路径
2 target 要跳转到当前的小程序 还是其他的小程序的页面
self 默认值 自己 小程序的页面
miniProgram 其他的小程序的页面
3 open-type 跳转的方式
1 navigate 默认值 保留当前页面,跳转到应用内的某个页面,但是不能跳到 tabbar 页面
2 redirect 关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到 tabbar 页面。
3 switchTab 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
4 reLaunch 关闭所有页面,打开到应用内的某个页面
-->
<navigator url="/pages/demo10/demo10"> 轮播图页面 </navigator>
<navigator url="/pages/index/index"> 直接跳转到 tabbar页面 </navigator>
<navigator open-type="redirect" url="/pages/demo10/demo10"> 轮播图页面 redirect </navigator>
<navigator open-type="switchTab" url="/pages/index/index"> switchTab直接跳转到 tabbar页面 </navigator>
<navigator open-type="reLaunch" url="/pages/index/index"> reLaunch 可以随便跳 </navigator>
十、rich-text 富文本标签
十二、buttom
1、属性
<!--
button 标签
1 外观的属性
1 size 控制按钮的大小
1 default 默认大小
2 mini 小尺寸
2 type 用来控制按钮的颜色
1 default 灰色
2 primary 绿色
3 warn 红色
3 plain 按钮是否镂空,背景色透明
4 loading 文字前显示正在等待图标
-->
<button>默认按钮</button>
<button size="mini"> mini 默认按钮</button>
<button type="primary"> primary 按钮</button>
<button type="warn"> warn 按钮</button>
<button type="warn" plain> plain 按钮</button>
<button type="primary" loading> loading 按钮</button>
2、buttom 开放能力
<!--
button 开发能力
open-type:
1 contact 直接打开 客服对话功能 需要在微信小程序的后台配置 只能够通过真机调试来打开
2 share 转发当前的小程序 到微信朋友中 不能把小程序 分享到 朋友圈
3 getPhoneNumber 获取当前用户的手机号码信息 结合一个事件来使用 不是企业的小程序账号 没有权限来获取用户的手机号码
1 绑定一个事件 bindgetphonenumber
2 在事件的回调函数中 通过参数来获取信息
3 获取到的信息 已经加密过了
需要用户自己待见小程序的后台服务器,在后台服务器中进行解析 手机号码,返回到小程序中 就可以看到信息了
4 getUserInfo 获取当前用户的个人信息
1 使用方法 类似 获取用户的手机号码
2 可以直接获取 不存在加密的字段
5 launchApp 在小程序当中 直接打开 app
1 需要现在 app中 通过app的某个链接 打开 小程序
2 在小程序 中 再通过 这个功能 重新打开 app
3 找到 京东的app 和 京东的小程序
6 openSetting 打开小程序内置的 授权页面
1 授权页面中 只会出现 用户曾经点击过的 权限
7 feedback 打开 小程序内置的 意见反馈页面
1 只能够通过真机调试来打开
-->
<button open-type="contact">contact</button>
<button open-type="share">share</button>
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">getPhoneNumber</button>
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo">getUserInfo</button>
<button open-type="launchApp">launchApp</button>
<button open-type="openSetting">openSetting</button>
<button open-type="feedback">feedback</button>
十三、icon
<!--
小程序中的字体图标
1 type 图标的类型
success|success_no_circle|info|warn|waiting|cancel|download|search|clear
2 size 大小
3 color 图标的颜色
-->
<icon type="cancel" size="60" color="#0094ff"> </icon>
十四、radio
WXML
<!--
radio 单选框
1 radio标签 必须要和 父元素 radio-group来使用
2 value 选中的单选框的值
3 需要给 radio-group 绑定 change事件
4 需要在页面中显示 选中的值
-->
<radio-group bindchange="handleChange">
<radio color="red" value="male">男</radio>
<radio color="red" value="female" >女</radio>
</radio-group>
<view>您选中的是:{{gender}}</view>
JS
Page({
data: {
gender: ""
},
handleChange(e){
// 1 获取单选框中的值
let gender=e.detail.value;
// 2 把值 赋值给 data中的数据
this.setData({
// gender:gender
gender
})
}
})
十五、checkbox
WXML
<view>
<checkbox-group bindchange="handleItemChange">
<checkbox value="{{item.value}}" wx:for="{{list}}" wx:key="id">
{{item.name}}
</checkbox>
</checkbox-group>
<view>
选中的水果:{{checkedList}}
</view>
</view>
JS
Page({
data: {
list:[
{
id:0,
name:"🍎",
value:"apple"
},
{
id:1,
name:"🍇",
value:"grape"
},
{
id:2,
name:"🍌",
value:"bananer"
}
],
checkedList:[]
},
// 复选框的选中事件
handleItemChange(e){
// 1 获取被选中的复选框的值
const checkedList=e.detail.value;
// 2 进行赋值
this.setData({
checkedList
})
}
})
十六、自定义组件
1、创建目录
2、声明组件
到需要引用该组件的页面的json文件中声明
{
"usingComponents": {
"Tabs":"../../components/Tabs/Tabs"
}
}
在页面wxml中直接使用
<Tabs></Tabs>
3、导航栏组件案例
4、父向子传递数据
5、子向父传递数据
子组件
wxml
<view class="tabs_title">
<view
wx:for="{{tabs}}"
wx:key="id"
class="title_item {{item.isActive?'active':''}} "
bindtap="handleItemTap"
data-index="{{index}}"
>
{{item.value}}
</view>
js
methods: {
hanldeItemTap(e){
// 2 获取索引
const {index}=e.currentTarget.dataset;
// 5 触发父组件中的自定义事件 同时传递数据给
this.triggerEvent("itemChange",{index});
}
}
父组件
WXML
<!--
1 父组件(页面) 向子组件 传递数据 通过 标签属性的方式来传递
1 在子组件上进行接收
2 把这个数据当成是data中的数据直接用即可
2 子向父传递数据 通过事件的方式传递
1 在子组件的标签上加入一个 自定义事件
-->
<Tabs tabs="{{tabs}}" binditemChange="handleItemChange" >
</Tabs>
JS
// 自定义事件 用来接收子组件传递的数据的
handleItemChange(e) {
// 接收传递过来的参数
const { index } = e.detail;
let { tabs } = this.data;
tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
this.setData({
tabs
})
}
6、slot 插槽
TAbs 组件 WXML
<view class="tabs_content">
<!--
slot 标签 其实就是一个占位符 插槽
等到 父组件调用 子组件的时候 再传递 标签过来 最终 这些被传递的标签
就会替换 slot 插槽的位置
-->
<slot></slot>
</view>
页面 WXML
<Tabs tabs="{{tabs}}" binditemChange="handleItemChange" >
<block wx:if="{{tabs[0].isActive}}">0 </block>
<block wx:elif="{{tabs[1].isActive}}">1 </block>
<block wx:elif="{{tabs[2].isActive}}">2 </block>
<block wx:else>3</block>
</Tabs>
7、其他属性
十七、小程序的生命周期
1、应用生命周期
App({
// 1 应用第一次启动的就会触发的事件
onLaunch() {
// 在应用第一次启动的时候 获取用户的个人信息
// console.log("onLaunch");
// aabbcc
// js的方式来跳转 不能触发 onPageNotFound
// wx.navigateTo({
// url: '/11/22/33'
// });
},
// 2 应用 被用户看到
onShow(){
// 对应用的数据或者页面效果 重置
// console.log("onShow");
},
// 3 应用 被隐藏了
onHide(){
// 暂停或者清除定时器
// console.log("Hide");
},
// 4 应用的代码发生了报错的时候 就会触发
onError(err){
// 在应用发生代码报错的时候,收集用户的错误信息,通过异步请求 将错误的信息发送后台去
// console.log("onError");
// console.log(err);
},
// 5 页面找不到就会触发
// 应用第一次启动的时候,如果找不到第一个入口页面 才会触发
onPageNotFound(){
// 如果页面不存在了 通过js的方式来重新跳转页面 重新跳到第二个首页
// 不能跳到tabbar页面 导航组件类似
wx.navigateTo({
url: '/pages/demo09/demo09'
});
// console.log("onPageNotFound");
}
})
2、页面生命周期
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
console.log("onLoad");
// onLoad发送异步请求来初始化页面数据
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
console.log("onShow");
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
console.log("onReady");
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
console.log("onHide");
},
/**
* 生命周期函数--监听页面卸载 也是可以通过点击超链接来演示
*
*/
onUnload: function () {
console.log("onUnload");
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
console.log("onPullDownRefresh");
// 页面的数据 或者效果 重新 刷新
},
/**
* 页面上拉触底事件的处理函数
* 需要让页面 出现上下滚动才行
*/
onReachBottom: function () {
console.log("onReachBottom");
// 上拉加载下一页数据
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
console.log("onShareAppMessage");
},
/**
* 页面滚动 就可以触发
*/
onPageScroll(){
console.log("onPageScroll");
},
/**
* 页面的尺寸发生改变的时候 触发
* 小程序 发生了 横屏竖屏 切换的时候触发
*/
onResize(){
console.log("onResize");
},
/**
* 1 必须要求当前页面 也是tabbar页面
* 2 点击的自己的tab item的时候才触发
*/
onTabItemTap(){
console.log("onTabItemTap");
}
小程序项目
一、完整项目
github
https://github.com/ScissorSeven/wechart-Shopping-Mall/tree/master
二、网络请求
1、文档
2、示例代码
onLoad: function(options) {
wx.request({
url: 'test.php', //仅为示例,并非真实的接口地址
data: {
x: '',
y: ''
},
header: {
'content-type': 'application/json' // 默认值
},
success (res) {
console.log(res.data)
}
})
}
三、轮播图图片
wxml
<!-- 轮播图 开始 -->
<view class="index_swiper">
<swiper autoplay indicator-dots circular>
<swiper-item
wx:for="{{swiperList}}"
wx:key="goods_id">
<image mode="widthFix" src="{{item.image_src}}"></image>
</swiper-item>
</swiper>
</view>
<!-- 轮播图 结束 -->
wx.request({
url: 'http://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata',
data: {},
header: {'content-type':'application/json'},
method: 'GET',
dataType: 'json',
responseType: 'text',
success: (result) => {
this.setData({
swiperList: result.data.message
})
},
fail: () => {},
complete: () => {}
});
四、Promise进行网络请求封装
request/index.js
export const request=(params)=>{
// 定义公共的url
const baseUrl="http://api-hmugo-web.itheima.net/api/public/v1";
return new Promise((resolve,reject)=>{
wx.request({
...params,
url:baseUrl+params.url,
success:(result)=>{
resolve(result.data.message);
},
fail:(err)=>{
reject(err);
}
});
})
}
页面.js 导入request
import { request } from "../../request/index.js";
Page({
data: {
// 轮播图数组
swiperList: [],
},
// 页面开始加载 就会触发
onLoad: function (options) {
// 1 发送异步请求获取轮播图数据 优化的手段可以通过es6的 promise来解决这个问题
this.getSwiperList();
},
// 获取轮播图数据
getSwiperList(){
request({ url: "/home/swiperdata" })
.then(result => {
this.setData({
swiperList: result
})
})
},
五、滚动视图 scroll-view
1、文档
https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html
wxml
<!-- 左侧菜单 -->
<scroll-view scroll-y class="left_menu">
<view
class="menu_item {{index===currentIndex?'active':''}}"
wx:for="{{leftMenuList}}"
wx:key="*this"
bindtap="handleItemTap"
data-index="{{index}}"
>
{{item}}
</view>
</scroll-view>
<!-- 右侧商品内容 -->
<scroll-view scroll-top="{{scrollTop}}" scroll-y class="right_content">
<view class="goods_group"
wx:for="{{rightContent}}"
wx:for-index="index1"
wx:for-item="item1"
>
<view class="goods_title">
<text class="delimiter">/</text>
<text class="title">{{item1.cat_name}}</text>
<text class="delimiter">/</text>
</view>
<view class="goods_list">
<navigator
wx:for="{{item1.children}}"
wx:for-index="index2"
wx:for-item="item2"
wx:key="cat_id"
url="/pages/goods_list/index?cid={{item2.cat_id}}"
>
<image mode="widthFix" src="{{item2.cat_icon}}"></image>
<view class="goods_name">{{item2.cat_name}}</view>
</navigator>
</view>
</view>
</scroll-view>
less
page{
height: 100%;
}
.cates{
height: 100%;
.cates_container{
/* less中使用calc的时候要注意 */
height: ~'calc( 100vh - 90rpx )';
display: flex;
.left_menu{
/* 子项 高度 100% flex */
flex: 2;
.menu_item{
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
}
.active{
color: var(--themeColor);
border-left: 5rpx solid currentColor;
}
}
.right_content{
/* 子项 高度 100% flex */
flex: 5;
.goods_group{
.goods_title{
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
.delimiter{
color: #ccc;
padding: 0 10rpx;
}
.title{}
}
.goods_list{
display: flex;
flex-wrap: wrap;
navigator{
width: 33.33%;
text-align: center;
image{
width: 50%;
}
.goods_name{}
}
}
}
}
}
}
六、缓存
网络请求资源过大时,可以使用本地缓存
0 web中的本地存储和 小程序中的本地存储的区别
1 写代码的方式不一样了
web: localStorage.setItem(“key”,“value”) localStorage.getItem(“key”)
小程序中: wx.setStorageSync(“key”, “value”); wx.getStorageSync(“key”);
2:存的时候 有没有做类型转换
web: 不管存入的是什么类型的数据,最终都会先调用以下 toString(),把数据变成了字符串 再存入进去
小程序: 不存在 类型转换的这个操作 存什么类似的数据进去,获取的时候就是什么类型
1 先判断一下本地存储中有没有旧的数据
{time:Date.now(),data:[…]}
2 没有旧数据 直接发送新请求
3 有旧的数据 同时 旧的数据也没有过期 就使用 本地存储中的旧数据即可
// 1 获取本地存储中的数据 (小程序中也是存在本地存储 技术)
const Cates = wx.getStorageSync("cates");
// 2 判断
if (!Cates) {
// 不存在 发送请求获取数据
this.getCates();
} else {
// 有旧的数据 定义过期时间 10s 改成 5分钟
if (Date.now() - Cates.time > 1000 * 10) {
// 重新发送请求
this.getCates();
} else {
// 可以使用旧的数据
this.Cates = Cates.data;
let leftMenuList = this.Cates.map(v => v.cat_name);
let rightContent = this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
}
}
七、ES7的async方法
1、使用步骤
es7的async号称是解决回调的最终方案
1、在小程序的开发工具,勾选es6转es5语法
2、下载facebook的regenerator库中的regenerator/packages/regenerator-runtime/runtime.js
3、在小程序目录下新建文件夹 lib/runtime/runtime.js ,将代码拷贝进去
4、在每一个需要使用async语法的页面js文件,都引入(不能全局引入)
`import regeneratorRuntime from '../../lib/runtime/runtime';
2、数据请求、async的js代码
JS
import { request } from "../../request/index.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
Page({
data: {
// 左侧的菜单数据
leftMenuList: [],
// 右侧的商品数据
rightContent: [],
// 被点击的左侧的菜单
currentIndex: 0,
// 右侧内容的滚动条距离顶部的距离
scrollTop: 0
},
// 接口的返回数据
Cates: [],
onLoad: function (options) {
/*
0 web中的本地存储和 小程序中的本地存储的区别
1 写代码的方式不一样了
web: localStorage.setItem("key","value") localStorage.getItem("key")
小程序中: wx.setStorageSync("key", "value"); wx.getStorageSync("key");
2:存的时候 有没有做类型转换
web: 不管存入的是什么类型的数据,最终都会先调用以下 toString(),把数据变成了字符串 再存入进去
小程序: 不存在 类型转换的这个操作 存什么类似的数据进去,获取的时候就是什么类型
1 先判断一下本地存储中有没有旧的数据
{time:Date.now(),data:[...]}
2 没有旧数据 直接发送新请求
3 有旧的数据 同时 旧的数据也没有过期 就使用 本地存储中的旧数据即可
*/
// 1 获取本地存储中的数据 (小程序中也是存在本地存储 技术)
const Cates = wx.getStorageSync("cates");
// 2 判断
if (!Cates) {
// 不存在 发送请求获取数据
this.getCates();
} else {
// 有旧的数据 定义过期时间 10s 改成 5分钟
if (Date.now() - Cates.time > 1000 * 10) {
// 重新发送请求
this.getCates();
} else {
// 可以使用旧的数据
this.Cates = Cates.data;
let leftMenuList = this.Cates.map(v => v.cat_name);
let rightContent = this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
}
}
},
// 获取分类数据
async getCates() {
// request({
// url: "/categories"
// })
// .then(res => {
// this.Cates = res.data.message;
// // 把接口的数据存入到本地存储中
// wx.setStorageSync("cates", { time: Date.now(), data: this.Cates });
// // 构造左侧的大菜单数据
// let leftMenuList = this.Cates.map(v => v.cat_name);
// // 构造右侧的商品数据
// let rightContent = this.Cates[0].children;
// this.setData({
// leftMenuList,
// rightContent
// })
// })
// 1 使用es7的async await来发送请求
const res = await request({ url: "/categories" });
// this.Cates = res.data.message;
this.Cates = res;
// 把接口的数据存入到本地存储中
wx.setStorageSync("cates", { time: Date.now(), data: this.Cates });
// 构造左侧的大菜单数据
let leftMenuList = this.Cates.map(v => v.cat_name);
// 构造右侧的商品数据
let rightContent = this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
},
// 左侧菜单的点击事件
handleItemTap(e) {
/*
1 获取被点击的标题身上的索引
2 给data中的currentIndex赋值就可以了
3 根据不同的索引来渲染右侧的商品内容
*/
const { index } = e.currentTarget.dataset;
let rightContent = this.Cates[index].children;
this.setData({
currentIndex: index,
rightContent,
// 重新设置 右侧内容的scroll-view标签的距离顶部的距离
scrollTop: 0
})
}
})
八、组件数据的父传子与子传父
1、TAbs
1.1、properties 接收父组件传递过来的数据
1.2、this.triggerEvent("",value);自定义方法传递数据给父组件
Tabs.wxml
<view class="tabs">
<view class="tabs_title">
<view
wx:for="{{tabs}}"
wx:key="id"
class="title_item {{item.isActive?'active':''}} "
bindtap="handleItemTap"
data-index="{{index}}"
>
{{item.value}}
</view>
</view>
<view class="tabs_content">
<slot></slot>
</view>
</view>
Tabs.js
Component({
/**
* 组件的属性列表
*/
properties: {
tabs:{
type:Array,
value:[]
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
// 点击事件
handleItemTap(e){
// 1 获取点击的索引
const {index}=e.currentTarget.dataset;
// 2 触发 父组件中的事件 自定义
this.triggerEvent("tabsItemChange",{index});
}
}
})
2、index页面
2.1、标签自定义属性给子组件传递数据
2.2、标签绑定子组件的方法 来接收子组件的数据
<Searchinput>
</Searchinput>
<!-- 监听自定义事件 -->
<Tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange" >
<block wx:if="{{tabs[0].isActive}}">0</block>
<block wx:elif="{{tabs[1].isActive}}">1</block>
<block wx:elif="{{tabs[2].isActive}}">2</block>
</Tabs>
// 标题点击事件 从子组件传递过来
handleTabsItemChange(e){
// 1 获取被点击的标题索引
const {index}=e.detail;
// 2 修改源数组
let {tabs}=this.data;
tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
// 3 赋值到data中
this.setData({
tabs
})
},
九、下拉触底事件
onReachBottom()
1、官方文档
https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onReachBottom
2、代码
index.wxml
<SearchInput></SearchInput>
<!-- 监听自定义事件 -->
<Tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange" >
<block wx:if="{{tabs[0].isActive}}">
<view class="first_tab">
<navigator class="goods_item"
wx:for="{{goodsList}}"
wx:key="goods_id"
url="/pages/goods_detail/index?goods_id={{item.goods_id}}"
>
<!-- 左侧 图片容器 -->
<view class="goods_img_wrap">
<image mode="widthFix" src="{{item.goods_small_logo?item.goods_small_logo:'https://ww1.sinaimg.cn/large/007rAy9hgy1g24by9t530j30i20i2glm.jpg'}}"></image>
</view>
<!-- 右侧 商品容器 -->
<view class="goods_info_wrap">
<view class="goods_name">{{item.goods_name}}</view>
<view class="goods_price">¥{{item.goods_price}}</view>
</view>
</navigator>
</view>
</block>
<block wx:elif="{{tabs[1].isActive}}">1</block>
<block wx:elif="{{tabs[2].isActive}}">2</block>
</Tabs>
index.js
/*
1 用户上滑页面 滚动条触底 开始加载下一页数据
1 找到滚动条触底事件 微信小程序官方开发文档寻找
2 判断还有没有下一页数据
1 获取到总页数 只有总条数
总页数 = Math.ceil(总条数 / 页容量 pagesize)
总页数 = Math.ceil( 23 / 10 ) = 3
2 获取到当前的页码 pagenum
3 判断一下 当前的页码是否大于等于 总页数
表示 没有下一页数据
3 假如没有下一页数据 弹出一个提示
4 假如还有下一页数据 来加载下一页数据
1 当前的页码 ++
2 重新发送请求
3 数据请求回来 要对data中的数组 进行 拼接 而不是全部替换!!!
*/
import { request } from "../../request/index.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
Page({
data: {
tabs: [
{
id: 0,
value: "综合",
isActive: true
},
{
id: 1,
value: "销量",
isActive: false
},
{
id: 2,
value: "价格",
isActive: false
}
],
goodsList:[]
},
// 接口要的参数
QueryParams:{
query:"",
cid:"",
pagenum:1,
pagesize:10
},
// 总页数
totalPages:1,
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.QueryParams.cid=options.cid||"";
this.QueryParams.query=options.query||"";
this.getGoodsList();
},
// 获取商品列表数据
async getGoodsList(){
const res=await request({url:"/goods/search",data:this.QueryParams});
// 获取 总条数
const total=res.total;
// 计算总页数
this.totalPages=Math.ceil(total/this.QueryParams.pagesize);
// console.log(this.totalPages);
this.setData({
// 拼接了数组
goodsList:[...this.data.goodsList,...res.goods]
})
},
// 标题点击事件 从子组件传递过来
handleTabsItemChange(e){
// 1 获取被点击的标题索引
const {index}=e.detail;
// 2 修改源数组
let {tabs}=this.data;
tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
// 3 赋值到data中
this.setData({
tabs
})
},
// 页面上滑 滚动条触底事件
onReachBottom(){
// 1 判断还有没有下一页数据
if(this.QueryParams.pagenum>=this.totalPages){
// 没有下一页数据
// console.log('%c'+"没有下一页数据","color:red;font-size:100px;background-image:linear-gradient(to right,#0094ff,pink)");
wx.showToast({ title: '没有下一页数据' });
}else{
// 还有下一页数据
// console.log('%c'+"有下一页数据","color:red;font-size:100px;background-image:linear-gradient(to right,#0094ff,pink)");
this.QueryParams.pagenum++;
this.getGoodsList();
}
}
})
十、上拉刷新事件
1、page.json
"enablePullDownRefresh":true,
"backgroundTextStyle":"dark"
2、js代码,接第九章代码
/*
2 下拉刷新页面
1 触发下拉刷新事件 需要在页面的json文件中开启一个配置项
找到 触发下拉刷新的事件
2 重置 数据 数组
3 重置页码 设置为1
4 重新发送请求
5 数据请求回来 需要手动的关闭 等待效果
*/
// 下拉刷新事件
onPullDownRefresh(){
// 1 重置数组
this.setData({
goodsList:[]
})
// 2 重置页码
this.QueryParams.pagenum=1;
// 3 发送请求
this.getGoodsList();
}
加入关闭窗口方法
// 获取商品列表数据
async getGoodsList(){
const res=await request({url:"/goods/search",data:this.QueryParams});
// 获取 总条数
const total=res.total;
// 计算总页数
this.totalPages=Math.ceil(total/this.QueryParams.pagesize);
// console.log(this.totalPages);
this.setData({
// 拼接了数组
goodsList:[...this.data.goodsList,...res.goods]
})
// 关闭下拉刷新的窗口 如果没有调用下拉刷新的窗口 直接关闭也不会报错
wx.stopPullDownRefresh();
},
十一、消息对话框
1、官方文档
https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showToast.html
2、代码
写在网络请求封装中
requert/index.js
// 同时发送异步代码的次数
let ajaxTimes=0;
export const request=(params)=>{
// 判断 url中是否带有 /my/ 请求的是私有的路径 带上header token
let header={...params.header};
if(params.url.includes("/my/")){
// 拼接header 带上token
header["Authorization"]=wx.getStorageSync("token");
}
ajaxTimes++;
// 显示加载中 效果
wx.showLoading({
title: "加载中",
mask: true
});
// 定义公共的url
const baseUrl="http://api-hmugo-web.itheima.net/api/public/v1";
return new Promise((resolve,reject)=>{
wx.request({
...params,
header:header,
url:baseUrl+params.url,
success:(result)=>{
resolve(result.data.message);
},
fail:(err)=>{
reject(err);
},
complete:()=>{
ajaxTimes--;
if(ajaxTimes===0){
// 关闭正在等待的图标
wx.hideLoading();
}
}
});
})
}
十二、在新界面中全局预览图片
1、官方文档
https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.previewImage.html
wx.previewImage({
current: '', // 当前显示图片的http链接
urls: [] // 需要预览的图片http链接列表
})
十三、获取收货地址
1、openSetting 设置权限
调起客户端小程序设置界面,返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
2、wx.chooseAddress
获取用户收货地址。调起用户编辑收货地址原生界面,并在编辑完成后返回用户选择的地址。
1 获取用户的收货地址
1 .1、绑定点击事件
2.1、 调用小程序内置 api 获取用户的收货地址 wx.chooseAddress
2 获取 用户 对小程序 所授予 获取地址的 权限 状态 scope
1 假设 用户 点击获取收货地址的提示框 确定 authSetting scope.address
scope 值 true 直接调用 获取收货地址
2 假设 用户 从来没有调用过 收货地址的api
scope undefined 直接调用 获取收货地址
3 假设 用户 点击获取收货地址的提示框 取消
scope 值 false
1 诱导用户 自己 打开 授权设置页面(wx.openSetting) 当用户重新给与 获取地址权限的时候
2 获取收货地址
十四、every数组方法
every数组方法会遍历数组,接收一个回调函数,如果每个回调函数的返回值都是true ,那么every方法返回的结果就为true ,
如果,有一个回调函数返回的是false ,那么不在继续遍历,直接返回 false
空数组返回 ture
十五、模态对话框
1、官方文档
https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showModal.html
2、示例代码
wx.showModal({
title: '提示',
content: '这是一个模态弹窗',
success (res) {
if (res.confirm) {
console.log('用户点击确定')
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
十六、微信支付
1 哪些人 哪些帐号 可以实现微信支付
1 企业帐号
2 企业帐号的小程序后台中 必须 给开发者 添加上白名单
1 一个 appid 可以同时绑定多个开发者
2 这些开发者就可以公用这个appid 和 它的开发权限
1、官方文档
https://developers.weixin.qq.com/miniprogram/dev/api/open-api/payment/wx.requestPayment.html
十七、获取“我的信息”
1、buttom 的 open-type属性
getUserinfo
十八、路由
跳转页面
1、官方文档
https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.switchTab.html
2、wx.navigateBack(Object object)
关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages 获取当前的页面栈,决定需要返回几层。
2.1、示例代码
wx.navigateBack({
delta: 1
});
十九、回车事件
bindconfirm
向被打开的页面传递数据
search(e) {
wx.navigateTo({
url: "../../pages/search/index",
success: function (res) {
// 通过eventChannel向被打开页面传送数据
res.eventChannel.emit("search_value", { data: e.detail["value"] });
// console.log(e)
},
});
},
页面接收
onLoad: function(option){
//获取事件对象
const eventChannel = this.getOpenerEventChannel()
// 监听aishang事件,获取上一页面通过eventChannel传送到当前页面的数据
let inpValue="";
eventChannel.on("search_value", function (data) {
// console.log("传递的参数", data);
inpValue=data.data;
});
},
改变上一页监听的数据时调用
let eventChannel = this.getOpenerEventChannel();
let obj = {
name:'aishang',
value:'爱尚丽明'
}
eventChannel.emit('aishang', { data: obj });
//第一步就会监听到数据的变化