《小程序从入门到入坑》框架语法

前言

哈喽大家好,我是 SuperYing,我们继续小程序入门系列,本文将对小程序框架语法进行比较全面的介绍。在《小程序从入门到入坑》简介及工程创建中,我们提到小程序项目结构,主要包括 app.jsonapp.jsapp.wxss 以及页面(组件)级的 .wxml.wxss.js.json。接下来我将逐步介绍以上重点组成部分。

读完本文您将收获:

  • 了解小程序项目主体文件,明确 app.jsonapp.jsapp.wxss 等全局文件的作用及使用方式。
  • 了解小程序页面结构 WXML 语法,完成诸如 数据绑定列表渲染条件渲染 等常用开发方式。
  • 了解小程序样式 WXSS 的使用以及基于 css 语法的扩展方面。
  • 了解小程序脚本 WXS 的使用及典型应用场景。
  • 了解小程序常用开发技巧,基本可以独立完成小程序页面开发。

主体文件介绍

主体文件包括 app.jsonapp.jsapp.wxss

app.json

app.json 位于小程序工程的根目录下,负责对微信小程序进行全局配置。文件内容是一个 JSON 对象,编写时需要严格按照 JSON 格式规范。

app.json 的常用配置项主要分为以下几类:

  • pages: 设置页面路径,必填项。
  • window: 设置小程序外观。
  • tabBar: 设置 tabBar 表现及对应页面。
  • networkTimeout: 设置网络请求的超时时间。
  • debug: 设置是否开启 debug 模式,默认关闭

app.json 整体结构如下:

{ 
    // 页面路径设置 
    "pages" : []// 默认页面的窗口设置 
    "window" : {}// 底部tab设置 
    "tabBar" : {}// 设置网络请求API的超时时间 
    "networkTimeout" : {}// 是否为debug模式 
    "debug" : false 
}

pages 配置

用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径(含文件名) 信息。文件名不需要写文件后缀,框架会自动去寻找对应位置的 .json, .js, .wxml, .wxss 四个文件进行处理。

未指定 entryPagePath 配置时,数组的第一项代表小程序的初始页面,即首页。

配置示例:

"pages": [
    "pages/index/index",
    "pages/logs/logs"
],

pages/index/index 即为小程序首页。

tip

使用微信开发者工具 增加 pages 配置,会自动在小程序根目录(默认 miniprogram)的 pages 目录下创建对应页面文件,包括 .wxml.wxss.js.json

小程序每次需要增加/删除页面时,都需要修改 pages 配置。另外,小程序路由也是基于 pages 配置的路径进行导航的,可以说 pages 是小程序不可或缺的重要配置。

window 配置

window 配置用于设置小程序的状态栏导航条标题窗口背景色 等外观表现。

详细配置可参考小程序开发文档

配置示例:

{
"window": {
    // 导航栏标题、状态栏颜色,仅支持 `black` / `white`
    "navigationBarTextStyle": "black",
    // 导航栏背景颜色
    "navigationBarBackgroundColor": "#ffffff",
    // 导航栏标题文字内容
    "navigationBarTitleText": "miniprogram-study",
    // 窗口的背景色
    "backgroundColor": "#eeeeee",
    // 下拉 loading 的样式,仅支持 `dark` / `light`
    "backgroundTextStyle": "light"
  },
}

以下是 window 配置对应的小程序页面部分,小伙伴们可参考以验证配置是否正确。

image.png

注意

关于颜色的配置,应使用 十六进制 形式,如 #ffffff。

tabBar 配置

什么是 tabBar

tabBar 是移动端应用比较常见的页面效果,用于实现页面的快速切换。

在小程序中 tabBar 主要分为:

  • 顶部 tabBar
  • 底部 tabBar
配置项

如果小程序是一个多 tab 应用,即客户端窗口的底部或顶部有 tab 栏可以切换页面,可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。

tabBar 常用配置项如下:

  1. list 配置 tab 列表,限制最少 2 个,最多 5 个 tabtab 每个项目可配置以下内容:
    • pagePath: 必填,页面路径,必须在 pages 配置中先定义。
    • text: 必填,tab 上按钮文字。
    • iconPath: tab 默认图标路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片。positiontop 时,不显示 icon
    • selectedIconPath: tab 选中时的图标路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片。positiontop 时,不显示 icon
  2. colorselectedColor 分别设置 tab 上的默认文字颜色及选中时的文字颜色。
  3. backgroundColor 设置 tab 的背景色。
  4. borderStyle 设置 tab 的边框的颜色, 仅支持 black / white
  5. position 设置 tab 的tabBar 的位置,仅支持 bottom / top

配置示例:

{
"tabBar": {
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "images/tabs/home.png",
        "selectedIconPath": "images/tabs/home-active.png"
      },
      {
        "pagePath": "pages/mine/mine",
        "text": "我的",
        "iconPath": "images/tabs/mine.png",
        "selectedIconPath": "images/tabs/mine-active.png"
      }
    ]
  },
}

tabBar 部分标注:

image.png

注意:

  1. tabBar 的数量应控制在 2 ~ 5 个之间。

  2. 当渲染顶部 tabBar 时,不会渲染 icon,仅渲染文字。

networkTimeout 配置

小程序中各种网络请求 API 的超时时间只能通过 networkTimeout 统一设置,单位 毫秒(ms)。

配置项如下:

配置示例:

{
    "networkTimeout": {
        "request": 60000,
        "connectSocket": 60000,
        "uploadFile": 60000,
        "downloadFile": 60000
    },
}

debug 配置

此配置可以在开发者工具中开启 debug 模式,默认 关闭。开启后,在开发者工具的控制台面板,调试信息以 info 的形式给出,包括 Page 的注册页面路由数据更新事件触发 等。可以帮助开发者快速定位一些常见的问题。

配置示例:

{
    "debug": true
}

控制台开启 debug 后打印信息:

image.png

app.js

该文件是小程序应用的入口文件,在该文件中调用 App() 函数来 注册小程序实例,内容包括 全局生命周期函数全局函数全局属性 等。已注册的小程序实例可以在其他逻辑层代码中通过 getApp()获取。

示例:

App({
    globalData: '我是全局属性',
    onLaunch() {
        // 小程序初始化完成时触发,全局只触发一次
    },
    onShow() {
        // 小程序启动或从后台进入前台显示时触发
    },
    onHide() {
        // 小程序从前台进入后台时触发
    },
    onError(e) {
        // 小程序发生脚本错误或 API 调用报错时触发
    },
    onPageNotFound() {
        // 小程序要打开的页面不存在时触发
    },
    onUnhandledRejection() {
        // 小程序有未处理的 Promise 拒绝时触发
    },
    onThemeChange() {
        // 系统切换主题时触发
    }
})

app.wxss(可选)

该文件是 全局样式表,对项目中每个页面都有效,可将一些系统级别的统一样式风格写入 app.wxss。页面渲染时,页面(组件)级的 .wxss 文件样式会覆盖 app.wxss 中相同的选择器样式。关于 WXSS 使用,详见后文 「小程序样式 - WXSS」

页面文件介绍

配置文件 - .json

页面配置文件与 app.json 配置文件一样,也是一个 json 文件。

app.json 中的部分配置,也支持在页面(组件)级的 .json 文件进行配置。.json 文件配置项在当前页面会覆盖 app.json 中相同的配置项。

.json 配置文件的配置项设计 window 部分的,即设置小程序的外观表现时,不需要在 .json 中显示声明 window 属性,直接设置指定配置即可。

配置示例:

{ 
    "navigationBarBackgroundColor": "#000000""navigationBarTextStyle": "black""navigationBarTitleText": "我的页面""backgroundColor": "#efefef""backgroundTextStyle": "light" 
}

注意:

页面(组件)级的 .json 文件非必需,若页面相关配置与 app.json 完全形同,可省略该配置文件。

逻辑文件 - .js

小程序的每个页面都需要在对应的 .js 中进行注册。逻辑文件的主要功能包括:注册小程序页面定义初始化数据注册页面声明周期函数注册事件处理函数 等。

小程序的逻辑文件使用 javascript 进行开发。所有的逻辑文件,包括 app.js 最终将会打包成一份 JavaScript 文件。在小程序启动时运行,直到小程序销毁,类似于 ServiceWorker,所以逻辑层也称为 App Service

小程序中的每个逻辑文件,都有独立的作用域,具备模块化能力。由于小程序的逻辑层运行在 纯 javascript 引擎 中,而并非是浏览器环境,因此即便同样使用 javascript 编写,但在小程序中无法使用浏览器对象,如 documentwindow 等。同样的,基于浏览器特有对象的框架也无法使用,如 JQuery 等。因为无法通过操作 DOM 操作页面,小程序中需要通过 数据绑定事件响应 来处理页面。

注册页面

.js 逻辑文件中需要调用 Page() 函数来注册页面,接受一个 Object 类型参数,指定页面的初始数据、生命周期回调、事件处理函数等。

示例代码(属性特性见对应注释):

Page({
  /**
   * 页面的初始数据
   * 页面加载时,`data` 将会以 `JSON` 字符串的形式由逻辑层传至渲染层,因此 `data` 中的数据必须是可以转成 `JSON` 的类型:字符串,数字,布尔值,对象,数组。
   * 渲染层可以通过 `WXML` 对数据进行绑定
   */
  data: {},
  /**
   * 生命周期函数--监听页面加载
   * 一个页面只会调用一次,可以在 `onLoad` 的参数中获取打开当前页面路径中的参数
   */
  onLoad() {},
  /**
   * 生命周期函数--页面初次渲染完成时触发。
   * 一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。   
   */
  onReady() {},
  /**
   * 生命周期函数--页面显示/切入前台时触发
   */
  onShow() {},
  /**
   * 生命周期函数--页面隐藏/切入后台时触发,如 `wx.navigateTo` 或底部 `tab` 切换到其他页面,小程序切入后台等
   */
  onHide() {},
  /**
   * 生命周期函数--页面卸载时触发,如 `wx.redirectTo` 或 `wx.navigateBack` 到其他页面时
   */
  onUnload() {},
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {},
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {},
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {},
  /**
   * 自定义事件响应
   */
  handleButtonTap() {},
  /**
   * 自定义数据
   * 逻辑中可通过 this.customData 获取以下自定义数据
   */
  customData() {
      hi: '我是自定义数据'
  },
})

获取当前页面栈

可在逻辑代码中调用 getCurrentPages() 获取当前页面栈的实例,页面栈以数组形式按栈顺序给出,数组第一个元素为 首页,最后一个元素为 当前页面

注意:

  • 不要尝试修改页面栈,会导致路由以及页面状态错误。
  • 不要在 App.onLaunch 的时候调用 getCurrentPages(),此时 page 还没有生成。

触发页面渲染

在逻辑代码中,可以调用 this.setData() 函数触发页面渲染,不能直接修改 data 数据。因为 this.setData() 被调用时,会将数据从逻辑层发送到视图层触发视图层重绘,同时会修改 Pagedata 值。

setData 函数接受一个对象参数,用于更新 data 中的指定数据。参数属性的键值非常灵活,以下情况均可生效:

  • data 键索引:更新键对应的值。
  • 数组下标路径索引:更新数组指定下标对应的值。
  • 对象属性路径索引:更新对象指定属性对应值。
  • 动态路径索引:按结果路径更新对应的值。

代码示例:

Page({

  /**
   * 页面的初始数据
   */
  data: {
    text: '123',
    userInfo: {
      name: 'SuperYing',
      age: '18'
    },
    hobbies: ['games', 'football', 'busketball'],
  },

  handleTextChange() {
    /** data 键值索引,更新 this.data.text 数据 */
    this.setData({
      text: '234'
    })
  },

  handleArrayChange() {
    /** 数组下标路径索引,更新 this.data.hobbies 第一个元素为 'run' */
    this.setData({
      'hobbies[0]': 'run'
    })
  },

  handleObjectChange() {
    /** 普通对象路径索引,更新 this.data.useInfo.name 为 'new name' */
    this.setData({
      'userInfo.name': 'new name',
    })
  },

  handleObjectChange2() {
    /** 动态路径索引,更新 this.data.useInfo.name 为 'new name' */
    const prop = 'name'
    this.setData({
      [`userInfo.${prop}`]: 'new name',
    })
  }
})

结构文件 - .wxml

WXML(WeiXin Markup Language) 是框架设计的一套标签语言,用于渲染界面。

小程序框架通过 WXML,在不同平台被渲染为不同端的渲染文件。

image.png 如上图,WXML 语言最终会转译为宿主环境对应的语言,所以 WXML 中使用的标签一定要使用小程序定义的标签,不能使用自定义标签,以保证页面结构能被正确的转译。

使用微信开发者工具开发时,在 WXML 中编写 HTML 标签或自定义标签,也可以正常渲染。这是因为微信开发者工具内核是浏览器内核,而且微信开发者工具并没有对 WXMLWXSS 的内容进行强验证。但是无法保证在真机上可以正常渲染。因此,微信开发者工具的效果仅用于方便开发调试,而真正的效果一定要以真机效果为准。

WXML 可以实现 数据绑定、列表渲染、条件渲染、模板、事件等能力。

数据绑定

WXML 文件可以绑定对应 Page 中定义的 data 数据,以在页面中直接使用 data 中的属性。

数据绑定使用 Mustache 语法(双大括号)将变量或简单运算包裹起来。主要有以下几种用法:

简单绑定

简单绑定就是使用 Mustache 语法 将变量包裹起来。

代码示例:

<!--index.wxml-->
<view>
  <!-- 作为内容绑定 -->
  <view>{{message}}</view>
  <!-- 作为属性绑定 -->
  <view id='item-{{id}}' style="border: {{border}}">我是属性绑定</view>
  <!-- 作为控制属性绑定 -->
  <view wx:if="{{show}}">我是控制属性绑定</view>
  <!-- 关键字绑定 -->
  <checkbox checked="{{false}}">关键字绑定</checkbox>
</view>
index.js
Page({
  data: {
    message: 'hello world',
    id: '0',
    border: '2rpx solid black',
    show: true
  }
})

效果如下:

image.png

注意:

若关键字为布尔值时,如 checkboxchecked 属性,不能直接设置为 checked="false",这样会将 checked 的值解析为一个 ‘false’ 字符串,转成布尔值时为 true。这种情况,需要将 false{{}} 包裹起来。

运算

可以在 {{}} 内进行简单的运算,支持以下运算:

  • 三元运算
  • 算数运算
  • 逻辑判断
  • 字符串运算
  • 数据路径运算

代码示例:

<!--index.wxml-->
<view>
  <!--三元运算-->
  <checkbox checked="{{checked === 'Y'? true : false}}">三元运算</checkbox>
  <!-- 算数运算 -->
  <view>{{ a + b}} + {{c}} + d</view>
  <!-- 逻辑判断 -->
  <view wx:if="{{length > 5}}"> </view>
  <!-- 字符串运算 -->
  <view>{{"hello" + name}}</view>
  <!-- 数据路径运算 -->
  <view>{{object.key}} {{array[0]}}</view>
</view>
// pages/index/index.js
Page({
  data: {
    checked: 'Y',
    a: 1,
    b: 2,
    c: 3,
    length: 1,
    name: 'world',
    object: {
      key: 'hello'
    },
    array: ['小程序从入门到入坑']
  }
})

效果如下:

image.png

组合

data 中的数据可以在 {{}} 中再次组合,构成新的数组或对象。

代码示例:

// index.wxml
<text wx:for="{{[zero, 1, 2, 3, 4]}}"> {{item}} </text>
  <button data-obj="{{ { ...obj1, c: 3 } }}" bind:tap="onButton1Tap">按钮1</button>
  <button data-obj="{{ { ...obj1, ...obj2 } }}" bind:tap="onButton2Tap">按钮2</button>
  <button data-obj="{{ {field1, field2} }}" bind:tap="onButton3Tap">按钮3</button>
// index.js
Page({
  data: {
    zero: 0,
    obj1: {
      a: 1,
      b: 2
    },
    obj2: {
      c: 3,
      d: 4
    },
    field1: 'abc',
    field2: 'def'
  },
  onButton1Tap(e) {
    console.log('按钮1', e.currentTarget.dataset.obj)
  },
  onButton2Tap(e) {
    console.log('按钮2', e.currentTarget.dataset.obj)
  },
  onButton3Tap(e) {
    console.log('按钮3', e.currentTarget.dataset.obj)
  }
})

效果及控制台打印如下: image.png

列表渲染

wx:for

在组建上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。

默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item

代码示例:

<view wx:for="{{myArray}}">
    {{index}}-{{item}}
</view>
Page({
  data: {
    myArray: ['hello', 'world']
  }
})

以上代码遍历 myArray,重复渲染了两个 view

效果如下:

image.png

wx:for-index wx:for-item

wx:for-item 可以指定数组当前元素的变量名(默认 item)。

wx:for-index 可以指定数组当前下标的变量名(默认 index)。

代码示例:

<view 
    wx:for="{{myArray}}" 
    wx:for-index="myIndex" 
    wx:for-item="myItem">
    {{myIndex}}-{{myItem}}
</view>
Page({
  data: {
    myArray: ['hello', 'world']
  }
})

效果与 wx:for 示例一致。

单列表渲染时一般不需要手动设置 wx:for-indexwx:for-item,当wx:for嵌套使用时,就有必要设置,以避免命名冲突。

嵌套列表代码示例:

<view 
    wx:for="{{nestedArray}}" 
    wx:for-index="myIndex" 
    wx:for-item="myItem">
    <view wx:for="{{myItem}}">
      {{index}}-{{item}}
    </view>
</view>
Page({
  data: {
    nestedArray: [
      ['hello', 'world'],
      ['hello', '小程序']
    ]
  }
})

结果如下:

image.png

wx:key

若列表中项目存在位置动态改变或者新增的情况,且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),则需要使用 wx:key 来指定列表中项目的唯一的标识符。

熟悉 vue 的同学,应该比较了解 v-forkey 属性。小程序的 wx:keyv-forkey 作用差不多,目的在于提升列表渲染性能

wx:key 的值可设为以下两种: 1. 字符串,应为 wx:for 循环的数组项目的某个属性,该属性的值需要是数组中唯一的 字符串数字,且不能动态改变。 2. 保留关键字 *this 代表在 wx:for 循环的数组项目本身,需要数组项目本身是一个唯一的 字符串 或者 数字

当数据改变触发渲染层重新渲染的时候,会校正带有 wx:key 的组件,框架会确保他们被重新排序,而不是重新创建,以使组件保持自身的状态,并且提高列表渲染时的效率

注:

如不提供 wx:key,会报一个 warning,如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略

代码示例:

<!-- item 属性 -->
<switch 
  wx:for="{{objectArray}}" 
  wx:key="unique" 
  style="display: block;"> 
  {{item.id}} 
</switch>
<button bindtap="switch">Switch</button>
<button bindtap="addToFront">添加</button>
<!-- *this -->
<switch 
  wx:for="{{numberArray}}" 
  wx:key="*this" 
  style="display: block;"> 
  {{item}} 
</switch>
<button bindtap="addNumberToFront">添加</button>
Page({
  data: {
    objectArray: [
      {id: 5, unique: 'unique_5'},
      {id: 4, unique: 'unique_4'},
      {id: 3, unique: 'unique_3'},
      {id: 2, unique: 'unique_2'},
      {id: 1, unique: 'unique_1'},
      {id: 0, unique: 'unique_0'},
    ],
    numberArray: [1, 2, 3, 4]
  },
  switch: function(e) {
    const length = this.data.objectArray.length
    for (let i = 0; i < length; ++i) {
      const x = Math.floor(Math.random() * length)
      const y = Math.floor(Math.random() * length)
      const temp = this.data.objectArray[x]
      this.data.objectArray[x] = this.data.objectArray[y]
      this.data.objectArray[y] = temp
    }
    this.setData({
      objectArray: this.data.objectArray
    })
  },
  addToFront: function(e) {
    const length = this.data.objectArray.length
    this.data.objectArray = [{id: length, unique: 'unique_' + length}].concat(this.data.objectArray)
    this.setData({
      objectArray: this.data.objectArray
    })
  },
  addNumberToFront: function(e){
    this.data.numberArray = [ this.data.numberArray.length + 1 ].concat(this.data.numberArray)
    this.setData({
      numberArray: this.data.numberArray
    })
  }
})

体验以上代码后可以发现,无论是更新列表顺序,还是新增列表项目,绑定了 wx:keyswitch 组件的状态都不会刷新,即没有重新创建 switch 组件,而仅仅调整组件的位置。

wx:for block

wx:for 可以应用于 <block> 标签上,用于渲染包含多个节点的结构块。

<block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。

代码示例:

<block wx:for="{{myArray}}">
    <view> {{index}}: </view>
    <view> {{item}} </view>
</block>
Page({
  data: {
    myArray: ['hello', 'world']
  }
})

效果如下: image.png

注意事项
  1. wx:for 的值为字符串时,会将字符串解析成字符串数组
<view wx:for="array">
  {{item}}
</view>

等同于

<view wx:for="{{['a','r','r','a','y']}}">
  {{item}}
</view>
  1. 花括号和引号之间如果有空格,将最终被解析成为字符串
<view wx:for="{{[1,2,3]}} ">
  {{item}}
</view>

等同于

<view wx:for="{{[1,2,3] + ' '}}" >
  {{item}}
</view>

条件渲染

wx:if

在页面结构中,经常需要使用逻辑分支,这时候可以使用 wx:if="{{判断条件}}" 来进行条件渲染,当条件成立时渲染该代码块。

同时提供了 wx:elifwx:else 来添加一个 else 块。

代码示例:

<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

以上代码的渲染逻辑:当 length > 5 时,渲染 1,当 length > 2 是渲染 2,其余情况渲染 3

wx:if block

wx:for 相同,wx:if 也可以用于 <block> 标签上,用于一次性判断多个标签,而不影响布局。

代码示例:

<block wx:if="{{true}}">
  <view> view1 </view>
  <view> view2 </view>
</block>
与 hidden 对比

wx:if 外,组件还可以通过 hidden 属性控制显隐,那么他们有什么区别呢?

  • wx:if 用于控制是是否渲染控制块内的模板。当条件切换时,会触发局部渲染,以确保条件块在切换时销毁或重新渲染;wx:if惰性的,若初始渲染条件为 false,框架什么也不做,在条件第一次变 true 时才开始局部渲染。
  • hidden 用于控制组件是否显示。组件始终会被渲染,只是简单控制显示与隐藏,并不会触发重新渲染和销毁。

一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,若需要频繁切换的情景下,用 hidden 更好,若在运行时条件基本不改变则 wx:if 更好。

事件绑定

WXML 中的事件通过 bind(或 catch)+事件名 属性进行事件绑定。当触发事件时,框架会调用逻辑层中对应的事件处理函数,并将当前状态通过参数传递给事件处理函数。

由于小程序中没有 DOM 的概念,因此事件只能通过 WXML 绑定,不能在逻辑层动态绑定。

代码示例:

<view data-params="123" bindtap="handleTap">点击</view>
Page({
  data: {},
  handleTap(e) {
    const params = e.currentTarget.dataset.params
    console.log(`参数:${params}`)
  }
})

点击按钮会触发 点击事件 tap, 在控制台打印 参数: 123

效果如下: image.png

由于 事件系统 可以说的内容比较多,后续会整一篇专题,此处仅供小伙伴做初步了解。

模板

在实际项目开发中,经常会遇到不同页面使用相同的页面结构的情况。这是可以将相同的页面结构放入到一个模板中,然后在不同的页面调用,传入对应的绑定数据即可。以提升代码复用性,提升开发效率,避免重复开发。

定义模板

模板的代码片段在 <template> 标签内定义,通过 name 属性指定模板名称即可。

<template name="myTemplate">
  <view>内容</view>
  <view>{{content}}</view>
</template>
使用模板

通过 is 属性指定需要渲染的模板。通过 data 属性传入模板所需的数据。

模板有自己的作用域,只能使用传入的 data 以及模板定义文件中定义的 <wxs /> 模块,不能直接使用 Page 中定义的 data

<template name="myTemplate">
  <view>内容</view>
  <view>{{content}}</view>
</template>
<template is="myTemplate" data="{{ content: 'hello 小程序' }}" />

模板也可以嵌套使用:

<template name="myTemplate">
  <view>myTemplate 内容:</view>
  <view>{{content}}</view>
</template>
<template name="myTemplate1">
  <view>myTemplate1 内容:</view>
  <view>{{content}}</view>
  <template is="myTemplate" data="{{ content: '我是 myTemplate 的 内容' }}" />
</template>
<template is="myTemplate1" data="{{ content: '我是 myTemplate1 的 内容' }}" />

is 属性可以使用 Mustache 语法,来动态决定具体需要渲染哪个模板:

<template name="myTemplate">
  <view>myTemplate 内容:</view>
  <view>{{content}}</view>
</template>
<template name="myTemplate1">
  <view>myTemplate1 内容:</view>
  <view>{{content}}</view>
</template>
<block wx:for="{{[1,2]}}">
  <template is="{{item === 1 ? 'myTemplate' : 'myTemplate1'}}" data="{{content: item}}" />
</block>

引用

WXML 提供两种方式来引用其他 WXML 文件:importinclude

import

用于引入目标文件中定义的模板,忽略模板定义之外的所有内容。

<import /> 通过 src 属性指定被引入文件的相对地址。<import/> 引入会忽略引入文件中 <template/> 定义以外的内容。如下例中,仅引入了 tpl1.wxml 中定义的模板,模板外的 view 标签内容不会渲染到页面中。

例如在 pages/template/tpls 目录下新增一个 tpl1.wxml 文件,内容如下:

// pages/template/tpls/tpl1.wxml
<template name="tpl1-A">
  <view>我是 tpl1-A 模板</view>
</template>
<view>hello 小程序</view>

pages/template/template.wxml 中使用 import 引用 tpl1.wxml,即可使用在 tpl1.wxml 中定义的模板 tpl1-A

// pages/template/template.wxml
<import src="./tpls/tpl1.wxml" />
<template is='tpl1-A' />

效果如下: image.png

import 存在作用域的概念,即只会引用目标文件中定义的模板,而不会引用在目标文件中引用的模板。

pages/template/tpls 目录下新增 tpl1.wxmltpl2.wxml,存在如下引用关系:template.wxml 引用 tpl1.wxmltpl1.wxml 引用 tpl2.wxml,则 template.wxml 文件中可以使用 tpl1.wxml 中定义的 tpl1-A 模板,而无法使用 tpl2.wxml 中定义的 tpl2-A 模板

// pages/template/tpls/tpl1.wxml
<import src="./tpl2.wxml" />
<template name="tpl1-A">
  <view>我是 tpl1-A 模板</view>
  <template is="tpl2-A" />
</template>
// pages/template/tpls/tpl2.wxml
<template name="tpl2-A">
  <view>我是 tpl2-A 模板</view>
</template>
// pages/template/template.wxml
<import src="./tpls/tpl1.wxml" />
<template is='tpl1-A' />
<!-- 不生效 -->
<template is='tpl2-A' />
inlcude

include 的作用域 import 恰恰相反,会将目标文件中除 template<wxs /> 以外的代码全部引入,相当于将目标文件代码拷贝到 <include> 的位置。

我们对上面的例子做一下调整:

// pages/template/tpls/tpl1.wxml
<import src="./tpl2.wxml" />
<template name="tpl1-A">
  <view>我是 tpl1-A 模板</view>
  <template is="tpl2-A" />
</template>
<view>我是 tpl1 组件内容</view>
// pages/template/template.wxml
<include src="./tpls/tpl1.wxml" />
<view>我是 template 内容</view>
<!-- 引用 tpl1-A 模板不生效 -->
<template is='tpl1-A' />

效果如下: image.png

通过以上应用可以发现:

import 更适合引用模板定义文件,而 include 更适合引用组件文件。

样式文件 - .wxss

WXSS (WeiXin Style Sheets) 是一套基于 CSS 拓展的样式语言,用于描述 WXML 的组件样式。其具有 CSS 的大部分特性,同时为了更适合小程序开发,对 CSS 进行了扩充和修改。

扩展的特性包括:

  • 尺寸单位
  • 样式导入

注:

熟悉 CSS 对于 WXSS 的使用有很大的帮助,还不了解 CSS 的小伙伴,建议先补习一下。可查阅 MDN 做个整体的了解。

尺寸单位

小程序新提供一个尺寸单位:rpx,以实现页面布局根据屏幕宽度进行自适应,在渲染过程中 rpx 会按比例转化为 px

rpx 转换规则:

WXSS 规定屏幕宽度为 750 rpx,若屏幕宽度为 375 px(如 iphone6),共有 750 个物理像素,则 750rpx = 375px = 750 物理像素,得出 1rpx = 0.5px = 1 物理像素

常用设备屏幕转换如下:

设备 rpx 转 px(公式:屏幕宽度/750) px 转 rpx(公式:750/屏幕宽度)
iPhone5 1rpx = 0.42px 1px = 2.34rpx
iPhone6 1rpx = 0.5px 1px = 2rpx
iPhone6 Plus 1rpx = 0.552px 1px = 1.81rpx

从上表可知,iphone6 设备的转换结果最为标准,即 1px = 2rpx,因此建议小程序的设计师可以以 iphone6 最为视觉标准进行页面设计。

样式导入

WXSS 中可以使用 @import 导入外联样式表,@import 后跟要导入的外联样式表的相对路径,结尾用 ; 表示语句结束。

代码示例:

/** common.wxss **/
.small-p {
  padding:5px;
}
/** app.wxss **/
@import "common.wxss";
.middle-p {
  padding:15px;
}

内联样式

框架组件上支持使用 styleclass 属性来控制组件的样式。

  • style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,尽量避免将静态的样式写进 style 中,以免影响渲染速度。
<view style="color:{{color}};" />
  • class:用于指定样式类,样式类名之间用空格分隔。
<view class="custom_view normal_view" />

选择器

目前小程序支持的选择器如下:

选择器 样例 样例描述
.class .intro 选择所有拥有 class=“intro” 的组件
#id #firstname 选择拥有 id=“firstname” 的组件
element view 选择所有 view 组件
element, element view, checkbox 选择所有文档的 view 组件和所有的 checkbox 组件
::after view::after 在 view 组件后边插入内容
::before view::before 在 view 组件前边插入内容

样式文件分为全局样式app.wxss)和局部样式(页面级的 .wxss),局部样式只作用于对应页面,且会覆盖全局样式中相同的选择器。

脚本文件 - .wxs

WXS(WeiXin Script) 是小程序独有的一套脚本语言,结合 WXML,可以构建出页面的结构。

WXML 无法调用页面的 .js 中定义的函数,但是可以调用定义在 .wxs 中的函数,鉴于此,WXS 最典型的应用场景就是 筛选器

WXS 语法上有与 JavaScript 相近的地方,但他们属于完全不同的两种语言,主要区别如下:

  • WXS 有自己的数据类型。
  • WXS 不支持类似于 es6 以上的语法。
  • WXS 模块遵循 CommonJs 规范。

定义 WXS

WXS 代码可以编写在 wxml 文件中的 <wxs> 标签内,或以 .wxs 为后缀名的文件内。

pages/wxs/a.wxs 中编写 wxs 代码:

// pages/wxs/a.wxs
const foo = 'hello 小程序'
const bar = function() {
  console.log('触发 a.wxs bar 函数')
}
module.exports = {
  foo,
  bar
}

pages/wxs/wxs.wxml 中引用 a.wxs 导出的内容:

// pages/wxs/wxs.wxml
<wxs src="./a.wxs" module="a" />
<view> {{a.foo}} </view>
<view> {{a.bar()}} </view>

<wxs> 标签

该标签用于包裹 wxs 脚本段或引入指定脚本文件。

包含以下两个属性:

  • module: 当前 <wxs> 标签的模块名。必填字段。
  • src: 引用 .wxs 文件的相对路径。仅当本标签为 单闭合标签标签的内容为空 时有效。
module 属性

在单个 wxml 文件内,建议其值唯一。若有重复模块名则按照先后顺序覆盖(后者覆盖前者)。不同 wxml 文件之间的 wxs 模块名互补影响。

module 属性值的命名规则:

  • 首字符必须是:字母(a-zA-Z)下划线(_)
  • 剩余字符可以是:字母(a-zA-Z)下划线(_)数字(0-9)
src 属性

使用 src 引用其他 wxs 模块是,需注意:

  • 只能引用 .wxs 文件模块,且必须使用 相对路径
  • wxs 模块均为单例,wxs 模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个 wxs 模块对象。
  • 如果一个 wxs 模块在定义之后,一直没有被引用,则该模块不会被解析与运行。

模块

每一个 .wxs 文件和 <wxs> 标签都是一个单独的模块。

每个模块都有自己独立的作用域。即在一个模块里面定义的变量与函数,默认为私有的,对其他模块不可见。

一个模块要想对外暴露其内部的私有变量与函数,只能通过 module.exports 实现。

pages/wxs/a.wxs 中导出 foobar

// pages/wxs/a.wxs
const foo = 'hello 小程序'
const bar = function() {
  console.log('触发 a.wxs bar 函数')
}
module.exports = {
  foo,
  bar
}

若需要在.wxs模块中引用其他 wxs 文件模块,可以使用 require 函数。

pages/wxs/b.wxs 中引用 a.wxs

// pages/wxs/b.wxs
var a = require('./a.wxs')

console.log(a.foo)
console.log(a.bar())

使用 require 函数引用 wxs 模块时与使用 <wxs> src 属性引用类似,同样需要注意以下几点:

  • 只能引用 .wxs 文件模块,且必须使用相对路径。
  • wxs 模块均为单例,wxs 模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个 wxs 模块对象。
  • 如果一个 wxs 模块在定义之后,一直没有被引用,则该模块不会被解析与运行。
注意事项
  • <wxs> 模块只能在定义该模块的 WXML 文件中被访问到。使用 <include><import> 引用时,<wxs> 模块不会被引入到对应的 WXML 文件中。
  • <template> 标签中,只能使用定义该 <template>WXML 文件中定义的 <wxs> 模块。

数据类型

WXS 目前支持以下数据类型:

  • number : 数值
  • string :字符串
  • boolean:布尔值
  • object:对象
  • function:函数
  • array : 数组
  • date:日期
  • regexp:正则
es5 差异
  1. WXS 数据类型额外新增一个 constructor 属性
数据类型 contructor
number Number
string String
boolean Boolean
object Object
function Function
array Array
date Date
regexp RegExp
  1. 对象生成方式
  • 生成 date 对象需要使用 getDate函数, 返回一个当前时间的对象。
  • 生成 regexp 对象需要使用 getRegExp函数。
  • 其余数据类型均支持使用字面量方式定义数据对象。
  1. 数据类型判断
  • constructor 属性

由于每个数据类型都包含指定的 contructor 属性,因此可以直接通过该属性进行判断。

var number = 10;
console.log( "Number" === number.constructor );

var string = "str";
console.log( "String" === string.constructor );

var boolean = true;
console.log( "Boolean" === boolean.constructor );

var object = {};
console.log( "Object" === object.constructor );

var func = function(){};
console.log( "Function" === func.constructor );

var array = [];
console.log( "Array" === array.constructor );

var date = getDate();
console.log( "Date" === date.constructor );

var regexp = getRegExp();
console.log( "RegExp" === regexp.constructor );
  • tyoeof

此处 typeof 作用与 JavaScript 相同,其中 objectarraydateregexpnull 类型均返回 object

var number = 10;
var boolean = true;
var object = {};
var func = function(){};
var array = [];
var date = getDate();
var regexp = getRegExp();

console.log( 'number' === typeof number );
console.log( 'boolean' === typeof boolean );
console.log( 'object' === typeof object );
console.log( 'function' === typeof func );
console.log( 'object' === typeof array );
console.log( 'object' === typeof date );
console.log( 'object' === typeof regexp );

console.log( 'undefined' === typeof undefined );
console.log( 'object' === typeof null );

更多数据类型详细内容可以查阅小程序文档

其他

小程序还支持其他如变量定义、注释、运算符、条件\循环语句以及内置的基础类库等,其使用规则与 es5 大同小异,此处就不多占篇幅了,有疑问的小伙伴可以私聊或评论,确实有必要补充的话后续可更新上来。

结语

今天的「小程序入门系列-简介及工程创建」就到这里啦,读到这里的小伙伴应该对小程序主体文件处理小程序页面开发等有了大致的印象,爱动手的小伙伴们快去动起来吧😁。

若本文内容有错误或遗漏的地方,欢迎评论指出。感谢阅读,愿你我共同进步,谢谢!!!

相关推荐

  1. UNIAPP&程序入门精通

    2024-03-15 05:58:03       9 阅读
  2. 微信程序入门进阶(一)

    2024-03-15 05:58:03       30 阅读
  3. 微信程序入门进阶(三)

    2024-03-15 05:58:03       36 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-15 05:58:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-15 05:58:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-15 05:58:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-15 05:58:03       20 阅读

热门阅读

  1. 音视频实战---从音视频文件中提取h264裸流

    2024-03-15 05:58:03       20 阅读
  2. CMake 脚本命令(Scripting Commands)之find_package

    2024-03-15 05:58:03       21 阅读
  3. 谈谈对chatgpt的看法

    2024-03-15 05:58:03       21 阅读
  4. ChatGPT的核心技术

    2024-03-15 05:58:03       20 阅读
  5. 导出pdf

    导出pdf

    2024-03-15 05:58:03      17 阅读
  6. 华为OD应聘感受

    2024-03-15 05:58:03       34 阅读
  7. Rust镜像配置

    2024-03-15 05:58:03       21 阅读
  8. 使用Excel导入和导出数据

    2024-03-15 05:58:03       23 阅读
  9. [Django 0-1] Core.Files

    2024-03-15 05:58:03       19 阅读
  10. Redis-Sentinel哨兵

    2024-03-15 05:58:03       20 阅读
  11. PYTHON 120道题目详解(118-120)

    2024-03-15 05:58:03       16 阅读