微信小程序
day1
微信小程序开发
下载微信开发者工具
注册小程序账号
-
注册小程序账号
-
填写信息
-
获取小程序的 AppID
搭建开发环境
认识小程序
小程序的页面布局
- 小程序组件(标签)
- view 定义块级区块⇒ 相当于 html 中的 div 标签
- text 定义行内区块⇒ 相当于html中的 span 标签
- 样式选择器
- 组成页面的4种文件类型
-
.wxml
页面结构和内容
-
.wxss
页面样式
-
.js
逻辑处理
-
.json
配置文件
小程序的基本逻辑处理
- 注册页面:
Page函数
- 数据处理:
-
data
选项 , 初始化页面数据
-
this.setData
方法 , 更新页面数据
- 事件处理:
bind:事件名称=事件回调
小程序的页面路径的配置
- app.json 全局配置
- 小程序组件
- navigator 跳转页面(相当于 html 中的 a 标签)
- 如何配置小程序的页面路径?
- app.json文件
- 在Pages下新建demo文件夹,在输入Page文件名称,自动生成4个文件,文件路径也自动生成
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/demo/index"
],
- 如何配小程序的启动页面(首页)?
// 启动页面的路径(首页)
"entryPagePath": "pages/logs/logs",
小程序的配置 – window(窗口的表现)⇒ app.json
- 配置下拉刷新
-
enablePullDownRefresh
是否启用下拉刷新
-
backgroundTextStyle
下拉loading状态的样式
- 配置导航栏
-
navigationBarTitleText
导航栏标题
-
navigationBarTextStyle
导航栏标题文字颜色
-
navigationBarBackgroundColor
导航标背景颜色
-
navigationStyle
自定义导航栏
"window": {
"enablePullDownRefresh": true,//是否启用下拉刷新
"backgroundTextStyle": "dark",//下拉loading状态的样式
"navigationBarTitleText": "我的第一个小程序",//导航栏标题
"navigationBarTextStyle": "white",//导航栏标题文字颜色
"navigationBarBackgroundColor": "#f60",//导航标背景颜色
"navigationStyle": "default"//自定义导航栏
}
小程序的配置 – tabBar(tab栏)(2-5个)⇒ app.json
-
list
:配置 tab 栏列表
-
text
: 配置 tab 的文字
-
pagePath
: 配置 tab的页面路径
-
iconPath
& selectedIconPath
:配置 tab 对应的图标
-
color
& selectedColor
:配置 tab 文字的颜色
-
backgroundColor
:配置 tab 栏的背景颜色
-
borderStyle
:配置 tab 栏边框样式
"tabBar": {
"color": "#f60",
"selectedColor": "#f60",
"backgroundColor": "#ccc",
"borderStyle": "black",
"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/logs-default.png",
"selectedIconPath": "static/tabbar/logs-active.png"
},
{
"pagePath": "pages/demo/index",
"text": "首页",
"iconPath": "static/tabbar/face-default.png",
"selectedIconPath": "static/tabbar/face-active.png"
}
]
}
小程序的配置 – 页面配置(页面的 .json 文件)⇒ 如pages/demo/index.json
- 与 window 对应的配置参数:
-
enablePullDownRefresh
是否启用下拉刷新
-
navigationBarTitleText
导航栏标题文字
-
navigationStyle
自定义导航栏
- …省略其它
- 页面独有的配置参数
- 全局配置项和页面自己的配置项冲突了,以页面自己的配置项为准
{
"enablePullDownRefresh": true,//启用下拉刷新
"navigationBarTitleText": "我的",//导航栏标题文字
"navigationStyle": "default",//自定义导航栏
"disableScroll": false//可以滚动
}
小程序的长度单位:rpx
移动端适配 ⇒ 等不缩放
- 特点:
- 小程序的屏幕宽度均为 750rpx
- 根据屏幕宽度自动换算成 px 长度
- 应用实践:
- 建议设计稿宽度以 750px 做为基准
- 设计稿中元素的尺寸即为小程序代码中 rpx 的尺寸
.box{
width: 750rpx;
height: 400rpx;
line-height: 400rpx;
text-align: center;
background-color: #f5a11c;
}
小程序组件
- 小程序导航组件
- 小程序图片组件
- 小程序轮播图组件
- 小程序表单组件
- 小程序区域滚动组件
小程序常用组件:navigator
- url:页面路径
-
hover-class
:点击态的样式
-
open-type
:跳转方式
- switchTab 跳转到 tabBar 页面
- 底部导航栏的跳转,一般要跳转到底部导航栏,不可以跳转,配置open-type:switchTab 才可以跳转
//inde.wxml
<navigator url="/pages/index/index" hover-class="nav-hover" open-type="switchTab">跳转到首页页面</navigator>
<navigator url="/pages/demo/index" open-type="switchTab" hover-class="none" >跳转到我的页面</navigator>
<navigator url="/pages/logs/logs" open-type="switchTab">跳转到日志页面</navigator>
//index.wxss
.nav-hover{
color: #f5a11c;
background-color: chartreuse;
font-size: xx-large;
}
小程序常用组件:image
- 占位容器:
- image 组件是一个有默认大小(320 x 240px)的“盒子”
- mode缩放模式:
-
scaleToFill
不保证缩放比,图片拉伸填满容器⇒ 默认拉伸(不考虑宽高比)
-
aspectFit
保证缩放比,使图片的长边显示出来(留白)
-
aspectFill
保证缩放比,使图片的短边显示出来(长边溢出并裁剪长边,短边拉伸屏幕⇒ 放大)
- 使用原则
- 指定“容器”的大小,根据设计稿设置 image 的宽高尺寸
- 图片铺满容器,设置
aspectFill
缩放模式
//index.wxml
<image class="image" src="/static/uploads/goods_1.jpg" mode="aspectFill"/>
小程序常用组件:swiper
- 组件结构
-
swiper
:滑块容器,只能嵌套 swiper-item
组件
-
swiper-item
:滑块单元,可以嵌套任何内容,如 image
- 组件属性
-
indicator-dots
是否显示面板指示点
-
autoplay
是否自动切换
-
circular
是否衔接滑动
//inde.wxml
<!-- swiper轮播 -->
<swiper class="banana" indicator-dots autoplay circular>
<swiper-item >
<image class="img-item" src="/static/uploads/slide_1.jpg" />
</swiper-item>
<swiper-item >
<image class="img-item" src="/static/uploads/slide_2.jpg" />
</swiper-item>
<swiper-item >
<image class="img-item" src="/static/uploads/slide_3.jpg" />
</swiper-item>
</swiper>
//index.wxss
.banana {
height: 320rpx;
}
.img-item{
width: 750rpx;
height: 320rpx;
}
小程序常用组件:表单相关
- 输入框:
input
-
password
密码类型、placeholder
占位文字
- 单选框:
radio-group
和 radio
-
value
指定表单数据、checked
选中状态
- 复选框:
checked-group
和 checkbox
-
value
指定表单数据、checked
选中状态
- 选择框:
picker
-
mode
选择类型,region
省市县、date
日期
//index.wxml
<!--pages/demo/index.wxml-->
<text>pages/demo/index.wxml</text>
<!-- 提前准备好的布局结构代码 -->
<view class="register">
<view class="legend">信息登记:</view>
<view class="form-field">
<label for="">姓名:</label>
<view class="field">
<input type="text" password placeholder="请输入姓名"/>
</view>
</view>
<view class="form-field">
<label for="">性别:</label>
<view class="field">
<radio-group bindchange="">
<radio value="0"checked />男
<radio value="1"/>女
</radio-group>
</view>
</view>
<view class="form-field">
<label for="">爱好:</label>
<view class="field">
<checkbox-group bindchange="">
<checkbox value="0" checked />唱
<checkbox value="1"/>跳
<checkbox value="2"/>rap
</checkbox-group>
</view>
</view>
<view class="form-field">
<label for="">籍贯:</label>
<view class="field">
<picker mode="region" > 请选择籍贯</picker>
</view>
</view>
<view class="form-field">
<label for="">生日:</label>
<view class="field">
<picker mode="date"> 请选择生日日期 </picker>
</view>
</view>
</view>
小程序常用组件:scroll-view
- 组件结构:
-
scroll-view
内嵌套可任意内容,要求必须有溢出
- 垂直滚动时必须要指定高度
- 组件属性:
-
scroll-x
允许水平方向滚动
-
scroll-y
允许垂直方向滚动
-
refresher-enable
启用下拉刷新交互
//index.wxml
<!--pages/scroll-view/index.wxml-->
<text>pages/scroll-view/index.wxml</text>
<!-- 提前准备好的布局结构代码 -->
<!-- 搜索框 -->
<view class="search-bar">
<input type="text" placeholder="输入搜索关键字" />
</view>
<!-- 页面主体 -->
<view class="page-body" >
<scroll-view class="aside" scroll-y>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
</scroll-view>
<scroll-view class="content" scroll-y refresher-enabled>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
</scroll-view>
</view>
//index.wxss
/* pages/scroll-view/index.wxss */
/* 提前准备的样式代码 */
page {
background-color: #f2f2f2;
}
.search-bar {
padding: 20rpx 30rpx;
background-color:#fff;
}
.search-bar input {
padding: 14rpx 20rpx 10rpx 40rpx;
border: 1rpx solid #eee;
border-radius: 70rpx;
font-size: 28rpx;
color: #333;
}
.page-body {
height: 700rpx;
display: flex;
margin-top: 20rpx;
}
.page-body .aside {
width: 200rpx;
padding: 20rpx;
background-color: #fff;
}
.page-body .aside .item {
height: 60rpx;
margin-bottom: 20rpx;
border-radius: 10rpx;
background-color: #eee
}
.page-body .aside .active {
background-color: pink;
}
.page-body .content {
flex: 1;
padding: 20rpx;
margin-left: 20rpx;
background-color: #fff;
}
.page-body .content .item {
float: left;
width: 215rpx;
height: 215rpx;
margin: 0 20rpx 20rpx 0;
border-radius: 10rpx;
background-color: #eee;
}
.page-body .content .item:nth-child(even) {
margin-right: 0;
}
小程序样式
小程序的样式
- 全局样式
view {
color:chartreuse;
}
- 静态资源
- .wxss 中只能使用网络路径或 base64,比如背景图
.box{
width: 750rpx;
height: 400rpx;
line-height: 400rpx;
text-align: center;
/* background-color: #f5a11c; */
background-image: url(https://ts1.cn.mm.bing.net/th/id/R-C.15e970cd0765096178a6da16993cfbb1?rik=IT5KfevidZcTig&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fwallpaper%2f1210%2f22%2fc0%2f14558824_1350879506501.jpg&ehk=X9ro%2fg%2fGTmsglVrbV%2bmy8c3wsAvcHseqcEhsf80RMWA%3d&risl=&pid=ImgRaw&r=0);
background-size: cover;
}
- 字体图标
- iconfont 字体平台生成字体
- @import 导入字体对应的样式文件
- 根据类名引用不同的字体图标
//iconfont.wxss
@font-face {
font-family: "iconfont"; /* Project id 4071059 */
src: url('//at.alicdn.com/t/c/font_4071059_1ror2rb6gdw.woff2?t=1684230396072') format('woff2'),
url('//at.alicdn.com/t/c/font_4071059_1ror2rb6gdw.woff?t=1684230396072') format('woff'),
url('//at.alicdn.com/t/c/font_4071059_1ror2rb6gdw.ttf?t=1684230396072') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-guanbi_24:before {
content: "\e611";
}
.icon-sousuo_xian24:before {
content: "\e612";
}
.icon-pinglun_xian24:before {
content: "\e613";
}
//app.wxss
@import "./iconfont.wxss";
//index.wxml
<view class="iconfont icon-sousuo_xian24"></view>
day2
导入小程序
-
选择代码目录
-
项目配置文件
-
appid
当前小程序的 AppID
-
projectname
当前小程序的项目名称
- 变更AppID(视情况而定,如果没有开发权限时需要变更成个人的 AppID)
小程序模板语法
小程序模板语法:数据绑定
模板语法 : 在页面中渲染数据时所用到的一系列语法叫做模板语法,对应到 Vue 中就是指令的概念
- 插值
{{ }}
语法:只要有需要访问变量的地方均要使用 {{ }}
- 内容绑定
- 属性绑定
//index.js
Page({
data: {
isChecked:true
}
})
//index.wxml
<switch checked="{{isChecked}}" bindchange="" />
- 简易双向绑定:
-
model:value
支持双向数据绑定
- 仅
input
和 textarea
组件支持
//index.js
Page({
data: {
msg:"hello"
}
})
//index.wxml
<input type="text" model:value="{{msg}}" />
<view>{{msg}}</view>
小程序模板语法:条件渲染
- 控制属性:
wx:if
和 wx:else
-
wx:if
表达式的值为真时渲染内容
-
wx:else
紧跟在 wx:if
之后,wx:if
表达式值为假时渲染
- 组件属性:
hidden
- 组件始终会被渲染,根据表达式的值显示/隐藏内容
-
[hidden] { display: none }
隐藏节点内容(不是删除节点)
- v-show⇒ display:none
- hidden⇒ display:none
- 内容频繁显示/隐藏时推荐使用
hidden
//index.js
Page({
data: {
isShow:true,
isHidden:true
}
})
//index.wxml
<view wx:if="{{isShow}}">大师兄</view>
<view wx:else>二师兄</view>
<view hidden="{{!isHidden}}">正在加载...</view>
小程序模板语法:列表渲染
-
wk:for
:重复渲染组件内容
-
index
默认值,访问数组的索引值
-
item
默认值,访问数组的单元值
//index.js
Page({
data: {
students: [
{id: 1, name: '贺洋', age: 20, gender: '男', level: '菜鸟'},
{id: 2, name: '唐刚', age: 18, gender: '女', level: '笨鸟'},
{id: 3, name: '常超', age: 20, gender: '女', level: '老鸟'}
]
}
})
//index.wxml
<!-- 列表渲染 -->
<view class="students">
<view class="item">
<text>序号</text>
<text>姓名</text>
<text>年龄</text>
<text>性别</text>
<text>级别</text>
</view>
<view class="item" wx:for="{{ students }}" wx:key="id">
<text>{{ index+1 }}</text>
<text>{{ item.name }}</text>
<text>{{ item.age }}</text>
<text>{{ item.gender }}</text>
<text>{{ item.level }}</text>
</view>
</view>
-
wx:key
列表项唯一标识符
- 数组单元为对象时,只写属性名
- 数组单元为简单类型时,推荐使用
* this
//index.js
Page({
data: {
history: ['苹果', '华为', 'OPPO', '三星']
}
})
//index.wxml
<!-- 简单数组 -->
<view class="history">
<text wx:for="{{history}}" wx:key="*this">{{ item }}</text>
</view>
- 自定义访问变量:一般在
wx:for
嵌套时使用
-
wx:for-index
自定义访问数组索引值的变量
-
wx:for-item
自定义访问数组单元值的变量
小程序内置API:网络请求 wx.request
- 基本用法
//index.js
wx.request({
url: '这里是接口的地址',
method: '这里是请求的方法',
data: '请求时提交的数据',
header: {/* 请求头信息 */},
success: () => {/* 成功的回调 */},
fail: () => {/* 失败的回调 */},
complete: () => {/* 成功或失败的回调 */}
})
- 功能步骤:
- 监听按钮的点击事件
- 调用
wx.request
- 列表数据渲染
- 配置合法域名:
- 开发环境设置不校验合法域名
- 在管理后台配置,必须为
https
协议
- 多个域名间使用分号分隔
- 开发小技巧:
//index.js
Page({
/**
* 页面的初始数据
*/
data: {
books:[]
},
getBooks(){
wx.request({
url: 'https://hmajax.itheima.net/api/books',
method:'GET',
data: {
creator: 'zhangsan'
},
success:(res)=>{
console.log(res.data.data)
this.setData({books:res.data.data})
},
fail:()=>{console.log('请求失败')},
complete:()=>{console.log('请求完成')}
})
}
})
//index.wxml
<button class="button" size="mini" type="primary" bind:tap="getBooks">查询书单</button>
<view class="books">
<view class="item">
<text>序号</text>
<text>名称</text>
<text>作者</text>
<text>出版社</text>
<text>操作</text>
</view>
<view class="item" wx:for="{{books}}" wx:key="id">
<text>{{ index+1 }}</text>
<text>{{ item.bookname }}</text>
<text>{{ item.author }}</text>
<text>{{ item.publisher }}</text>
<text>删除</text>
</view>
</view>
小程序内置API:界面交互
-
wx.showLoading
显示 loading 提示框 ⇒ 获取请求之前
-
title
文字提示内容
-
mask
是否显示透明蒙层,防止触摸穿透
-
wx.hideLoading
隐藏 loading
提示框
-
wx.showToast
消息提示框(轻提示)
-
title
文字提示内容
-
mask
是否显示透明蒙层,防止触摸穿透
-
duration
提示框持续的时长
-
icon
更改图标,none
不使用图标
//index.wxml
<button class="button" size="mini" type="primary" bind:tap="getBooks">查询书单</button>
<view class="books">
<view class="item">
<text>序号</text>
<text>名称</text>
<text>作者</text>
<text>出版社</text>
<text>操作</text>
</view>
<view class="item" wx:for="{{books}}" wx:key="id">
<text>{{ index+1 }}</text>
<text>{{ item.bookname }}</text>
<text>{{ item.author }}</text>
<text>{{ item.publisher }}</text>
<text>删除</text>
</view>
</view>
//index.js
getBooks(){
wx.showLoading({
title: '正在加载',
mask:true
}),
wx.request({
url: 'https://hmajax.itheima.net/api/books',
method:'GET',
data: {
creator: 'zhangsan'
},
success:(res)=>{
console.log(res.data.data)
this.setData({books:res.data.data})
wx.showToast({
title: '查询书单成功!',
mask:true,
duration:5000,//默认1500(1.5秒)
icon:"success"
})
},
fail:()=>{console.log('请求失败')},
complete:()=>{
console.log('请求完成')
// 取消加载
wx.hideLoading()
}
})
}
小程序内置API:本地存储
-
wx.setStorageSync
存储数据
-
wx.getStorageSync
读取数据
-
wx.removeStorageSync
删除数据
-
wx.clearStorageSync
清空数据
// pages/interface/index.js
// 1. 监听按钮
// 2. 发请求
// 3. 渲染列表
Page({
/**
* 页面的初始数据
*/
data: {
books:[]
},
// 存数据
saveData(){
wx.setStorageSync('books', ['三国演义','水浒传'])
},
// 取数据
getData(){
const res = wx.getStorageSync('books')
console.log(res)
},
// 删数据
delData(){
wx.removeStorage({
key: 'books',
})
},
// 清空数据
clearData(){
wx.clearStorage()
}
})
<!--pages/interface/index.wxml-->
<!-- 本地存储 -->
<view class="storage">
<button size="mini" type="primary" bind:tap="saveData">存数据</button>
<button size="mini" type="primary" bind:tap="getData">读数据</button>
<button size="mini" type="primary" bind:tap="delData">删数据</button>
<button size="mini" type="primary" bind:tap="clearData">清数据</button>
</view>
小程序内置API:API 的特征
- 同步 API:同步方式读取 API 执行结果
- 异步 API:回调函数读取 API 执行结果
- 支持
success
、fail
、complete
回调函数
-
Promise
:部分 API 支持(查看文档)
- 配合
async/await
wx.chooseMedia
-
wx.chooseMedia
调起摄像头拍照或读取相册内容,该 API 既支持回调方式获取结果,也支持 Promise
方式返回结果
// pages/interface/index.js
Page({
/**
* 页面的初始数据
*/
data: {
imgPath:''
},
// 选取照片
async chooseImg(){
const res = await wx.chooseMedia()
console.log(res)
this.setData({
imgPath:res.tempFiles[0].tempFilePath
})
// wx.chooseMedia()
// .then(res=>{
// console.log(res.tempFiles[0].tempFilePath)
// this.setData({imgPath:res.tempFiles[0].tempFilePath})
// console.log(this.data.imgPath)
// })
}
})
<!--pages/interface/index.wxml-->
<view>
<image src="{{imgPath}}" />
<button type="primary" size="mini" bind:tap="chooseImg">上传图片</button>
</view>
小练习:搜索历史
- 双向数据绑定:获取搜索关键字
-
model:value
获取表单内容
-
this.setData
记录到数组中
- 列表数据渲染:渲染搜索历史
-
wx:for
渲染搜索历史
- 本地存储:持久化存储
wx.setStorageSync
wx.getStorageSync
// pages/interface/index.js
// 1. 监听按钮
// 2. 发请求
// 3. 渲染列表
Page({
/**
* 页面的初始数据
*/
data: {
books:[],
keywords:'',
history:wx.getStorageSync('history') || []
},
// 历史搜索
doSearch(){
console.log(this.data.keywords)
const { history , keywords } = this.data
history.unshift(keywords)
console.log(history)
this.setData({ history , keywords:'' })
//todo:数据持久化
wx.setStorageSync('history', history)
}
})
<!--pages/interface/index.wxml-->
<!-- 搜索历史 -->
<view class="history">
<view class="search-bar">
<input type="text" model:value="{{ keywords }}" />
<text class="label" bind:tap="doSearch">搜索</text>
</view>
<view class="title">
历史搜索 <text class="icon-delete">x</text>
</view>
<view class="keywords">
<navigator url="/pages/test/index" wx:for="{{ history }}" wx:key="*this">{{ item }}</navigator>
</view>
</view>
小程序事件处理:事件对象
- 事件对象:事件回调的第1个参数
ev.mark
ev.target.dataset
- 应用:
Tab
交互
- 确定点击
tab
的索引值
- 根据条件添加
active
类名
// pages/events/index.js
Page({
/**
* 页面的初始数据
*/
data: {
activeIndex:0,
isPulling:false
},
//点击高亮
selectItem(ev){
// console.log(ev)
// console.log(ev.target.dataset)
console.log(ev.mark.key)
this.setData({ activeIndex:ev.mark.key
})
}
})
<!--pages/events/index.wxml-->
<!-- 页面主体 -->
<view class="page-body">
<scroll-view scroll-y class="aside">
<view
wx:for="{{6}}"
wx:key="*this"
class="item {{ index === activeIndex ? 'active' : ''}} "
mark:key="{{ index }}"
data-key=" {{ index }} "
bind:tap="selectItem"></view>
</scroll-view>
</view>
小程序事件处理:组件事件
-
scroll-view
组件
-
bind:scrolltolower
滚动到底部时触发
-
bind:refresherrefresh
执行下拉操作时触发
-
refresher-triggered
变更下拉刷新状态(结束下拉刷新)
// pages/events/index.js
Page({
/**
* 页面的初始数据
*/
data: {
isPulling:false
},
loadMore(){
console.log('触底了,加载更多')
// 请求接口,获取下一页数据
},
refreshPage(){
console.log('下拉刷新')
// 重新调接口,获取当前页面的数据,渲染页面
// 需要将 isPulling重新设置为 false
setTimeout(() => {
this.setData({ isPulling: false })
}, 2000);
}
})
<!--pages/events/index.wxml-->
<scroll-view
scroll-y
refresher-enabled
bind:scrolltolower="loadMore"
bind:refresherrefresh="refreshPage"
refresher-triggered="{{ isPulling }}"
class="content" >
</scroll-view>
- 表单组件
-
change
表单数据发生改变时触发(input
不支持)
-
submit
表单提交时触发(指定 form-type
)
- 上拉触底,加载更多
// pages/events/index.js
Page({
// 表单组件中的事件
changeGender(ev){
console.log(ev.detail.value)
},
selectHobby(ev) {
console.log(ev.detail.value)
},
selectRegion(ev) {
console.log(ev.detail)
// ev.datail.value ==> '北京市-北京市-东城区'
this.setData({ region:ev.detail.value.join('-') })
},
formSubmit(){
console.log('表单提交了')
}
})
<!--pages/events/index.wxml-->
<!-- 用户信息 -->
<view class="register">
<form bind:submit="formSubmit">
<view class="form-field">
<label for="">姓名:</label>
<view class="field">
<input type="text" placeholder="请输入您的姓名" />
</view>
</view>
<view class="form-field">
<label for="">性别:</label>
<view class="field">
<radio-group bind:change="changeGender">
<radio value="男" checked />男
<radio value="女" />女
</radio-group>
</view>
</view>
<view class="form-field">
<label for="">爱好:</label>
<view class="field">
<checkbox-group bind:change="selectHobby">
<checkbox value="写代码" checked />写代码
<checkbox value="睡大觉" />睡大觉
</checkbox-group>
</view>
</view>
<view class="form-field">
<label for="">籍贯:</label>
<view class="field">
<picker mode="region" bind:change="selectRegion">{{ region || '请选择籍贯'}}</picker>
</view>
</view>
<button size="mini" type="primary" form-type="submit">保存</button>
</form>
</view>
小程序生命周期:页面生命周期
-
onLoad
:初次加载
- 页面加载完成时会执行 1 次
- 常用于获取地址参数、发起网络请求等
-
onShow
:渲染页面被看到时 ⇒ 可以多次触发
-
onHide
:页面看不到时 ⇒ 可以多次触发
- 页面处于不可见状态时执行
- 常用于销毁长时间运行的任务,如定时器
// pages/lifetimes/index.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
console.log('页面加载',options)
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
console.log('页面渲染')
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
console.log('页面处于显示状态')
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
console.log('页面处于隐藏状态')
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
小程序生命周期:应用生命周期
onLaunch
- 小程序启动时会执行 1 次
- 常用于获取场景值或参数
-
onShow
⇒ 显示
-
onHide
⇒ 隐藏
// app.js
App({
onLaunch(options){
console.log('应用启动',options)
},
onShow(){
console.log('应用处于前台--能被用户看到');
},
onHide(){
console.log('应用处于后台--用户看不到');
}
})
页面分享
- 有
onShareAppMessage(){}
这个函数就可以分享给好友(虚拟)
// pages/index/index.js
Pages({
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
小练习:头像&昵称 ⇒ 知识总结:
- 微信昵称:
-
input
组件 type
属性值设置为 nickname
- 监听
blur
事件获取用户昵称(在开发者工具中需要 blur
两次)
- 微信头像:
-
button
组件 open-type
属性值设置为 chooseAvatar
- 监听
chooseavatar
事件获取头像临时地址(只能在小程序中使用)
day3
分包加载
小程序分包加载(按需加载)
- 为什么进行分包加载?
- 小程序限制单个代码包体积不超过2M
- 分包可以优化小程序页面的加载速度
"packOptions": {
"ignore": [
{
"value": "/static/uploads",
"type": "folder"
}
],
"include": []
}
- 启用/使用分包:
subPackages
app.json
-
root
分包所在的(根)目录
-
pages
分包中包含的页面
-
tabBar
的页面不允许分包
- 所有包不超过20M
//app.json
"subPackages": [
{
"root": "subpack_demo",
"pages": [
"pages/demo/index"
]
},
{
"root": "subpack_user",
"pages":[
"pages/profile/index",
"pages/my/index"
]
}
]
- 分包预加载:
preloadRule
- 页面路径做为
key
(属性)
-
network
预加载的网络环境
-
packages
需要预加载的包
//app.json
"preloadRule": {
"pages/framework/index":{
"network": "all",
"packages": ["subpack_user"]
}
}
自定义组件
小程序自定义组件 - 基本语法
- 创建自定义组件
-
.json
文件中 component: true
-
.js
中调用 Component
函数
- 使用自定义组件
- 全局配置或页面配置
usingComponents
- 页面中以组件形式引入
- 局部注册
//pages/component/index.json
{
"component": true,
"usingComponents": {
"navigation-bar" :"/components/navigation-bar/index"
}
}
- 使用
//pages/component/index.wxml
<navigation-bar></navigation-bar>
//app.json
"usingComponents": {
"navigation-bar":"./components/navigation-bar/index"
}
- 在哪使用在哪写
//pages/component/index.wxml
<navigation-bar></navigation-bar>
小程序自定义组件 – 组件样式
- 样式隔离
-
.js
文件中传入 options: { addGlobalClass: true }
// components/navigation-bar/index.js
Component({
options:{
addGlobalClass:true
}
})
/* pages/component/index.wxss */
.navigation-bar{
color: rgb(236, 247, 243);
font-size: xxx-large;
font-weight:bold;
line-height: 300rpx;
border: 10px solid rgb(206, 142, 236);
}
- 尽量不要使用标签、ID、属性选择器
- 样式修改以权重比较为主
- 外部样式类
-
.js
文件中传入 externalClasses: [ xxx, yyy]
-
xxx、yyy
可以理解成“变量
- 步骤
- 组件内部同意修改
// components/navigation-bar/index.js
Component({
externalClasses:['title-class']
})
<!--components/navigation-bar/index.wxml-->
<view >
<view class="title-class">自定义导航栏</view>
</view>
<!--pages/component/index.wxml-->
<navigation-bar title-class="title"></navigation-bar>
/* pages/component/index.wxss */
.title{
width: 800rpx;
height: 300rpx;
text-align: center;
line-height: 300rpx;
background-color: rgb(238, 247, 160);
color: rgb(247, 173, 231);
font-size: xxx-large;
font-weight:bold;
border: 10px solid rgb(206, 142, 236);
}
小程序自定义组件 – <slot />
(插槽)
- 创建插槽:
- 默认情况只能 1 个
<slot />
-
.js
文件中传入 options: { multipleSlots: true }
-
<slot name=“title”/>
为插槽命名
- 使用插槽
- 单个插槽:在组件中间填充内容
- 多个插槽:使用 slot 属性指定插槽位置
// components/navigation-bar/index.js
Component({
options:{
// 样式隔离(同意外部修改)
addGlobalClass:true,
// 开启多插槽
multipleSlots:true
},
// 外部样式类
externalClasses:['title-class']
})
<!--components/navigation-bar/index.wxml-->
<!-- <text>components/navigation-bar/index.wxml</text> -->
<view >
<view class="title-class">
<slot name="left"> </slot>
<slot> </slot>
<slot name="right"> </slot>
</view>
</view>
<!--pages/component/index.wxml-->
<navigation-bar title-class="title">
<text slot="left">返回</text>
自定义导航栏
<text slot="right">去注册</text>
</navigation-bar>
/* pages/component/index.wxss */
.title{
width: 740rpx;
height: 300rpx;
text-align: center;
line-height: 300rpx;
background-color: rgb(238, 247, 160);
color: rgb(204, 12, 162);
font-size: x-large;
font-weight:lighter;
border: 3px solid rgb(255, 34, 126);
}
小程序自定义组件 – lifetimes
(生命周期)
-
created
:组件创建时触发
- 类似 Vue 中的
created
- 不能调用
this.setData
-
attached
:组件初始完毕时触发
- 类似于 Vue 中的
mounted
- 在
attached
中可以调用this.setData
修改data中的数据
- 应用示例:状态栏适配
// components/navigation-bar/index.js
Component({
options:{
// 样式隔离(同意外部修改)
addGlobalClass:true,
// 开启多插槽
multipleSlots:true
},
// 外部样式类
externalClasses:['title-class'],
// 生命周期
data:{
msg:'测试数据'
},
lifetimes:{
// 可以理解为Vue里面的created
created(){
console.log('组件创建完成')
// this.setData({msg:'hello world'})
},
// 可以理解为Vue里面的mounted
attached(){
console.log('组件挂载完成');
this.setData({msg:'hello world'})
},
detached(){
console.log('组件卸载完成');
}
}
})
<!--components/navigation-bar/index.wxml-->
<!-- <text>components/navigation-bar/index.wxml</text> -->
<view >
<view class="title-class">
<slot name="left"> </slot>
<slot> </slot>
<slot name="right"> </slot>
<view>{{ msg }}</view>
</view>
</view>
小程序自定义组件 – 组件通信 – 父传子
- 自定义属性:
properties
父传子
- 应用示例:路由返回
// components/navigation-bar/index.js
Component({
data:{
//自定义导航适配
statusBarHeight:0
},
// 生命周期
lifetimes:{
// 可以理解为Vue里面的mounted
attached(){
//自定义导航适配
const info = wx.getSystemInfoSync()
console.log(info.statusBarHeight)
this.setData({ statusBarHeight : info.statusBarHeight })
}
},
properties:{
back:Boolean,
delta:{
type:Number,
value:1
}
},
methods:{
goBack(){
console.log('返回')
wx.navigateBack()
}
}
})
<!--components/navigation-bar/index.wxml-->
<!-- 自定义导航适配 -->
<view class="navigation-bar" style="padding-top: {{ statusBarHeight }}px;">
<view class="navigation-bar-back" wx:if="{{ back }}" bind:tap="goBack"></view>
<view class="navigation-bar-title">
<slot></slot>
</view>
</view>
/* components/navigation-bar/index.wxss */
.navigation-bar {
left: 0;
top: 0;
z-index: 999;
display: flex;
align-items: center;
height: 44px;
background-color: #fff;
}
.navigation-bar-title {
flex: 1;
text-align: center;
font-size: 14px;
}
.navigation-bar-back {
position: absolute;
left: 10px;
width: 44rpx;
height: 44rpx;
background-image: url(https://lotjol.github.io/static-store/enjoy-plus/images/back-arrow.svg);
background-repeat: no-repeat;
}
//subpack_user/pages/profile/index.json
{
"usingComponents": {},
"navigationStyle": "custom"
}
<!--subpack_user/pages/profile/index.wxml-->
<navigation-bar back>自定义导航栏</navigation-bar>
<text>subpack_user/pages/profile/index.wxml</text>
小程序自定义组件 – 组件通信 – 倒计时组件 – 子传父
- 应用示例:倒计时组件
- 外部样式类
-
<slot />
插槽
- 自定义属性
- 生命周期
- 自定义事件:
bind:事件类型(自定义
子传父
-
this.triggerEvent(‘事件类型’, 参数)
子传父
// components/count-down/index.js
Component({
/**
* 组件的初始数据
*/
data: {
timeStr: ''
},
/**
* 组件的属性列表
*/
properties: {
time:Number,
format: {
type: String,
value:'ss秒' //10秒,9秒...
}
},
/**
* 组件的方法列表
*/
methods: {
},
lifetimes:{
created(){},
attached(){
let { time ,format } = this.data
this.setData({ timeStr: format.replace('ss',time) })
const timer = setInterval(()=>{
this.setData({
time: --time,
timeStr: format.replace('ss',time)
})
// 停止倒计时的条件:0
if(this.data.time <= 0){
console.log('倒计时停止');
clearInterval(timer)
// 告诉父组件,我结束了
this.triggerEvent('finish')
}
},1000)
}
}
})
<!--components/count-down/index.wxml-->
<view class="count-down">{{timeStr}}</view>
// pages/login/index.js
Page({
/**
* 页面的初始数据
*/
data: {
isWaitingCode:false
},
// 发送验证码
sendMsgCode(){
this.setData({ isWaitingCode: true })
},
// 重置短信发送状态
resetWaitingStatus(){
this.setData({ isWaitingCode : false })
}
})
//pages/login/index.json注册组件
{
"usingComponents": {
"count-down":"/components/count-down/index"
}
}
<!--pages/login/index.wxml-->
<view class="login">
<view class="form-field">
<input type="text" placeholder="请输入手机号" />
<text wx:if="{{ !isWaitingCode }}" bind:tap="sendMsgCode" class="label">获取短信验证码</text>
<!-- 倒计时组件 -->
<count-down wx:else class="label" time="3" format="ss秒后重新发送" bind:finish="resetWaitingStatus"></count-down>
</view>
<view class="form-field">
<input type="text" placeholder="请输入短信验证码" />
</view>
<button>登录</button>
</view>
/* pages/login/index.wxss */
page {
padding: 0;
}
.login {
margin: 20rpx 20rpx 0;
padding: 30rpx 20rpx;
border-radius: 10rpx;
background-color: #fff;
}
.login .form-field {
display: flex;
padding: 10rpx 0;
border-bottom: 1rpx solid #eee;
}
.login .label {
padding-right: 20rpx;
line-height: 80rpx;
color: #5591AF;
}
.login input {
flex: 1;
height: 54rpx;
border: none;
}
.login button {
width: 100% !important;
margin-top: 30rpx;
color: #fff;
background-color: #5591AF;
}
小程序自定义组件 – Vant 组件库
- 安装
Vant
组件库
-
npm i @vant/weapp -S --production
- 移除
style: “v2”
- 需要手动在
project.config.json
内添加如下配置
{
...
"setting": {
...
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram/"
}
]
}
}
-
npm
构建
-
Vant
组件演示
"usingComponents": {
"navigation-bar":"./components/navigation-bar/index",
"van-button": "@vant/weapp/button/index"
}
<van-button type="default">默认按钮</van-button>
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
- 示例
van-button
van-field
van-cell-group
//app.json全局注册vant组件
{
"usingComponents": {
"van-button": "@vant/weapp/button/index",
"van-field": "@vant/weapp/field/index",
"van-cell": "@vant/weapp/cell/index",
"van-cell-group": "@vant/weapp/cell-group/index"
}
}
// pages/component/index.js
Page({
/**
* 页面的初始数据
*/
data: {
//手机号数据双向绑定
value: ''
}
})
<!--pages/component/index.wxml-->
<van-cell-group title="用户登录">
<van-field
value="{{ username }}"
clearable
label="手机号"
placeholder="请输入手机号码"
bind:click-icon="onClickIcon"
/>
<van-field
value="{{ sms }}"
center
clearable
label="短信验证码"
placeholder="请输入短信验证码"
border="{{ false }}"
use-button-slot
>
<van-button slot="button" size="small" type="primary">
发送验证码
</van-button>
</van-field>
</van-cell-group>
框架接口
小程序框架接口:应用实例(数据共享)
- 基本用法:
-
App
函数中定义属性和方法
-
getApp
函数获取应用实例
- 应用示例:
-
onLaunch
中读取本地数据
- 应用实例读取或更新数据
小程序框架接口:页面栈(页面实例)
- 基本用法:
-
getCurrentPages
获取当前页面栈(数组)
-
wx.redirectTo
关闭当前页,再跳转到新页面
-
wx.navigateTo
保留当前页,再跳转到新页面
// pages/framework/index.js
const app = getApp()
Page({
gotRedirectPage(){
wx.redirectTo({
url: '/subpack_user/pages/profile/index'
})
},
gotNavigatePage(){
wx.navigateTo({
url: '/subpack_user/pages/profile/index'
})
}
})
<!--pages/framework/index.wxml-->
<button type="primary" bind:tap="gotNavigatePage">去用户页面navigateTo</button>
<button type="primary" bind:tap="gotRedirectPage">去用户页面redirectTo(不能返回)</button>
- 页面实例:
-
data
页面初始数据
-
setData
更新数据
-
onShow
生命周期
-
route
页面路径
- 报错
uni-app
-
uni-app
是一个使用 Vue.js
开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。
- 明天开始上 uniapp 课程,请大家提前下载工具:
HbuilderX:https://www.dcloud.io/hbuilderx.html
文档:
uniapp官方文档:https://uniapp.dcloud.net.cn/
黑马优购在线文档:https://applet-base-api-t.itheima.net/docs-uni-shop/index.htm
黑马优购接口文档:https://www.showdoc.com.cn/128719739414963/2513235043485226
day1
创建 uni-app 项目
-
通过 HBuilderX 创建
-
创建项目
-
目录结构
-
文件讲解
-
把项目运行到微信开发者工具
-
填写自己的微信小程序的 AppID:
-
在 HBuilderX 中,配置“微信开发者工具”的安装路径:
C:\Program Files (x86)\Tencent\微信web开发者工具
-
在微信开发者工具中,通过 设置 -> 安全设置 面板,开启“微信开发者工具”的服务端口:
-
先打开一个文件,点击运行
-
成功下载编译工具
停止运行, 重新点击运行⇒ 信任并运行
-
初次运行成功之后的项目效果:
配置tabBar
- 将 资料 目录下的 static 文件夹 拷贝一份,替换掉项目根目录中的 static 文件夹
- 直接覆盖过去,项目里运来的 static 不要删除!
- 新建tabBar文件夹
删除默认的 index 首页
- 在 HBuilderX 中,把 pages 目录下的 index首页文件夹 删除掉
- 同时,把 page.json 中记录的 index 首页 路径删除掉
显示效果
首页
- 微信小程序不支持axios
-
uni-request
⇒ 发请求
- 配置网络请求
- 由于平台的限制,小程序项目中不支持 axios,而且原生的 wx.request() API 功能较为简单,不支持拦截器等全局定制的功能。因此,建议在 uni-app 项目中使用
@escook/request-miniprogram
第三方包发起网络数据请求。
封装器
封装器