逻辑层是小程序对业务逻辑进行处理的地方。对于开发者而言,小程序的逻辑层就是所有 js 脚本文件的集合。虽然微信小程序开发框架 MINA 的逻辑层是使用JavaScript 编写的,但是小程序使用的 JavaScript 与我们平常在 Web 开发中使用的 JavaScript 并不完全一致。一方面,小程序在 JavaScript 语法的基础上增加了一些新的 API,以提高小程序开发的效率和能力;另一方面,由于小程序并非运行在浏览器中,所以诸如 document、window 等 JavaScript 在 Web 中的一些能力是无法使用的。
一、微信小程序文件结构
一般来说,一个完整的微信小程序项目的基础目录结构如下所示:
从目录结构可以看出,一个小程序项目分为两个部分:主体文件和页面文件。除此之外,有的小程序还会有一些工具类页面,这些文件可能会方便我们的项目开发(对微信小程序来说,这些并不是必需的)。
二、微信小程序主体文件
小程序的主体文件是全局文件,必须放在项目的根目录,它的配置会影响整个小程序项目。小程序的主体文件主要由 3 个文件组成,如下所示:
三、微信小程序页面文件
微信小程序的页面文件主要由四个文件组成,如下所示:
小程序的页面文件都放在 pages 目录下,每个页面都有一个独立的文件夹。例如,要实现 index 页面,我们就会在 pages 目录下新建一个 index 文件夹,在index 文件夹下面放置 4 个基本文件:index.wxml 作为页面结构描述,负责页面渲染(开发者通过 wxml 就可以完成列表条件渲染、模板、事件绑定等操作,微信小程序框架还为开发者提供了一系列基础组件以便快速组合开发);index.json 作为页面配置,可以覆盖上述主体文件中 app.json 的配置;index.wxss 是针对 index.wxml 的样式补充;index.js 用来作为业务逻辑处理。
在项目目录中,js、app.json、wxml、wxss 文件会经过编译,因此上传之后无法直接访问,其中 wxml 和 wxss 文件仅针对在 app.json 中配置了的页面。除此之外,只有后缀名在白名单内的文件可以被上传,不在白名单内的文件可以在开发工具内访问,但无法被上传。
四、微信小程序的配置
微信小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,决定页面文件的路径(pages)、窗口表现(window)、设置网络超时时间(networkTimeout)、设置多 tab(tabBar)等。
4.1 全局配置文件解析
app.json 各个配置项的含义不同,但是并不需要对每一项进行单独设置。为了增强小程序自定义的能力,小程序官方提供了强大的配置项表,可以通过阅读微信小程序官网的全局配置项描述来了解全局配置(3 个核心配置项的内容:页面路由配置项、窗口表现配置项和底部标签导航配置项)的含义。
4.2 页面路由配置项
页面路由配置项为 pages 参数,它定义了一个数组,用来存放当前项目中所有可以访问的路径。小程序的所有页面都需要在这个配置项中完成定义。定义页面时无须写文件的后缀,小程序框架会自动将对应的文件解析为 js、json、wxml、wxss文件去寻路。
微信小程序后台不否会按照开发者的定义顺序(开发者定义的顺序仅仅用于定义页面路径)逐一执行,实际执行时是按照字母序(文件排序方式)加载的。当然,默认的第一个页面会设定为小程序的首页。
如果配置的页面路由地址不存在,小程序开发工具就会自动生成对应的文件夹及文件夹下对应的 js、json、wxml、wxss 基本文件。
4.3 窗口表现配置项
窗口表现配置项的 key 为 window 属性,主要包含窗口展示配置和窗口效果配置两部分。窗口展示配置包括导航栏背景颜色配置(navigationBarBackgroundColor)、导航栏标题颜色配置(navigationBarTextStyle)、导航栏标题文字内容配置(navigationBarTitleText)、导航栏样式配置(navigationStyle)等;窗口效果配置主要包含是否开启全局的下拉刷新(enablePullDownRefresh)、屏幕旋转方向设置(pageOrientation)等。该配置项的内容基本是可选的。
对大部分开发来说,只需要完成配导航栏标题文字内容配置(navigationBarTitleText) 即可,这个配置标注了小程序默认的导航栏标题文字内容。
4.4 底部标签导航配置项
很多移动 App 都包含底部标签导航配置,如果你要开发的微信小程序是一个多 tab 应用(即客户端窗口的底部或顶部有 tab 栏可以切换页面),那么就可以通过tabBar 配置项指定 tab 栏的表现以及 tab 切换时显示的对应页面等。当然,开发者也可以自己实现底部标签导航的效果,不过微信小程序为大家提供了标准的底部标签导航的样式,只需要进行简单的配置就可以实现这个效果。
这个配置项为 tabBar 属性,它包含 4 个必填属性:color 属性是指 tab 上的文字默认颜色,仅支持十六进制颜色;selectedColor 属性是指 tab 上的文字选中时的颜色;backgroundColor 指出了 tab 的背景色;list 属性是 tab 的列表数组,它只能配置最少 2 个、最多 5 个 tab。
list 配置项是这个配置中的核心,它是一个数组,tab 按数组的顺序排序,每个项都是一个对象,每个对象包含 pagePath、text 两个必填项和 iconPath、selectedIconPath 两个选填项。后面两个配置项是 tabBar 支持配置图片 icon。
五、页面配置文件解析
除了全局配置文件外,每一个小程序页面也可以使用同名 json 文件来对本页面的窗口表现进行配置,这个配置通常比 app.json 的配置简单很多。
页面配置文件只能设置全局配置文件中 window 相关的配置项,也就是可以指定各个页面的窗口表现。页面配置文件中的配置项会覆盖 app.json 的 window 中相同的配置项。例如,如果 app.json 中的 window 字段里配置了navigationBarBackgroundColor,同时该页面也在自己的 page.json 中配置了navigationBarBackgroundColor,那么小程序的框架会优先采用页面的 JSON配置项。
备注:app.json 本身是标准的 JSON 格式,因此开发者不可以在配置中设置注释。
六、微信小程序的场景值
场景值用来描述用户进入小程序的路径,可以帮助开发者了解用户是如何访问开发者的微信小程序的。场景值的作用有很多,我们通常用来做一些场景营销。例如,你可以做“添加到小程序”即可获得礼包的活动,引导用户将你的小程序加入收藏。(不过现在微信官方已经不鼓励使用这种营销方案了,因此你无法区分用户是从“我的小程序”访问的还是从“最近的小程序”访问的)。我们还可以利用场景值做一些数值统计,例如,统计用户大多是从哪里打开小程序的,方便我们关注渠道的质量。一些线上和线下场景也可以使用场景值,假如你做的是订餐小程序,那么如果用户是扫描店内的二维码打开的,我们可以认为这是在店内就餐的用户,因此可以直接进入点餐模式;而如果用户是通过“公众号菜单”打开小程序的,则可以认为该用户是在远程订餐,因此可以进入预定模式。还有一些特殊的场景,例如场景 1129(微信爬虫访问)可以用来对爬虫做一些优化,方便索引;场景 1038(从另一个小程序返回)可以用来对小程序免密签约的场景进行判断。
开发者可以在 App 的 onLaunch 和 onShow 或wx.getLaunchOptionsSync 中获取上述场景值。参考代码(options.scene 就代表当前的场景值)如下:
七、页面注册与生命周期
微信小程序是通过 app.js 来管理生命周期的。小程序生命周期的概念虽然简单,但是贯穿小程序开发的始终。
7.1 微信小程序生命周期的注册
一般使用 App(Object object) 来注册小程序,从而管理自己的生命周期。每个小程序都需要在 app.js 中调用 App() 方法注册小程序示例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。在 app.js 中,这个函数接收一个 Object参数,其中指定了小程序的生命周期回调。需要注意的是,App() 必须在 app.js 中注册。在一个小程序中,App() 方法有且仅有一个,不能注册多个,否则可能会出现无法预期的后果。小程序代码的每个根目录下都有一个名为 app.js 文件的生命周期函数,其核心代码如下所示:
微信小程序启动后,后台会首先完成微信小程序的初始化,该过程全局只会触发一次;之后会完成显示的工作;用户可以操作微信小程序从前台进入后台以及从后台恢复到前台显示;微信小程序在后台运行一段时间,当系统资源不足时会被注销。app.js 除了定义生命周期外,还有一些其他的妙用。如果需要在微信小程序中定义一个全局变量,就可以利用 app.js 文件来实现。微信给开发者提供了 getApp() 函数(可以定义一些全局变量、绑定一些全局的事件处理函数),在微信小程序代码的任何地方都可以调用它。它会返回一个微信小程序的实例供业务逻辑使用,这样在需要使用微信小程序对象相关属性时就可以引用它。
(1) App() 必须在 app.js 中注册,不可以在 App() 函数内或在定义 App() 函数前调用 getApp() 方法;
(2) 通过 getApp() 方法获取实例后,不可以私自调用生命周期函数。
7.2 页面生命周期的注册
小程序不仅有应用的生命周期,还有页面的生命周期。应用的生命周期会影响页面的生命周期。小程序中的每个页面都需要在页面对应的 js 文件中进行注册,以指定页面的初始数据、生命周期回调、事件处理函数等。可以使用 Page 构造器注册页面,也可以使用 Component 构造器构造页面。简单的页面可以直接使用 Page(params) 进行构造,通过设定不同的 params 来指定页面的初始数据、生命周期回调、事件处理函数等。
页面的生命周期比应用的生命周期丰富,它除了有应用生命周期中的 onShow()、onHide() 方法外,还有 onLoad()、onReady()、onUnload()、onPullDownRefresh()、onShareAppMessage() 等方法。另外,开发者还可以添加任意函数或数据到 Object 参数中,来定义一个方法,在页面的函数中可以使用this 访问。
开发者也可以使用 getCurrentPages() 函数获取当前页面栈。该函数用于获取当前页面栈的实例,并以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。而 Page.prototype.data 就是当前页面的数据内容。
开发者指定小程序的每个页面都有一个 data 对象来存放当前页面的数据,可以使用Page.prototype.setData() 来修改。最常犯的错误之一就是直接修改 data属性的值来修改页面数据,而没有使用原型中的 setData() 方法。当然,也可以利用 getCurrentPage() 函数获取当前页面的实例。
八、渲染页面
不论使用哪种前端渲染模板引擎,都需要掌握的 3 个重要功能:如何渲染页面、如何绑定事件、如何修改页面。
8.1 如何渲染页面
在代码中写的初始化 data 数据将传递给渲染层进行第一次渲染。data 会以JSON 的形式由逻辑层传至渲染层,所以数据必须可以转成 JSON 的格式:字符串、数字、布尔值、对象、数组。
渲染层可以通过 WXML 对数据进行绑定:
8.2如何绑定事件
要处理事件,除了初始化数据和生命周期函数,Page 中还可以定义一些特殊的函数:事件处理函数。渲染层可以在组件中加入事件绑定,当触发事件时,就会执行 Page 中定义的事件处理函数。
8.3 如何修改页面
完成第一次渲染和事件函数绑定后,可能还需要更新页面的渲染。不同于传统Web 开发需要通过指定DOM节点进行调整,小程序支持数据的双向绑定,因此可以使用 setData(Object data, Function callback) 函数随时调整页面的数值,从而引起页面的变化。
Object 以 key: value 的形式表示,将 this.data 中的 key 对应的值改变成value。其中 key 以数据路径的形式给出,支持改变数组中的某一项或对象的某个属性,如 array[2].info、a.b.c.d 等,并且不需要在 this.data 中预先定义。开发者不应该直接修改 this.data 的值,这不仅无法改变页面的状态,还会造成数据不一致,而是应该通过调用 this.setData() 方法来修改。很多Vue.js 前端开发在刚开始写微信小程序时会遇到这个问题,因为在双向绑定的语言Vue.js中是可以直接通过修改 this.data 来修改绑定数据的。
这个函数仅支持可 JSON 化的数据(即字符串、数字、布尔值、对象、数组)。单次设置的数据不能超过 1024 KB,尽量避免一次设置过多的数据。但不要把 data 中任何一项的 value 设为 undefined,否则这一项将不被设置,还可能遗留一些潜在异常问题。
九、文件作用域
在 JavaScript 文件中声明的变量和函数只在该文件中有效,在不同的文件中可以声明相同名字的变量和函数,不会互相影响。
当我们需要设置全局变量时,应在微信小程序可以设置一个全局变量。通过全局函数 getApp() 可以获取全局的应用实例,如果需要全局的数据,可以在 App() 中设置。
十、注册路由
在微信小程序的框架中是以栈的形式维护当前所有页面的。当发生路由切换的时候,页面栈就会产生变化,微信小程序的页面生命周期也会产生变化。微信小程序页面栈的表现形式如下所示:
对于不同的 tab 页面产生的 tab 切换,对应的生命周期变化会稍微复杂一些,主要是因为不同的 tab间切换时会触发老 tab 的 onHide() 和新 tab 的 onShow(),如果 tab 是第一次载入还会触发 onLoad()。另外从非 tab 页面跳转回到 tab 页面时,会触发老页面的销毁。
页面路由的跳转的几个注意事项:
(1)navigateTo、redirectTo 只能打开非 tabBar 页面。
(2)switchTab 只能打开 tabBar 页面,但是不能传递参数。
(3)reLaunch 可以打开任意页面。
(4)页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有tabBar。
(5)调用页面路由带的参数可以在目标页面的 onLoad 中获取。
十一、模块化
对于任何一个现代化框架而言,模块化都是现代化编程的标配,小程序也不例外。在小程序中,我们可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口。
例如,我们可以定义一个公用方法,然后通过 module.exports 或者 exports 对外暴露接口。
在需要使用这些模块的文件中,使用 require() 将公共代码引入:
这样我们就可以在不同的页面使用这个预定义的方法了。使用时应注意事项:
(1) exports 是 module.exports 的一个引用,在模块里随意更改 exports 的指向会造成未知的错误。微信官方推荐开发者采用 module.exports 来暴露模块接口。
(2) 微信小程序目前不支持直接引入 node_modules,建议开发者在需要使用node_modules 时复制相关的代码到小程序的目录中,或者使用小程序支持的npm 功能。
十二、强大的 API
为什么不直接使用 H5 进行 Web 开发的一个很重要的原因就是微信小程序提供了强大的 API,可以完成 Web 开发者无法完成的很多功能。微信小程序开发框架提供了丰富的微信原生 API,可以方便地调起微信提供的能力,如获取用户信息、本地存储、支付功能等。
小程序的 API 有很多,但是总体可以简单分为以下 3 类:
事件监听类
约定以“on”开头的 API 用来监听某个事件是否触发,如wx.onSocketOpen、wx.onCompassChange 等。这类 API 接收一个回调函数作为参数,当事件触发时会调用这个回调函数,并将相关数据以参数形式传入。
同步执行类
约定以“Sync”结尾的 API 都是同步 API,如 wx.setStorageSync、wx.getSystemInfoSync 等。此外,也有一些其他的同步 API,如wx.createWorker,wx.getBackgroundAudioManager 等。同步 API 的执行结果可以通过函数返回值直接获取,如果执行出错会抛出异常。
异步执行类
大多数 API 都是异步 API,如 wx.request、wx.login 等。这类 API 通常接收一个 Object 类型的参数,这个参数支持如下中的字段来接收接口调用结果。
异步 API 的执行结果需要通过 Object 类型的参数中传入的对应回调函数获取。部分异步 API 也有返回值,可以用来实现更丰富的功能,如 wx.request、wx.connectSocket 等。
微信小程序提供的各种异步方法非常强大,但是微信官方没有给出 Promise API 来处理异步操作,而且异步的 API 又非常多,因此场景的多异步编程会产生层层回调,难以处理。但Promise 又是必要的,因为微信小程序已经支持ES6 了,因此我们可以直接封装 Promise 方案,将常用的异步方法以 Promise 的形式封装起来,方便页面调用(也有很多开源工具封装好的库,支持 Promise 的链式操作,做到像同步一样写异步)。例如:
--------------------------------------
版权声明:本文为【PythonJsGo】博主的原创文章,转载请附上原文出处链接及本声明。
博主主页:https://my.oschina.net/u/3375733
本篇文章同步在个人公众号: