目录结构
小程序包含一个描述整体程序的 app
和多个描述各自页面的 page
。
描述页面的四个文件必须具有相同的路径与文件名
网页开发者需要面对的环境是各式各样的浏览器,PC 端需要面对 IE、Chrome、QQ浏览器等,在移动端需要面对Safari、Chrome以及 iOS、Android 系统中的各式 WebView 。而小程序开发过程中需要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具,小程序中三大运行环境也是有所区别的,
JSON 配置
JSON 是一种数据格式,并不是编程语言,在小程序中,JSON扮演的静态配置的角色。
我们可以看到在项目的根目录有一个 app.json
和 project.config.json
,此外在 pages/logs
目录下还有一个 logs.json
,我们依次来说明一下它们的用途。
app.json
是当前小程序的全局配置,包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。QuickStart 项目里边的 app.json
配置内容如下:
工具配置 project.config.json(个性化配置)
通常大家在使用一个工具的时候,都会针对各自喜好做一些个性化配置,例如界面颜色、编译配置等等,当你换了另外一台电脑重新安装工具的时候,你还要重新配置。
考虑到这点,小程序开发者工具在每个项目的根目录都会生成一个 project.config.json
,你在工具上做的任何配置都会写入到这个文件,当你重新安装工具或者换电脑工作时,你只要载入同一个项目的代码包,开发者工具就自动会帮你恢复到当时你开发项目时的个性化配置,其中会包括编辑器的颜色、代码上传时自动压缩等等一系列选项。
页面配置 page.json
因此我们提供了 page.json
,让开发者可以独立定义每个页面的一些属性,例如刚刚说的顶部颜色、是否允许下拉刷新等等
JSON 语法
这里说一下小程序里JSON配置的一些注意事项。
JSON文件都是被包裹在一个大括号中 {},通过key-value的方式来表达数据。JSON的Key必须包裹在一个双引号中,在实践中,编写 JSON 的时候,忘了给 Key 值加双引号或者是把双引号写成单引号是常见错误。
JSON的值只能是以下几种数据格式,其他任何格式都会触发报错,例如 JavaScript 中的 undefined。
- 数字,包含浮点数和整数
- 字符串,需要包裹在双引号中
- Bool值,true 或者 false
- 数组,需要包裹在方括号中 []
- 对象,需要包裹在大括号中 {}
- Null
还需要注意的是 JSON 文件中无法使用注释,试图添加注释将会引发报错
要换一个思路了,在框架,其他的开发中,在html中的标签,现在都叫
组件了,要记得转换过来。所谓封装好的组件,在使用的时候就已经有了
一些js,css交互的功能了。而标签更多的还只是语义化
标签名字有点不一样
往往写 HTML 的时候,经常会用到的标签是 div
, p
, span
,开发者在写一个页面的时候可以根据这些基础的标签组合出不一样的组件,例如日历、弹窗等等。换个思路,既然大家都需要这些组件,为什么我们不能把这些常用的组件包装起来,大大提高我们的开发效率。
从上边的例子可以看到,小程序的 WXML
用的标签是 view
, button
, text
等等,这些标签就是小程序给开发者包装好的基本能力,我们还提供了地图、视频、音频等等组件能力。
多了一些 wx:if
这样的属性以及 这样的表达式
在网页的一般开发流程中,我们通常会通过 JS
操作 DOM
(对应 HTML
的描述产生的树),以引起界面的一些变化响应用户的行为。例如,用户点击某个按钮的时候,JS
会记录一些状态到 JS
变量里边,同时通过 DOM
API 操控 DOM
的属性或者行为,进而引起界面一些变化。当项目越来越大的时候,你的代码会充斥着非常多的界面交互逻辑和程序的各种状态变量,显然这不是一个很好的开发模式,因此就有了 MVVM 的开发模式(例如 React, Vue),提倡把渲染和逻辑分离。简单来说就是不要再让 JS
直接操控 DOM
,JS
只需要管理状态即可,然后再通过一种模板语法来描述状态和界面结构的关系即可。
小程序的框架也是用到了这个思路,如果你需要把一个 Hello World
的字符串显示在界面上。
WXML 是这么写 :
<text>{{msg}}</text>
JS 只需要管理状态即可:
this.setData({ msg: "Hello World" })
通过 {{ }} 的语法把一个变量绑定到界面上,我们称为数据绑定。仅仅通过数据绑定还不够完整的描述状态和界面的关系,还需要if
/else
,for
等控制能力,
在小程序里边,这些控制能力都用wx:
开头的属性来表达
WXSS 样式
WXSS
具有 CSS
大部分的特性,小程序在 WXSS
也做了一些扩充和修改。
新增了尺寸单位。在写
CSS
样式时,开发者需要考虑到手机设备的屏幕会有不同的宽度和设备像素比,采用一些技巧来换算一些像素单位。WXSS
在底层支持新的尺寸单位rpx
,开发者可以免去换算的烦恼,只要交给小程序底层来换算即可,由于换算采用的浮点数运算,所以运算结果会和预期结果有一点点偏差。提供了全局的样式和局部样式。和前边
app.json
,page.json
的概念相同,你可以写一个app.wxss
作为全局样式,会作用于当前小程序的所有页面,局部页面样式page.wxss
仅对当前页面生效。此外
WXSS
仅支持部分CSS
选择器
JS 逻辑交互
我们改变数据必须用setData吗? this.setDate({msg:hello world})
而且这个setData还必须在函数里面
响应用户的操作就是这么简单,更详细的事件可以参考文档 WXML - 事件 。
此外你还可以在 JS 中调用小程序提供的丰富的 API,利用这些 API 可以很方便的调起微信提供的能力,例如获取用户信息、本地存储、微信支付等。在前边的 QuickStart 例子中,在 pages/index/index.js
就调用了 wx.getUserInfo 获取微信用户的头像和昵称,最后通过 setData
把获取到的信息显示到界面上。更多 API 可以参考文档 小程序的API 。
这些得自己看官方文档的API内容
程序与页面
微信客户端在打开小程序之前,会把整个小程序的代码包下载到本地。
紧接着通过 app.json
的 pages
字段就可以知道你当前小程序的所有页面路径:
于是微信客户端就把首页的代码装载进来,通过小程序底层的一些机制,就可以渲染出这个首页。
小程序启动之后,在 app.js
定义的 App
实例的 onLaunch
回调会被执行:
整个小程序只有一个 App 实例,是全部页面共享的,更多的事件回调参考文档 注册程序 App 。
Page({
data: { // 参与页面渲染的数据
logs: []
},
onLoad: function () {
// 页面渲染后 执行
}
})
Page
是一个页面构造器,这个构造器就生成了一个页面。在生成页面的时候,小程序框架会把 data
数据和 index.wxml
一起渲染出最终的结构,于是就得到了你看到的小程序的样子。
在渲染完界面之后,页面实例就会收到一个 onLoad
的回调,你可以在这个回调处理你的逻辑。
有关于 Page
构造器更多详细的文档参考 注册页面 Page
组件
小程序提供了丰富的基础组件给开发者,开发者可以像搭积木一样,组合各种组件拼合成自己的小程序。
<map bindmarkertap="markertap" longitude="广州经度" latitude="广州纬度"></map>
API
要获取用户的地理位置时,只需要:
wx.getLocation({ type: 'wgs84', success: (res) => { var latitude = res.latitude // 纬度 var longitude = res.longitude // 经度 } })
调用微信扫一扫能力,只需要:
wx.scanCode({ success: (res) => { console.log(res) } })
需要注意的是:多数 API 的回调都是异步,你需要处理好代码逻辑的异步问题。
2.1.2 JSON 语法
相比于XML ,JSON格式最大的优点是易于人的阅读和编写,通常不需要特殊的工具,就能读懂和修改,是一种轻量级的数据交换格式。
JSON文件都是被包裹在一个大括号中 {},通过key-value的方式来表达数据。
看起来同 JavaScript 的对象表达方式十分相似,但是有所不同。
报错信息的箭头附近是有语法错误的
2.2 WXML 模板
WXML 全称是 WeiXin Markup Language,是小程序框架设计的一套标签语言,结合小程序的基础组件、事件系统,可以构建出页面的结构。
使用 wx:elif 和 wx:else 来添加一个 else 块:
<view wx:if="{{length > 5}}"> 1 </view> <view wx:elif="{{length > 2}}"> 2 </view> <view wx:else> 3 </view>
因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个<block/>
标签将多个组件包装起来,并在上边使用 wx:if 控制属性。
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
2.2.5 列表渲染
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
使用 wx:for-item 指定数组当前元素的变量名,使用 wx:for-index 指定数组当前下标的变量名:
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName"> {{idx}}: {{itemName.message}} </view>
就是说指定他们的变量名,不是叫index和item了
类似 block wx:if
,也可以将 wx:for
用在 <block/>
标签上,以渲染一个包含多节点的结构块。例如:
<block wx:for="{{[1, 2, 3]}}"> <view> {{index}}: </view> <view> {{item}} </view> </block>
难道不放在block里就不能渲染多个标签了吗
2.2.6 模板
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。使用 name 属性,作为模板的名字。然后在 <template/>
内定义代码片段,如:
这个东西我以前还百度过
看例子还是有点看不懂
但好像也少用 知道他是模板就好了
就重复代码段,到时候用到再百度
WXML 提供两种文件引用方式import和include。
import 可以在该文件中使用目标文件定义的 template,
需要注意的是 import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件中 import 的 template,简言之就是 import 不具有递归的特性。
例如:C 引用 B,B 引用A,在C中可以使用B定义的 template,在B中可以使用A定义的 template ,但是C不能使用A定义的template ,
2.3 WXSS 样式
WXSS(WeiXin Style Sheets)是一套用于小程序的样式语言,用于描述WXML的组件样式,也就是视觉上的效果。
在小程序开发中,开发者不需要像Web开发那样去优化样式文件的请求数量,只需要考虑代码的组织即可。样式文件最终会被编译优化,具体的编译原理我们留在后面章节再做介绍。
2.3.6 官方样式库
为了减轻开发者样式开发的工作量,我们提供了WeUI.wxss基础样式库。
WeUI是一套与微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。包含button、cell、dialog、progress、toast、article、actionsheet、icon等各式原生。
2.4 JavaScript 脚本
小程序的主要开发语言是 JavaScript ,开发者使用 JavaScript 来开发业务逻辑以及调用小程序的 API 来完成业务需求。
小程序中的 JavaScript 是由ECMAScript 以及小程序框架和小程序 API 来实现的。同浏览器中的JavaScript 相比没有 BOM 以及 DOM 对象,所以类似 JQuery、Zepto这种浏览器类库是无法在小程序中运行起来的,同样的缺少 Native 模块和NPM包管理的机制,小程序中无法加载原生库,也无法直接使用大部分的 NPM 包。
小程序目前可以运行在三大平台:
- iOS平台,包括iOS9、iOS10、iOS11
- Android平台
- 小程序IDE
小程序IDE提供语法转码工具帮助开发者,将 ECMAScript 6代码转为 ECMAScript 5代码,从而在所有的环境都能得到很好的执行
开发者需要在项目设置中,勾选 ES6 转 ES5 开启此功能。
2.4.3 模块化
浏览器中,所有 JavaScript 是在运行在同一个作用域下的,定义的参数或者方法可以被后续加载的脚本访问或者改写。同浏览器不同,小程序中可以将任何一个JavaScript 文件作为一个模块,通过module.exports 或者 exports 对外暴露接口。(ES6提出的模块化)
请看是一个简单模块示例,B.js 引用模块A,并使用A暴露的multiplyBy2方法完成一个变量乘以 2 的操作。
// moduleA.js module.exports = function( value ){ return value * 2; }
// B.js // 在B.js中引用模块A var multiplyBy2 = require('./moduleA') var result = multiplyBy2(4)