Notes
微信小程序
代码结构

​在上一章中,我们开始了一个Hello World项目。你可能留意到了这个项目里边生成了不同类型的文件:

  • json 后缀的 JSON 配置文件
  • wxml 后缀的 WXML 模板文件
  • wxss 后缀的 WXSS 样式文件
  • js 后缀的 JS 脚本逻辑文件
    接下来我们分别看看这4种文件的作用。

JSON

Json全称JavaScript Object Notation(JavaScript对象表示法)。
JSON 是一种数据格式,并不是编程语言,在小程序中,JSON扮演的静态配置的角色。

我们可以看到在项目的根目录有一个 app.json 和 project.config.json,此外在 pages/logs 目录下还有一个 logs.json,我们依次来说明一下它们的用途。

Json语法

JSON文件都是被包裹在一个大括号中 {},通过 key-value 的方式来表达数据。JSON的 Key 必须包裹在一个双引号中,在实践中,编写 JSON 的时候,忘了给 Key 值加双引号或者是把双引号写成单引号是常见错误。

JSON的值只能是以下几种数据格式,其他任何格式都会触发报错,例如 JavaScript 中的 undefined。

  • 数字,包含浮点数和整数
  • 字符串,需要包裹在双引号中
  • Bool值,true 或者 false
  • 数组,需要包裹在方括号中 []
  • 对象,需要包裹在大括号中 {}
  • Null 还需要注意的是 JSON 文件中无法使用注释,试图添加注释将会引发报错。

全局配置 app.json

:::danger 重要内容 ::: app.json 是当前小程序的全局配置,包括了小程序的所有页面路径、界面表现、底部 tab 等。一个示例 app.json 配置内容如下:

{
  "pages": [
    //配置小程序的配置信息
    "pages/index/index",    //主页,第一条是主页
    "pages/logs/index"  //其他页面
    ···
  ],
  "window": {
    //配置小程序的顶部栏样式和底部栏样式
    "navigationBarTitleText": "Demo",
  },
  "tabBar": {
    // 配置底部导航栏的跳转关系
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页"
    }, {
      "pagePath": "pages/logs/index",
      "text": "日志"
    }]
  },
  "networkTimeout": {
    // 延时信息,不重要
    "request": 10000,
    "downloadFile": 10000
  },
}

我们简单说一下这个配置各个项的含义:

  • pages字段 —— 用于描述当前小程序所有页面路径,这是为了让微信客户端知道当前你的小程序页面定义在哪个目录。
  • window字段 —— 定义小程序所有页面的顶部背景颜色,文字颜色定义等。
  • tabbar字段 —— 底部栏的跳转关系

单个页面xxx.json

如果你整个小程序的风格是蓝色调,那么你可以在 app.json 里边声明顶部颜色是蓝色即可。实际情况可能不是这样,可能你小程序里边的每个页面都有不一样的色调来区分不同功能模块,因此我们提供了 page.json,让开发者可以独立定义每个页面的一些属性,例如刚刚说的顶部颜色、是否允许下拉刷新等等。
单个页面的json在每个页面中决定当前页面的配置信息,详细配置信息参考这里 (opens in a new tab)
示例

{
  "navigationBarBackgroundColor": "#ffffff",
  "navigationBarTextStyle": "black",
  "navigationBarTitleText": "微信接口功能演示",
  "backgroundColor": "#eeeeee",
  "backgroundTextStyle": "light"
}

以上这些配置中有冲突的地方会覆盖app.json的配置项。也就是说,对于单个页面,页面的json优先级大于全局json。

工具配置 project.config.json

主要是记录微信开发工具的配置情况,对实际小程序没有意义,忽略。

wxml模板文件

在WEB开发中,网页编程采用的是 HTML + CSS + JS 这样的组合,其中 HTML 是用来描述当前这个页面的结构,CSS 用来描述页面的样式,JS 通常是用来处理这个页面和用户的交互

同样道理,在小程序中也有同样的角色,其中 WXML 充当的就是类似 HTML 的角色。打开 pages/index/index.wxml,你会看到以下的内容:

<view class="container">
  <view class="userinfo">
    <button wx:if="{{!hasUserInfo && canIUse}}"> 获取头像昵称 </button>
    <block wx:else>
      <image src="{{userInfo.avatarUrl}}" background-size="cover"></image>
      <text class="userinfo-nickname">{{userInfo.nickName}}</text>
    </block>
  </view>
  <view class="usermotto">
    <text class="user-motto">{{motto}}</text>
  </view>
</view>

WXML和 HTML 非常相似,WXML 由标签、属性等等构成。下面是二者不同的地方:

  • 标签名字有点不一样

往往写 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中管理msg对应的数据:

this.setData({ msg: "Hello World" })

当某个函数功能中需要修改页面显示的东西,执行下面的语句:

this.setData({ msg: "nihao" })

此时Hello World会变为nihao。
这种{{}}方法被称为插值语法,它广泛应用于vue等MVVM框架中。

列表渲染

当有多条数据的时候,实现类似C语言中的for循环功能。

<!--wxml-->
<view wx:for="{{array}}"> {{item}} </view>
 
// page.js
Page({
  data: {
    array: [1, 2, 3, 4, 5]
  }
})

以上的代码将实现类似于下面的功能:

for (int i=0;i<5;i++){
    cout<<i+1;
}

条件渲染

通过对变量的控制,来决定是否渲染某些内容。

<!--wxml-->
<view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
<view wx:elif="{{view == 'APP'}}"> APP </view>
<view wx:else="{{view == 'MINA'}}"> bingo </view>
// page.js
Page({
  data: {
    view: 'MINA'
  }
})

在上述的代码中,将会显示bingo
同样用C++做比方:

if (strcmp(view,"MINA")==0){
    cout<<"bingo";
}

模板

帮助你复杂的代码只写一次,下次调用只用传数据即可。

<!--wxml-->
<template name="staffName">
  <view>
    FirstName: {{firstName}}, LastName: {{lastName}}
  </view>
</template>
 
<template is="staffName" data="{{...staffA}}"></template>
<template is="staffName" data="{{...staffB}}"></template>
<template is="staffName" data="{{...staffC}}"></template>
// page.js
Page({
  data: {
    staffA: {firstName: 'Hulk', lastName: 'Hu'},
    staffB: {firstName: 'Shang', lastName: 'You'},
    staffC: {firstName: 'Gideon', lastName: 'Lin'}
  }
})

用C++打比方,就是将操作封装在函数中了:

void Template(string firstname,string lastname){
    cout<<"FirstName"<<firstname<<endl;
    cout<<"LastName" <<lastName<<endl;
}
int main(){
    Template("Shang","You");
    return 0;
}

wxss

相当于css,用于定制样式。
WXSS 具有 CSS 大部分的特性,小程序在 WXSS 也做了一些扩充和修改。
:::danger 重要内容 :::

  • 新增了尺寸单位。在写 CSS 样式时,开发者需要考虑到手机设备的屏幕会有不同的宽度和设备像素比,采用一些技巧来换算一些像素单位。WXSS 在底层支持新的尺寸单位 rpx开发者可以免去换算的烦恼,只要交给小程序底层来换算即可,由于换算采用的浮点数运算,所以运算结果会和预期结果有一点点偏差。

  • 提供了全局的样式和局部样式。和前边 app.json, page.json 的概念相同,你可以写一个 app.wxss 作为全局样式,会作用于当前小程序的所有页面,局部页面样式 page.wxss 仅对当前页面生效。

此外 WXSS 仅支持部分 CSS 选择器。

Javascript

一个服务仅仅只有界面展示是不够的,还需要和用户做交互:响应用户的点击、获取用户的位置等等。在小程序里边,我们就通过编写 JS 脚本文件来处理用户的操作。

<view>{{ msg }}</view>
<button bindtap="clickMe">点击我</button>

点击 button 按钮的时候,我们希望把界面上 msg 显示成 "Hello World",于是我们在 button 上声明一个属性: bindtap ,在 JS 文件里边声明了 clickMe 方法来响应这次点击操作:

Page({
  clickMe: function() {
    this.setData({ msg: "Hello World" })
  }
})