准备工作

小程序开发要先注册小程序账号,有了小程序账号才可以使用开发者工具。小程序是一种特殊的开发形式,里面的 API 和组件都是自己定制的,因此在普通的浏览器中不能预览,要预览功能和页面就需要使用开发者工具。

注册小程序账号

先准备一个没有注册过公众号的邮箱,然后访问 小程序介绍页面并点击底部的「前往注册」按钮,再按照提示填写个人信息,最后进入邮箱激活账号即可。

图片
图片

详细流程请参考官方文档

安装开发者工具

小程序有自己的开发者工具,可以编写代码,实时查看页面和功能效果,还能在开发者工具中进行 debug。小程序开发者工具是使用 NW.js 编写的。

开发者工具下载地址:微信开发者工具

使用开发者工具演示 WeUI

  • WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。包含 button、cell、dialog、 progress、 toast、article、actionsheet、icon 等各式组件。

WeUI 有两个版本,一个是普通的 HTML5 版本,另外是小程序版本。现在通过 WeUI 来简单学习开发者工具的使用。

首先下载 WeUI 源码:

# https 方式(推荐)
git clone https://github.com/Tencent/weui-wxss.git
# 或者 ssh 方式
git clone git@github.com:Tencent/weui-wxss.git

打开开发者工具,使用注册时绑定的微信账号扫码登录,这时在进入的界面中选择「小程序项目」,如果是初次使用小程序开发者工具,没有创建过小程序项目,就会进入下面的页面:

图片

在这个页面,先选择我们 clone 下来的 WeUI 项目的 dist 文件夹。对于 AppID 选项,如果已经有了小程序账号,可以在账号后台找到 AppID 并填写上;若还没有注册小程序账号,可以直接在「或使用测试号:」后面单击「小程序」,就会自动填好。然后给项目起个名字,比如「WeUI 演示」,点击确定后就打开了 WeUI 项目。这时候看到的是开发者工具的开发界面,如下图所示,开发界面主要由三部分组成:模拟器、编辑器和调试器。

图片

  • 模拟器:提供小程序的运行环境,模拟小程序在手机上的界面效果
  • 编辑器:简单 IDE 功能,点击左侧树形菜单可以打开多个文件直接编写保存,做到实时预览效果,但是开发者工具的编辑器做得比较简单,而且使用体验并不好,建议选择自己顺手的 IDE 增加 WXML 和 WXSS 的语法高亮插件等来编辑代码
  • 调试器:订制版的 Chrome 开发者工具,提供从页面结构到网络请求等多个面板支持,会用 Chrome DevTools 就很容易上手该工具

  • Tips: 常用 IDE 推荐

  • VS Code + minapp 插件
  • Sublime Text 3 + Sublime wxapp 插件
  • Vim + wxapp

除了三个重要组成部分之外,在开发者工具的顶部还有各种操作按钮。左侧主要是模拟器、编辑器、调试器和小程序云开发控制台的视图开关,可以控制对应视图的开启关闭。

图片

中间部分是跟开发、编译、测试和上线相关的各种按钮,我们在开发和测试小程序中会经常使用,最常用的有:

  • 预览、远程调试:是可以在手机上直接预览效果,开启远程 debug 功能的
  • 清缓存:对于一些授权登录、缓存、数据之类的操作,我们需要清理状态和数据,可以通过这个按钮操作
  • 上传:如果是创建项目的时候填写了 AppID,那么会出现这个按钮,小程序开发完毕后可以通过这个来上传,上传之后可以在小程序后台申请测试版和审核,审核通过后就可以正式上线了

最后介绍的是开发者工具右上角的「详情」。

图片

详情下面主要有三个 Tab:项目设置、域名信息和腾讯云状态。

  • 项目设置:这个是用得比较多的,可以指定上传代码时候的编译情况,比如支持 ES6 语法、支持 css autoprefixer、代码压缩等
  • 域名信息:小程序的 request 等访问的域名采用了白名单形式,在这里可以看到小程序管理后台设置的域名白名单
  • 腾讯云状态:可以看到小程序账号和腾讯云的绑定情况

小程序开发语言

小程序的开发语言跟前端开发者比较熟悉的 HTML5 非常相似(甚至相同),小程序的视图层由 WXML 和 WXSS 组成,分别对应 HTML 和 CSS,逻辑层则跟 HTML5 一样,也是 JavaScript 语言实现。

  • WXML:小程序自己发明的 XML 语法描述,用来构造小程序的页面结构
  • WXSS:小程序的页面的样式表语言,描述 WXML 的样式
  • JavaScript:小程序 JS 的执行环境并不是普通的 WebView 浏览器,也不是 Node.js 环境,它执行在微信 App 内上下文, 跟 Node.js 一样,也不能像在浏览器内一样对页面 DOM 进行操作

  • 微信小程序运行在三端:iOS、Android 和用于调试的开发者工具

  • 在 iOS 上,小程序的 JavaScript 代码运行在 JavaScriptCore 中
  • 在 Android 上,小程序的 JavaScript 代码通过 X5 内核来解析
  • 在 开发工具上, 小程序的 JavaScript 代码运行在 NW.js(Chromium 内核) 中

除了普通的 JavaScript,小程序还支持一种类似 JS 的 WXS 语言,WXS 对于小程序开发不是必需的,它的主要目的是为了增强 WXML 的数据处理能力而新引入一种技术实现,其实际解析的语言规范还是 JS,并没有引入新的语法,仅仅对 JS 做了上层的封装和限制,所以学习上基本没什么成本,大致了解下开发文档马上就能上手。

  • 对于 WXS 和 JavaScript 的性能比较,官方给出的数据是:由于运行环境的差异,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 Android 设备上二者运行效率无差异。

小程序项目相关知识

小程序目录结构

小程序项目由配置文件、页面文件、静态资源和其他相关(比如组件、小程序云函数等)内容组成,一般小程序会由四类文件组成:

  • .json 后缀的 JSON 配置文件
  • .wxml 后缀的 WXML 模板文件
  • .wxss 后缀的 WXSS 样式文件
  • .js 后缀的 JS 脚本逻辑文件

小程序项目的目录结构组成没有严格的要求,按照前端项目的经验,一般会分为:配置、页面、静态资源、基础库、组件等多个目录,例如下面的目录结构:

├── app.js            小程序全局app相关js
├── app.json          小程序配置文件
├── app.wxss          小程序全局app样式
├── cloud-functions   云函数目录
│   ├── decrypt
│   ├── geocoder
│   ├── he-air
│   ├── he-weather
│   ├── jscode2session
│   └── weather
├── components        组件库
│   └── icon
├── images            图片等静态资源
│   └── cloud.jpg
├── pages             页面目录
│   ├── diary
│   └── weather
└── project.config.json  工具项目配置文件

当然根据不同的项目,可能目录结构不同,但是小程序必需的 app.json 和页面组成是必不可少的。另外,在开发复杂的项目时,我们会用到开发框架或者编译工具,这时候目录结构只需要保证编译之后的目录结构符合规范即可。

小程序的配置

小程序有三个重要的配置,分别放在三个 JSON 文件内:project.config.json(工具项目配置)、app.json(小程序配置)、page.json(单页面配置)

  • project.config.json:这个是配置项目工具相关的,比如开发者工具的编译设置(是否使用 ES6 语法等)、界面设置,以及云函数相关的 cloudfunctionRoot,详细可以参考项目配置文件
  • app.json:小程序的全局配置,包括了所有页面路径、界面表现、网络超时时间、底部 tab、插件等,常用的两个配置是 window 和 pages,详细配置参考全局配置
  • page.json:是相对于 app.json 更细粒度的单页面配置,详细参考页面配置

组件和插件

小程序页面是由各种组件组成的,组件可以类比成原生 HTML5 的标签。

组件

小程序内部定义了很多组件,可以对应 HTML5 的标签和基础能力来理解,小程序的组件根据实现不同,可以分为 Web 组件和 Native 组件,Web 组件是由 HTML5 原生 Web 组件封装的组件,比如 view、image 等;Native 组件是为了增强小程序的体验,用客户端技术实现的组件,包括一些交互复杂、原生 Web 组件性能不高的组件,例如 input、map、canvas、video 等。

小程序一共提供 8 大类 30 多个组件:

  • 视图容器:主要是实现页面布局的,对常见的布局形式进行了封装,比如滚动 sroll-view 等
  • 基本内容:类似 HTML5 中内容相关的 p、em 等
  • 表单相关:要比 HTML5 的 form 表单丰富一些
  • 导航:类似 a 标签
  • 媒体:类比 HTML5 中的 video、audio 和 img 等,但是提供更标准的界面和更丰富的 API 支持
  • 画布:Native 实现的 Canvas
  • 地图:结合腾讯地图数据 Native 实现的组件
  • 开放能力:这部分组件偏通用和小程序业务

自定义组件

小程序本身支持很多组件,比如地图、按钮等,开发者也可以自己做项目内公共组件,比如我们后面实战部分会介绍做一个 icon 组件,放在 components 目录下面,这样此小程序的任何页面如果要使用这个 icon 公共组件,只需在自己的 page.json 中添加如下字段:

"usingComponents": {
  "icon": "../../components/icon/index"
}

添加完成之后,在页面代码中就可以直接使用 的 tag 了。官方文档有更加详细的介绍。

插件

插件是对一组 JS 接口、自定义组件或页面的封装,用于提供给第三方小程序调用。简单来说,插件是组件的升级版本,组件只能在自己项目中使用,插件则更独立,是可以发布到全网,供其他开发者使用的。例如实战中,笔者使用了一款日历插件,则需要在 app.json 中增加 plugins 字段:

"plugins": {
  "calendar": {
    "version": "1.1.3",
    "provider": "wx92c68dae5a8bb046"
  }
}

如果想开发一个插件,则可以参考官方文档

小程序开发基础

微信小程序是由数据驱动的开发框架,本小节主要介绍小程序开发中的基础概念和知识。

数据驱动

微信小程序是数据驱动模型,在 WXML 中可以对页面的数据进行绑定,小程序的 WXML 内使用的是 Mustache 语法,在 {{}} 内可以将变量名包裹起来。 例如:

<view>{{ message }}</view>
Page({
  data: {
    message: 'Hello MINA!'
  }
})

但是小程序不支持复杂的表达式,目前支持简单的三元、加减和逻辑判断,如果要使用形如 {{parseInt(num)}} 的函数调用语法,需要 WXS 支持:

WXML 内容:

<wxs src="./demo.wxs" module="tools" />
<view>{{ tools.toNumber(num) }}</view>

WXS 内容:

// demo.wxs
function toNumber(n){
  return parseInt(n)
}
module.exports.toNumber = toNumber

小程序内对页面的数据修改只能通过 setData 方法,不能使用直接赋值的方式 this.data.key = value:

Page({
  data: {
    message: 'Hello MINA!'
  },
  onLoad(){
    this.setData({
      message: 'hello world~'
    })
  }
})
记住:修改页面数据,只能使用 this.setData 修改!

事件绑定和处理

在小程序内,除了标准 HTML5 中遇见的 touchstart 等事件外,增加了 tap 类的事件,主要包括以下几种:

事件名称 说明
tap 手指触摸后马上离开
longpress 手指触摸后,超过 350ms 再离开,如果指定了事件回调函数并触发了这个事件,tap 事件将不被触发
longtap 手指触摸后,超过 350ms 再离开(推荐使用 longpress 事件代替)

事件冒泡

小程序内的事件分为可冒泡和不可冒泡的事件,除了 submit、input 之类的事件,多数是可冒泡的事件,对于事件的绑定,除了 bind* 的方式,还可以通过 catch* 的方式来绑定,两者的区别在于:

  • bind 不会阻止冒泡,变形写法为 bind : *
  • catch 会阻止事件继续冒泡,变形写法为 catch : *

事件捕获

小程序内,触摸类事件支持捕获阶段,捕获是先于冒泡的触发,绑定捕获事件,可以使用 capture-bind、capture-catch,后者将中断捕获阶段和取消冒泡阶段,下面是官方的示例:

  • 在下面的代码中,点击 inner view 会先后调用 handleTap2、handleTap4、handleTap3、handleTap1。
<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">
  outer view
  <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
    inner view
  </view>
</view>
  • 如果将上面代码中的第一个 capture-bind 改为 capture-catch,将只触发 handleTap2。
<view id="outer" bind:touchstart="handleTap1" capture-catch:touchstart="handleTap2">
  outer view
  <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
    inner view
  </view>
</view>

关于事件的详细说明,建议阅读官方的文档,以获取更大的帮助。

事件对象

当事件触发时,处理函数会响应,传入 event 对象,通过 event 对象可以获取事件触发时候的一些信息,包括时间戳、detail 等。

因为小程序内的事件绑定都是在 WXML 中实现的,所以传递参数只能通过 WXML 上面的属性值来传递,例如下面的代码中,indexDetail 处理函数需要接收生活指数的名称和详情,来弹出弹层提示,这时候需要在标签上增加 data-xx 这样的属性,data-name 和 data-detail 就是两个属性,通过这两个值,可以在 indexDetail 内 event 对象的 target/currentTarget 的 dataset 获取参数。

<view class="life-style">
    <view class="item" wx:for="{{lifeStyle}}" data-name="{{item.name}}" data-detail="{{item.detail}}" bindtap="indexDetail">
      <view class="title">
        <icon type="{{item.icon}}"></icon>
        {{item.name}}
      </view>
      <view class="content">{{item.info}}</view>
    </view>
</view>
// weather/index.js
// 响应事件的处理函数
indexDetail(e) {
  const {name, detail} = e.currentTarget.dataset
  wx.showModal({
    title: name,
    content: detail,
    showCancel: false
  })
}

按照官方文档,target 和 currentTarget 都有个 dataset,正确获取 dataset 的姿势是使用 currentTarget 的,但是有时候 target 和 currentTarget 的数据又是完全一样的,如果这里使用 target 的话,那么有时候点击会弹出弹窗,有时候不会弹出,这两者究竟是怎样的关系呢?官方的解释有点模棱两可:

  • target:触发事件的源组件
  • currentTarget:事件绑定的当前组件

这里笔者做下详细解释:

  • target:触发事件的源组件,上面的代码中,target 可能是 view.title、view.content、view.item 任意触发事件的组件
  • currentTarget:事件绑定的当前组件,上面的代码中,只能是真正绑定了 bindtap 的 view.item

下面再来看下例子:

<view id="outer" bindtap="handleTap1">
  outer view
  <view id="middle" catchtap="handleTap2">
    middle view
    <view id="inner" bindtap="handleTap3">
      inner view
    </view>
  </view>
</view>
  • 点击 inner view 时,handleTap3 收到的事件对象 target 和 currentTarget 都是 inner,而 handleTap2 收到的事件对象 target 就是 inner,currentTarget 就是 middle。

  • 由此一看,可以简单总结出来:target 是事件触发源头的地方,即事件开始的地方,可以冒泡到父节点触发父节点的绑定事件;而 currentTarget 是开发者自己绑定事件的地方,即实际的绑定事件的节点。所以,如果绑定的事件有子节点,那么 target 不会等于 currentTarget,有可能是冒泡触发的,由此可见,获取 dataset 的时候使用 currentTarget 是靠谱的。

小程序的事件驱动和数据绑定模型

由上面数据驱动和事件监听的处理方式可见,小程序是一套数据和事件驱动的模型,即下面的形式:

图片

路由

在小程序内,不能像 HTML5 中 a 标签那样,随便跳转,也不能像 location 对象中对应的属性那样随意跳转,小程序提供了对应 a 标签和 location 对象的方法:navigator 组件wx 中的导航相关函数

在小程序中,路由是由路由栈来维护的,小程序的路由栈中最多维护 5 个页面,这样在 5 个页面内,小程序维护其渲染页面,可以实现快速的切换。

小程序中跳转页面可以通过下面两种方式:

  1. 使用 navigator 组件:
<navigator url="跳转页面URL" >跳转到新页面</navigator>
  1. 使用 wx 中的导航相关函数:
<view bindtap="gotoUrl">跳转页面</view>
Page({
  gotoUrl(){
    let url = 'pages/another/url'
    wx.navigateTo({
      url
    })
  }
})

JavaScript 的限制和增强

微信内的 JavaScript 相对于浏览器中的有限制也有增强,增强的部分是基于小程序 Native 端能力做的增强,比如增强的文件操作类(相册、录音等);除了增强,跟 HTML5 浏览器环境最大的不同是限制部分。

小程序的执行环境是没有浏览器了,所以浏览器环境特有的 window 对象、BOM 和 DOM 等相关 API 都存在缺失(有对应的补充 API),小程序的执行环境是类似于 Node.js 的一种执行环境。因为没有浏览器环境,所以跟浏览器相关的操作如 cookie、Ajax 请求(XMLHttpRequest)、DOM 选择器、DOM 事件、路由 history、缓存、设备信息、位置等都不存在,与之相对应的是小程序的私有 API,比如我们在小程序中不能使用 XMLHttpRequest,但是可以使用功能更加强大的 wx.request 方法。

小程序布局相关知识

rpx

小程序 WXSS 中使用了 rpx 这个长度单位,可以用于表示元素的宽高和边距、字体的大小等。对于习惯使用 px 或者 rem 来做页面的前端来说,这可能让人有点迷糊。rpx 是以小程序容器宽度(等于设备宽度)恒等于 750rpx 来做定义的。对于 iPhone 6 来说,因为 dpr 为 2,所以 iPhone 的宽度为 375px,这样在 iPhone 6 上使用 rpx 的话,换算关系为 2rpx=1px。根据这样的关系类推,得到官方给的表格:

设备 rpx换算 px (屏幕宽度/750) px 换算 rpx (750/屏幕宽度)
iPhone 5 1rpx = 0.42px 1px = 2.34rpx
iPhone 6 1rpx = 0.5px 1px = 2rpx
iPhone 6 Plus 1rpx = 0.552px 1px = 1.81rpx

看起来很麻烦,但是只需要按照官方建议,让设计师按照 iPhone 6 的视觉稿标准出图即可,即宽度为 750px,按照 750px 出图,那么我们写页面时直接使用测量的尺寸来设置 rpx 就行了。

关于 flex 布局相关知识,可以参考阮一峰的 flex 布局教程:语法篇实例篇

总结

更多微信小程序的基础知识可以参考官方文档:简易教程框架组件API


千万别成为大多数人