1、小程序基础一
小程序开发
目录结构
pages
utils
other
小程序配置
项目配置文件
站点映射文件
全局配置文件
页面配置文件
小程序开发初探
MVVM架构
双线程模型
App实例
onLaunch生命周期
onShow生命周期
场景值
全局数据共享
本地数据存储
Page实例
data对象
普通事件处理函数
内置事件处理函数
onPullDownRefresh
生命周期钩子函数
生命周期流程
目录结构
pages
utils
other
小程序配置
项目配置文件
站点映射文件
全局配置文件
页面配置文件
小程序开发初探
MVVM架构
双线程模型
App实例
onLaunch生命周期
onShow生命周期
场景值
全局数据共享
本地数据存储
Page实例
data对象
普通事件处理函数
内置事件处理函数
onPullDownRefresh
生命周期钩子函数
生命周期流程
小程序开发
原生小程序开发最主要包含三个技术,第一个为WXML,它类似HTML,WXML是小程序的文档语言,内部有各种各样的元素,这些元素在WXML中称之为组件,甚至有的组件和原生HTML的元素是一摸一样的,比如text、button、input等;
第二个为WXSS,和CSS大体上差不多,只不过内部做了很多的优化,比如某些选择器在WXSS中不支持,同时,也对某些技术进行了增强,但是基本上都是一致的;
第三个为JavaScript+WXS,在小程序内我们可以直接使用原生JavaScript语言,同时在这个基础上,还增加了WXS语言,它本质也是JavaScript代码,但是它对JavaScript进行了进一步的封装;
目录结构
当我们使用微信开发者工具创建出一个项目之后,默认会生成一个基本的小程序的项目结构,如下示例;
[cce@doorta ~]# tree /usr/local/Project/wechat
├── app.js
├── app.json
├── app.wxss
├── pages
│ ├── index
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ └── logs
│ ├── logs.js
│ ├── logs.json
│ ├── logs.wxml
│ └── logs.wxss
├── project.config.json
├── project.private.config.json
├── sitemap.json
└── utils
└── util.js
pages
pages目录主要是用来存放页面的一个目录,该目录下面每一个子目录都是一个页面,每个页面下面总共有四类文件,即后缀为wxml、wxss、js和json;
后缀为wxml为小程序内容结构文件,类似html的超文本语言,后缀为wxss为样式文件,类似css,后缀为js为逻辑文件,后缀为json为页面配置文件;
utils
该目录非常简单,其主要的作用就是用来存放封装工具模块的一个目录,该目录下面,我们可以存放实现特定功能的功能函数以模块的方式放在utils目录下;
other
除了上述两个目录之外,还有非常多的文件,这些文件都在项目根目录下,它们的主要作用就是为了对整个项目进行设置的文件,如下;
app.js:小程序逻辑文件,如创建App对象,设置App的生命周期等;
app.json:App的全局配置文件;
app.wxss:App的全局样式文件;
project.config.json:项目配置文件;
project.private.config.json:和project.config.json一样,主要用来配置当前小程序项目,但是该配置的优先级更高;
sitemap.json:用于配置小程序及其页面是否允许被微信索引;
小程序配置
在默认情况下,我们使用微信开发工具创建一个小程序框架,会有很多的配置文件,如app.js、app.json、project.config.json和project.private.config.json;
此外,小程序的很多开发需求是被归档在配置文件当中的,也就是说,在小程序当中想要开发一个页面,不一定需要我们自己去手动开发这个页面,我们可以直接通过配置,将页面直接创建出来,这样非常有利于我们的高效开发;
项目配置文件
在小程序中,project.config.json作为整个小程序项目的配置文件,该文件的主要作用就是声明当前项目的一些配置信息,比如项目名称、描述、版本、依赖的基础库版本甚至AppID等信息,具体的配置信息可查看微信小程序开发文档中的工具栏的设置项;
其实,我们也很少去直接修改project.config.json中的配置,一般来讲,我们都会去修改project.private.config.json中的配置,它们两者的作用是一样的,但是project.private.config.json的优先级较高,只不过为了更加方便多人开发,所以微信在后面加入了project.private.config.json的配置,在project.private.config.json配置文件中,我们可以加入一些个人的设置,然后将这个配置文件加入.gitignore文件,进行忽略提交,从而解决版本管理的冲突文件;
站点映射文件
在一个小程序框架中,有一个sitemap.json的配置文件,微信开放了小程序内的内容搜索功能,所以开发者可以通过sitemap.json配置文件,或者管理后台页面收录开关来配置小程序的页面是否允许微信索引,当开发者允许微信索引时,微信会通过爬虫的形式,为小程序的页面内容建立索引,当用户的搜索词条触发该索引时,小程序的页面将可能展示在搜索结果中,有点类似SEO优化;
所以,如果我们希望将小程序的内容能够被微信的搜索框搜索到,我们可以直接在sitemap.json配置文件中进行相应的配置,默认情况下,所有的页面都可以被搜索,具体的配置信息,请查看微信官方文档;
全局配置文件
全局配置文件就是项目根目录下面的app.json文件,它主要用来配置框架的一些配置信息,比如路由列表、页面窗口、tab栏等,这些配置项非常多,具体的请查看微信官方文档;
pages配置为一个列表,列表内每一个元素都是一个页面路径,小程序中所有的页面都必须在pages中进行注册,才可以能被使用;
window配置主要的作用就是用来配置全局的默认窗口展示,内部有非常多的属性,比如导航栏的颜色、导航栏标题的颜色、导航栏标题内容等等;
tabBar配置,主要是用来配置底部的tab栏的展示,直接通过配置的方式来定义底部的tabbar;
页面配置文件
项目根目录下的app.json配置文件,主要是用来规定整个项目的配置文件,那么如果我们希望对某个页面进行一些单独的配置,我们就需要用到页面配置文件,在每个页面目录下,都有一个以json结尾的一个文件,该文件就可以用来配置指定页面的一些配置信息,具体的请查看官方文档;
小程序开发初探
对于小程序模版语言,其实和Vue的模版语言,很相像,小程序内也有插值语法,同样的它也是使用Mustache插值语法来实现的,此外,对于小程序来讲,它也有专属自己的循环、条件判断语法,在这一块的某些地方也和Vue相似,比如每次循环出来元素都建议加上一个key的属性,用来优化动态渲染,如下示例;
// pages/home/home.js
Page({
data: {
text: "Mustache插值语法",
names: ["cce", "cfj", "csw"]
}
})
<!--pages/home/home.wxml-->
<view style="margin-left: 10px;">
<text>{{ text }}</text>
<view wx:for="{{ names }}" wx:key="*this">
{{item}}
</view>
</view>
同样的,在小程序内,也支持事件处理,在元素上添加一个bindtap的属性来指定点击事件发生时需要调用的事件处理函数,比如我们通过一个按钮,来对一个响应式数据进行动态变化,但是,我们需要知道的是,默认情况下,数据并非响应式,如果我们希望将一个数据变成响应式,需要调用this.setData的函数来实现,该函数接受一个参数,该参数为一个对象,在这个对象内,可以指定对数据的操作方法
// pages/home/home.js
Page({
data: {
counter: 0
},
add() {
this.setData({
counter: this.data.counter + 1
})
}
})
<!--pages/home/home.wxml-->
<view style="margin-left: 10px;">
<view>
<text>{{ counter }}</text>
</view>
<button size="mini" type="primary" bindtap="add">按钮</button>
</view>
MVVM架构
小程序的整体开发思想和Vue的开发思想是非常相似的,只不过是在语法上有一些微小的差异,但是它们的开发思想都是一样的,所以在掌握Vue之后,再来开发小程序,会发现,很多地方都是差不多的,因此,软件开发最重要的还是掌握其开发思想;
小程序也是采用MVVM的架构,和Vue是一样的,在小程序当中也是有自己的View视图,另外,它也包含自己的逻辑层,即Model,然后View视图层和Model逻辑层就是通过小程序的底层框架,ViewModel层进行结合的,ViewModel层将View视图层和Model层进行了结合;
在ViewModel层,有两个主要的作用,第一个作用,就是将Model层的数据绑定到View层,第二个作用就是监听View的变化,一旦View层数据发生变化,能够直接将该变化反应到Model层,从而实现数据双向绑定;
双线程模型
小程序是运行在微信客户端之上的,所以其实它的基于微信的,当我们编写的各种小程序代码文件,如wxml文件、wxss文件或js文件,最终都是交给微信来解析的,所以微信除了基本的聊天、语音、视频等功能之外,它还可以充当一个浏览器的作用;
当我们在微信中打开一个小程序之后,它会将这个小程序的wxml文件、wxss文件或js文件缓存到本地,然后进行渲染,那么微信为了去渲染这些文件,需要用到一个WebView控件,微信首先会将wxml文件、wxss文件或js文件编译成WebView能够识别的元素,从而用户就能看到对应的页面;
但是因为JS逻辑中如果存在网络请求,或者类似setTimeout这种阻塞性技术,那么WebView控件就可能会阻塞住整个渲染过程,从而导致界面卡顿;
那么因为这种问题,所以小程序就采用了双线程模型,第一个线程主要是用来做渲染的,主要是处理DOM树创建、CSS解析、样式计算等等,第二个线程专门用来执行JS代码逻辑,那么当JS逻辑执行完成之后,会通过将结果,通过微信客户端,来进行中转,交给第一个WebView线程来进行渲染,所以微信就采用了双线程模型解决了页面卡顿的问题;
App实例
对于小程序来讲,当用户打开微信当那一刻微信本身首先会注册一个小程序的App,这个App就是根据小程序框架内有一个app.js的文件来注册的,该文件内必须存在一个App函数,在该函数内可以绑定对应的生命周期函数,然后作为开发者,我们可以根据不同的生命周期函数来完成不同的功能,比如判断小程序的进入场景,是通过分享进入的还是通过搜索进入的等等;
甚至在正式进入小程序之前,还可以加入用户登陆的逻辑或网络请求的一系列的操作,那么由于App实例在全局只有一个,所以App实例其实是一个单例对象,因此我们也可以将一些共享的数据放在这个APP函数内,然后在别的地方通过getApp()函数,来拿到这个App实例,继而取出对应的数据;
那么在这个App实例内,一共有七个生命周期函数,此外,作为开发者,我们还可以加入一些自定义的一些数据,如下;
生命周期函数
|
类型
|
必填
|
描述
|
onLaunch
|
function
|
否
|
监听小程序初始化,只触发一次;
|
onShow
|
function
|
否
|
监听小程序启动或从后台切换到前台;
|
onHide
|
function
|
否
|
监听小程序从前台切换后台;
|
onError
|
function
|
否
|
监听小程序发生脚本错误或API调用报错时触发,也可以使用wx.onError绑定监听;
|
onPageNotFound
|
function
|
否
|
小程序要打开的页面不存在时触发,也可以使用wx.onPageNotFound绑定监听;
|
onUnhandledRejection
|
function
|
否
|
小程序有未处理的Promise拒绝时触发,也可以使用wx.onUnhandledRejection绑定监听;
|
onThemeChange
|
function
|
否
|
系统切换主题时触发(黑天/白天),也可以使用wx.onThemeChange绑定监听;
|
其他
|
any
|
否
|
开发者可以添加任意的函数或数据变量到Object参数中,可以直接使用this可以访问;
|
onLaunch生命周期
onLaunch生命周期函数为初始化生命周期函数,即onLaunch钩子函数,该钩子函数会在用户首次进入小程序时触发,所以,它只会触发一次,同时,该函数默认接受一个参数,小程序在回调该钩子函数时,会传入一个对象,该对象内包含,访问路径、参数、场景等信息,如下示例;
// app.js
App({
onLaunch(options) {
console.log("监听小程序初始化,参数为:", options)
}
})
# 首次进入小程序之后的输出信息为
监听小程序初始化,参数为:
{path: "pages/home/home", query: {…}, scene: 1001, shareTicket: undefined, referrerInfo: {…}, …}
onShow生命周期
onShow生命周期函数,和onLaunch生命周期函数类似,但是onLaunch生命周期函数只会执行一次,而onShow生命周期函数会执行多次,在进入小程序时,会调用一次,当微信从后台切换到前台也会调用一次,同样的,该生命周期函数也接受一个参数,该参数和onLaunch生命周期函数的值是一样的,如下;
// app.js
App({
onShow(options) {
console.log("监听小程序初始化,参数为:", options)
}
})
# 首次进入小程序之后的输出信息为
监听小程序初始化,参数为:
{path: "pages/home/home", query: {…}, scene: 1001, shareTicket: undefined, referrerInfo: {…}, …}
# 从后台切换到前台的输出信息为:
监听小程序初始化,参数为:
{path: "pages/home/home", query: {…}, scene: 1011, shareTicket: undefined, referrerInfo: {…}}
场景值
对于onLaunch、onShow及onHide三个回调函数的参数对象内默认有一个属性,即scene,该属性为一个串数字,该数字为场景值,即从何处进入微信,不同的场景下打开小程序都有不同的场景值,非常之多,具体的请查看官方文档;
全局数据共享
在App函数内,除了定义生命周期函数,我们还可以将一些一成不变的数据放在App函数内,但是这里需要关注一个关键词,即一成不变,因为它不是响应式的,我们无法动态去修改它;
那么将这些自定义数据放在App函数内之后,我们可以在别的地方使用getApp这个全局函数对象来直接获取,该函数返回一个对象,这个对象就是我们在定义App时传入的这个对象,如下示例;
// app.js
App({
userInfo: {
username: "cce"
}
})
// pages/home/home.js
Page({
onLoad() {
const app = getApp()
console.log(app.userInfo)
}
})
# 输出结果
{username: "cce"}
如果我们希望将这个数据在WXML中使用,那么我们就需要将这个数据放在页面的Page函数传入对象的data对象内,此时,我们可以使用sedData来像data对象来动态的新增数据,如下示例;
// pages/home/home.js
Page({
onLoad() {
const app = getApp()
let username = app.userInfo.username
this.setData({
username // 对象简写形式
})
},
data: {}
})
<!--pages/home/home.wxml-->
<view style="margin-left: 10px;">
<text>{{ username }}</text>
</view>
本地数据存储
onLanch生命周期函数,在实际应用场景下,一般用来进行用户登陆,也就是说,如果当用户首次进入小程序一般都会在onLaunch生命周期函数中进行登陆,因为一旦在onLaunch里面登陆成功之后,在访问其他页面时,都是一个登陆的状态,且onLanch生命周期函数也非常符合登陆的逻辑,每次进入小程序只会调用一次;
但是这里有一个问题,就是如何实现状态的保存问题,当用户第一次进入小程序进行了登陆,虽然在登陆之后,我们将这个数据保存在了全局对象当中,但是这个数据始终都是存在内存当中,所以每次关闭小程序,这个数据就丢失了;
所以为了解决这个问题,我们其实也可以将这个用户登陆的状态保存在本地存储当中,微信小程序也是支持类似浏览器的LocalStorage的,那么我们将这个状态保存在本地存储中后,就可以实现无需二次登陆的功能;
那么想要使用微信小程序的本地存储,就需要用到小程序的一个API,即使wx.setStorageSync和wx.getStorageSync,前者存,后者取,如下示例;
// app.js
App({
userInfo: {},
onLaunch() {
if (!wx.getStorageSync('token') && !wx.getStorageSync('username')) { // 如果本地存储没数据,那么通过网络请求拿到切同时存储到全局共享对象和本地存储中
const token = "asdsadsafdsdasdd";
const username = "cce";
wx.setStorageSync('token', token);
wx.setStorageSync('username', username);
this.userInfo.token = token;
this.userInfo.username = username;
} else { // 如果全局共享对象中没有数据,但是本地存储有数据,那么就将本地存储的数据加载到全局共享对象当中
this.userInfo.token = wx.getStorageSync('token');
this.userInfo.username = wx.getStorageSync('username');
}
}
})
Page实例
在小程序内,每一个页面都会有一个js结尾的文件,该文件内一般都有一个Page函数,该Page函数就是向App实例来注册一个页面,那么在注册页面的时候,和App实例一样,只不过App实例它针对的是全局性的,而Page实例只针对于当前页面;
那么在使用Page函数注册页面时,和App实例一样,我们可以绑定一些初始化数据,这些数据可以在本页面进行数据共享,同时,我们还可以绑定一些生命周期,当当前页面在某种特殊的场景下,小程序框架会自动调用这些生命周期函数,从而实现不同的功能,此外,我们还可以在Page实例内注册一些事件处理函数,当当前页面发生某些事件的时候,可以绑定对应事件处理函数;
data对象
在Page实例当中,有一个data对象,它和Vue的createApp实例内的data对象很相似,它们都是用来初始化当前页面数据的,换而言之,这个里面的数据可以直接被当前页面的WXML文件使用Mustache插值语法所引用,同时,在data对象内的数据,我们也可以直接在Page实例当中的事件处理函数、生命周期函数直接使用this.data直接引用,如下示例;
// pages/page/page.js
Page({
data: {
name: "cce"
}
})
<!--pages/page/page.wxml-->
<text>{{ name }}</text>
普通事件处理函数
在Page实例当中,我们也可以定义一些普通的事件处理函数,那么当我们将这些事件处理函数,和页面当中的事件进行绑定时,会自动调用当前页面的Page实例内对应的事件处理函数,这一点,同样的,和Vue非常相像,如下;
<!--pages/page/page.wxml-->
<button size="mini" type="primary" bindtap="clickEvent">触发事件</button>
// pages/page/page.js
Page({
clickEvent(event) {
console.log(event)
}
})
内置事件处理函数
在小程序中,每一个Page实例内,都有众多的内置事件处理函数,当某些特定的事件发生之后,小程序会默认的去调用对应的事件处理函数,比如常见的页面刷新事件、上拉触底事件、下拉触底事件、页面滚动事件等等;
如下,这就是Page实例当中内置的所有事件处理函数,此外,这些事件处理函数其实有很多参数可以定义,具体的使用方法还请查看官方文档,不在此做过多的搬运;
事件处理函数
|
类型
|
描述
|
onPullDownRefresh
|
function
|
监听用户下拉刷新事件;
|
onReachBottom
|
function
|
监听用户上拉触底事件;
|
onPageScroll
|
function
|
监听用户滑动页面事件;
|
onAddToFavorites
|
function
|
监听用户点击右上角菜单”收藏“按钮的行为,并自定义收藏内容;
|
onShareAppMessage
|
function
|
监听用户点击页面内转发按钮(button 组件 open-type=”share”)或右上角菜单“转发”按钮的行为,并自定义转发内容;
|
onShareTimeline
|
function
|
监听右上角菜单“分享到朋友圈”按钮的行为,并自定义分享内容;
|
onResize
|
function
|
监听小程序屏幕旋转时触发的事件;
|
onTabItemTap
|
function
|
点击tab时触发的事件;
|
onSaveExitState
|
function
|
每当小程序可能被销毁之前,页面回调函数 onSaveExitState 会被调用,可以进行退出状态的保存;
|
onPullDownRefresh
onPullDownRefresh为监听用户刷新页面事件,使用该事件,需要在app.json的window选项中或页面配置中开启enablePullDownRefresh,可以通过wx.startPullDownRefresh触发下拉刷新,调用后触发下拉刷新动画,效果与用户手动下拉刷新一致,当处理完数据刷新后,wx.stopPullDownRefresh可以停止当前页面的下拉刷新;
Page({
onPullDownRefresh() {
console.log("用户刷新页面")
}
})
生命周期钩子函数
在Page实例当中,小程序也提供了针对页面的一些生命周期函数,当页面流转到指定的阶段时,会自动调用对应的生命周期函数,那么页面的生命周期函数,一共有五个,它们的执行顺序就是排列顺序,如下示例;
生命周期函数
|
类型
|
描述
|
onLoad
|
function
|
页面加载时触发,一个页面只会调用一次,可以在onLoad的参数中获取打开当前页面路径中的参数;
|
onShow
|
function
|
页面显示/切入前台时触发;
|
onReady
|
function
|
页面初次渲染完成时触发,一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互;
|
onHide
|
function
|
页面隐藏/切入后台时触发, 如wx.navigateTo或底部tab切换到其他页面,小程序切入后台等;
|
onUnload
|
function
|
页面卸载时触发,如wx.redirectTo或wx.navigateBack到其他页面时;
|
生命周期流程
这些生命周期函数的使用,都是非常简单的,我们只需要搞懂它具体在什么时候调用即可,首先,我们必须确定一点,小程序采用双线程模型,对于WXML和WXSS会在一个独立的线程中执行,而以JS和JSON结尾的文件会在第二个线程运行;
当第一个线程渲染WXML和WXSS时,首先会进行初始化,然后它会等待第二个线程的onLoad生命周期函数执行,当onLoad生命周期函数执行完成之后,会调用onReady生命周期函数函数,随后,第二个线程会将数据发送到第一个线程,第一个线程会正式进行页面渲染,此致,小程序的第一次渲染执行完成;
那么当,第一次渲染执行完成之后,我们通过某种手段调用了this.setData函数修改了data中数据,且这个数据在WXML中使用Mustache插值语法引用,那么当第二个线程修改完成data对象中的数据之后,会调用onReady生命周期函数,调用完成之后,会通知第一个线程重新渲染页面,次为第二次渲染;
那么当,我们的小程序被切换到后台时,第二个线程会首先调用onHide生命周期函数,那么当页面从后台切换到前台时,会首先调用onShow事件周期函数,随后将页面切换到前台;
如果,当页面通过wx.redirectTo或者wx.navigateBack时,会首先调用onUnload事件周期函数,如下图;