简介
2017年 1 月 9 日,微信正式发布小程序,这是一个不需要下载安装注册登录等步骤,只需要安装微信即可使用的 MINI APP,天生跨平台 成本低
其他小程序与微信小程序类似,微信小程序最具代表性
支付宝小程序
钉钉小程序
百度小程序
qq 小程序
头条系小程序
优秀三方小程序
- 拼多多
- 滴滴出行
- 斗地主
- 智行火车票等
使用 WEUI 等 npm 依赖
在微信开发者工具中的菜单栏:工具 --> 项目详情, 勾选“使用 npm 模块”选项
npm init
npm i weui-miniprogram -S
安装完毕后
微信开发者工具中菜单栏:工具 --> 构建 npm
然后会看到根目录的 miniprogram_npm/weui-miniprogram 文件夹
设置 git 忽略依赖文件夹
根目录 + .gitignore
文件
node_modules
miniprogram_npm
引入全局样式
app.wxss
@import '/miniprogram_npm/weui-miniprogram/weui-wxss/dist/style/weui.wxss';
标签
view = div
text = span
小程序说为了安全,只能用框架里的标签
block = template
循环
循环默认了 item 和 index 两个关键字
循环对象时 item = value index = key
可自定义
wx:for="{{}}" wx:key=""
data
小程序提倡 data 内只存放页面内用得到的数据,如果一个对象有很多属性都用不到则最好优化一下
data: {
initData: {
}
}
通过 this.setData() 这个 API 改变
Page({
data: { msg: 0 },
handleChangeMsg() { this.setData({ msg: someValue })}
})
事件绑定
bind 为关键字
bind* = @*
<view bindtap="handleTap" >
传值只能通过自定义属性传,很 tm 恶心
<view bindtap="handleTap(1)"> // 错误
<view bindTap="handleTap" data-operation="{{1}}">
// js
handleTap(e) {
const opeartion = e.currentTarget.?.value
}
CSS
小程序默认把设备宽给定位 750rpx,即
width(px) = 750rpx
1px = 750rpx / width(px)
当设备为 375px:iphone 6
1px = 750rpx / 375px
当设计稿为 750px 屏幕为 375px,元素宽为 100px
element(100px) / 375px = element(rpx) / 750rpx
element(rpx) = (750 * 100 / 375)rpx
element {
width: calc(750rpx * 100 / 375)
}
@import 只能是相对路径
@import "../style/common.wxss"
* 通配符不管用
:这意味着下面代码失效
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
如果需要初始化
view, text, a, img {
...
}
image 标签
小程序包限制 2m,所以所有图片资源都要链入
image 标签有默认宽高 ,320px * 240px
components
小程序支持组件化
需要在 *.json 中配置要使用的组件,相当于 important from
index.json
{
"usingComponents": {
"Tabs": "../../components/Tabs/Tabs"
}
}
组件中的事件要写在 methods
中
Component({
methods: {
handleTap(e) {}
}
})
setData
在小程序中,已声明的状态不可以像 vue 那样直接修改,必须调用 this.setData()
API
官方推荐不要直接处理 this.data 中的数据,需要深拷贝一份出来,然后再通过 API 赋值
someMethods() {
const { list } = this.data;
list.forEach((item, index) => {
item.active = index === activeIndex ? true : false;
});
this.setData({ list });
}
组件通讯
- 父子通讯
子组件内 properties 接收传值
// father
<Nav navList="navList" />
data: {
navList: []
}
// son
properties: {
list: {
type: Array,
value: []
}
}
- 子父通讯
如果子组件内直接修改父组件传递过来的数据, VUE 会报错,小程序不会报错
properties: {
list: {
type: Array,
value: []
}
},
methods: {
changeProps() {
this.setData({
list: list.push({})
})
}
}
上面的写法不会报错,但是这么写有问题,
相当于在 子组件 data 内声明 list 并赋值
data: {}
=>
data: {
list: this.properties.list.push({})
}
小程序内正确的子触发父自定义事件的写法:
// father
<Son
list="{{ sonList }}"
bindactiveChange="activeChange"
/>
activeChange(e) {
const activeIndex = e.detail.activeIndex;
this.setData(); // do someting...
}
// Son
<view
bindtap="handleTap"
data-index="{{1}}"
></>
properties: {
list: {
type: Array,
value: []
}
},
methods: {
handleTap(e) {
const activeIndex = e.currentTarget.dataset.index;
this.triggerEvent(
'activeChange',
{ activeIndex }
)
}
}
其中,不同于 Vue,this.triggerEvent()
第二个参数不是原封不动的传递给父组件绑定的函数。而是在 e.detail 中
生命周期
生命周期
1. 应用生命周期( 入口文件 app.js )
App({})
onLaunch() {} // 应用第一次启动,获取用户个人信息 共享到所有页面
onShow() {} // 应用被前台,重置数据,定时器等
onHide() {} // 应用被后台,停止定时器等性能消耗
onError(error) {} // 程序报错时,收集错误信息,反馈到后台做异常捕获
onPageNotFound() {} // 应用第一次启动,找不到路由
Page({})
onLoad() {}
onShow() {}
onReady() {}
onHide() {} // 页面被切换,但没被关闭 ps: 小程序的页面被关闭是被替换掉,没关闭是路由被记录,没被清掉。。。
onUnload() {} // 路由被关闭
onPullDownRefresh() {} // 下拉刷新钩子
onReachBottom() {} // 当有滚动条且触底,一般做上拉加载下一页
onShareAppMessage() {} // 点击右上角分享
onPageScroll() {} // 页面滚动钩子
onResize() {} // 用来 横屏 | 竖屏 切换时触发
onTabItemTap() {} // 当在 tabBar 上点击当前页面的 tab 时触发
Component({})
数据请求
如果请求报没有权限
- 没有添加 APPID 可能会提示没有权限请求接口
- 开发时可以在详情勾选配置里的 不校验合法域名 HTTP …,但是上线后还是不合法的(合法域名必须是 https,i 必须手动添加进后台配置 后台 - 开发 - 开发设置 - 服务器域名 - request 合法域名)
onLoad() {
wx.request({
url: '',
success: res => {
this.setData({})
}
})
}
本地存储
wx.getStorageSync('key')
wx.setStorageSync('key', value)
wx.removeStorageSync('key')
滑动组件 scroll-view
MINA 封装好了滚动组件,我们可以直接用
需要先定高,不然和普通的组件没区别
如果需要重置滚动条距离
路由传参
封装网络请求
request.js
let httpTimes = 0;
export default (options = {}) =>
new Promise((resolve, reject) => {
++httpTimes;
wx.showLoading({
mask: true,
icon: 'loading',
title: '加载中',
});
wx.request({
...options,
success: (res) => resolve(res),
fail: (error) => reject(error),
complete: () => !--httpTimes && wx.hideLoading(),
});
});
一个完整的列表页 demo
- 路由传参获取独特 ID
// father.wxml
`<navigator url="/pages/goods_list/index?id={{_id}}">导航</navigator>`
// 列表页.js
import request from '../../utils/request.js';
Page({
data: {
goodsList: [],
total: 0,
pagenum: 1,
pagesize: 10,
activeIndex: 0,
cid: '',
},
onLoad(options) {
// 获取 url 参数
const { cid = '' } = options;
this.setData({ cid });
this.getGoodsData();
},
async onPullDownRefresh() {
this.setData({ pagenum: 1 });
await this.getGoodsData();
wx.stopPullDownRefresh(); // 数据加载完毕手动停止导航栏的 loading
},
async onReachBottom() {
// 如果数据加载没了 阻止上拉的接口调用
let { pagenum, pagesize } = this.data;
let currentTotal = Number(pagenum) * Number(pagesize);
if (currentTotal >= this.data.total) {
return wx.showToast({
title: '到底了',
icon: 'none',
});
}
this.setData({ pagenum: ++pagenum });
await this.getGoodsData();
},
// methods
handleTap(e) {
const { index: activeIndex } = e.currentTarget.dataset; // tap 事件传的 dataset 参
this.setData({ activeIndex });
},
async getGoodsData() {
const { pagenum, pagesize, cid } = this.data;
await request({
url: 'https://api-hmugo-web.itheima.net/api/public/v1/goods/search',
data: {
cid,
pagenum,
pagesize,
},
}).then((res) => {
let { goods: goodsList, total, pagenum } = res.data.message;
if (pagenum != 1) { // 如果请求非第一页数据数据,则叠加,否则直接用接口返回的第一个数据就好了
goodsList = [...this.data.goodsList, ...goodsList];
}
this.setData({ goodsList, total, pagenum });
});
},
});
h5 拉微信登陆
https://segmentfault.com/a/1190000019025598
微信小程序框架
小程序不再是H5,有自己的框架和标签。API可以调用原生设备接口,服务端由腾讯云提供。小程序内嵌在微信内,但是大小只能有2M,所以开发周期短,项目小。
注意:
- 特别的是小程序不在基于HTML+JS,html有自己的标签,但在小程序内叫内置的组件
- 小程序组件的属性区分大小写,html 大小写不敏感
- 在微信小程序中,图片一般不放在当前开发环境下
- 小程序不操作DOM
- 变量使用{{}}语法,事件绑定不需要
- 云开发有免费服务器
框架
- wepy
- mpvue(美团)
- taro(较少使用)
UI库
- WEUI
开发流程
- 获取开发者ID
登录微信公众平台,点击小程序。邮箱登录,微信扫码,进入后台后找到:开发 – 开发者设置 – 开发者ID - 开发工具
- 官方工具(难用,可以预览)
开发版本不太稳定
预发布版本就是即将发布版本,有新功能较稳定
优先选择稳定版,但也有很多BUG - VScode(好用,不能预览)
- 官方工具(难用,可以预览)
- 打开工具,创建小程序项目,填写 AppId ,选一个新的空文件夹
- 可以用公司自己的服务器,对于个人也可以进行云开发(小程序提供的服务器)
工具栏
- 添加编译模式可以自定义启动页
- 通过预览按钮生成二维码来进行真机预览,用微信扫二维码就能在手机上使用。预览只有添加为开发成员扫二维码才管用
- 真机调试扫二维码后,在电脑打开调试工具,此时手机上的操作在调试工具里可以看到
- 切后台可以监听进入小程序的方式
- 上传是发布小程序的时候用
- 版本控制是内置的git
- 社区打开小程序开发社区web端
- 详情中是对小程序具体的配置设置
文件目录
components 自定义组件
components1
xx.wxml xx.js xx.json xx.wxss
pages pages下每个文件夹就是一个页面
index 页面--路由。
index.js 交互逻辑
index.json 页面配置文件
index.wxml 布局,相当于html
index.wxss 与css完全一致
logs 页面--路由
utils
utils.js 日期格式化文件
app.js 入口性文件。里边的app相当于new vue根实例
app.json 全局配置
app.wxss 样式重置
project.config.json 整个项目工具配置文件,比如项目名称,APPID
https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html
sitemap.json 搜索相关
https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html
APP结构
APP
pages
components
内置组件
button text swiper 等
自定义组件
APP 自己有三部分
app.json 全局配置
app.js 全局逻辑
app.wxss 全局样式
pages
pages.js
pages.json
pages.wxml
pages.wxss
components
app.json
在 app.json 的 pages 数组内写路径,微信开发工具会自动建立对应路径的四个文件,很方便
{
pages 数组内每一项为页面路径,顺序决定启动页,谁在第一个谁就是启动页
也可以通过上方工具栏-普通编译-添加编译模式 ,来指定启动页
"pages": [
所有的页面都必须注册,否则不显示
"pages/index/index",
"pages/logs/logs"
],
"window": {
"navigationBarBackgroundColor": "#f00", 小程序头部背景色(包含状态栏),必须十六进制颜色
"navigationBarTextStyle": "white", 头部字体颜色 black/white
"navigationBarTitleText": "小程序", 全局小程序标题
"backgroundColor": "#eeeeee", 苹果系统下拉的背景色,测试手机只能用16进制颜色,模拟器上可以用单词
"backgroundTextStyle": "light",
"enablePullDownRefresh": false 全局下拉刷新
},
"tabbar":{
如果页面没在tabbar
"selectedColor":"#ff6777", 标题acitveColor
"list":[
"pagePath":"pages/xx/xx", 路径
"text":"标题", 标题
"iconPath":"assets/tabbar/home.png", 默认图标
"selectedIconPath":"assets/tabber/home_acitve/png" 激活图标
]
}
}
.wxss
在微信中,编写 css 不可以用 less 或者 sass 等工具,但在 mpvue 库中可以用
web-wiew:在小程序里嵌入另一个webapp
服务器域名:自动反向代理获取数据,相当跨域
如果要在VScode里编辑代码,需要安装插件wxml
小程序内置git,叫版本管理
生命周期
小程序启动流程
- 下载小程序包
- 启动小程序
- 加载解析app.json
- 注册App()
- 执行App生命周期函数
- 加载自定义组件代码,注册自定义组件
- 加载解析page.jison
- 注册Page()
- 执行Page生命周期
- 渲染完毕
注册App( app.js的App()函数 )
- 判断小程序进入从场景
- 监听生命周期函数,执行业务逻辑
- 放置单例共享数据
- 全局样式
数据共享
app.js
APP({
globalData:{
name:'zhangsan'
}
})
page.js
const app = getApp()
console.log(app)
onLaunch(opitons){}:小程序初始化完成时
onLaunch只会在2小时内执行一次,或者手动删除小程序使用记录,普通的关闭小程序后在进入只执行 onShow 和 onHide 两个函数
onShow(options){}:小程序显示时
- 获取用户信息方式
1.
wx.getUserInfo({
success:function(res){
}
})
2.
<button open-type="getUserInfo" bindgetUserInfo="getUserInfo"></button>
getUserInfo(e){
console.log(e)
}
3.只展示
<open-data type="userNickName"></open-data>
type有很多值,可以在官方文档中查找
https://developers.weixin.qq.com/miniprogram/dev/component/open-data.html
onHide(options){}:小程序隐藏时
onError:小程序产生错误时
捕获错误函数
onPageNotFound:页面不存在时监听
其他:可以添加任意函数或数据变量到 object 参数内,通过 this 访问
注册Page( Page()函数 )
- 请求数据
- 绑定初始化数据
- 事件监听
页面滚动:onPageScroll
到底部:onReachBottom
下拉刷新:onPullDownRefresh
onLoad:页面被加载
数据请求必须配置,否则报不在合法域名列表中。可以在详情-不校验合法域名…临时配置
onLoad(){
wx.request({
url:'',
success:res => {
if(res.code==1){
const list = res.data.list
this.setData({
list
})
}
}
})
}
onShow:页面渲染时,关闭打开执行多次
onReady:初次渲染完成时,只执行一次,关闭打开不执行
onHide:页面隐藏时
onUnload:页面跳转时
列表渲染
wx:for="{{ }}"
区别:
- 小程序为:而非 -
- 绑定数据要用 {{}} 语法,事件监听绑定处理函数不需要{{}}
- 不用 item in 变量,而是直接使 item
<view wx:for="{{students}}">
{{item.name}},{{item.age}}
</view>
事件
事件冒泡绑定:bind*
事件捕获绑定:capture-bind:*
bind 与 catch 的区别:bind 会冒泡,catch 阻止冒泡
- 绑定事件不用 {{}} 语法
- 处理函数写在 data 同级
- 变量为 this.data.xx,而非 vue 中 this.xx
- 直接对 data 操作不会更新视图,需要使用 this.setData()
- 不能直接在绑定事件的引号内写逻辑,报错
绑定形式
- bindtap=“handleChooseImg”
- bind:tap=""
- catch:tap=""
wxml:
<view>count为{{count}}</view>
<button size="mini" bindtap="handleButtonTap">+</button>
js:
Page({
data:{ count:0 },
handleButtonTap(){
this.setData({
count:this.data.count + 1
})
}
})
组件共有事件类型
10. tap 和 longpress 只会触发一个
11. 轻触时触发:touchstart tap touchend
12. 长按时触发:touchstart longpress touchend
13. touchstart:开始触摸
14. touchmove:手指移动
15. touchcancel:触摸被中断(电话,短信等)
16. touchend:触摸结束
17. tap:轻触
18. longpress:长按 350ms 以上
特有组件特有事件类型
???
事件对象
event:
1. type:事件类型
2. timeStamp:页面打开到触发事件经历的时间戳
3. target:当前组件的一些属性集合
4. currentTarget:当前组件的一些属性集合
5. detail:触发时距离页面顶部坐标
6. touches:所有触摸点的数组
7. changedTouches:所有触摸点的数组
touches:有几个手指在小程序上触摸了就有几个
changedTouches:记录变化
例子: 在单手指 touchend 事件中,手指离开,所以 touches 为0 ,changedTouches为1
target 与 currentTarget 的区别:
target:产生事件的组件
currentTarget:记录触发事件的 view
例子: 事件冒泡时大盒子有区别
事件传参数
小程序的事件传参数不能直接在事件绑定上括号内传入,需要用到 data- 属性。
获取参数需要在 event.target.dataset 对象内获取(currentTarget)
<view
bindtap="handleTap"
data-index="{{index}}"
data-title="{{item.title}}"
>
{{item.title}}
</view>
组件
组件共有属性
- hidden — 相当于 v-show,通过 display 控制
- data-*
- bind*
- catch*
系统组件:
text
6. selectable
7. space
8. decode
https://developers.weixin.qq.com/miniprogram/dev/component/text.html
text:默认不能长安选中
换行用/n br 不管用
<text selectable></text>
除了文本节点以外的其他节点都无法长按选中
button
hover-class:当手指按下时
<button hover-class="btn_click"></botton>
open-type:
view
image
- image 单双标签都可以,inline-block 可以设置宽高
- 默认有自己大小 320x240
- 绝对路径/相对路径/网络地址
bindload:图片加载完成事件,配合懒加载
lazy-load:上下三屏懒加载
show-menu-by-longpress:长按图片菜单,识别小程序
<img
wx:for="imgArr"
lazy-load
bindload="handleImgLoad"
/>
input
- type:弹出键盘类型( text number idcard digit )
- password:暗文
- 事件监听
wxml:
<input
bindinput="handleInput"
/>
js:
handleInput(e){
console.log(e.detail.value)
}
scroll-view
- scroll-view 中的需要滑动的元素不可以用 float 浮动
- scroll-view 中的包裹需要滑动的元素的大盒子用 display:flex; 没用
- scroll-view 中的需要滑动的元素要用
dislay:inline-block
进行元素的横向编排 - 包裹 scroll-view 的大盒子有明确的宽和加上样式–>
overflow:hidden;white-space:nowrap;
滚动到最左/顶触发
bindscrolltoupper(){}
upper-threshold:距左/顶多少距离时触发scrolltoupper
滚动到最右/底部触发
bindscrolltolower(){}
lower-threshold:距右/底多少距离时触发scrolltoupper
自定义组件:
小程序起初不支持组件化,在1.6.3版本后支持。组件化要我们将页面拆分成一个个小的,可复用的组件,这样我们的代码更加方便管理和维护,拓展性更强,所以组件化不管在哪里都是非常重要的概念
通常在根目录下创建 components 文件夹,存放公共组件
组件名只能是小写字母,下划线和中划线,不能用 wx-*。
组件的 class 与页面的样式不会造成影响,自定义组件不能用 id/属性/标签选择器
components.js
Component({
options:{
sttyleIsolation:"apply-shared"
}
})
- 编写组件
需要在自定义组件的 json 中将 component 设置为 true
component.json
{
"component": true
}
- 注册自定义组件
1. 全局注册 app.json
{
"usingComponents": {
"标签名" : "绝对/相对",
"标签名" : "路径"
}
}
2. 单页面内注册 index.json:
{
"navigationBarTitleText": "首页",
"usingComponents": {
"标签名" : "绝对/相对",
"标签名" : "路径"
}
}
- 使用
<my-com/>
组件通讯
页面
数据:properties 样式:externalClasses 标签:slot 自定义事件
组件
父子通讯与 vue 类似
Page.wxml:
<my-com title="自定义标题"/>
component.js
properties: {
与 vue 类似,这两种都可以
title:String,
title: {
type: String,
value: '默认值',
observer: function (oldValue, newValue) { // 观察者
console.log(oldValue, newValue)
}
}
}
自定义模板
以前小程序不支持自定义组件,为了代码复用,用 template 很多,现在小程序支持自定义组件,template 用的比较少了。
- 定义模板使用 name 属性
- 模板在没有使用时不会被渲染
- 通过 is 属性使用模板
- 通过 data 属性传入数据
单 wxml 内:
<template name="myTemplate">
<view>{{viewData}}</view>
<text>{{textData}}</text>
code...
</template>
<template is="myTemplate" data="{{ viewData:'viewData' , key:value }}" />
wxml 模板文件封装 -- template.wxml
login.wxlm:
<import scr="绝对/相对路径" />
引用
https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/import.html
WXML 提供两种文件引用方式 import 和 include
- import 主要导入 template,不可以递归导入
- include 引入除了
<template/> <wxs/>
外的所有代码
wxs脚本
wxml 中不能直接调用函数(除了事件绑定),但有些时候需要函数去处理一些数据,类似 vue 的 filter。在小程序中只能用 wxs
- wxs 不支持 ES6
- 使用 module 属性,模块化导出
- 只能用相对路径,不能用绝对路径
- wxs 的运行环境与正常 js 隔离,不能调用其他 js 文件中的函数,也不能调用小程序的 API
- 不可作为事件回调
- 在 IOS 设备上 wxs 运行速度比 js 快2-20 倍,在安卓上无差别
wxs 可以在 wxml 中直接书写
<wxs module="info">
var name = '张三',age = 18;
function showInfo() {
return name + ':' + age
}
module.exports = {
showInfo: showInfo
}
</wxs>
<view>
{{info.showInfo()}}
</view>
也可以封装为单独的 wxs 文件
info.wxs:
var name = 'zhangsna'
module.exports = {
name:name
}
wxml:
<wxs src="../../wxs/info.wxs" module="info" /> 不能用绝对路径
<view>{{info.name}}</view>
rpx
block
block不是组件,只是一个不被渲染的标签,它只接受控制类型的属性,有两种好处
- 将需要渲染或判断的内容包裹起来
- 将属性放在block标签,提高代码可读性
- 不被渲染,性能更好
<block wx:if={{isActive}}">
<button>xx</button>
<view>xxx</view>
</block>
指令
- wx:if wx:elif wx:else
<text wx:if="{{score>=90">优秀</text>
<text wx:elif="{{score>=60}}">及格</text>
<text wx:else>不及格></text>
- wx:for
需要设置 key
与 vue 不同的是,item 和 index 默认,不用写在{{}}内,如果多层便利可以自定义名字。但是二层直接用item就可以渲染出来了
index,item:
<view wx:for="{{['a','b','c']}}"> {{item , index}} </view>
多层循环:
<wx:for="list">
<wx:for="item">{{item{}}</>
</>
或者更改名字
wx:for="list" wx:for-item="v" wx:for-index="i"
mpvue(vue语法)
在小程序内 所有 DOM/BOM 都不能操作,所以在vue中使用第三方实例化会不起作用。因此只能使用小程序自己的swiper