简介
微信小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用。
小程序是微信生态的一部分, 它提供了一种更加 方便和高效 的用户交互方式.
好处
- 用户使用的便捷性
- 普通的APP我们需要的使用过程:了解APP - 下载 - 打开 - 注册 - 使用
- 小程序版本的APP的使用过程:了解APP - 打开(扫码/搜索) - 使用
-
释放手机内存空间
- 对于手机空间不大的人来说, 安装很多App会带来灾难, 经常面临空间不足
- 小程序不需要安装, 而且有规定的大小(目前不能超过8M,包括在使用分包的情况下)
-
让手机桌面更加简洁
- 不需要下载App, 就不需要花时间来管理自己的App了
-
一端开发,多端运行(iOS端、Android端),并且底层可以调用原生的各种接口
准备 --- 申请APPID
登录微信公众平台进行申请
- 一个账号对应一个小程序
- 企业、政府、媒体、其他组织主体可以注册50个小程序
- 个体户和个人类型主体可注册5个小程序
- 每个个体户和个人类型主体可以免费拥有2个云的存储开发空间
小程序组成
-
页面布局 --- WXML (类似于HTML)
-
页面样式 --- WXSS (类似于CSS, 某些不支持,某些做了增强)
-
页面脚本 --- JavaScript + WXS (WeixinScript)
app.json --- 全局配置文件
{
// 所有的小程序页面都需要在这里进行注册
// 数组的每一项都是对应页面的json文件所在的路径地址
"pages": [
// 数组的第一项会被作为整个小程序的首页被加载
"pages/home/home",
"pages/about/about"
]
}
初体验
数据绑定
<!--
在wxml中,有2个基本的容器组件
view <-> div
text <-> span
-->
<view>
<!-- 在wxml中绑定数据使用的是mustache语法 -->
{{ msg }}
</view>
// 对于每一个页面都需要调用Page函数并传递配置对象
// 对于App组件,则需要调用App函数并传递配置对象
// 在解析的时候,小程序会自动去创建对应的Page实例或App实例
Page({
// 在data选项中定义页面的状态
// 注意: 这里是对象不是函数,小程序创建实例的时候data并没有像vue一样放到组件实例的原型上
data: {
msg: 'Hello World'
}
})
循环遍历
<!-- wx:for是小程序提供的循环指令 -->
<!-- wx:for后边需要加上变量,如果跟上的是字符串,那么会将字符串转换为字符数组后再进行遍历 -->
<view wx:for="{{ users }}" wx:key="name">
<!-- item是循环的每一项 -> 小程序默认提供的变量 -->
{{ item.name }} --- {{ item.age }}
</view>
事件绑定
<view>
{{ counter }}
</view>
<!-- button的size属性可以调整button的大小, 默认宽度为100% -->
<!--
在小程序中 事件名都是以bind开头的 全部小写的
bindtap -> 屏幕轻击事件
注意: 事件绑定的时候不需要使用mustache语法,只有在进行属性值绑定的时候才需要使用mustache语法
-->
<button size="mini" bindtap="handleTap">+</button>
Page({
data: {
counter: 0
},
// 直接在配置对象中定义对应的事件响应函数
handleTap() {
// 和React一样,需要调用setData函数后,界面才会发生更新
// 注意: 是setData方法,不是setState方法
this.setData({
counter: this.data.counter + 1
})
}
})
mvvm
小程序使用了和vue类似的MVVM模型,其中充当ViewModel的是小程序原生框架MINA框架
MVVM为什么好用呢?
- DOM Listeners: ViewModel层可以将DOM的监听绑定到Model层
- Data Bindings: ViewModel层可以将数据的变量, 响应式的反应到View层
MVVM架构将我们从 命令式编程 转移到 声明式编程
配置
小程序的很多开发需求被规定在了配置文件中
- 这样做可以更有利于我们的开发效率
- 并且可以保证开发出来的小程序的某些风格是比较一致的
在小程序中,主要有以下四种配置文件
| 文件名 | 功能 |
|---|---|
| project.config.json | 项目配置文件, 比如项目名称、appid等, 类似于package.json,jsconfig.json,tsconfig.json的功能用以对编辑器的行为和执行环境进行统一的配置(尤其是在多人协作开发的时候) 默认不需要修改,如果需要修改在小程序的IDE的详情中进行配置, project.config.json会同步更新 |
| sitemap.json | 默认情况下,小程序会开启搜索功能 这就意味着微信会使用爬虫为我们的小程序设置检索索引,以提升小程序的曝光率 如果我们某些页面不需要被微信爬虫爬取的时候,可以修改该文件 |
| app.json | 全局配置文件 |
| page.json | 页面配置文件 每一个page都有自己单独的页面配置文件 |
全局配置
全局配置比较多, 我们这里将几个比较重要的. 完整的查看官方文档
pages: 页面路径列表
- 用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径(含文件名) 信息
- 小程序中所有的页面都是必须在pages中进行注册的
window: 全局的默认窗口展示
"window": {
// 导航栏背景色 --- 只支持 HexColor
"navigationBarBackgroundColor": "#ff5777",
// 导航栏文本颜色 --- white | black
"navigationBarTextStyle": "white",
// 导航栏标题
"navigationBarTitleText": "小程序",
// 下拉列表的背景色
"backgroundColor": "#f2f4f5",
// 下拉列表的loading颜色 --- dark | light
"backgroundTextStyle": "dark",
// 是否允许下拉刷新 --- ios默认情况下就可以下拉刷新,Android默认情况下不可以下拉刷新
"enablePullDownRefresh": true
}
tabBar: 底部tab栏的展示
"tabBar": {
// 被选中是tab的颜色
"selectedColor": "#ff5777",
// 底部tab列表,个数为[2, 5]
// 只有在list数组中注册过的页面定部才会存在tab栏
// 如果一个页面并没有在list数组中进行注册,那么这个页面底部并不会出现tab栏
"list": [
{
// 跳转路径
"pagePath": "pages/home/home",
// 显示文本
"text": "首页",
// 图片路径
"iconPath": "assets/tabbar/home.png",
// 选中时候图片路径
"selectedIconPath": "assets/tabbar/home_active.png"
},
{
"pagePath": "pages/profile/profile",
"text": "设置",
"iconPath": "assets/tabbar/profile.png",
"selectedIconPath": "assets/tabbar/profile_active.png"
}
]
}
页面配置
每一个小程序页面也可以使用 .json 文件来对本页面的窗口表现进行配置
页面中配置项在当前页面会覆盖 app.json 的 window 中相同的配置项
{
"usingComponents": {},
// 注意:这里的配置是顶层配置选项,不是windows的子配置选项
"navigationBarTitleText": "分类",
"enablePullDownRefresh": false
}
双线程模型
小程序的宿主环境是微信客户端
宿主环境为了执行小程序的各种文件:wxml文件、wxss文件、js文件,提供了小程序的双线程模型
- WXML模块和WXSS样式运行于 渲染层,渲染层使 用WebView线程渲染 (一个程序有多个页面,会使 用多个WebView的线程)
- JS脚本(app.js/home.js等)运行于 逻辑层,逻辑 层使用JsCore运行JS脚本 (沙盒环境运行JS代码,不允许执行任何和浏览器相关的接口,比如跳转页面、操作DOM等)
- 两个线程都会经由微信客户端(Native)进行中转 交互
wxml和DOM树
wxml等价于一棵DOM树,也可以使用一个JS对象来模拟, 而这个JS对象就是我们所说的虚拟DOM对象
初始化渲染
WXML可以先转成JS对象(虚拟DOM树),再渲染出真正的DOM树
数据发生变化
通过setData把msg数据从“Hello World”变 成“Goodbye”
- 产生的JS对象对应的节点就会发生变化
- 此时可以对比前后两个JS对象得到变化的部分 --- DOM Diff算法
- 然后把这个差异应用到原来的Dom树上
- 从而达到更新UI的目的,这就是数据驱动的原理
界面渲染的整体流程
-
在渲染层,宿主环境会把WXML转化成对应的JS对象;
-
将JS对象再次转成真实DOM树,交由渲染层线程渲染;
-
数据变化时,逻辑层提供最新的变化数据,JS对象发生变化比较进行diff算法对比;
-
将最新变化的内容反映到真实的DOM树中,更新UI;
小程序启动流程
App的生命周期函数
每个小程序都需要在 app.js 中调用 App 方法注册小程序示例
在注册时, 可以绑定对应的生命周期函数, 在生命周期函数中, 执行对应的代码
// 调用App方法注册App组件
App({
// 页面初始化完成的时候执行的函数
// 当一个小程序退出到后台后的2小时,是一直存在的,进程并没有被杀死
// 所以此时如果再次进入小程序只会执行onShow方法,并不会执行onLanch方法
onLaunch() {
console.log('页面初始化')
// 可以在这里获取用户信息
wx.getUserInfo({
success(res) {
console.log(res)
}
})
},
onShow() {
console.log('界面显示')
},
onHide() {
console.log('界面隐藏')
},
onError() {
console.log('界面发生了错误')
}
})
在App中常做的操作
- 判断小程序的进入场景 , 常见的打开场景:群聊会话中打开、小程序列表中打开、微信扫一扫打开、另一个小程序打开
在onLaunch和onShow生命周期回调函数中,会有options参数,其中有scene值, 可以用来判断小程序进入的场景值
App({
onLaunch(options) {
console.log(options.scene)
},
onShow(options) {
console.log(options.scene)
}
})
- 监听生命周期函数,在生命周期中执行对应的业务逻辑,比如在某个生命周期函数中获取微信用户的信息
获取微信用户的基本信息的方式:
-
wx.getUserInfo
wx.getUserInfo({ success(res) { console.log(res) } }) -
button组件 – 将open-type改成getUserInfo,并且绑定bindgetuserinfo事件去获取
<button
open-type="getUserInfo"
bindgetuserinfo="handleUserInfo"
size="mini"
>
click me
</button>
Page({
handleUserInfo(e) {
console.log(e.detail.userInfo)
}
})
- 使用open-data组件展示用户信息 --- 注意: 是展示,不是获取
<open-data type="userNickName" />
<!-- 这个展示出来的结果是一个图片 -->
<open-data type="userAvatarUrl" />
- 因为App()实例只有一个,并且是全局共享的(单例对象),所以我们可以将一些共享数据放在这里
// App.js
App({
// key的名称是自定义的,但是一般取名为globalData
globalData: {
username: 'Klaus',
age: 24
}
})
// home.js
// getApp方法可以获取 App组件的实例对象
const app = getApp()
console.log(app.globalData)
Page生命周期函数
在每一个页面的Page.js中可以绑定对应的生命周期函数, 在生命周期函数中, 执行对应的代码
Page({
// 监听页面加载
onLoad() {
console.log('onLoad')
},
// 页面显示出来
onShow() {
// 在生命周期中的this的值是当前Page实例
console.log(this)
console.log('onShow')
},
// 第一次被渲染完成
onReady() {
console.log('onReady')
},
// 页面隐藏
onHide() {
console.log('onHide')
},
// 页面卸载
onUnload() {
console.log('onUnload')
}
})
在Page中常做的操作
- 在生命周期函数中发送网络请求,从服务器获取数据;
- 初始化一些数据,以方便被wxml引用展示
- 监听wxml中的事件,绑定对应的事件函数
- 其他一些监听(比如页面滚动、上拉刷新、下拉加载更多等)
Page({
// 初始化数据
data: {
user: {}
},
onShow() {
// 发生网络请求
wx.request({
// 1. 默认情况下,在小程序中所有请求的接口都需要在控制台(网页端)进行配置,否则会无法请求
// 2. 在开发的过程中,为了调试方便,可以在详情选项卡中暂时关闭合法域名校验,此时就可以正常发生网络请求
url: 'http://httpbin.org/get?name=klaus&age=23',
// success方法推荐使用箭头函数进行定义
// 如果使用箭头函数进行定义,内部的this就是当前的Page实例
// 如果使用的是普通的function定义方式,那么内部的this就是undefined (因为小程序内部没有globalThis)
success: res => {
// console.log(this)
// 具体获取的数据可以在小程序开发工具的AppData选项卡中进行查看
this.setData({
user: res.data.args
})
}
})
},
// 绑定自定义事件
handleTap() {
// 自定义事件中的this依旧是Page实例
console.log(this)
},
// 内置事件
onPageScroll(detail) {
console.log(detail.scrollTop)
},
onReachBottom() {
console.log('触底了')
},
onPullDownRefresh() {
console.log('下拉刷新')
}
})
、