微信小程序


小程序基础知识

1. 认识什么是小程序

什么是微信小程序

微信小程序是一种运行在微信内部的 轻量级 应用程序。

在使用小程序时 不需要下载安装,用户 扫一扫 或 搜一下 即可打开应用。它也体现了 “用完即走” 的理念,用户不用关心安装太多应用的问题。它实现了应用“触手可及”的梦想,应用无处不在,随时可用,但又 无须安装卸载。

特点:

小程序的四大特性:无须安装、用完即走、无须卸载、触手可及

  1. 无须安装:体积小,用户感知不到下载的过程
  2. 触手可及:用户 扫一扫 或 搜一下 即可打开应用,通过下拉能访问使用的小程序
  3. 用完即走,无须卸载

2. 微信小程序账号注册

小程序开发 与 网页开发不一样,在开始微信小程序开发之前,需要访问 微信公众平台,注册一个微信小程序账号。

在拥有了小程序的账号以后,我们才可以开发和管理小程序,后续可以通过该账号进行开发信息的设置、成员的添加,也可以用该账号查看、运营小程序的相关数据。

📌 注意事项

在申请账号前,我们需要先准备一个邮箱,该邮箱要求:

1️⃣ 未被微信公众平台注册❗

2️⃣ 未被微信开放平台注册❗

3️⃣ 未被个人微信号绑定过 !如果被绑定了需要解绑 或 使用其他邮箱 ❗

  1. 打开 微信公众平台,点击立即注册

  2. 选择注册的帐号类型,在这里我们需要 选择小程序

  3. 输入账号信息

  4. 邮箱激活,需要进入邮箱进行激活

  5. 信息登记,注册类型 (需要选择中国大陆和个人,企业其他需要资质认证)

  6. 主体信息登记与确认

    📌 注意:

    ​ 在进行管理员身份验证的时候,推荐使用自已的微信进行扫码,

    ​ 将自已设置为小程序账号的管理员,方便以后对小程序进行开发、成员等相关的设置

  7. 小程序注册完成,点击前往小程序,即可进入小程序后台

3. 完善小程序账号信息

在完成小程序账号的注册后,你便可以打开微信公众平台对小程序账号进行一些设置,这是你开发前的准备工作,完善后才可以进入后续的开发步骤,这是因为小程序在后续进行提交审核的时候,小程序账号信息是必填项,因此在注册小程序以后,需要补充小程序的基本信息,如名称、图标、描述等。

📌 注意事项

​ 在填写小程序类目时不要选择游戏类型,否则微信官方将会视为小游戏开发 ❗

点击 前往填写,填写小程序基本信息即可

点击 前往设置 , 设置小程序类目信息

  1. 点击右上角添加类目

  2. 管理员授权

  3. 手机微信进行认证

  4. 添加小程序类目,注意:选择类目的时候不要选择小游戏类目 !!!!

4. 小程序开发者 ID

小程序的开发者账号是免费的,只要开发者满足开发资质就可以免费注册,并且会获得对应的开发者 ID

一个完整的开发者 ID 由 **小程序 ID(AppID)**和一个 **小程序密钥(AppSecret)**组成。

小程序 ID 即 AppId 是小程序在整个微信账号体系内的唯一身份凭证,后续在很多地方都会用到,例如:新建小程序项目、真机调试、发布小程序等操作时,必须有小程序 ID。

小程序密钥 是开发者对小程序拥有所有权的凭证,在进行 微信登录、微信支付,或进行发送消息等高级操作时会使用到

在微信公众后台,单击左侧开发标签,选择 “开发管理”,在新的页面中点击 “开发设置”,就可以看到开发者 ID 信息。请妥善保管你的小程序 ID 和小程序密钥,在后续的开发中会经常使用到,获取位置见下图:

5. 开发成员和体验成员

小程序提供了两种不同的成员角色:项目成员体验成员

项目成员:参与小程序开发、运营的成员,可登陆小程序管理后台,包括运营者、开发者及数据分析者。管理员可在“成员管理”中添加、删除项目成员,并设置项目成员的角色。

体验成员:参与小程序内测体验的成员,可使用体验版小程序,但不属于项目成员。管理员及项目成员均可添加、删除体验成员。

6. 微信开发者工具

为了帮助开发者简单和高效地开发和调试微信小程序,微信官方提供了 微信开发者工具,利用开发者工具可以很方便地进行小程序开发、代码查看以及编辑、预览和发布等功能。

微信开发者工具 下载页面,可以看到微信开发者工具包含三个版本:

  1. 稳定版:稳定性高,开发中一般推荐大家使用稳定版本

  2. 预发布版:稳定性尚可,一般包含新的、大的特性,通过了内部测试

  3. 开发版:稳定性差,主要用于尽快修复缺陷和敏捷上线小的特性,如果想体验新特性,可以使用这个版本

选择合适的版本进行下载,在下载完成后,双击下载好的微信开发者工具安装包,根据引导点击下一步、我接受、直至安装完成。第一次使用微信开发者工具的时候,需要使用手机微信扫码登录,登录成功即可进入项目选择界面

📌 注意事项

​ 微信开发者工具必须联网使用 ❗

8. 创建小程序项目

创建项目

使用小程序开发者工具创建一个新的项目,步骤如下:

  1. 打开微信开发者工具,左侧选择小程序,点击 + 号即可新建项目

  2. 在弹出的新页面,填写项目信息

    • 项目名称:输入项目名称
    • 目录:选择小程序开发文件夹,注意:**小程序的目录建议是空目录,否则官方会有提示,见下图**
    • AppID:填写自己申请的小程序 AppID
    • 开发模式:选择小程序
    • 后端服务:选择不使用云服务
    • 模板选择:选择不使用模板
  3. 点击确定,如果能够看到小程序的开发主界面,说明小程序项目已经创建成功

开发者工具设置

为了后续高效的对小程序项目进行开发,我们需要对微信开发者工具进行一些个性化的设置,例如:设置模拟器位置、编辑器主题、编辑区行高等,当然你可以继续使用官方默认的,也可以按照自己的喜好设置,设置步骤如下:

  1. 将小程序模拟器移动右侧:点击菜单栏的 视图 ➡ 外观 ➡ 将模拟器移到右侧,小程序模拟器即可调整到右侧

  2. 小程序主题设置,点击菜单栏的 设置 ➡ 外观设置 ➡ 在弹框中将主题和调试工具选择为深色

  3. 编辑区的设置,点击菜单栏的 设置 ➡ 编辑器设置 ➡ 按照自己的洗好调整行距和字号,或者其他设置

9. 小程序目录结构和文件介绍

在将小程序项目创建好以后,我们先来熟悉小程序项目的目录结构,如下图:

一个完整的小程序项目分为两个部分:主体文件页面文件

主体文件 又称小程序全局文件,顾名思义,全局文件能够作用于整个小程序,影响到小程序的每个页面,且主体文件必须放到项目的根目录下,主要由三部分组成:

文件名 作用 是否必须
app.js 小程序入口文件 必须
app.json 小程序的全局配置 必须
app.wxss 小程序的全局样式 非必须

页面文件 是每个页面所需的文件,小程序页面文件都存放在 pages 目录下,一个页面一个文件夹,每个页面通常由四个文件组成,每个文件只对当前页面有效:

文件名 作用 是否必须
.js 页面逻辑 必须
.wxml 页面结构 必须
.wxss 页面样式 非必须
.json 页面配置 非必须

📌 注意事项

  1. 页面文件,wxss、json 文件能够覆盖主体文件中的样式和配置
  2. 强烈建议:页面文件夹名称和页面文件名称要保持一致 感叹号

新建小程序文件和文件夹作用清单:

├─pages	                    ➡ 小程序页面存放目录
│
│  ├─index				   ➡ index 文件夹代表是 index 页面所需的文件
│  │      index.js          ➡ index 页面逻辑
│  │      index.json        ➡ index 页面配置
│  │      index.wxml        ➡ index 页面结构
│  │      index.wxss        ➡ index 页面样式
│  .eslintrc.js              ➡ Eslint 配置文件
│  app.js                    ➡ 小程序入口,即打开小程序首页执行的项目
│  app.json                  ➡ 小程序的全局配置
│  app.wxss                  ➡ 小程序的全局样式
│  project.config.json       ➡ 小程序开发者工具配置
│  project.private.config.json
│  sitemap.json              ➡ 小程序搜索优化

10. 如何调试小程序

在进行项目开发的时候,不可避免的需要进行调试,那么如何调试小程序呢 ?

📌 注意事项:

微信开发者工具缓存非常严重❗❗❗❗

如果发现代码和预期不一样,先点击编译❗

编译后还是没有达到预期的效果,就需要清除缓存❗ 甚至重启项目才可以❗

11. 如何新建页面

第一种方式:

  1. 在 pages 目录上 点击右键 新建文件夹,输入页面目录的名称,例如:list

  2. 在 list 目录上 点击右键 点击 page,输入页面文件的名称,例如:list

📌 注意事项:

  1. 在输入页面文件名称的时候,不要输入后缀名 ❗
  2. 新建页面成功以后,会在 app.json 的 pages 选项中新增页面路径

第二种方式:

在 app.json 的 pages 选项中,新增页面路径即可

在新增页面目录以后,会自动在 pages 目录下生成页面文件

12. 调试基础库

小程序调试基础库是指 微信开发者工具中可以选择的微信基础库版本。

微信基础库是指小程序的运行环境,给小程序提供了运行所需的各种 API 和工具,以及基础框架和运行逻辑等。

小程序开发者可以在微信开发者工具中选择所需的微信基础库版本,作为运行和调试小程序时的运行环境。

每个小程序有自己所允许使用的基础库最低版本要求,开发者需要选择要兼容的基础库版本,从而确保小程序的功能正常运行。

小程序配置文件

1. 配置文件介绍

JSON是一种轻量级的数据格式,常用于前后端数据的交互,但是在小程序中,JSON 扮演的静态配置的角色,用于配置当前页面或组件的属性和行为,每个页面或组件也都可以拥有一个对应的 json 文件。

小程序中常见的配置文件有以下几种:

  1. 全局配置文件:app.json ➡ 小程序全局配置文件,用于配置小程序的一些全局属性和页面路由。
  2. 局部配置文件:页面.json ➡ 小程序页面配置文件,用于配置当前页面的窗口样式、页面标题等
  3. 项目配置文件:project.config.json ➡ 小程序项目的配置文件,用于保存项目的一些配置信息和开发者的个人设置
  4. 搜索配置文件:sitemap.json ➡ 配置小程序及其页面是否允许被微信索引,提高小程序在搜索引擎搜索到的概率

下面我们依次来说明一下它们的用途。

2. 全局配置

小程序 app.json 文件是小程序的全局配置文件,用于配置小程序的一些全局属性和页面路由。

当小程序启动时,会自动读取 app.json 文件中的配置,根据配置生成对应的页面和组件、界面表现、网络超时时间、底部 tab,在小程序运行过程中起着关键的作用。

2.1 pages

pages 字段:用于指定小程序由哪些页面组成,用于为了让小程序知道由哪些页面组成以及页面定义在哪个目录,每一项都对应一个页面的 路径(含文件名) 信息。

{
  "pages": [
    "pages/index/index",
    "pages/list/list"
  ]
}

📌 注意事项:

  1. 文件名不需要写文件后缀框架会自动去寻找对应位置的 .json, .js, .wxml, .wxss 四个文件进行处理
  2. 小程序中新增/减少页面,都需要对 pages 数组进行修改。
  3. 未指定 entryPagePath 时,数组的第一项代表小程序的初始页面(首页)

🔔 开发小技巧

​ 可以通过配置小程序的页面路径快速生成小程序的页面

详细文档: pages 配置项

2.2 window

window字段: 用于设置小程序的状态栏、导航条、标题、窗口背景色。

属性 描述 类型 默认值
navigationBarBackgroundColor 导航栏背景颜色 HexColor #000000
navigationBarTextStyle 导航栏标题颜色,仅支持 black / white string white
navigationBarTitleText 导航栏标题文字内容 string
backgroundColor 下拉 loading 的样式,仅支持 dark / light string dark
enablePullDownRefresh 是否开启全局的下拉刷新 boolean false
onReachBottomDistance 页面上拉触底事件触发时距页面底部距离单位为 px number 50

我们按照下图来配置 window

{
  "window": {
    "backgroundTextStyle": "light",
    "backgroundColor": "#eee",
    "navigationBarBackgroundColor": "#f3514f",
    "navigationBarTitleText": "慕尚花坊",
    "navigationBarTextStyle": "white",
    "enablePullDownRefresh": true
  }
}

详细文档: window 配置项

2.3 tabBar

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

tabBar 配置项

属性 描述 类型 默认值
color tab 上的文字默认颜色,仅支持十六进制颜色 HexColor
selectedColor tab 上的文字选中时的颜色,仅支持十六进制颜色 HexColor
backgroundColor tab 的背景色,仅支持十六进制颜色 HexColor
borderStyle tabbar 上边框的颜色, 仅支持 black / white string black
list tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab
position tabBar 的位置,仅支持 bottom / top string bottom

List 配置项:list 是一个数组,只能配置最少 2 个、最多 5 个 tab,tab 按数组的顺序排序,每个项都是一个对象

属性 描述 类型 是否必填
pagePath 页面路径,必须在 pages 中先定义 string
text tab 上按钮文字 string
iconPath 图片路径,icon 大小限制为 40kb,
建议尺寸为 81px * 81px,
string
selectedIconPath 选中时的图片路径,icon 大小限制为 40kb,
建议尺寸为 81px * 81px,不支持网络图片。
string

📌 注意事项:

  1. list 是一个数组,只能配置最少 2 个、最多 5 个 tab
  2. positiontop 时,不显示 icon

我们按照下图来配置 tabBar

{
  "tabBar": {
    "color": "#252933",
    "selectedColor": "#FF734C",
    "backgroundColor": "#ffffff",
    "borderStyle": "black",
    "list": [
      {
        "text": "首页",
        "iconPath": "/assets/tabbar/home.png",
        "selectedIconPath": "/assets/tabbar/home-active.png",
        "pagePath": "pages/home/home"
      },
      {
        "text": "列表",
        "iconPath": "/assets/tabbar/list.png",
        "selectedIconPath": "/assets/tabbar/list-active.png",
        "pagePath": "pages/list/list"
      },
      {
        "text": "购物车",
        "iconPath": "/assets/tabbar/cart.png",
        "selectedIconPath": "/assets/tabbar/cart-active.png",
        "pagePath": "pages/cart/cart"
      },
      {
        "text": "我的",
        "iconPath": "/assets/tabbar/my.png",
        "selectedIconPath": "/assets/tabbar/my-active.png",
        "pagePath": "pages/my/my"
      }
    ]
  }
}

详细文档: window 配置项

3. 页面配置

小程序的页面配置,也称局部配置,每一个小程序页面也可以使用自己的 .json 文件来对本页面的窗口表现进行配置

需要注意的是:页面配置文件的属性和 全局配置文件中的 window 属性几乎一致

只不过这里不需要额外指定 window 字段,因此如果出现相同的配置项,页面中配置项 会覆盖全局配置文件中相同的配置项。

属性 描述 类型 默认值
navigationBarBackgroundColor 导航栏背景颜色 HexColor #000000
navigationBarTextStyle 导航栏标题颜色,仅支持 black / white string white
navigationBarTitleText 导航栏标题文字内容 string
backgroundColor 下拉 loading 的样式,仅支持 dark / light string dark
enablePullDownRefresh 是否开启全局的下拉刷新 boolean false
onReachBottomDistance 页面上拉触底事件触发时距页面底部距离单位为 px number 50

📌 注意事项

​ 页面配置项和 app.json 中的 window 属性几乎一致,但这里不需要额外指定 window 字段

{
  "usingComponents": {},
  "navigationBarTitleText": "商品分类",
  "navigationBarTextStyle": "white",
  "navigationBarBackgroundColor": "#00AF92",
  "enablePullDownRefresh": true,
  "backgroundColor": "#eee",
  "backgroundTextStyle": "light"
}

4. 项目配置文件

通常大家在使用一个工具的时候,都会针对各自喜好做一些个性化配置,例如 编译配置等等,当你换了另外一台电脑重新安装工具的时候,你还要重新配置。

考虑到这点,小程序开发者工具在每个项目的根目录都会生成一个 project.config.json,你在工具上做的任何配置都会写入到这个文件,当你重新安装工具或者换电脑工作时,你只要载入同一个项目的代码包,开发者工具就自动会帮你恢复到当时你开发项目时的个性化配置,其中会包括编辑器的颜色、代码上传时自动压缩等等一系列选项

project.config.json 文件中配置公共的配置,

project.private.config.json 配置个人配置

project.private.config.json 文件同样可以对项目进行配置

project.private.config.json中的相同设置优先级高于project.config.json

📌 注意事项

  1. project.private.config.json 写到 .gitignore 避免版本管理的冲突。

  2. 与最终编译结果有关的设置 必须 设置到 project.config.json 中

可以在微信开发者工具,点击以下两个配置选项进行相关的设置,然后观察两个文件的变化即可。

项目配置文件的配置选项比较多,如有需要

大家可以参考详细的官方配置文档:项目配置文件

5. 支持使用 sass/less

小程序代码包要求代码文件为 wxml / wxss / js / json / wxs。

如果我们希望使用 sass 或 less 去开发小程序,就需要将 sass / less 文件编译成对应的 wxss 文件。

project.config.json 文件中,修改 setting 下的 useCompilerPlugins 字段为 ["sass"],即可开启工具内置的 sass 编译插件。 如需同时开启 typescript 编译插件,可将该字段修改为 [“typescript “, “sass”]。 目前支持三个编译插件:typescript、less、sass

{
  "setting": {
+    "useCompilerPlugins": [
+      "sass"
+    ]
  }
}

配置好以后,需要将 .wxss 改成 .scss

📌 注意:

我们在配置的时候,编译插件的名称是 sass,但是文件的后缀名是 .scss

这是因为 Sass 是 Css 预处理语言,scss 是 Sass 的一个语法版本

官方文档:开发辅助,支持以编译插件的形式,扩展编译功能

6. sitemap.json

微信现已开放小程序内搜索,当开发者允许微信索引时,微信会通过爬虫的形式,为小程序的页面内容建立索引。当用户的搜索词条触发该索引时,小程序的页面将可能展示在搜索结果中。

可以通过 sitemap.json 配置,或者管理后台页面收录开关来配置其小程序页面是否允许微信索引。语法如下:

{
  "rules": [
    {
      "action": "allow",
      "page": "*"
    }
  ]
}

常用配置项:

属性 属性说明 属性值
action 是否允许被搜索 可选项有:allow 、disallow
page 允许/不允许被搜索的页面 页面路径或者通配符
// 所有页面都会被微信索引(默认情况)
{
  "rules": [
    {
      "action": "allow",
      "page": "*"
    }
  ]
}
// 配置 path/to/page 页面不被索引,其余页面允许被索引
{
  "rules":[
    {
      "action": "disallow",
      "page": "path/to/page"
    }
  ]
}

📌 注意事项

  1. 没有 sitemap.json 则默认所有页面都能被索引
  2. sitemap.json 文件中默认的设置,是优先级最低的默认规则,所有页面都会被微信索引

常用样式和组件

1. 组件和样式介绍

在开 Web 网站的时候:

页面的结构由 HTML 进行编写,例如:经常会用到 divpspanimga 等标签

页面的样式由 CSS 进行编写,例如:经常会采用 .class#idelement 等选择器

但在小程序中不能使用 HTML 标签,也就没有 DOMBOM,同时仅仅支持部分 CSS选择器

不过不用担心,小程序中提供了同样的角色:

其中 WXML 充当的就是类似 HTML 的角色,只不过在 WXML 中没有divpspanimga 等标签,在 WXML 中需要使用 小程序提供的 viewtextimagenavigator 等标签来构建页面结构,**小程序提供的这些标签,我们称为 “组件”**,开发者可以通过组合这些基础组件进行快速开发。

WXSS 则充当的就是类似 CSS 的角色,WXSS 具有 CSS 大部分的特性,小程序在 WXSS 也做了一些扩充和修改,新增了尺寸单位 rpx、提供了全局的样式和局部样式,另外需要注意的是WXSS 仅支持部分 CSS 选择器。

小程序给提供的组件文档:WXML

小程序样式官方文档:WXSS

2. 样式-尺寸单位

随着智能手机的发展,手机设备的宽度也逐渐多元化,这就需要开发者在开发的时候,需要适配不同屏幕宽度的手机。为了解决屏幕适配的问题,微信小程序推出了 rpx 单位

小程序运行在手机移动端,宿主环境是微信,因为手机尺寸的不一致,在写 CSS 样式时,开发者需要考虑到手机设备的屏幕会有不同的宽度和设备像素比,会采用一些技巧来算像素单位从而实现页面的适配。而 WXSS 在底层支持新的尺寸单位 rpx ,开发者可以免去换算的烦恼,只要交给小程序底层来换算即可。

rpx : 小程序新增的拓展单位,可以根据屏幕宽度进行自适应,小程序规定任何型号手机:屏幕宽都为 750 rpx

📌 注意事项:

​ 小程序规定任何型号手机:屏幕宽都为 750 rpx

🔔 开发建议

​ 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。

​ iPhone6 的设计稿一般是 750px,小程序的宽是 750rpx

​ 在我们开发小程序页面时,量取多少 px ,直接写多少 rpx,开发起来更方便,也能够适配屏幕的适配

​ 原因:

​ 设计稿宽度是 750px,而 iPhone6 的手机设备宽度是 375px, 设计稿想完整展示到手机中,就需要缩小一倍

​ 在 iPhone6 下,px 和 rpx 的换算关系是:1rpx = 0.5px, 750rpx = 375px,刚好能够填充满整个屏幕的宽度

落地代码:

➡️ pages/index/index.wxml

<!-- 需求:绘制一个盒子,让盒子的宽度占据屏幕的一半 -->

<!-- view 是小程序提供的组件,是容器组件,类似于 div,也是一个块级元素,占据一行 -->
<!-- 如果想实现需求,不能使用 px,px 使固定单位,不能实现自适应,需要使用小程序提供的 rpx  -->
<!-- 微信小程序规定,不管是什么型号的手机,屏幕的宽度都是 750rpx -->
<!-- rpx 单位能够实现自适应的 -->
<view class="box">尚硅谷</view>

➡️ pages/index/index.wxss

/* 通过演示使用 css 作为单位,px 是不具有响应式的 */

/* image {
  width: 375px;
  height: 600px;
  background-color: lightgreen;
} */

.box {
  width: 375rpx;
  height: 600rpx;
  background-color: lightgreen;
}

3. 样式-全局样式和局部样式

在进行网页开发时,我们经常创建 global.css、base.css 或者 reset.css 作为全局样式文件进行重置样式或者样式统一,然后在每个页面或组件中写当前页面或组件的局部样式,小程序中也存在全局样式和局部样式,这一节我们来学习一下小程序中的全局样式和局部样式

知识点:

全局样式:指在 app.wxss中定义的样式规则,作用于每一个页面,例如:设置字号、背景色、宽高等全局样式

局部样式:指在page.wxss中定义的样式规则,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。

案例:

app.wxss 中定义全局样式,设置 text 组件的颜色以及字号大小,这段样式将会作用于任意页面的 text 组件

/* app.wxss */

text {
  color: lightseagreen;
  font-size: 50rpx;
}

然后在 cate.wxss 中定义局部样式,设置 text 组件的颜色以及字号大小,会发现局部样式将全局样式进行了覆盖

/* pages/index/index.wxss */

text {
  color: red;
  font-size: 30rpx;
}

4. 划分页面结构-view 组件

使用小程序常用的组件实现项目首页的效果,会使用以下组件:

1.view 组件

2.text 组件

3.image 组件

4.navigator 组件

5.swiper 和 swiper-item 组件

6.scroll-view 组件

7.字体图标

分析页面结构,使用 view 组件将页面拆分成 4 个区域

view 是小程序提供的组件,是容器组件,类似于 div,也是一个块级元素,占据一行

<!-- 轮播图区域 -->
<view class="swiper">1</view>

<!-- 公司相关信息 -->
<view class="info">2</view>

<!-- 商品导航区域 -->
<view class="goods-nav">3</view>

<!-- 商品推荐区域 -->
<view class="hot">4</view>

5. 首页轮播图区域-swiper 组件

在前面我们已经介绍了小程序的组件应该怎么使用,又学习了小程序中的样式,接下来带着大家使用小程序提供的组件,完成小程序的基本结构,通过这个案例我们能够学习到小程序常用的组件以及一些布局技巧。

知识点:

在进行网页开发的时候,实现轮播图的时候,我们通常先使用 HTML 、CSS 实现轮播图的样式结构,然后使用 JS 控制轮播图的效果,或者直接使用插件实现轮播图的功能,而在小程序中实现小程序功能则相对简单很多。

在小程序中,提供了 swiperswiper-item 组件实现轮播图:

  1. swiper:滑块视图容器,常用来实现轮播图,其中只可放置 swiper-item 组件,否则会导致未定义的行为
  2. swiper-item:仅可放置在swiper组件中,宽高自动设置为100%,代表 swiper 中的每一项

我们可以使用 swiper 组件提供的属性,实现轮播图的订制,常见属性如下:

属性 说明 类型
indicator-dots 是否显示面板指示点 boolean (默认 false)
indicator-color 指示点颜色 color (默认:rgba(0, 0, 0, .3))
indicator-active-color 当前选中的指示点颜色 color (默认:#000000)
autoplay 是否自动切换 boolean (默认 false)
interval 自动切换时间间隔 number (默认 5000)
circular 是否采用衔接滑动 boolean (默认 false)
其他属性…

落地代码:

➡️ pages/index/index.wxml

<!-- 轮播图区域 -->
<view class="swiper">
  <!-- swiper 组件实现轮播图区域的绘制 -->
  <!-- swiper 组件,滑块视图容器 -->
  <swiper
    circular
    autoplay
    indicator-dots
    interval="2000"
    indicator-color="#efefef"
    indicator-active-color="#ccc"
  >
    <!-- swiper 组件内部不能写其他组件或内容 -->
    <!-- 在 swiper 组件内部只能写 swiper-item 组件 -->
    <!-- swiper-item 组件只能放到 swiper 组件中,宽高自动设置为 100% -->
    <swiper-item>
      第一张轮播图
    </swiper-item>

    <swiper-item>
      第二张轮播图
    </swiper-item>

    <swiper-item>
      第三张轮播图
    </swiper-item>
  </swiper>
</view>

➡️ pages/index/index.scss

page {
  height: 100vh;
  background-color: #efefef !important;
}

swiper {
  swiper-item {

    // 在 Sass 拓展语言中,& 符号表示父选择器的引用。它用于在嵌套的选择器中引用父选择器
    // 下面这段代码在编译以后,生成的代码是 swiper-item:first-child
    &:first-child {
      background-color: skyblue;
    }

    &:nth-child(2) {
      background-color: lightcoral;
    }

    &:last-child {
      background-color: lightseagreen;
    }
  }
}

6. 首页轮播图区域-image 组件

在这一节中,我们开始来学习小程序中的image组件,通过学习该组件掌握组件的学习方法和使用技巧

在小程序中没有 img 标签,添加图片需要使用小程序提供的image组件,image组件的语法如下:

<!-- src:图片资源地址 -->
<!-- mode:图片裁剪、缩放的模式 -->
<!-- lazy-load:图片懒加载,在即将进入一定范围(上下三屏)时才开始加载 -->
<image src="/assets/tom.png" mode="heightFix" lazy-load=”{{ true }}“ />

落地代码

➡️ pages/index/index.wxml

<!-- 轮播图区域 -->
<view class="swiper">
  <swiper
    circular
    autoplay
    indicator-dots
    interval="2000"
    indicator-color="#efefef"
    indicator-active-color="#ccc"
  >
    <swiper-item>
      <!-- 在小程序中图片不能使用 img 标签,使用后不会生效 -->
      <!-- <img src="../../assets/banner/banner-1.png" alt=""/> -->

      <!-- 需要使用 images 组件 -->
      <!-- image 组件不给 src 属性设置默认值,也占据宽和高 -->
      <!-- image 默认具有宽度,宽是 320px 高度是 240px -->

      <!-- mode 属性:用来设置图片的裁切模式、纵横比例、显示的位置 -->
      <!-- show-menu-by-longpress 属性:长按转发给朋友、收藏、保存图片 -->
      <image src="../../assets/banner/banner-1.png" mode="aspectFill" show-menu-by-longpress />
    </swiper-item>

    <swiper-item>
      <image src="../../assets/banner/banner-2.png" />
    </swiper-item>

    <swiper-item>
      <image src="../../assets/banner/banner-3.png" />
    </swiper-item>
  </swiper>
</view>

➡️ pages/index/index.scss

/** index.wxss **/
page {
  height: 100vh;
  background-color: #efefef !important;
}

swiper {
  height: 360rpx;

  swiper-item {
    image {
      width: 100%;
      height: 100%;
    }

    // 在 Sass 拓展语言中,& 符号表示父选择器的引用。它用于在嵌套的选择器中引用父选择器
    // 下面这段代码在编译以后,生成的代码是 swiper-item:first-child
    // &:first-child {
    //   background-color: skyblue;
    // }

    // &:nth-child(2) {
    //   background-color: lightcoral;
    // }

    // &:last-child {
    //   background-color: lightseagreen;
    // }
  }
}

7. 公司宣传语区域-text 组件

落地代码:

➡️ pages/index/index.wxml

<!-- 公司相关信息 -->
<view class="info">
  <!-- text 是文本组件,类似于 span,是行内元素 -->

  <!-- user-select:文本是否可选 -->
  <!-- space:是否连续展示空格 -->
  <!-- <text user-select space="ensp">同城        配送</text> -->

  <text>同城配送</text>
  <text>行业龙头</text>
  <text>半小时送达</text>
  <text>100% 好评</text>
</view>

➡️ pages/index/index.scss

.info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 16rpx 0rpx;
  padding: 20rpx;
  font-size: 24rpx;
  background-color: #fff;
  border-radius: 10rpx;
}

8. 商品导航的区域-组件结合使用

到目前为止我们已经学习了 viewtextimage 以及 swiperswiper-item组件的使用

接下来我们继续来使用小程序提供的组件实现首页的功能,这节我们需要实现的是商品导航区域的结构

知识点:

在网页开发中,实现该布局非常简单,使用 div 嵌套 或者 ul 包裹住 lili 在包裹住 img 就能够实现该效果

但在小程序中没有 HTML 中的 divulli 标签,所以绘制导航区域需要使用小程序提供的的组件,我们先来学习小程序的两个组件:

  1. view:视图容器组件,相当于 HTML 中的 div,是一个块级元素,独占一行
  2. text:文本组件,相当于 span,是一个行内元素

落地代码:

➡️ pages/index/index.wxml

<!-- view:视图容器,作用类似于 div,是一个块级元素,独占一行 -->
<view class="navs">
  <view>
    <!-- text:文本组件,类似于 span,是一个行内元素 -->
    <image src="/assets/cate-1.png" alt=""/>
    <text>爱礼精选</text>
  </view>
  <view>
    <image src="/assets/cate-2.png" alt=""/>
    <text>鲜花玫瑰</text>
  </view>
  <view>
    <image src="/assets/cate-3.png" alt=""/>
    <text>永生玫瑰</text>
  </view>
  <view>
    <image src="/assets/cate-4.png" alt=""/>
    <text>玫瑰珠宝</text>
  </view>
  <view>
    <image src="/assets/cate-5.png" alt=""/>
    <text>香水护体</text>
  </view>
</view>

➡️ pages/index/index.wxss

// 商品导航区域
.good-nav {
  display: flex;
  justify-content: space-between;
  background-color: #fff;
  padding: 20rpx 16rpx;
  border-radius: 10rpx;

  view {
    display: flex;
    flex-direction: column;
    align-items: center;

    image {
      width: 80rpx;
      height: 80rpx;
    }

    text {
      font-size: 24rpx;
      margin-top: 12rpx;
    }
  }
}

9. 跳转到商品列表页面-navigator

知识点:

点击商品导航区域,需要跳转到商品列表界面,在网页开发中,如果想实现页面的跳转需要使用 a 标签,在小程序中如果想实现页面的跳转则需要使用 navigator 组件,语法如下:

<!-- url:当前小程序内的跳转链接 --> 
<navigator url="/pages/list/list">

在小程序中,如果需要进行跳转,需要使用 navigation 组件,常用的属性有 2 个:

  1. url :当前小程序内的跳转链接

  2. open-type :跳转方式

    • navigate:保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面
    • redirect: 关闭当前页面,跳转到应用内的某个页面。但不能跳转到 tabbar 页面
    • switchTab:跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
    • reLaunch:关闭所有页面,打开到应用内的某个页面
    • navigateBack:关闭当前页面,返回上一页面或多级页面

📌 注意事项:

  1. 路径后可以带参数。参数与路径之间使用 ? 分隔,参数键与参数值用 = 相连,不同参数用 & 分隔
    例如:/list?id=10&name=hua,在 onLoad(options) 生命周期函数 中获取传递的参数

  2. 属性 open-type="switchTab" 时不支持传参

落地代码:

➡️ pages/index/index.wxml : 调整 view 为 navigator

<!-- view:视图容器,作用类似于 div,是一个块级元素,独占一行 -->
<view class="navs">
  <navigator url="/pages/list/list">
    <!-- text:文本组件,类似于 span,是一个行内元素 -->
    <image src="/assets/cate-1.png" alt=""/>
    <text>爱礼精选</text>
  </navigator>
  <navigator url="/pages/list/list">
    <image src="/assets/cate-2.png" alt=""/>
    <text>鲜花玫瑰</text>
  </navigator>
  <navigator url="/pages/list/list">
    <image src="/assets/cate-3.png" alt=""/>
    <text>永生玫瑰</text>
  </navigator>
  <navigator url="/pages/list/list">
    <image src="/assets/cate-4.png" alt=""/>
    <text>玫瑰珠宝</text>
  </navigator>
  <navigator url="/pages/list/list">
    <image src="/assets/cate-5.png" alt=""/>
    <text>香水护体</text>
  </navigator>
</view>

➡️ pages/index/index.wxss

// 商品导航区域
.good-nav {
  display: flex;
  justify-content: space-between;
  background-color: #fff;
  padding: 20rpx 16rpx;
  border-radius: 10rpx;

  view {
+    navigator {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+    }

    image {
      width: 80rpx;
      height: 80rpx;
    }

    text {
      font-size: 24rpx;
      margin-top: 12rpx;
    }
  }
}

10. 商品推荐区域-scroll-view

可滚动视图区域,适用于需要滚动展示内的场景,它可以在小程序中实现类似于网页中的滚动条效果,用户可以通过手指滑动或者点击滚动条来滚动内容,scroll-view 组件可以设置滚动方向、滚动条样式、滚动到顶部或底部时的回调函数等属性,可以根据实际需求进行灵活配置。

3.10.1 scroll-view 横向滚动

知识点

使用横向滚动时,需要添加 scroll-x 属性,然后通过 css 进行结构绘制,实现页面横向滚动

落地代码:

➡️ pages/index/index.wxml

<!-- 商品推荐区域 -->
<view class="hot">
  <scroll-view class="scroll-x" scroll-x>
    <view>1</view>
    <view>2</view>
    <view>3</view>
  </scroll-view>
</view>

➡️ pages/index/index.wxss

.hot {
  margin-top: 16rpx;

  .scroll-x {
    width: 100%;
    white-space: nowrap;
    background-color: lightblue;

    view{
      display: inline-block;
      width: 50%;
      height: 80rpx;

      &:last-child{
        background-color: lightseagreen;
      }

      &:first-child{
        background-color: lightpink;
      }
    } 
  }
}

3.10.2 scroll-view 纵向滚动

知识点:

使用竖向滚动时,需要给scroll-view一个固定高度,同时添加 scroll-y 属性,实现页面纵向滚动

落地代码:

➡️ pages/index/index.wxml:

<!-- 商品推荐区域 -->
<view class="hot">
  <scroll-view class="scroll-x" scroll-x>
    <view>1</view>
    <view>2</view>
    <view>3</view>
  </scroll-view>

  <scroll-view class="scroll-y" scroll-y>
    <view>1</view>
    <view>2</view>
    <view>3</view>
  </scroll-view>
</view>

➡️ pages/index/index.wxss

.hot {
  margin-top: 16rpx;

  .scroll-x {
    width: 100%;
    white-space: nowrap;
    background-color: lightblue;

    view{
      display: inline-block;
      width: 50%;
      height: 80rpx;

      &:last-child{
        background-color: lightseagreen;
      }

      &:first-child{
        background-color: lightcoral;
      }
    } 
  }

+  .scroll-y {
+    height: 400rpx;
+    background-color: lightsalmon;
+    margin-top: 60rpx;
+
+    view {
+      height: 400rpx;
+
+      &:nth-child(odd) {
+        background-color: lightseagreen;
+      }
+
+      &:nth-child(even) {
+        background-color: lightcoral;
+      }
+    }  
+  }
}

3.10.3 实现商品推荐区域横向滚动

落地代码

➡️ pages/index/index.wxml:

<!-- 推荐商品 -->
<view class="good-hot">
  <scroll-view scroll-x class="scroll-x">
    
    <view>
      <view class="good-item">
        <image src="../../assets/floor/1.png" mode=""/>
        <text>鲜花玫瑰</text>
        <text>66</text>
      </view>
    </view>

    <view>
      <view class="good-item">
        <image src="../../assets/floor/2.png" mode=""/>
        <text>鲜花玫瑰</text>
        <text>77</text>
      </view>
    </view>

    <view>
      <view class="good-item">
        <image src="../../assets/floor/3.png" mode=""/>
        <text>鲜花玫瑰</text>
        <text>88</text>
      </view>
    </view>

    <view>
      <view class="good-item">
        <image src="../../assets/floor/4.png" mode=""/>
        <text>鲜花玫瑰</text>
        <text>99</text>
      </view>
    </view>

    <view>
      <view class="good-item">
        <image src="../../assets/floor/5.png" mode=""/>
        <text>鲜花玫瑰</text>
        <text>100</text>
      </view>
    </view>

  </scroll-view>
</view>

➡️ pages/index/index.wxss:

// 推荐商品区域
.good-hot {
  background-color: #fff;
  padding: 16rpx;
  border-radius: 10rpx;
  font-size: 24rpx;

  .scroll-x {
    width: 100%;
    white-space: nowrap;

    view {
      display: inline-block;
      width: 320rpx;
      height: 440rpx;
      margin-right: 16rpx;

      .good-item {
        display: flex;
        flex-direction: column;
        justify-content: space-between;

        text {
          &:nth-of-type(1) {
            font-weight: bold;
          }
        }
      }

      image {
        width: 100%;
        height: 320rpx;
      }

      &:last-child {
        margin-right: 0;
      }
    }
  }
}

11. 字体图标的使用

在项目中使用到的小图标,一般由公司设计师进行设计,如果如果自行设计这些图标会比较麻烦且耗费时间,这时候我们就可以使用到阿里巴巴矢量图库,设计好以后上传到阿里巴巴矢量图标库,然后方便程序员来进行使用。

阿里巴巴矢量图库是阿里巴巴集团推出的一个免费的矢量图标库和图标管理工具。它汇集了大量的精美图标资源,包括品牌图标和各种主题和分类的图标。用户可以在阿里巴巴矢量图库中搜索和浏览所需的图标,也可以上传和管理自己的图标资源。

小程序中的字体图标使用方式与 Web 开发中的使用方式是一样的。

首先我们先找到所需的图标,加入到项目库,进入项目库中生成链接。也快将字体图标库下载到本地

点击链接,会将生成的 CSS 在新的链接页面进行打开,ctrl + s,将该文件重命名为.wxss 后缀名,然后保存到项目根目录下的static 文件夹下。

在全局样式文件app.wxss中导入fonts.wxss字体图标文件,然后获取到图标类名,在项目中使用即可,应用于页面

@import "./static/fonts.wxss";
<view class="myTest">
  <view class="iconfont icon-tuikuan"></view>
</view>

注意:使用字体图标可能会报错:

[渲染层网络层错误] Failed to load font http://at.alicdn.com/t/c/font_3946178_q5oidsl5xo.woff2?t=1680795910637 net::ERR_CACHE_MISS (env: Windows,mp,1.06.2303220; lib: 2.30.4)

该错误可忽略,详见下面这个链接:

https://developers.weixin.qq.com/miniprogram/dev/api/ui/font/wx.loadFontFace.html

但在控制台出现错误,会影响开发调试,解决方案是:将字体图标转换成 base64 的格式

落地代码:

➡️ app.wxss

// 在导入样式文件以后,必须以分号结尾,否则会出现异常

@import "./iconfont/iconfont.scss";

➡️ pages/index/index.wxml

<!-- 公司信息 -->
<view class="info">
  <text><text class="iconfont icon-ps"></text> 同城配送</text>
  <text><text class="iconfont icon-lx"></text> 行业龙头</text>
  <text><text class="iconfont icon-time"></text> 半小时送达</text>
  <text><text class="iconfont icon-hp"></text> 100% 好评</text>
</view>

➡️ pages/index/index.wxss

// 公司信息区域
.info {
  display: flex;
  justify-content: space-between;
  background-color: #fff;
  padding: 20rpx 16rpx;
  border-radius: 10rpx;
  font-size: 24rpx;

+   .iconfont {
+     font-size: 24rpx;
+   }
}

12. 背景图片的使用

注:提供的网络地址连接:

  1. http://8.131.91.46:6677/bgimage.png
  2. http://8.131.91.46:6677/TomAndJerry.jpg

当编写小程序的样式文件时,我们可以使用 background-image 属性来设置一个元素的背景图像,但是小程序的 background-image 不支持本地路径。

<view class="image"></view>
.image {
  background-image: url('../../static/微信.jpg');
}

如图,在使用了本地资源图片以后,微信开发者工具提供的提示:

本地资源图片无法通过 WXSS 获取,可以使用网络图片,或者 base64,或者使用<image/>标签

.image {
  width: 100%;
  height: 400rpx;
  /* 本地资源图片无法通过 WXSS 获取 */
  /* background-image: url('../../static/微信.jpg'); */

  /* 使用网络图片 */
  /* background-image: url('http://8.131.91.46:6677/TomAndJerry.jpg'); */
    
  /* 使用 base64 格式展示图片 */
  /* base64 编码的文件很长,这个地址老师在这边进行了简写,在测试的时候,需要自己将这里转成完成的 64 编码 */
  background-image: url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAeAB4AAD/.....")
  background-position: center;
  background-size: cover;

}

事件处理

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

1. 事件绑定和事件对象

小程序中绑定事件与在网页开发中绑定事件几乎一致,只不过在小程序不能通过 on 的方式绑定事件,也没有 click 等事件,小程序中绑定事件使用 bind 方法,click 事件也需要使用 tap 事件来进行代替,绑定事件的方式有两种:

第一种方式:bind:事件名,bind 后面需要跟上冒号,冒号后面跟上事件名

<button bind:tap="handler">按钮</button>

第二种方式:bind事件名,bind 后面直接跟上事件名

<button bindtap="handler">按钮</button>

事件处理函数需要写到 .js 文件中,在 .js 文件中需要调用小程序提供的 Page 方法来注册小程序的页面,我们可以直接在 Page 方法中创建事件处理函数。例如:

// pages/home/home.js
Page({
  // 页面的初始数据
  data: {},

  // 事件处理程序
  handler () {
    console.log('我被执行啦~~~')
  }
    
  // 其他 coding...
})

当组件触发事件时,绑定的事件的处理函数会收到一个事件对象,用来记录事件发生时的相关信息。在触发事件时,事件处理程序会主动的给我们传入一个参数 —— event(事件对象)

// pages/home/home.js
Page({
  // 页面的初始数据
  data: {},

  // 事件处理程序
  handler (event) {
    // console.log('我被触发了~~~')
    console.log(event)
  }
    
  // 其他 coding...
})

2. 绑定并阻止事件冒泡

事件分为冒泡事件和非冒泡事件:

  1. 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
  2. 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。

使用 bind 绑定的事件,会触发事件冒泡,如果想阻止事件冒泡,可以使用 catch 来绑定事件。

<view bindtap="parentHandler">
  <!-- 使用 bind 绑定的事件,会产生事件冒泡 -->
  <!-- <button bindtap="handler">按钮</button> -->

  <!-- 使用 catcht 绑定的事件,会阻止事件冒泡 -->
  <button catchtap="handler">按钮</button>
</view>
Page({
  // 页面的初始数据
  data: {},

  // 事件处理程序
  handler (event) {
    console.log('我是子绑定的事件 ~~~')
  },
  
  parentHandler () {
    console.log('我是父绑定的事件 ~~~')
  }
    
  // 其他 coding...
})

WXML 中冒泡事件列表如下表:

类型 触发条件
touchstart 手指触摸动作开始
touchmove 手指触摸后移动
touchcancel 手指触摸动作被打断,如来电提醒,弹窗
touchend 手指触摸动作结束
tap 手指触摸后马上离开
longpress 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发
longtap 手指触摸后,超过350ms再离开(推荐使用 longpress 事件代替)
transitionend 会在 WXSS transition 或 wx.createAnimation 动画结束后触发
animationstart 会在一个 WXSS animation 动画开始时触发
animationiteration 会在一个 WXSS animation 一次迭代结束时触发
animationend 会在一个 WXSS animation 动画完成时触发
touchforcechange 在支持 3D Touch 的 iPhone 设备,重按时会触发

📌 注意事项

​ 除上表之外的其他组件自定义事件,如无特殊声明都是非冒泡事件
​ 例如 formsubmit事件,inputinput事件

3. 事件传参-data-*自定义数据

在小程序中,可以通过事件传参的方式,将数据传递给事件处理函数。常见的事件包括点击事件、输入事件等。

在组件节点中可以通过 data- 的方式传递一些自定义数据,传递的数据可以通过事件对象的方式进行获取

📌 注意事项

​ 使用 data- 方法传递参数的时候,多个单词由连字符 - 连接

​ 连字符写法会转换成驼峰写法,而大写字符会自动转成小写字符

​ 例如:

data-element-type ,最终会呈现为 event.currentTarget.dataset.elementType

data-elementType ,最终会呈现为 event.currentTarget.dataset.elementtype

在 wxml 文件中,使用 data-* 属性将数据传递给事件处理函数。例如:

<view bindtap="parentHandler" data-parent-id="1" data-parentName="tom">
  <!-- 如果需要进行事件传参,需要再组件上通过 data- 的方式传递数据 -->
  <!-- <button bindtap="btnHandler" data-id="1" data-name="tom">按钮</button> -->

  <button data-id="1" data-name="tom">按钮</button>
</view>

在 js 文件中,可以通过 event.currentTarget.dataset 获取传递的数据

// cate.js
Page({

  // 按钮触发的事件处理函数
  btnHandler (event) {
    // currentTarget 事件绑定者,也就是指:哪个组件绑定了当前事件处理函数
    // target 事件触发者,也就是指:哪个组件触发了当前事件处理函数
    // currentTarget 和 target 都是指按钮,因为是按钮绑定的事件处理函数,同时点击按钮触发事件处理函数
    // 这时候通过谁来获取数据都可以
    console.log(event.currentTarget.dataset.id)
    console.log(event.target.dataset.name)
  },


  // view 绑定的事件处理函数
  parentHandler (event) {
    // 点击蓝色区域(不点击按钮)
    // currentTarget 事件绑定者:view
    // target 事件触发者:view
    // currentTarget 和 target 都是指 view,如果想获取 view 身上的数据,使用谁都可以

    // 点击按钮(不点击蓝色区域)
    // currentTarget 事件绑定者:view
    // target 事件触发者:按钮
    // 如果想获取 view 身上的数据,就必须使用 currentTarget 才可以
    // 如果想获取的是事件触发者本身的数据,就需要使用 target
    console.log(event)

    // 在传递参数的时候,如果自定义属性是多个单词,单词与单词直接使用中划线 - 进行连接
    // 在事件对象中会被转换为小托峰写法
    console.log(event.currentTarget.dataset.parentId)

    // 在传递参数的时候,如果自定义属性是多个单词,单词如果使用的是小托峰写法
    // 在事件对象中会被转为全部小写的
    console.log(event.currentTarget.dataset.parentname)
  }

})

4. 事件传参-mark 自定义数据

小程序进行事件传参的时候,除了使用 data-* 属性传递参数外,还可以使用 mark 标记传递参数

mark是一种自定义属性,可以在组件上添加,用于来识别具体触发事件的 target 节点。同时 mark 还可以用于承载一些自定义数据(类似于 dataset

markdataset 很相似,主要区别在于:

mark 会包含从触发事件的节点到根节点上所有的 mark: 属性值 (事件委托的)

dataset 仅包含触发事件那一个节点的 data- 属性值。

在 wxml 文件中,使用 mark:自定义属性 的方式将数据传递给事件处理函数

<!-- pages/index/index.wxml -->

<view bindtap="parentHandler" mark:parentid="1" mark:parentname="tom">
  <!-- 如果需要使用 mark 进行事件传参,需要使用 mark:自定义属性的方式进行参数传递 -->
  <!-- <button bindtap="btnHandler" mark:id="1" mark:name="tom">按钮</button> -->
  <button mark:id="1" mark:name="tom">按钮</button>
</view>
// cart.js
Page({

  // 按钮绑定的事件处理函数
  btnHandler (event) {
    console.log(event.mark.id)
    console.log(event.mark.name)
  },

  // view 绑定的事件处理函数
  parentHandler (event) {
    // 先点击蓝色区域 (不点击按钮)
    // 通过事件对象获取的是 view 身上绑定的数据

    // 先点击按钮 (不点击蓝色区域)
    // 通过事件对象获取到的是 触发事件的节点 已经 父节点身上所有的 mark 数据
    console.log(event)
  }

})

模板语法

1. 声明和绑定数据

小程序页面中使用的数据均需要在 Page() 方法的 data 对象中进行声明定义

在将数据声明好以后,需要在 WXML 中绑定数据,数据绑定最简单的方式是使用 Mustache 语法(双大括号)将变量包起来。

{{ }}内部可以做一些简单的运算,支持如下几种方式:

  1. 算数运算

  2. 三元运算

  3. 逻辑判断

  4. 其他…

📌 注意事项:

​ 在 {{ }} 语法中,只能写表达式,不能写语句,也不能调用 js 相关的方法

定义数据:

Page({
  // 页面的初始数据
  data: {
    num: 1
  }
    
  // coding...
}

使用数据:

➡️ pages/index/index.wxml

<!-- 如果需要展示数据,在 wxml 中需要使用双大括号写法将变量进行包裹 -->

<!-- 展示内容 -->
<view>{{ school }}</view>
<view>{{ obj.name }}</view>

<!-- 绑定属性值,如果需要动态绑定一个变量,属性值也需要使用双大括号进行包裹 -->
<view id="{{ id }}">绑定属性值</view>

<!-- 如果属性值是布尔值,也需要使用双大括号进行包裹 -->
<checkbox checked="{{ isChecked }}" />

<!-- 算术运算 -->
<view>{{ id + 1 }}</view>
<view>{{ id - 1 }}</view>

<!-- 三元运算 -->
<view>{{ id === 1 ? '等于' : '不等于' }}</view>

<!-- 逻辑判断 -->
<view>{{ id === 1 }}</view>

<!-- 在双大括号写法内部,只能写表达式,不能写语句,也不能调用 js 的方法 -->
<!-- <view>{{ if (id === 1) {} }}</view> -->
<!-- <view>{{ for (const i = 0; i <= 10; i++) {} }}</view> -->
<view>{{ obj.name.toUpperCase() }}</view>

➡️ pages/index/index.js

// index.js
Page({

  // 在小程序页面中所需要使用的数据均来自于 data 对象
  data: {
    id: 1,
    isChecked: false,
    school: '尚硅谷',
    obj: {
      name: 'tom'
    }
  }

})
     

2. 声明和修改数据

小程序中修改数据并不能直接进行赋值,而是要通过调用 this.setData 方法才能实现

将需要修改的数据以 key:value 的形式传给 this.setData 方法。

this.setData 方法有两个作用:

  1. 更新数据
  2. 驱动视图更新
Page({
  // 页面的初始数据
  data: {
    num: 1
  },

  updateNum() {
    this.setData({
      // key 是需要修改的数据
      // value 是最新值
      num: this.data.num + 1
    })
  }
 
  // coding...
}

3. setData-修改对象类型数据

在实际开发中,我们经常会在 data 中声明对象类型的数据,小程序中通过调用 setData 方法可以修改页面的数据,包括对象类型的数据。下面是修改对象类型数据的方法:

  1. 定义一个对象

    Page({
      // 定义页面中使用的数据
      data: {
        userInfo: {
          name: 'Tom',
          age: 10,
          gender: '男'
        }
      }
    }
  2. 修改对象中的单个属性:

    this.setData({
      'userInfo.name': 'Jerry'
    })
  3. 修改对象中的多个属性

    // 修改对象中的多个属性
    this.setData({
      'userInfo.name': 'Jerry',
      'userInfo.age': 100
    })
  4. 使用 ES6 的展开运算符

    在修改对象类型的数据时,可以使用 ES6 的展开运算符先复制对象,然后利用新值对旧值覆盖的方式修改

    const userInfo = {
      ...this.data.userInfo,
      name: 'Jerry',
      age: 100
    }
    
    // 修改对象中的多个属性
    this.setData({
      userInfo
    })
  5. 使用解构赋值修改对象属性

    在修改对象类型的数据时,可以使用解构赋值来修改对象属性

    // 将 userInfo 从 data 中进行解构
    const { userInfo } = this.data
    // 产生一份新数据
    const newUserInfo = {
      ...userInfo,
      name: 'Jerry',
      age: 100
    }
    // 修改对象中的多个属性
    this.setData({
      userInfo: newUserInfo
    })
  6. 使用 Object.assign 方法合并对象

    在修改对象类型的数据时,可以使用 Object.assign 方法将多个对象合并为一个对象

    // 使用 Object.assign 方法将多个对象合并为一个对象
    const userInfo = Object.assign(
      this.data.userInfo, 
      { name: 'Jerry' },
      { age: 100 }
    )
    
    // 修改对象中的多个属性
    this.setData({
      userInfo
    })
  7. 删除对象中的属性

    在删除对象中的属性时,不能使用 delete 操作符,因为小程序的数据绑定机制不支持监听 delete 操作

    // 使用展开运算符拷贝一份数据,产生一个新对象
    const newUser = { ...this.data.userInfo }
    // 使用 delete 删除新对象中的属性
    delete newUser.age
    
    this.setData({
      // 将新的对象进行赋值
      userInfo: newUser
    })

📌 注意事项

小程序的数据绑定机制只能监听到 setData 方法中修改的数据,无法监听到直接删除属性的操作,所以在删除对象属性时,需要先将对象复制一份再进行操作,然后再调用 setData 方法更新数据。

4. setData-修改数组类型数据

数组类型数据也是经常会使用的数据格式之一,下面是修改数组类型数据的方法:

  1. 定义一个数组

    Page({
      // 定义页面中使用的数据
      data: {
        animalList: ['Tom', 'Jerry', 'Spyke']
      }
    
      // coding...
    }
  2. 使用数组的 concat 方法合并数组

    在修改数组类型的数据时,可以使用数组的 concat 方法来合并数组

    // 使用 concat 方法来合并数组
    const newList = this.data.animalList.concat('Tyke')
    
    // 使用 setData 进行赋值
    this.setData({
      animalList: newList
    })
  3. 使用数组的 push 方法新增属性

    在修改数组类型的数据时,可以使用数组的 push 方法来添加元素

    // 使用数组的 push 方法来添加元素
    this.data.animalList.push('Tyke')
    
    // 使用 setData 进行赋值
    this.setData({
      animalList: this.data.animalList
    })
  4. 使用 ES6 的展开运算符

    在数组类型的数据时,可以使用 ES6 的展开运算符先复制数组,然后进行合并

    // 使用 ES6 的展开运算符先复制数组,然后进行合并
    const newList = [...this.data.animalList, 'Tyke']
    
    // 使用 setData 进行赋值
    this.setData({
      animalList: newList
    })
  5. 修改数组中某个元素的值:

    利用索引的方式进行修改,但必须使用 wx:for 来进行渲染数组,否则会出现错误

    this.setData({
      'animalList[2]': 'Tyke' 
    })
  6. 使用数组的 filter 方法删除元素

    在修改数组类型的数据时,可以使用数组的 filter 方法来删除元素

    // 使用数组的 filter 方法来删除元素
    const newList = this.data.animalList.filter(item => item !== 'Tom')
    
    // 使用 setData 进行赋值
    this.setData({
      animalList: newList
    })

5. 数据绑定-简易双向绑定

在 WXML 中,普通属性的绑定是单向的,例如:

<input value="{{ num }}" />

如果使用 this.setData 来更新 numnum 和输入框的中显示的值都会被更新为值。但如果用户修改了输入框里的值,却不会同时改变 data 中的 num

如果需要在用户输入的同时也将 data 中的数据修改 ,需要借助简易双向绑定机制。此时可以在对应项目之前加入 model: 前缀即可,例如:

<input model:value="{{ value }}" />

如果使用 this.setData 来更新 numnum 和输入框的中显示的值都会被更新为值。

如果输入框的值被改变了, data 的数据也会随着改变。

同时, WXML 中所有绑定了数据的位置也会被一同更新

📌 注意事项:

简易双向绑定的属性值如下限制:

  1. 只能是一个单一字段的绑定,例如:错误用法:

  2. 尚不能写 data 路径,也就是不支持数组和对象,例如:错误用法:

落地代码:

<!-- 单向绑定:数据能够影响页面,但是页面更新不会影响到数据 -->
<!-- <input type="text" value="{{ value }}" /> -->

<!-- 双向绑定:数据能够影响页面,页面更新也能够影响数据 -->
<!-- 如果想实现简易双向绑定,需要再对应的属性之前添加 model: -->
<!-- <input type="text" model:value="{{ value }}" /> -->

<!-- 如果需要获取复选框的选中效果,需要给 checked 添加 model: -->
<checkbox model:checked="{{ isChecked }}" /> 是否同意该协议

<!-- 注意事项1:属性值只能是一个单一字段的绑定 -->
<!-- <input type="text" model:value="值为 {{ value }}" /> -->

<!-- 注意事项2:属性值不能写数据路径,也就是不支持对象和数组 -->
<!-- <input type="text" model:value="{{ obj.value }}" /> -->

6. 列表渲染-基本使用

基本使用

列表渲染:就是指通过循环遍历一个数组或对象,将其中的每个元素渲染到页面上

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

默认数组当前项的变量名默认为 item

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

在使用 wx:for 对数组进行遍历的时候,建议加上 wx:key 属性,如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,即以后数据不会改变,或者不必关注其顺序,可以选择忽略。

wx:key 的值以两种形式提供:

  1. 字符串:代表需要遍历的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变
  2. 保留关键字 *this 代表在 for 循环中的 item 本身,当 item 本身是一个唯一的字符串或者数字时可以使用

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

📌 注意事项

​ 在使用 wx:for 对数组进行遍历的时候,建议加上 wx:key 属性,否则控制台会报警告

落地代码:

<!-- 如果需要进行列表渲染,需要使用 wx:for 属性 -->
<!-- 属性值需要使用双大括号进行包裹 -->
<!-- 每一项的变量名默认是 item -->
<!-- 每一项下标(索引)的变量名默认是 index -->

<!-- 如果渲染的是数组,item:数组的每一项,index:下标 -->
<!-- <view wx:for="{{ numList }}">{{ item }} - {{ index }}</view> -->

<!-- 如果渲染的是对象,item:对象属性的值,index:对象属性 -->
<!-- <view wx:for="{{ obj }}">{{ item }} - {{ index }}</view> -->

<!-- ------------------------ 关于 Key --------------------------------- -->

<!-- wx:key 提升性能 -->
<!-- wx:key 的属性值不需要使用双大括号进行包裹,直接写遍历的数组 中 item 的某个属性 -->

<!-- wx:key 属性值有两种添加形式 -->
<!-- 字符串,需要是遍历的数组 中 item 的某个属性,要求该属性是列表中唯一的字符串或者数字,不能进行动态改变 -->
<view wx:for="{{ fruitList }}" wx:key="id">{{ item.name }}</view>
<view wx:for="{{ fruitList }}" wx:key="index">{{ item.name }}</view>

<!-- 保留关键字 *this,*this 代表的是 item 本身,item 本身是唯一的字符串或者数字 -->
<view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>
// profile.js
Page({

  data: {
    numList: [1, 2, 3],
    fruitList: [
      { id: 1, name: '🍎', price: 66 },
      { id: 2, name: '🍋', price: 77 },
      { id: 3, name: '🍅', price: 88 }
    ],
    obj: {
      name: 'tom',
      age: 10
    }
  }

})

7. 列表渲染-使用进阶

修改默认下标和变量名:

如果需要对默认的下标和变量名进行修改,可以使用 wx:for-itemwx:for-index

  1. 使用 wx:for-item 可以指定数组当前元素的变量名

  2. 使用 wx:for-index 可以指定数组当前下标的变量名

<view wx:for="{{ animal }}" wx:for-item="itemName" wx:for-index="i">
  {{ itemName.name }} - {{ itemName.avatar }} - {{ i }}
</view>

渲染多节点结构块:

如果需要渲染一个包含多节点的结构块,可以使用一个 <block/> 标签将多个组件包装起来

<block wx:for="{{ animal }}">
  <view>
    <span>{{ item.name }}</span>
    <span>{{ item.avatar }}</span>
  </view>
</block>

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

8. 条件渲染

知识点:

条件渲染主要用来控制页面结构的展示和隐藏,在微信小程序中实现条件渲染有两种方式:

  1. 使用 wx:ifwx:elifwx:else 属性组
  2. 使用 hidden 属性
<view wx:if="{{condition}}"> True </view>
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view
<view hidden="{{condition}}"> True </view>

wx:ifhidden 二者的区别:

  • wx:if :当条件为 true 时将内容渲染出来,否则元素不会进行渲染,通过移除/新增节点的方式来实现
  • hidden :当条件为 true 时会将内容隐藏,否则元素会显示内容,通过 display 样式属性来实现的

落地代码:

<!-- 使用 wx:if、wx:elif、wx:else 属性组控制元素的隐藏和控制 -->
<view wx:if="{{ num === 1 }}">num 等于 {{ num }}</view>
<view wx:elif="{{ num === 2 }}">num 等于 {{ num }}</view>
<view wx:else>大于 2</view>

<view hidden="{{ num !== 1 && num !== 2 && num !== 3 && num < 3}}">
  {{ num < 3 ? 'num 等于' + num : '大于 2' }}
</view>

<button type="primary" bindtap="updateNum">修改数据</button>
Page({
  // 页面的初始数据
  data: {
    num: 1
  },
  
  // 更新数据
  updateNum() {
    this.setData({
      num: this.data.num + 1
    })
  }
    
  // coding...
}

生命周期

1. 小程序运行机制

冷启动与热启动:

小程序启动可以分为两种情况,一种是冷启动,一种是热启动

冷启动:如果用户首次打开,或小程序销毁后被用户再次打开,此时小程序需要重新加载启动

热启动:如果用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时小程序并未被销毁,只是从后台状态进入前台状态

前台 以及 后台状态:

小程序启动后,界面被展示给用户,此时小程序处于「前台」状态。

当用户「关闭」小程序时,小程序并没有真正被关闭,而是进入了「后台」状态,当用户再次进入微信并打开小程序,小程序又会重新进入「前台」状态

切后台的方式包括但不限于以下几种:

  • 点击右上角胶囊按钮离开小程序
  • 安卓点击返回键离开小程序
  • 屏幕左侧右滑离开小程序

挂起:

小程序进入「后台」状态一段时间后(5 秒),微信停止小程序 JS 线程执行,小程序进入「挂起」状态,当开发者使用了后台播放音乐、后台地理位置等能力时,小程序可以在后台持续运行,不会进入到挂起状态

销毁:

如果用户很久没有使用小程序,或者系统资源紧张,小程序会被销毁,即完全终止运行。

当小程序进入后台并被「挂起」后,如果很长时间(目前是 30 分钟)都未再次进入前台,小程序会被销毁

当小程序占用系统资源过高,可能会被系统销毁或被微信客户端主动回收。

2. 小程序更新机制

在访问小程序时,微信会将小程序代码包缓存到本地。

开发者在发布了新的小程序版本以后,微信客户端会检查本地缓存的小程序有没有新版本,并进行小程序代码包的更新。

小程序的更新机制有两种:启动时同步更新启动时异步更新

启动时异步更新

启动时同步更新:微信运行时,会定期检查最近使用的小程序是否有更新。如果有更新,下次小程序启动时会同步进行更新,更新到最新版本后再打开小程序。如果 用户长时间未使用小程序时,会强制同步检查版本更新

启动时异步更新

启动时异步更新:在启动前没有发现更新,小程序每次 冷启动 时,都会异步检查是否有更新版本。如果发现有新版本,将会异步下载新版本的代码包,将新版本的小程序在下一次冷启动进行使用,当前访问使用的依然是本地的旧版本代码

在启动时异步更新的情况下,如果开发者希望立刻进行版本更新,可以使用 wx.getUpdateManager API 进行处理。在有新版本时提示用户重启小程序更新新版本。

App({

  /**
   * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
   */
   
  onLaunch: function () {
    const updateManager = wx.getUpdateManager()

    updateManager.onCheckForUpdate(function (res) {
      // 请求完新版本信息的回调
      console.log(res.hasUpdate)
    })

    updateManager.onUpdateReady(function () {
      wx.showModal({
        title: '更新提示',
        content: '新版本已经准备好,是否重启应用?',
        success(res) {
          if (res.confirm) {
            // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
            updateManager.applyUpdate()
          }
        }
      })
    })

    updateManager.onUpdateFailed(function () {
      // 新版本下载失败
    })
  }
  
})

落地代码:

// app.js
App({
  
  // onLaunch 是小程序的钩子函数,这个钩子函数在冷启动时肯定会执行到
  // 当小程序冷启动时,会自动微信后台请求新版本的信息,如果有新版本,会立即进行下载
  onLaunch () {
    // 使用 wx.getUpdateManager 方法监听下载的状态
    const updateManager = wx.getUpdateManager()

    // 当下载完成新版本以后,会触发 onUpdateReady 回调函数
    updateManager.onUpdateReady(function () {
      // 在回调函数中给用户提示,
      wx.showModal({
        title: '更新提示',
        content: '新版本已经准备好,是否重启应用?',
        success(res) {
          if (res.confirm) {
            // 强制当前小程序使用新版本并且会重启当前小程序
            updateManager.applyUpdate()
          }
        }
      })
    })
  }

})

3. 生命周期介绍

应用生命周期是指应用程序进程从创建到消亡的整个过程

小程序的生命周期指的是 小程序从启动到销毁的整个过程

在打开一个小程序应用的时候都需要经历一系列的初始化步骤,比如页面是否加载完成、页面是否初次渲染完成等等。

在此过程中,小程序也会运行被称为生命周期钩子的函数,这些函数由小程序框架本身提供,被称为生命周期函数,生命周期函数会按照顺序依次自动触发调用。帮助程序员在特定的时机执行特定的操作,辅助程序员完成一些比较复杂的逻辑。让开发者有机会在特定阶段运行自己的代码。

小程序的生命周期分类三类:应用级别、页面级别和组件级别 3 种类型,我们先学习应用级别和页面级别的生命周期

4. 应用级别生命周期

知识点:

应用生命周期通常是指一个小程序从 启动 → 运行 → 销毁的整个过程

应用生命周期伴随着一些函数,我们称为 应用生命周期函数,应用生命周期函数需要 在 app.js 文件的 App() 方法中定义

当整个小程序应用运行到某个时机的时候,我们需要做一些事情。例如:当小程序启动成功之后,我们要获取小程序的一些信息,就可以在小程序启动成功时的钩子函数中写代码获取我们想要的信息。

生命周期 必填 说明
onLaunch 监听小程序初始化,全局只会执行 1 次
onShow 监听小程序启动或切前台
onHide 监听小程序切后台

📌 注意事项:

  1. 从小程序生命周期的角度来看,我们一般讲的「启动」专指冷启动,热启动一般被称为后台切前台。

  2. App() 必须在 app.js 中调用,必须调用且只能调用一次。不然会出现无法预期的后果。

小程序启动后,后台会首先完成小程序的初始化,该过程只会触发一次;之后会完成显示的工作,用户可以操作小程序从前台进入后台以及从后台回复到前台显示;小程序在后台运行一段时间,当系统资源不足时会被注销。

落地代码:

➡️ app.js

App({
  /**
   * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
   */
  onLaunch: function () {
    // 监听小程序初始化
    console.log('onLaunch: 当小程序初始化完成时,会触发 onLaunch')
  },

  /**
   * 当小程序启动,或从后台进入前台显示,会触发 onShow
   */
  onShow: function (options) {
    // 监听小程序的显示
    console.log('onShow: 当小程序启动,或从后台进入前台显示')
  },

  /**
   * 当小程序从前台进入后台,会触发 onHide
   */
  onHide: function () {
    // 监听小程序的隐藏
    console.log('onHide: 小程序从前台进入后台')
  }
})

5. 页面级别生命周期

知识点:

页面生命周期就是指小程序页面从 加载 → 运行 → 销毁的整个过程

当某个页面运行到某个时机的时候,我们需要做一些事情,例如: 当某个页面加载完毕之后,需要发请求获取当前页面所需的数据,就可以在对应的页面加载完成后的钩子函数中执行发送请求的代码。

小程序中的一个页面都需要在对应页面的 .js 文件中调用 Page() 方法来注册。Page() 接受一个 Object 类型参数,其指定页面的初始数据、生命周期回调、事件处理函数等。

生命周期 必填 说明
onLoad 页面加载时触发 (一个页面只会调用一次)
onShow 页面显示时触发,页面显示/切入前台时触发
onReady 页面初次渲染完成时触发(一个页面只会调用一次)
代表页面已经准备妥当,可以和视图层进行交互
onHide 页面隐藏/切入后台时触发
onUnload 页面卸载时触发

落地代码:

// pages/home/home.js
Page({
   
  // coding...
    
  // 生命周期函数--监听页面加载
  onLoad: function (options) {
    console.log('页面加载完毕')
  },

  // 生命周期函数--监听页面显示
  onShow: function () {
    console.log('监听页面显示,此时页面处于显示状态')
  },

  // 生命周期函数--监听页面初次渲染完成
  onReady: function () {
    console.log('页面初次渲染已经完成')
  },

  // 生命周期函数--监听页面隐藏
  onHide: function () {
    console.log('当前页面处于隐藏状态')
  },

  // 生命周期函数--监听页面卸载
  onUnload: function () {
    console.log('页面卸载时触发')
  }
})

6. 生命周期两个细节补充说明

  1. tabBar 页面之间相互切换,页面不会被销毁

  2. 点击左上角,返回上一个页面,会销毁当前页面(被打开页面)

微信原生 API

1. API 基础

小程序开发框架提供丰富的微信原生 API,可以方便的调起微信提供的能力,如获取用户信息,本地存储,支付功能等,几乎所有小程序的 API 都挂载在 wx 对象底下,例如:wx.chooseMedia()wx.request(), wx 对象实际上就是小程序的宿主环境微信所提供的全局对象

通常,在小程序 API 有以下几种类型:

  1. 事件监听 API:约定以 on 开头 API 用来监听某个事件是否触发,例如:wx.onThemeChange()
  2. 同步 API:约定以 Sync 结尾的 API 都是同步 API,例如:wx.setStorageSync()
  3. 异步 API:大多数 API 都是异步 API,例如:wx.setStorage()

异步 API 支持 callback & Promise 两种调用方式:

  1. 当接口参数 Object 对象中不包含 success/fail/complete 时将默认返回 Promise

  2. 部分接口如 request, uploadFile 本身就有返回值,因此不支持 Promise 风格的调用方式,它们的 promisify 需要开发者自行封装。

小程序 API 介绍

微信小程序 API 文档

2. 网络请求

知识点:

在微信小程序中,如果需要发起 HTTPS 网络请求需要使用:wx.request(),语法如下:

wx.request({
  // 接口地址,仅为示例,并非真实的接口地址
  url: 'example.php',
  // 请求的参数
  data: { x: '' },
  // 请求方式
  method: 'GET|POST|PUT|DELETE',
  success (res) {
    console.log(res.data)
  },
  fail(err) {
    console.log(err)
  }
})

注意:wx.request() 请求的域名需要在小程序管理平台进行配置,如果小程序正式版使用wx.request请求未配置的域名,在控制台会有相应的报错。

这时候就需要在小程序管理后台进行设置请求的域名,打开微信公众后台:点击左侧 开发 → 开发管理 → 开发设置 → 服务器域名。域名只支持 https 而且要求已备案

但一般我们在开发阶段时,处于开发阶段的服务器接口可能还没部署到对应的域名下,经常会通过另一个域名来进行开发调试,考虑到这一点,为了方便开发者进行开发调试,开发者工具、小程序的开发版和小程序的体验版在某些情况下允许 wx.request 请求任意域名 (只适用于开发环境,只能在小程序开发者工具中生效),在开发工具中设置步骤如下:

不校验合法域名、web-view (业务域名)、TLS版本以及HTTPS证书 勾选上:

📌 注意事项:

​ 这两种方式只适用于开发者工具、小程序的开发版和小程序的体验版

​ 项目上线前必须在小程序管理平台进行合法域名的配置

落地代码:

Page({
  // 页面的初始数据
  data: {},

  // 获取数据
  getPostInfo() {
    wx.request({
      url: 'https://jsonplaceholder.typicode.com/posts/1',
      method: 'GET',
      success(res) {
        console.log(res)
      },
      fail(err) {
        console.log(err)
      }
    })
  },
    
  // coding...
}

3. 界面交互

小程序还提供了一些用于界面交互的 API,如消息提示框、模态对话框、 loading 提示框等等

3.1 loading 提示框

知识点:

小程序提供了一些用于界面交互的 API,例如: loading 提示框、消息提示框、模态对话框等 API。

loading 提示框常配合网络请求来使用,用于增加用户体验,对应的 API 有两个:

  1. wx.showLoading 显示加载提示框
  2. wx.hideLoading 隐藏加载提示框

语法如下:

wx.showLoading({
  title: '提示内容', // 提示的内容
  mask: true, // 是否显示透明蒙层,防止触摸穿透
  success() {}, // 接口调用成功的回调函数
  fail() {} // 接口调用失败的回调函数
})

官方文档:

wx.showLoading 官方文档

wx.hideLoading 官方文档

落地代码:

Page({

  data: {
    list: []
  },

  // 获取数据
  getData () {

+     // 显示 loading 提示框
+     wx.showLoading({
+       // 用来显示提示的内容
+       // 提示的内容不会自动换行,如果提示的内容比较多,因为在同一行展示
+       // 多出来的内容就会被隐藏
+       title: '数据加载中...',
+       // 是否展示透明蒙层,防止触摸穿透
+       mask: true
+     })

    // 如果需要发起网络请求,需要使用 wx.request API
    wx.request({
      // 接口地址
      url: 'https://gmall-prod.atguigu.cn/mall-api/index/findBanner',
      // 请求方式
      method: 'GET',
      // 请求参数
      data: {},
      // 请求头
      header: {},
      // API 调用成功以后,执行的回调
      success: (res) => {
        // console.log(res)
        if (res.data.code === 200) {
          this.setData({
            list: res.data.data
          })
        }
      },
      // API 调用失败以后,执行的回调
      fail: (err) => {
        console.log(err)
      },
      // API 不管调用成功还是失败以后,执行的回调
      complete: (res) => {
        // console.log(res)

+         // 关掉 loading 提示框
+         // hideLoading 和 showLoading 必须结合、配对使用才可以
+         wx.hideLoading()
      }
    })

  }

})

3.2 模态对话框以及消息提示框

知识点:

wx.showToast():消息提示框用来根据用户的某些操作来告知操作的结果,如退出成功给用户提示,提示删除成功等,语法如下:

wx.showToast({
  title: '标题', // 提示的内容
  duration: 2000, // 提示的延迟时间
  mask: true, // 是否显示透明蒙层,防止触摸穿透
  icon: 'success', // 	图标
  success() {}, // 接口调用成功的回调函数
  fail() {} // 接口调用失败的回调函数
})

wx.showModal() 模态对话框也是在项目中频繁使用的一个小程序 API,通常用于向用户询问是否执行一些操作,例如:点击退出登录,显示模态对话框,询问用户是否真的需要退出等等

wx.showModal({
  title: '提示', // 提示的标题
  content: '您确定执行该操作吗?', // 提示的内容
  confirmColor: '#f3514f',
  // 接口调用结束的回调函数(调用成功、失败都会执行)
  success({ confirm }) {
    confirm && consle.log('点击了确定')
  }
})

官方文档:

wx.showToast 官方文档

wx.showModal 官方文档

落地代码:

Page({
  // coding...
  
  // 删除商品
  async delHandler () {

    // showModal 显示模态对话框
    const { confirm } = await wx.showModal({
      title: '提示',
      content: '是否删除该商品 ?'
    })

    if (confirm) {
      // showToast 消息提示框
      wx.showToast({
        title: '删除成功',
        icon: 'none',
        duration: 2000
      })
    } else {
      wx.showToast({
        title: '取消删除',
        icon: 'error',
        duration: 2000
      })
    }

  }
   
  // coding...

})

4. 本地存储

小程序中也能够像网页一样支持本地数据缓存,本地数据缓存是小程序存储在当前设备上硬盘上的数据,本地数据缓存有非常多的用途,我们可以利用本地数据缓存来存储用户在小程序上产生的操作,在用户关闭小程序重新打开时可以恢复之前的状态。我们还可以利用本地缓存一些服务端非实时的数据提高小程序获取数据的速度,在特定的场景下可以提高页面的渲染速度,减少用户的等待时间。其包含以下 8个主要的 API

同步 API 异步 API 作用
wx.setStorageSync wx.setStorage 将数据存储在本地缓存中指定的 key 中
wx.getStorageSync wx.getStorage 从本地缓存中同步获取指定 key 的内容
wx.removeStorageSync wx.removeStorage 从本地缓存中移除指定 key
wx.clearStorageSync wx.clearStorageSync 清理本地数据缓存

异步方式的 API,在调用的时候都需要传入对象类型的参数

同步方式执行的 API 在使用时简洁比较好,缺点是同步会阻塞程序执行,执行效率上相较异步版本要差一些。

📌 注意事项

  1. 对象类型的数据,可以直接进行存储,无需使用 JSON.stringify 转换
  2. 对象类型的数据存的时候没有使用转换,因此获取的时候也不需要使用 JSON.parse 转换

落地代码:

<button type="primary" bindtap="handler" size="mini" plain bindtap="setData">
  存储数据
</button>

<button type="primary" bindtap="handler" size="mini" plain bindtap="getData">
  获取数据
</button>

<button type="warn" bindtap="handler" size="mini" plain bindtap="delData">
  删除数据
</button>

<button type="warn" bindtap="handler" size="mini" plain bindtap="clearData">
  移除数据
</button>
Page({

  // 将数据存储到本地
  setStorage () {

    // 第一个参数:本地存储中指定的 key
    // 第二个参数:需要存储的数据
    // wx.setStorageSync('num', 1)

    // 在小程序中
    // 如果存储的是对象类型数据,不需要使用 JSON.stringify 和 JSON.parse 进行转换
    // 直接进行存储和获取即可
    // wx.setStorageSync('obj', { name: 'tom', age: 10 })

    // ------------------- 异步 API -------------------
 
    wx.setStorage({
      key: 'num',
      data: 1
    })

    wx.setStorage({
      key: 'obj',
      data: { name: 'jerry', age: 18 }
    })

  },

  // 获取本地存储的数据
  async getStorage () {

    // 从本地存储的数据中获取指定 key 的数据、内容
    // const num = wx.getStorageSync('num')
    // const obj = wx.getStorageSync('obj')

    // console.log(num)
    // console.log(obj)

    // ------------------- 异步 API -------------------

    const { data } = await wx.getStorage({
      key: 'obj'
    })

    console.log(data)

  },

  // 删除本地存储的数据
  removeStorage () {

    // 从本地移除指定 key 的数据、内容
    // wx.removeStorageSync('num')

    // ------------------- 异步 API -------------------

    wx.removeStorage({
      key: 'num'
    })

  },

  // 清空本地存储的全部数据
  clearStorage () {
    // wx.clearStorageSync()
      
    // ------------------- 异步 API -------------------

    wx.clearStorage()
  },

})

5. 路由与通信

知识点:

在小程序中实现页面的跳转,有两种方式:

  1. 声明式导航:navigator 组件
  2. 编程式导航:使用小程序提供的 API
    • wx.navigateTo():保留当前页面,跳转到应用内的某个页面,但是不能跳到 tabbar 页面
    • wx.redirectTo():关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面
    • wx.switchTab():跳转到 tabBar 页面,路径后不能带参数
    • wx.navigateBack():关闭当前页面,返回上一页面或多级页面
  3. 路径后可以带参数,参数需要在跳转到的页面的 onLoad 钩子函数中通过形参进行接收
    • 参数与路径之间使用 ? 分隔
    • 参数键与参数值用 = 相连
    • 不同参数用 & 分隔
    • 例如 path?key=value&key2=value2

落地代码:

Page({


  navigateTo() {

    // 保留当前页面,跳转到应用中其他页面,不能跳转到 tabBar 页面
    wx.navigateTo({
      url: '/pages/list/list?id=1&name=tom'
      // url: '/pages/cate/cate'
    })

  },

  redirectTo() {

    // 关闭(销毁)当前页面,跳转到应用中其他页面,不能跳转到 tabBar 页面
    wx.redirectTo({
      url: '/pages/list/list?id=1&name=tom'
      // url: '/pages/cate/cate'
    })

  },

  switchTab() {

    // 跳转到 tabBar 页面,不能跳转到 非 tabBar 页面,路径后面不能传递参数
    wx.switchTab({
      // url: '/pages/list/list'
      url: '/pages/cate/cate?id=1&name=tom'
    })

  },

  reLaunch() {

    // 关闭所有的页面,然后跳转到应用中某一个页面
    wx.reLaunch({
      url: '/pages/list/list?id=1&name=tom'
      // url: '/pages/cate/cate?id=1&name=tom'
    })

  }

})
// list.js
Page({

  navigateBack() {

    // 关闭当前页面,返回上一页或者返回多级页面
    // 默认返回上一页
    wx.navigateBack({
      delta: 1
    })

  },

  onLoad(options) {
    console.log(options)
  }

})

6. 事件监听-上拉加载更多

上拉加载是小程序中常见的一种加载方式,当用户滑动页面到底部时,会自动加载更多的内容,以便用户继续浏览

小程序中实现上拉加载的方式:

1.在 app.json 或者 page.json 中配置距离页面底部距离: onReachBottomDistance;默认 50px

2.在 页面.js 中定义 onReachBottom 事件监听用户上拉加载

落地代码:

<view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>
/* pages/market/market.wxss */

view {
  height: 400rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

view:nth-child(odd) {
  background-color: lightskyblue;
}

view:nth-child(even) {
  background-color: lightsalmon;
}
Page({

  data: {
    numList: [1, 2, 3]
  },

  // 监听用户上拉加载
  onReachBottom() {
    // console.log('监听用户上拉加载')
    // 产品需求:
    // 当用户上拉,需要数字进行累加

    // 当用户上拉加载时,需要对数字进行累加,每次累加 3 个数字
    // 目前是 [1, 2, 3],[1, 2, 3, 4, 5, 6]
    // 怎么进行追加 ?
    // 获取目前数组中最后一项 n,n + 1, n + 2, n + 3

    wx.showLoading({
      title: '数据加载中...'
    })

    setTimeout(() => {
      // 获取数组的最后一项
      const lastNum = this.data.numList[this.data.numList.length - 1]
      // 定义需要追加的元素
      const newArr = [lastNum + 1, lastNum + 2, lastNum + 3]

      this.setData({
        numList: [...this.data.numList, ...newArr]
      })

      wx.hideLoading()
    }, 1500)

  }

})

7. 事件监听-下拉刷新

下拉刷新是小程序中常见的一种刷新方式,当用户下拉页面时,页面会自动刷新,以便用户获取最新的内容。

小程序中实现上拉加载更多的方式:

1.在 app.json 或者 page.json 中开启允许下拉,同时可以配置 窗口、loading 样式等

2.在 页面.js 中定义 onPullDownRefresh 事件监听用户下拉刷新

落地代码:

<view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>
/* pages/market/market.wxss */

view {
  height: 400rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

view:nth-child(odd) {
  background-color: lightskyblue;
}

view:nth-child(even) {
  background-color: lightsalmon;
}
Page({

  data: {
    numList: [1, 2, 3]
  },

  // 监听用户上拉加载
  onReachBottom() {
    
     // coding...

  },

  // 监听用户下拉刷新
  onPullDownRefresh () {
    // console.log('监听用户下拉刷新')

    // 产品需求:
    // 当用户上拉加载更多以后,如果用户进行了下拉刷新
    // 需要将数据进行重置
    this.setData({
      numList: [1, 2, 3]
    })

    // 在下拉刷新以后,loading 效果有可能不会回弹回去
    if (this.data.numList.length === 3) {
      wx.stopPullDownRefresh()
    }
  }

})

8. 增强 scroll-view

8.1 scroll-view 上拉加载

知识点

bindscrolltolower:滚动到底部/右边时触发

lower-threshold:距底部/右边多远时,触发 scrolltolower 事件

enable-back-to-top:让滚动条返回顶部,iOS 点击顶部状态栏、安卓双击标题栏时,只支持竖向

落地代码:

<scroll-view
  class="scroll-y"
  scroll-y
  lower-threshold="100"
  bindscrolltolower="getMore"
  enable-back-to-top
>

  <view wx:for="{{ arr }}" wx:key="*this">{{ item }}</view>

</scroll-view>
// index.js
Page({

  data: {
    arr: [1, 2, 3]
  },

  // 上拉加载更多
  getMore() {

    wx.showLoading({
      title: '数据正在加载中...'
    })

    setTimeout(() => {

      // 记录当前数组的最后一个元素
      let lastNum = this.data.arr[this.data.arr.length - 1]
      // 最后一个元素加 1
      lastNum++
      // 每次向数组中新增三项
      const newArr = [lastNum, lastNum + 1, lastNum + 2]

      this.setData({
        arr: [...this.data.arr, ...newArr]
      })

     // 数据返回,隐藏 Loading
     wx.hideLoading()

     wx.showToast({
       title: '数字请求完毕,上滑继续浏览',
       icon: 'none'
     })

    }, 1000)

  }
})

8.2 scroll-view 下拉刷新

知识点:

refresher-enabled:开启自定义下拉刷新

refresher-default-style自定义下拉刷新默认样式支持设置 black | white | none, none 表示不使用默认样式

refresher-background:自定义下拉刷新区域背景颜色

bindrefresherrefresh:自定义下拉刷新状态回调

refresher-triggered:设置当前下拉刷新状态,(true 下拉刷新被触发,false 表示下拉刷新未被触发,用来关闭下拉效果)

落地代码:

<scroll-view
  class="scroll-y"
  scroll-y
  lower-threshold="100"
  bindscrolltolower="getMore"
  enable-back-to-top
             
  refresher-enabled
  refresher-default-style="black"
  refresher-background="#f7f7f8"
  refresher-triggered
  bindrefresherrefresh="onrefresh"
  refresher-triggered="{{ triggered }}"
>

  <view wx:for="{{ arr }}" wx:key="*this">{{ item }}</view>

</scroll-view>
// index.js
Page({

  data: {
    triggered: false, // 控制 scroll-view 下拉刷新效果
    arr: [1, 2, 3]
  },

  // scroll-view 下拉刷新回调函数
  onrefresh() {

    wx.showLoading({
      title: '数据正在加载中...'
    })

    // 定时器模拟网络请求,1 秒后数据返回
    setTimeout(() => {

      // 重置数据
      this.setData({
        arr: [1, 2, 3]
      })

      // 数据返回,隐藏 Loading
      wx.hideLoading()

      wx.showToast({
        title: '下拉刷新完成,数据已重置...',
        icon: 'none'
      })

      // 数据返回,关闭 scroll-view 下拉刷新效果
      this.setData({
        triggered: false
      })

    }, 1000)
  }
})

8.3 增强 scroll-view 完整代码

<scroll-view
  scroll-y
  class="scroll-y"

  lower-threshold="100"
  bindscrolltolower="getMore"
  enable-back-to-top

  refresher-enabled
  refresher-default-style="black"
  refresher-background="#f7f7f8"
  bindrefresherrefresh="refreshHandler"
  refresher-triggered="{{isTriggered}}"
>
  
  <view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>

</scroll-view>
/* pages/index/index.wxss */

.scroll-y {
  height: 100vh;
  background-color: #efefef;
}

view {
  height: 500rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

view:nth-child(odd) {
  background-color: skyblue;
} 

view:nth-child(even) {
  background-color: lightsalmon;
}
Page({

  data: {
    numList: [1, 2, 3],
    isTriggered: false
  },

  // 下拉刷新
  refreshHandler () {

    wx.showToast({
      title: '下拉刷新...'
    })
    
    setTimeout(() => {
      this.setData({
        numList: [1, 2, 3],
        isTriggered: false
      })
    }, 2000)

  },


  // scroll-view 上拉加载更多事件的事件处理函数
  getMore () {

    
    wx.showLoading({
      title: '数据加载中...'
    })

    setTimeout(() => {
      // 获取数组的最后一项
      const lastNum = this.data.numList[this.data.numList.length - 1]
      // 定义需要追加的元素
      const newArr = [lastNum + 1, lastNum + 2, lastNum + 3]

      this.setData({
        numList: [...this.data.numList, ...newArr]
      })

      wx.hideLoading()
    }, 1500)


  }

})

自定义组件

1. 创建-注册-使用组件

组件介绍

小程序目前已经支持组件化开发,可以将页面中的功能模块抽取成自定义组件,以便在不同的页面中重复使用;

也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。

开发中常见的组件有两种:

  1. 公共组件:将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用
  2. 页面组件:将复杂的页面拆分成多个低耦合的模块,有助于代码维护

如果是公共组件,建议将其放在小程序的目录下的 components 文件夹中

如果是页面组件,建议将其放在小程序对应页面目录下,当然你也可以放到页面的 components 文件夹中

同时建议:一个组件一个文件夹,文件夹名称和组件名称保持一致

📌 注意事项

  1. 自定义组件的需要在 json 文件中需要配置 component 字段设为 true
  2. 自定义组件通过 Component 构造器进行构建,在构造器中可以指定组件的属性、数据、方法等

创建自定义组件:

创建组件的步骤很简单,以公共组件为例,创建的步骤如下:

  1. 在小程序的目录下新建 components 文件夹

  2. components 文件夹上,点击右键,选择新建文件夹 ,然后输入文件夹名称,我们建议文件夹的名称和组件的名称保持一致,这样方便后期对组件进行维护。我们这里新的的组件名称叫做:custom-checkbox

  3. 在新建的组件文件夹上,点击右键,选择新建 Component,然后输入组件的名称,组件的名称建议和文件夹保持一致

  4. 此时就已经创建了一个功能组件

使用自定义组件

开发中常见的组件主要分为 公共组件 和 页面组件 两种,因此注册组件的方式也分为两种:

  1. 全局注册:在 app.json 文件中配置 usingComponents 节点进行引用声明,注册后可在任意组件使用
  2. 局部注册:在页面的 json 文件中配置 usingComponents 节点进行引用声明,只可在当前页面使用

在配置 usingComponents 节点进行引用声明时,需要提供自定义组件的标签名和对应的自定义组件文件路径,语法如下:

{
  "usingComponents": {
    "自定义组件的标签名": "自定义组件文件路径"
  }
}

这样,在页面的 wxml 中就可以像使用基础组件一样使用自定义组件。节点名即自定义组件的标签名,节点属性即传递给组件的属性值。

{
  "usingComponents": {
    "custom-checkbox": "/components/custom-checkbox/custom-checkbox"
  }
}
<!--pages/index/index.wxml-->

<view>
  <!-- 将导入的自定义组件当成标签使用 -->
  <custom-checkbox
                   />
</view>

2. 自定义组件-数据和方法

在组件的 .js 中,需要调用 Component 方法创建自定义组件,Component 中有以下两个属性:

data 数据:组件的内部数据

methods 方法:在组件中事件处理程序需要写到 methods 中才可以

落地代码:

➡️ components/custom-checkbox/custom-checkbox.wxml

<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->

<view class="custom-checkbox-container">
  <view class="custom-checkbox-box">
    <checkbox checked="{{ isChecked }}" bindtap="updateChecked" />
  </view>
</view>

➡️ components/custom-checkbox/custom-checkbox.wxss

/* components/custom-checkbox/custom-checkbox.wxss */

.custom-checkbox-container {
  display: inline-block;
}

➡️ components/custom-checkbox/custom-checkbox.js

Component({

  /**
   * 组件的初始数据:用来定义当前组件内部所需要使用的数据
   */
  data: {
    isChecked: false
  },

  /**
   * 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
   */
  methods: {
    
    // 更新复选框的状态
    updateChecked () {

      this.setData({
        isChecked: !this.data.isChecked
      })

      console.log(this.data.isChecked)
    }

  }
  
})

➡️ app.json

{
  "usingComponents": {
    "custom-checkbox": "./components/custom-checkbox/custom-checkbox"
  }
}

➡️ index.wxml

<custom-checkbox />

<view class="line"></view>

<custom-checkbox />

3. 自定义组件-属性

属性 Properties 是指组件的对外属性,主要用来接收组件使用者传递给组件内部的数据,和 data 一同用于组件的模板渲染

📌 注意事项:

  1. 设置属性类型需要使用 type 属性,属性类型是必填项,value 属性为默认值
  2. 属性类型可以为 String、Number、Boolean、Object、Array ,也可以为 null 表示不限制类型

落地代码:

➡️ index.wxml

<!-- label 文本显示的内容 -->
<!-- position 控制文本显示的位置 -->

<custom-checkbox label="我已阅读并同意 用户协议 和 隐私协议" position="right" />

<view class="line"></view>

<custom-checkbox label="匿名提交" position="left" />

➡️ components/custom-checkbox/custom-checkbox.wxml

<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->

<view class="custom-checkbox-container">
+   <view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
+     <checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />
+ 
+     <view>
+       <text>{{ label }}</text>
+     </view>
  </view>
</view>

➡️ components/custom-checkbox/custom-checkbox.wxss

/* components/custom-checkbox/custom-checkbox.wxss */

.custom-checkbox-container {
  display: inline-block;
}

.custom-checkbox-box {
  display: flex;
  align-items: center;
}

.custom-checkbox-box.left {
  flex-direction: row-reverse;
}

.custom-checkbox-box.right {
  flex-direction: row;
}

.custom-checkbox {
  margin-left: 10rpx;
}

➡️ components/custom-checkbox/custom-checkbox.js

Component({

+   /**
+    * 组件的属性列表:组件的对外属性,主要用来接收组件使用者传递给组件内部的属性以及数据
+    */
+   properties: {
+     // 如果需要接收传递的属性,有两种方式:全写、简写
+     // label: String
+ 
+     label: {
+       // type 组件使用者传递的数据类型
+       // 数据类型:String、Number、Boolean、Object、Array
+       // 也可以设置为 null,表示不限制类型
+       type: String,
+       value: ''
+     },
+ 
+     // 文字显示位置
+     position: {
+       type: String,
+       value: 'right'
+     }
+   },

  /**
   * 组件的初始数据:用来定义当前组件内部所需要使用的数据
   */
  data: {
    isChecked: false
  },

  /**
   * 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
   */
  methods: {
    
    // 更新复选框的状态
    updateChecked () {

      this.setData({
        isChecked: !this.data.isChecked,
+        // label: '在组件内部也可以修改 properties 中的数据'
      })

+       // 在 JS 中可以访问和获取 properties 中的数据
+       // 但是一般情况下,不建议修改,因为会造成数据流的混乱
+       // console.log(this.properties.label)
      // console.log(this.data.isChecked)
    }

  }
  
})

4. 组件 wxml 的 slot

在使用基础组件时,可以给组件传递子节点传递内容,从而将内容展示到页面中,自定义组件也可以接收子节点内容

只不过在组件模板中需要定义 <slot /> 节点,用于承载组件引用时提供的子节点

默认情况下,一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时,可以在组件 js 中声明启用。

同时需要给 slot 添加 name 来区分不同的 slot,给子节点内容添加 slot 属性来将节点插入到 对应的 slot 中

知识点讲解:

➡️ custom01.html

<view>
  <slot name="slot-top" />
  <!-- slot 就是用来接收、承载子节点内容 -->
  <!-- slot 只是一个占位符,子节点内容会将 slot 进行替换 -->
  <!-- 默认插槽 -->
  <view><slot /></view>

  <slot name="slot-bottom" />
</view>

➡️ custom01.js

// components/custom01/custom01.js

Component({

  options: {
    // 启用多 slot 支持
    multipleSlots: true
  }

})

➡️ cart.wxml

<custom01>
  <text slot="slot-top">我需要显示到顶部</text>
    
  <!-- 默认情况下,自定义组件的子节点内容不会进行展示 -->
  <!-- 如果想内容进行展示,需要再组件模板中定义 slot 节点 -->
  我是子节点内容
    
  <text slot="slot-bottom">我需要显示到低部</text>
</custom01>

完善复选框案例

➡️ custom-checkbox.html

<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->

<view class="custom-checkbox-container">
  <view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
    <checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />

+     <view>
+       <!-- lable 和 子节点内容都进行了展示 -->
+       <!-- 要么展示 lable 要么展示 子节点内容 -->
+       <!-- 如果用户传递了 lable 属性,就展示 lable -->
+       <!-- 如果用户没有传递 lable 属性,就展示 子节点内容 -->
+       <text wx:if="{{ label !== '' }}">{{ label }}</text>
+ 
+       <slot wx:else />
+     </view>
  </view>
</view>

➡️ index.html

<!-- label 文本显示的内容 -->
<!-- position 控制文本显示的位置 -->
<custom-checkbox label="我已阅读并同意 用户协议 和 隐私协议" position="right">
  我已阅读并同意 用户协议 和 隐私协议 - 111
</custom-checkbox>

<view class="line"></view>

<custom-checkbox label="匿名提交" position="left">
  匿名提交 - 222
</custom-checkbox>

5. 组件样式以及注意事项

选择器使用注意事项:

类似于页面,自定义组件拥有自己的 wxss 样式,组件对应 wxss 文件的样式,只对组件wxml内的节点生效。

编写组件样式时,需要注意以下几点:

  1. app.wxss 或页面的 wxss 中使用了标签名(view)选择器(或一些其他特殊选择器)来直接指定样式
    这些选择器会影响到页面和全部组件,通常情况下这是不推荐的做法

  2. 组件和引用组件的页面不能使用 id 选择器(#a)、属性选择器([a]) 和 标签名选择器,请改用 class 选择器

  3. 组件和引用组件的页面中使用后代选择器(.a .b)在一些极端情况下会有非预期的表现,如遇,请避免使用

  4. 子元素选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况。

  5. 继承样式,如 font 、 color ,会从组件外继承到组件内。

  6. 除继承样式外, 全局中的样式、组件所在页面的的样式对自定义组件无效 (除非更改组件样式隔离选项)

#a { } /* 在组件中不能使用 */
[a] { } /* 在组件中不能使用 */
button { } /* 在组件中不能使用 */
.a > .b { } /* 除非 .a 是 view 组件节点,否则不一定会生效 */

落地代码:

➡️ custom02.wxml

<text id="content" class="content son">
  <text class="label">给自定义组件设置样式</text>
</text>

➡️ custom02.wxss

/* components/custom02/custom02.wxss */

/* 第一个注意事项:在自定义的 wxss 文件中,不允许使用标签选择器,ID 选择器,属性选择器 */
/* 请改为 class 选择器 */
/* text {
  color: lightseagreen;
} */

/* #content {
  color: lightseagreen;
} */

/* [id=content] {
  color: lightseagreen;
} */

/* .content {
  color: lightseagreen;
} */

/* 第二个注意事项:子选择器,只能用于 view 和 子组件,用于其他组件可能会出现样式失效的问题 */
/* .content > .label {
  color: lightseagreen;
} */

/* 第三个注意事项:继承样式,例如:color\font 都会从组件外继承 */

/* 第四个注意事项:全局样式、组件所在页面的样式文件中的样式都对自定义组件无效 */

/* 第五个注意事项:官方不推荐做法 */
/* 不建议在 全局样式文件 以及 父级页面之间使用标签选择器设置样式 */
/* 如果是在全局样式文件中设置样式,会影响项目中全部的相同组件 */
/* 如果是再页面样式文件中设置样式,会影响当前页面所有的相同组件 */

/* 第六个注意事项: */
/* 组件和组件使用者,如果使用了后代选择器,可能会出现一些非预期情况 */
/* 如果出现,请避免 */

➡️ cate.wxml

<view class="custom parent">
  <view>
    <custom02 />

    <view class="son test">我是父级页面中的结构</view>
  </view>
</view>

➡️ cate.wxss

/* pages/cate/cate.wxss */

/* .custom  {
  color: lightseagreen;
  font-size: 50rpx;
} */

/* .label {
  color: lightseagreen;
} */

/* text {
  color: lightseagreen;
} */

.parent .son.test {
  color: lightsalmon;
}

➡️ app.wxss

/* .label {
  color: lightseagreen;
} */

/* text {
  color: lightseagreen;
} */

6. 组件样式隔离

默认情况下,自定义组件的样式只受到自定义组件 wxss 的影响。除非以下两种情况:

  1. app.wxss 或页面的 wxss 中使用了标签名(view)选择器(或一些其他特殊选择器)来直接指定样式,这些选择器会影响到页面和全部组件。通常情况下这是不推荐的做法。

  2. 指定特殊的样式隔离选项 styleIsolation

    Component({
      options: {
        styleIsolation: 'isolated'
      }
    })

styleIsolation 选项它支持以下取值:

  • isolated 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值);
  • apply-shared 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;
  • shared 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-sharedshared 的自定义组件。

落地代码:

➡️ custom03.wxml

<!--components/custom03/custom03.wxml-->

<text class="label">演示组件样式隔离</text>

➡️ custom03.wxss

/* components/custom03/custom03.wxss */

.test {
  color: lightseagreen;
  font-size: 50rpx;
}

➡️ custom03.js

// components/custom03/custom03.js
Component({

  options: {

    // styleIsolation:配置组件样式隔离

    // isolated:开启样式隔离,默认值
    // 在默认情况下,自定义组件和组件使用者如果存在相同的类名,类名不会相互影响

    // apply-shared:表示组件使用者、页面的 wxss 样式能够影响到自定义组件
    // 但是自定义组件的样式不会影响组件使用者、页面的 wxss 样式
    // styleIsolation: "apply-shared"

    // shared:表示组件使用者、页面的 wxss 样式能够影响到自定义组件
    // 自定义组件的样式会影响组件使用者、页面的 wxss 样式
    // 和其他使用了 apply-share 以及 share 属性的自定义组件
    styleIsolation: 'shared'

  }
    
})

➡️ cate.wxml

<custom03 />

➡️ cate.wxss

.label {
  color: lightsalmon;
}

7. 拓展-小程序修改checkbox样式

知识点:

技巧:在官方文档,找到官方提供的案例,审查元素,就能看到对应的类名

📌 注意事项

  1. .custom-checkbox .wx-checkbox-input {}:复选框没有选中时默认的样式
  2. .custom-checkbox .wx-checkbox-input-checked {}: 复选框选中时默认的样式
  3. .custom-checkbox .wx-checkbox-input.wx-checkbox-input-checked:before {}:复选框选中时 √ 样式

这几个类名,在全局样式文件、页面样式文件都可以对修改复选框样式,

但是在自定义组件内部使用的时候,需要添加 styleIsolation: 'shared' 属性

落地代码:

➡️ components/custom-checkbox/custom-checkbox.wxss

/* 复选框组件是公共组件 */
/* 以后需要再多个页面或者需要再多个项目中进行使用 */
/* 所以呢,需要先给复选框组件准备、设置一些默认样式 */
/* 如果在其他页面或者其他项目中使用的时候,发现样式不符合产品需求 */
/* 可以进行修改、对默认的样式进行修改 */

/* 1. 需要给复选框设置默认样式 */
/* 需要先找到小程序给复选框提供的类名,通过小程序给提供的类名修改才可以 */
/* 需要先打开小程序开发文档,找到复选框文档,审查元素,进行查找 */

/* 在自定义组件中,不能直接修改复选框样式 */
/* 如果需要进行修改,需要设置 styleIsolation 才可以 */
/* shared:修改其他页面的样式、组件使用者的样式、以及其他使用了 share 以及 apply-share 的组件 */
/* 这时候,不是想要的结果 */
/* 需求是:只想影响当前组件,可以添加命名空间 */

/* 复选框没有选中时默认的样式 */
.custom-checkbox .wx-checkbox-input {
  width: 24rpx !important;
  height: 24rpx !important;
  border-radius: 50% !important;
  border: 1px solid #fda007 !important;
  margin-top: -6rpx;
}

/* 复选框选中时默认的样式 */
.custom-checkbox .wx-checkbox-input-checked {
  background-color: #fda007 !important;
}

/* 复选框选中时 √ 样式 */
.custom-checkbox .wx-checkbox-input.wx-checkbox-input-checked:before {
  font-size: 22rpx;
  color: #fff;
}

/* 2. 组件使用者也能够修改默认的样式 */

➡️ components/custom-checkbox/custom-checkbox.js

Component({

  options: {
    styleIsolation: 'shared'
  }
  
})

➡️ index.wxss

/* 组件使用者修改复选框的样式 */
.custom .custom-checkbox .wx-checkbox-input {
  border: 1px solid lightseagreen !important;
}

.custom .custom-checkbox .wx-checkbox-input-checked {
  background-color: lightseagreen !important;
}

8. 数据监听器

知识点:

数据监听器可以用于监听和响应任何属性和数据字段的变化,有时,需要在一些数据字段被 setData 设置时,需要执行一些操作。那么就可以使用 observers 数据监听器来实现。语法如下:

Component({
  data: {
    num: 10,
    count: 1,
    obj: { name: 'Tom', age: 10 },
    arr: [1, 2, 3]
  },
  observers: {
    // key 是需要检测数据
    // value 是一个函数,函数接收一个形参作为参数,是最新的值
    num: function(newNum) {
      console.log(newNum)
    },
    
    // 数据监听器支持监听属性或内部数据的变化,可以同时监听多个
    'num, count': function (newNum, newCount) {
       console.log(newNum, newCount)
    }
    
    // 监听器可以监听子数据字段
    'obj.age': function(newAge) {
      console.log(newAge)
    },
    
    // 如果需要监听所有子数据字段的变化,可以使用通配符 ** 
    'obj.**': function(newAge) {
      console.log(newAge)
    },
        
    'arr[0]': function (val) {}
  }
})

9. 组件间通信与事件

9.1 父往子传值

知识点:

父组件如果需要向子组件传递指定属性的数据,在 WXML 中需要使用数据绑定的方式

与普通的 WXML 模板类似,使用数据绑定,这样就可以向子组件的属性传递动态数据。

父组件如果需要向子组件传递数据,只需要两个步骤:

1.在父组件 WXML 中使用 数据绑定 的方式向子组件传递动态数据

2.子组件内部使用 properties 接收父组件传递的数据即可

知识点代码:

<!-- 引用组件的页面模板 -->
<view>
  <costom prop-a="{{ name }}" prop-b="{{ age }}" />
</view>

在组件内部,需要在 Component 构造器中通过 properties 接收传递的数据,接收方式有两种:

Component({
  /**
   * 组件的属性列表 props
   */
  properties: {
    propA: {
      type: String, // 传递的数据类型
      value: '' // 默认值
    },
    propB: Number // 简化的定义方式
  },

  // coding...
})

在子组件中也可以通过 this.setData()properties 中的数据进行修改,但是一般不建议修改

// components/custom01/custom01.js
Component({

  /**
   * 组件的方法列表
   */
  methods: {
    // 修改列表中的数据
    updateProp () {
      this.setData({
        propB: this.properties.propB + 1
      })
    }
  }
})

复选框组件案例:

➡️ index.js

Page({

  data: {
    isChecked: true
  },
    
  // coding...

})

➡️ index.wxml

<custom-checkbox
  label="我已阅读并同意 用户协议 和 隐私协议"
  position="right"
+   checked="{{ isChecked }}">
  我已阅读并同意 用户协议 和 隐私协议 - 111
</custom-checkbox>

➡️ components/custom-checkbox/custom-checkbox.js

Component({

  options: {
    styleIsolation: 'shared'
  },

  properties: {
    
    // coding...

    // 复选框组件公共组件
    // 需要再多个页面、在多个项目中进行使用
    // 在使用的时候,有的地方希望默认是选中的效果,有的地方希望默认是没有被选中的效果
    // 怎么处理 ?
    // 首先让复选框默认还是没有被选中的效果
    // 如果希望复选框默认被选中,这时候传递属性(checked=true)到复选框组件
+     checked: {
+       type: Boolean,
+       value: false
+     }
  },

  /**
   * 组件的初始数据:用来定义当前组件内部所需要使用的数据
   */
  data: {
    isChecked: false
  },

+   observers: {
+     // 如果需要将 properties 中的数据赋值给 data
+     // 可以使用 observers 进行处理
+     checked: function (newChecked) {
+       // console.log(newChecked)
+       this.setData({
+         isChecked: newChecked
+       })
+     }
+   },

  /**
   * 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
   */
  methods: {
    
    // 更新复选框的状态
    updateChecked () {

      this.setData({
+         isChecked: !this.data.isChecked,
+         // checked: !this.properties.checked
        // label: '在组件内部也可以修改 properties 中的数据'
      })

      // 在 JS 中可以访问和获取 properties 中的数据
      // 但是一般情况下,不建议修改,因为会造成数据流的混乱
      // console.log(this.properties.label)
      // console.log(this.data.isChecked)
    }

  }
  
})

➡️ components/custom-checkbox/custom-checkbox.wxml

<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->

<view class="custom-checkbox-container">
  <view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
+     <checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />

    <view class="content">
      <!-- lable 和 子节点内容都进行了展示 -->
      <!-- 要么展示 lable 要么展示 子节点内容 -->
      <!-- 如果用户传递了 lable 属性,就展示 lable -->
      <!-- 如果用户没有传递 lable 属性,就展示 子节点内容 -->
      <text wx:if="{{ label !== '' }}">{{ label }}</text>

      <slot wx:else />
    </view>
  </view>
</view>

9.2 子往父传值

子组件如果需要向父组件传递数据,可以通过小程序提供的事件系统实现传递传递,可以传递任意数据。

事件系统是组件间通信的主要方式之一,自定义组件可以触发任意的事件,引用组件的页面可以监听这些事件,流程如下:

  1. 自定义组件触发事件时,需要使用 triggerEvent 方法发射一个自定义的事件
  2. 自定义组件标签上通过 bind 方法监听发射的事件

触发事件:

<!-- 在自定义组件中 -->
<button type="primary" plain bindtap="sendData">传递数据</button>
// components/custom05/custom05.js
Component({

  // 组件的初始数据
  data: {
    num: 666
  },

  // 组件的方法列表
  methods: {

    // 将数据传递给父组件
    sendData () {

      // 如果需要将数据传递给父组件
      // 需要使用 triggerEvent 发射自定义事件
      // 第二个参数,是携带的参数
      this.triggerEvent('myevent', this.data.num)
      
    }

  }
})

监听事件:

<view>{{ num }}</view>
<!-- 需要在自定义组件标签上通过 bind 方法绑定自定义事件,同时绑定事件处理函数 -->
<custom05 bind:myevent="getData" />
Page({

  data: {
    num: ''
  },

  getData (event) {
    // 可以通过事件对象.detail 获取子组件传递给父组件的数据
    // console.log(event)
    this.setData({
      num: event.detail
    })
  }

})

复选框组件案例:

➡️ components/custom-checkbox/custom-checkbox.js

Component({

  /**
   * 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
   */
  methods: {
    
    // 更新复选框的状态
    updateChecked () {

      this.setData({
        isChecked: !this.data.isChecked,
        // label: '在组件内部也可以修改 properties 中的数据'
      })

      // 在 JS 中可以访问和获取 properties 中的数据
      // 但是一般情况下,不建议修改,因为会造成数据流的混乱
      // console.log(this.properties.label)
      // console.log(this.data.isChecked)

+       // 目前复选框组件的状态是存储在复选框组件内部的、存储在自定义组件内部的
+       // 但是,在以后实际开发中,组件使用者、父组件有时候也需要获取到复选框内部的状态
+       // 怎么办 ?
+       // 这时候,自定义组件内部就需要发射一个自定义事件,
+       // 如果组件使用者、父组件需要使用数据,绑定自定义事件进行获取即可
+       this.triggerEvent('changechecked', this.data.isChecked)
    }

  }
  
})

➡️ index.html

<custom-checkbox
  label="我已阅读并同意 用户协议 和 隐私协议"
  position="right"
  checked="{{ isChecked }}"
  class="getchild"
+  bind:changechecked="getData"
>
  我已阅读并同意 用户协议 和 隐私协议 - 111
</custom-checkbox>

➡️ index.js

Page({

  data: {
    isChecked: true
  },

  getData (event) {
    console.log(event.detail)

    if (event.detail) {
      console.log('提交')
    } else {
      console.log('请同意协议!')
    }
  }

})

9.3 获取组件实例

如果前面两种方式不足以满足需要。

可在父组件里调用 this.selectComponent() ,获取子组件的实例对象,就可以直接拿到子组件的任意数据和方法。调用时需要传入一个匹配选择器 selector,如:this.selectComponent(".my-component")

<!-- 父组件 -->
<costom bind:myevent="getData" class="custom" />
<button bindtap="getChildComponent"></button>
// 父组件
Page({
  data: {},
  getChildComponent: function () {
    const child = this.selectComponent('.custom')
    console.log(child)
  }
})

复选框组件案例:

➡️ index.html

<custom-checkbox
  label="我已阅读并同意 用户协议 和 隐私协议"
  position="right"
  checked="{{ isChecked }}"
+   class="child"
+  id="child"
  bind:changechecked="getData"
>
  我已阅读并同意 用户协议 和 隐私协议 - 111
</custom-checkbox>

<button type="primary" plain bindtap="getChild">获取子组件实例对象</button>

➡️ index.js

Page({

  // coding...

  // 获取子组件的实例对象
  getChild () {

    // this.selectComponent 方法获取子组件实例对象
    // 获取到实例对象以后,就能获取子组件所有的数据、也能调用子组件的方法
    const res = this.selectComponent('#child')
    console.log(res.data.isChecked)

  }

})

10. 组件生命周期

组件的生命周期:指的是组件自身的一些钩子函数,这些函数在特定的时间节点时被自动触发

组件的生命周期函数需要在 lifetimes 字段内进行声明

最重要的生命周期是 created attached detached 包含一个组件生命周期流程的最主要时间点

定义段 描述
created 在组件实例刚刚被创建时执行,注意此时不能调用 setData (还没有对模板解析)
attached 在组件实例进入页面节点树时执行 (模板已经解析完毕,并且挂载到页面上)
ready 在组件布局完成后执行
moved 在组件实例被移动到节点树另一个位置时执行
detached 在组件实例被从页面节点树移除时执行 (组件被销毁了)
  1. 【组件实例刚刚被创建好时】, created 生命周期被触发。此时,组件数据 this.data 就是在 Component 构造器中定义的数据 data此时还不能调用 setData 通常情况下,这个生命周期只应该用于给组件 this 添加一些自定义属性字段。

  2. 【在组件完全初始化完毕】、进入页面节点树后, attached 生命周期被触发。此时, this.data 已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。

  3. 【在组件离开页面节点树后】, detached 生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则 detached 会被触发。

Component({
  
  lifetimes: {
    created: function () {
      // 在组件实例刚刚被创建时执行,注意此时不能调用 setData 
      // 一般用来为组件添加一些自定义属性字段。
    },
    attached: function() {
      // attached 在组件完全初始化完毕、进入页面节点树后执行
      // 模板已经解析完毕,并且挂载到页面上
      // 一般都是在这里写对应的交互
    },
    detached: function() {
      // 在组件实例被从页面节点树移除时执行
    },
    
    // coding...
  }
    
  // coding...
})

11. 组件所在页面的生命周期

组件还有一些特殊的生命周期,这类生命周期和组件没有很强的关联

主要用于组件内部监听父组件的展示、隐藏状态,从而方便组件内部执行一些业务逻辑的处理

组件所在页面的生命周期有 4 个: show、 hide、 resize、 routeDone,需要在 pageLifetimes 字段内进行声明

// components/custom06/custom06.js
Component({

  // coding...

  // 组件所在页面的生命周期
  pageLifetimes: {

    // 监听组件所在的页面展示(后台切前台)状态
    show () {
      console.log('组件所在的页面被展示')
    },

    // 监听组件所在的页面隐藏(前台切后台、点击 tabBar)状态
    hide () {
      console.log('组件所在的页面被隐藏')
    }

  }

})

12. 小程序生命周期总结

小程序冷启动,钩子函数执行的顺序

保留当前页面(navigate) 以及 关闭当前页面(redirect)

切后台 以及 切前台(热启动)

13. 拓展:使用 Component 构造页面

Component 方法用于创建自定义组件

小程序的页面也可以视为自定义组件,因此页面也可以使用 Component 方法进行创建,从而实现复杂的页面逻辑开发

📌 注意事项:

  1. 要求对应 json 文件中包含 usingComponents 定义段

  2. 页面使用 Component 构造器创建,需要定义与普通组件一样的字段与实例方法

  3. 页面 Page 中的一些生命周期方法(如 onLoad() 等以“on”开头的方法),在 Component 中要写在 methods 属性中才能生效

  4. 组件的属性 Properties 可以用于接收页面的参数,在 onLoad() 中可以通过 this.data 拿到对应的页面参数

落地代码:

Component({

  // 为什么需要使用 Component 方法进行构造页面
  // Component 方法功能比 Page 方法强大很多
  // 如果使用 Component 方法构造页面,可以实现更加复杂的页面逻辑开发

  // 小程序页面也可以使用 Component 方法进行构造
  // 注意事项:
  // 1. 要求 .json 文件中必须包含 usingComponents 字段
  // 2. 里面的配置项需要和 Component 中的配置项保持一致
  // 3. 页面中 Page 方法有一些钩子函数、事件监听方法,这些钩子函数、事件监听方法必须方法 methods 对象中
  // 4. 组件的属性 properties 也可以接受页面的参数,在 onLoad 钩子函数中可以通过 this.data 进行获取

  properties: {
    id: String,
    title: String
  },

  data: {
    name: 'tom'
  },

  // onLoad () {
  //   console.log('页面加载 - 1')
  // },

  methods: {

    // 更新 name
    updateName() {
      this.setData({
        name: 'jerry'
      })
    },

    onLoad (options) {
      // console.log('页面加载 - 2')
      // console.log(options)
      console.log(this.data.id)
      console.log(this.data.title)
      console.log(this.properties.id)
    },

  }

})

14. 拓展:behaviors

小程序的 behaviors 方法是一种代码复用的方式,可以将一些通用的逻辑和方法提取出来,然后在多个组件中复用,从而减少代码冗余,提高代码的可维护性。

如果需要 behavior 复用代码,需要使用 Behavior() 方法,每个 behavior 可以包含一组属性、数据、生命周期函数和方法

组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。

注册 behavior:

如果需要注册一个 behavior,需要借助 Behavior() 方法,接受一个 Object 类型的参数

// my-behavior.js

module.exports = Behavior({
  behaviors: [],
  properties: {
    myBehaviorProperty: {
      type: String
    }
  },
  data: {
    myBehaviorData: 'my-behavior-data'
  },
  created: function () {
    console.log('[my-behavior] created')
  },
  attached: function () {
    console.log('[my-behavior] attached')
  },
  ready: function () {
    console.log('[my-behavior] ready')
  },

  methods: {
    myBehaviorMethod: function () {
      console.log('[my-behavior] log by myBehaviorMehtod')
    },
  }
})

使用 behavior:

// my-component.js
const myBehavior = require('my-behavior')

Component({
  behaviors: [myBehavior]
  
  // coding...
})

组件和它引用的 behavior 中可以包含同名的字段,对这些字段的处理方法如下:

  1. 如果有同名的属性或方法,采用 “就近原则”,组件会覆盖 behavior 中的同名属性或方法

  2. 如果有同名的数据字段且都是对象类型,会进行对象合并,其余情况会 采用 “就近原则” 进行数据覆盖

  3. 生命周期函数和 observers 不会相互覆盖,会是在对应触发时机被逐个调用,也就是都会被执行

详细的规则:同名字段的覆盖和组合规则

15. 拓展:外部样式类

默认情况下,组件和组件使用者之间如果存在相同的类名不会相互影响,组件使用者如果想修改组件的样式,需要就解除样式隔离,但是解除样式隔离以后,在极端情况下,会产生样式冲突、CSS 嵌套太深等问题,从而给我们的开发带来一定的麻烦。

外部样式类:在使用组件时,组件使用者可以给组件传入 CSS 类名,通过传入的类名修改组件的样式。

如果需要使用外部样式类修改组件的样式,在 Component 中需要用 externalClasses 定义若干个外部样式类。

外部样式类的使用步骤:

1.在 Component 中用 externalClasses 定义段定义若干个外部样式类

2.自定义组件标签通过 属性绑定 的方式提供一个样式类,属性是 externalClasses 定义的元素,属性值是传递的类名

3.将接受到的样式类用于自定义组件内部

📌注意事项:

​ 在同一个节点上使用普通样式类和外部样式类时,两个类的优先级是未定义的

​ 因此需要添加 !important 以保证外部样式类的优先级

落地代码:

➡️ custom09.js

// components/custom09/custom09.js
Component({

  // 组件接受的外部样式类
  externalClasses: ['extend-class']
})

➡️ custom09.wxml

<!-- 在同一个节点上,如果存在外部样式类 和 普通的样式类 -->
<!-- 两个类的优先级是未定义的 -->
<!-- 建议:在使用外部样式类的时,样式需要通过 !important 添加权重 -->
<view class="extend-class box">通过外部样式类修改组件的样式</view>

➡️ custom09.wxss

.box {
  color: lightseagreen;
}

➡️ profile.wxml

<!-- 属性是在 externalClasses 里面定义的元素 -->
<!-- 属性值必须是一个类名 -->
<custom09 extend-class="my-class" />

➡️ profile.wxss

/* pages/index/index.wxss */

.my-class {
  color: lightsalmon !important;
}

16. 完善复选框案例并总结自定义组件

总结自定义组件:

  1. 组件基本使用:数据、属性、方法、插槽

  2. 组件样式使用:组件样式、注意事项、样式隔离、外部样式类

  3. 组件通信传值:父往子传值、子往父传值、获取组件实例

  4. 组件生命周期:组件的生命周期、组件所在页面的生命周期、总结了小程序全部的生命周期

  5. 组件数据监听器:observers

  6. 组件拓展:使用 Component 构造页面、组件复用机制 behaviors 等

完善复选框案例

➡️ components/custom-checkbox/custom-checkbox.wxml

<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->

<view class="custom-checkbox-container">
  <view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
+    <label class="custom-label">
      <checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />

      <view class="content">
        <!-- lable 和 子节点内容都进行了展示 -->
        <!-- 要么展示 lable 要么展示 子节点内容 -->
        <!-- 如果用户传递了 lable 属性,就展示 lable -->
        <!-- 如果用户没有传递 lable 属性,就展示 子节点内容 -->
        <text wx:if="{{ label !== '' }}">{{ label }}</text>

        <slot wx:else />
      </view>
+    </label>
  </view>
</view>

➡️ components/custom-checkbox/custom-checkbox.wxss

+ .custom-checkbox-box .custom-label {
  display: flex;
  align-items: center;
}

npm 支持

1. 构建 npm

目前小程序已经支持使用 npm 安装第三方包,但是这些 npm 包在小程序中不能够直接使用,必须得使用小程序开发者工具进行构建后才可以使用。

为什么得使用小程序开发者工具需要构建呢❓

因为 node_modules 目录下的包,不会参与小程序项目的编译、上传和打包,因此。在小程序项目中要想使用 npm 包,必须走一遍 构建 npm 的过程。

在构建成功以后,默认会在小程序项目根目录,也就是 node_modules 同级目录下生成 miniprogram_npm目录,里面存放这构建打包后的 npm 包,也就是小程序运行过程中真正使用的包

微信开发者工具如何构建❓

我们以使用 Vant Weapp 小程序 UI 组件库为例,来说明小程序如何安装和构建 npm,构建 npm 的步骤如下:

  1. 初始化 package.json
  2. 通过 npm 安装项目依赖
  3. 通过微信开发者工具构建 npm

📌 注意事项

  1. 小程序运行在微信内部,因为运行环境的特殊性,这就导致 并不是所有的包都能够在小程序使用

  2. 我们在小程序中提到的包指专为小程序定制的 npm 包,简称小程序 npm 包,在使用包前需要先确定该包是否支持小程序

  3. 开发者如果需要发布小程序包,需要参考官方规范:https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html#发布-npm-包

构建的详细步骤:

  1. 初始化 package.json这一步至关重要,要不然后续的步骤都很难进行下去

    npm init -y
  2. 通过 npm 安装 @vant/weapp

    npm i @vant/weapp -S --production

  3. 构建 npm

  4. 修改 app.json

    到这一步 npm 的构建已经完成了,但是 Vant 组件库,会和基础组件的样式冲突,因此我们需要继续往下配置

    将 app.json 中的 "style": "v2" 去除,小程序的新版基础组件强行加上了许多样式,难以覆盖,不关闭将造成部分组件样式混乱。

  5. 在页面中使用 vant 提供的小程序组件,这里以 Button 按钮组件为例

    • app.jsonindex.json中引入组件
    • app.json 中注册的组件为全局注册,可以在任意组件中进行使用
    • index.json 中注册组件为组件组件,只能在当前组件中进行使用
    • 按照组件提供的使用方式,在页面中使用即可
    "usingComponents": {
      "van-button": "@vant/weapp/button/index"
    }
    <van-button type="default">默认按钮</van-button>
    <van-button type="primary">主要按钮</van-button>
    <van-button type="info">信息按钮</van-button>
    <van-button type="warning">警告按钮</van-button>
    <van-button type="danger">危险按钮</van-button>
  6. 页面预览效果

2. 自定义构建 npm

在实际的开发中,随着项目的功能越来越多、项目越来越复杂,文件目录也变的很繁琐,为了方便进行项目的开发,开发人员通常会对目录结构进行调整优化,例如:将小程序源码放到 miniprogram 目录下。

但是在调整目录以后,我们按照上一小节 Vant Weapp 的构建流程进行构建,发现没有构建成功,并且弹出构建失败的弹框

[错误提示翻译意思是] :没有找到可以构建的 npm 包

[解决方式]:

  1. 请确认需要参与构建的 npm 都在 miniprogramRoot 目录内
  2. 配置 project.config.jsonpackNpmManuallypackNpmRelationList 进行构建

产生这个错误的原因是因为小程序的构建方式有两种:

  1. 默认构建 npm
  2. 自定义构建 npm

默认构建 npm

默认情况下,不使用任何模版,miniprogramRoot 是小程序项目根目录,在 miniprogramRoot 内正确配置了 package.json 并执行 npm install 之后,在项目的根目录下就有 node_modules 文件夹,然后对 node_modules 中的 npm 进行构建,其构建 npm 的结果是,为 package.json 对应的 node_modules 构建一份 miniprogram_npm,并放置在对应 package.json 所在目录的子目录中

自定义构建 npm

默认的构建 npm 方式不一样,自定义构建 npm 的方式为了更好的优化目录结构,更好的管理项目中的代码。

需要开发者在 project.config.json 中指定 node_modules 的位置 和 目标 miniprogram_npm 的位置

project.config.json中详细的配置流程和步骤如下:

  1. 新增 miniprogramRoot 字段,指定调整后了的小程序开发目录
  2. 新增 setting.packNpmManually设置为 true,开启指定node_modules 的位置以及构建成功后文件的位置
  3. 新增 setting.packNpmRelationList 项,指定 packageJsonPathminiprogramNpmDistDir 的位置
    • packageJsonPath 表示 node_modules 源对应的 package.json
    • miniprogramNpmDistDir 表示 node_modules 的构建结果目标位置
{
  // 指定调整后了的小程序开发目录
  "miniprogramRoot": "miniprogram/",
  "setting": {
    // 开启自定义 node_modules 和 miniprogram_npm 位置的构建 npm 方式
    "packNpmManually": true,
    // 指定 packageJsonPath 和 miniprogramNpmDistDir 的位置
    "packNpmRelationList": [
      {
        "packageJsonPath": "./package.json",
        "miniprogramNpmDistDir": "./miniprogram"
      }
    ]
  }
}

落地代码:

  1. 将小程序核心源码放到 miniprogram 目录下

  2. project.config.json中进行配置

    {
      "compileType": "miniprogram",
     
    +   "miniprogramRoot": "miniprogram/",
    +   "setting": {
    +     "packNpmManually": true,
    +     "packNpmRelationList": [
    +       {
    +         "packageJsonPath": "./package.json",
    +         "miniprogramNpmDistDir": "./miniprogram"
    +       }
    +     ]
    +   }
        
      // coding... 其他配置项
    }

3. Vant 组件的使用方式

Vant Weapp 是有赞前端团队开源的小程序 UI 组件库,基于微信小程序的自定义组件开发,可用来快速搭建小程序项目。

在使用 Vant 提供的组件时,只需要两个步骤:

1.将组件在 app.json 中进行全部注册 或者 index.json 中进行局部注册

2.在引入组件后,可以在 wxml 中直接使用组件

在前面我们以 image 组件为例,讲解 Vant 组件库的基本使用方式

首先还是需要将先将组件进行引入,这里我们进行全局引入

// app.json

"usingComponents": {
  "van-image": "@vant/weapp/image/index"
}

引入组件后,可以在 wxml 中直接使用组件

<van-image width="100" height="100" src="https://img.yzcdn.cn/vant/cat.jpeg" />

<!-- 坑: -->
<!-- 在使用 van-image 图片组件时,如果需要渲染本地的图片,不能使用 ../ -->
<!-- 需要相对于小程序源码的目录来查找图片才可以 -->
<!-- <van-image width="100" height="100" src="../../assets/Jerry.png" /> -->

如果我们想给 van-field 添加一些属性,这时候我们需要查看 API 手册

<van-image width="100" height="100" round src="/assets/Jerry.png"/>

如果我们想给 van-field 添加一些事件,这时候我们需要查看 事件 手册

<van-image
  width="100"
  height="100"
  round
  src="/assets/Jerry.png"
  bind:click="imageHandler"
/>
Page({

  imageHandler () {
    console.log('点击图片时触发点击事件,执行该事件处理函数~~~~')
  }

}

如果我们想给 van-field 添加一些插槽,这时候我们需要查看 slot 手册

<van-image
  width="100"
  height="100"
  round
+   use-loading-slot
+   use-error-slot
  src="/assets/Jerry.png"
  bind:click="imageHandler"
>
+   <!-- slot: loading -->
+   <van-loading slot="loading" type="spinner" size="20" vertical />
+ 
+   <!-- slot: error -->
+   <text slot="error">加载失败</text>
</van-image>

如果我们想给 van-field 添加一些外部样式类,这时候我们需要查看 外部样式类 手册

<van-image
  width="100"
  height="100"
  round
  use-loading-slot
  use-error-slot
+   custom-class="custom-class"
  src="/assets/Jerry.png"
  bind:click="imageHandler"
>
  <!-- slot: loading -->
  <van-loading slot="loading" type="spinner" size="20" vertical />

  <!-- slot: error -->
  <text slot="error">加载失败</text>
</van-image>
/* pages/index/index.wxss */

.custom-class {
  border: 10rpx solid lightseagreen !important;
}

4. Vant 组件的样式覆盖

Vant Weapp 基于微信小程序的机制,为开发者提供了以下 3 种修改组件样式的方法

  1. 解除样式隔离:在页面中使用 Vant Weapp 组件时,可直接在页面的样式文件中覆盖样式
  2. 使用外部样式类:需要注意普通样式类和外部样式类的优先级是未定义的,需要添加 !important 保证外部样式类的优先级
  3. 使用 CSS 变量:在页面或全局对多个组件的样式做批量修改以进行主题样式的定制

第 1 种:解除样式隔离

Vant Weapp 的所有组件都开启了addGlobalClass: true以接受外部样式的影响,因此我们可以通过审核元素的方式获取当前元素的类名,然后复制到组件的 .wxss 中进行修改

第 2 种:使用外部样式类

Vant Weapp 开放了大量的外部样式类供开发者使用,具体的样式类名称可查阅对应组件的 “外部样式类” 部分。

需要注意的是普通样式类和外部样式类的优先级是未定义的,因此使用时请添加!important以保证外部样式类的优先级。

第 3 种:使用 CSS 变量

Vant Weapp 可以通过 CSS 变量的方式多个组件的样式做批量修改。CSS 的变量基础用法如下:

  1. 声明一个自定义属性,属性名需要以两个减号(--)开始,属性值则可以是任何有效的 CSS 值
/* app.wxss */

/* 声明全局的变量,可在项目中任意组件中使用 */
page {
  --main-bg-color: lightcoral;
}
  1. 使用一个局部变量时用 var() 函数包裹以表示一个合法的属性值
/* 声明局部的变量 */
/* 只有被当前类名容器包裹住的元素,使用该变量才生效 */
.container {
  --main-bg-color: lightseagreen;
}

.custom-class {
  /* 使用一个局部变量时用 var() 函数一个合法的属性值 */
  background-color: var(--main-bg-color) !important;
  color: #fff !important;
}
  1. 页面中使用该变量
<view class="container">
  <van-button
    type="default"
    custom-class="custom-class"
  >
    默认按钮
  </van-button>
</view>

<van-button
  type="default"
  custom-class="custom-class"
>
  默认按钮
</van-button>

![](/assets/wx/CSS 变量修改演示.jpg)


也可以在按钮身上添加类名:

<!-- 使用 CSS 变量:如果需要再多个页面或者一个组件中 需要批量修改组件、定制主题 -->
<van-button type="primary" class="my-button">主要按钮</van-button>
.my-button {
  --color: rgb(221, 152, 24);
}

.van-button--primary {
  font-size: 28rpx !important;
  background-color: var(--color) !important;
  border: 1px solid var(--color) !important;
}

分包加载

1. 什么是分包加载

什么是分包加载

小程序的代码通常是由许多页面、组件以及资源等组成,随着小程序功能的增加,代码量也会逐渐增加,体积过大就会导致用户打开速度变慢,影响用户的使用体验。

分包加载是一种小程序优化技术。将小程序不同功能的代码,分别打包成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载,在构建小程序分包项目时,构建会输出一个或多个分包。每个使用分包小程序必定含有一个主包。每个分包可以包含多个页面、组件、样式和逻辑等。当小程序需要使用某个分包时,才会加载该分包中的代码。

主包:包含默认启动页面 / TabBar 页面 以及 所有分包都需用到公共资源的包

分包:根据开发者的配置进行划分出来的子包

小程序分包后如何加载

在小程序启动时,默认会下载主包并启动主包内页面,在用户访问分包内某个页面时,微信客户端才会把对应分包下载下来,下载完成后再进行展示。

目前小程序分包大小有以下限制:**

  1. 整个小程序所有分包大小不超过 20MB
  2. 单个分包/主包大小不能超过 2MB

📌 注意事项

​ 整个小程序所有分包大小可能会随时调整,截止到目前整个小程序所有分包大小不超过 20M

###2. 分包的基本使用

知识点:

在进行分包加载之前,需要对小程序的业务逻辑进行分析,将代码划分成多个模块。每个模块应该有一个明确的功能,并与其他模块之间有明确的依赖关系

需要按照功能拆分分包,并且每个分包都需要与其他包有依赖关系(可以通过 a 分包跳转到 b 分包)

开发者在小程序的配置文件 app.json 中,通过 subPackages 或者 subpackages字段声明项目分包结构。

每个分包需要指定 root 字段、name 字段和 pages 字段

  1. root 字段指定了分包的根目录,该目录下的所有文件都会被打包成一个独立的包
  2. name 字段为分包的名称,用于在代码中引用该分包
  3. pages 字段指定了该分包中包含的页面,可以使用通配符 * 匹配多个页面

落地代码:

{

  "subPackages": [
    {
      "root": "modules/goodModule",
      "name": "goodModule",
      "pages": [
        "pages/list/list",
        "pages/detail/detail"
      ]
    },
    {
      "root": "modules/marketModule",
      "name": "marketModule",
      "pages": [
        "pages/market/market"
      ]
    }
  ]

}

3. 打包和引用原则(注意事项)

打包原则:

  1. tabBar 页面必须在主包内

  2. 最外层的 pages 字段,属于主包的包含的页面

  3. 按 subpackages 配置路径进行打包,配置路径外的目录将被打包到主包中

  4. 分包之间不能相互嵌套,subpackage 的根目录不能是另外一个 subpackage 内的子目录

引用原则:

  1. 主包不可以引用分包的资源,但分包可以使用主包的公共资源

  2. 分包与分包之间资源无法相互引用, 分包异步化时不受此条限制

4. 独立分包的配置

什么是独立分包

独立分包:独立分包是小程序中一种特殊类型的分包,可以独立于主包和其他分包运行

从独立分包中页面进入小程序时,不需要下载主包,但是当用户进入普通分包或主包内页面时,主包才会被下载 !

开发者可以将功能相对独立的页面配置到独立分包中,因为独立分包不依赖主包即可运行,可以很大程度上提升分包页面的启动速度

如果是独立分包,不需要下载主包,直接就能够访问,独立分包是自己独立运行的

而如果是其他分包,需要先下载主包,通过路径访问,才能加载对应路径的分包

📌 注意事项:

  1. 独立分包中不能依赖主包和其他分包中的资源

  2. 主包中的 app.wxss 对独立分包无效

  3. App 只能在主包内定义,独立分包中不能定义 App,会造成无法预期的行为

如何配置独立分包:

开发者在app.json中找到需要配置为独立分包的subpackages字段

在该字段配置项中定义independent字段声明对应分包为独立分包。

落地代码:

{

  "subPackages": [
    {
      "root": "modules/goodModule",
      "name": "goodModule",
      "pages": [
        "pages/list/list",
        "pages/detail/detail"
      ]
    },
    {
      "root": "modules/marketModule",
      "name": "marketModule",
      "pages": [
        "pages/market/market"
      ],
+       "independent": true
    }
  ]
}

5. 分包预下载

知识点:

分包预下载是指访问小程序某个页面时,预先下载分包中的代码和资源,以提高用户的使用体验。当用户需要访问分包中的页面时,已经预先下载的代码和资源可以直接使用,通过分包预下载加快了页面的加载速度和显示速度。

小程序的分包预下载需要在 app.json 中通过 preloadRule 字段设置预下载规则。preloadRule 是一个对象,对象的 key 表示访问哪个路径时进行预加载,value 是进入此页面的预下载配置,具有两个配置项:

字段 类型 必填 默认值 说明
packages StringArray 预下载的分包名称,进入页面后预下载分包的 rootname
__APP__ 表示主包。
network String wifi 在指定网络下预下载,
可选值为: all: 不限网络 wifi: 仅wifi下预下载
{
  "subPackages": [
    {
      "root": "modules/goodModule",
      "name": "goodModule",
      "pages": [
        "pages/list/list",
        "pages/detail/detail"
      ]
    },
    {
      "root": "modules/marketModule",
      "name": "marketModule",
      "pages": [
        "pages/market/market"
      ],
      "independent": true
    }
  ],
  "preloadRule": {
    "pages/index/index": {
      "network": "all",
      "packages": ["modules/goodModule"]
    },
    "modules/marketModule/pages/market/market": {
      "network": "all",
      "packages": ["__APP__"]
    }
  }
}

落地代码

{
  "pages": [
    "pages/index/index",
    "pages/user/user"
  ],
  "subPackages": [
    {
      "root": "pages/music",
      "name": "music",
      "pages": [
        "player/player",
        "lyric/lyric"
      ]
    },
    {
      "root": "pages/settings",
      "name": "settings",
      "pages": [
        "theme/theme",
        "language/language"
      ]
    }
  ],
  "preloadRule": {
    "pages/music/player/player": {
      "packages": ["settings"],
      "network": "wifi"
    }
  }
}

小程序开发能力

1. 获取用户头像

当小程序需要让用户完善个人资料时,我们可以通过微信提供的头像、昵称填写能力快速完善。如图:

想使用微信提供的头像填写能力,需要两步:

  1. button 组件 open-type 的值设置为 chooseAvatar
  2. 当用户选择需要使用的头像之后,可以通过 bindchooseavatar 事件回调获取到头像信息的临时路径。
<!-- 给 button 添加 open-type 属性,值为 chooseAvatar -->
<!-- 绑定 bindchooseavatar 事件获取回调信息 -->
<button open-type="chooseAvatar" bindchooseavatar="getAvatar">
  按钮
</button>

落地代码:

<view class="avatar">
  <button open-type="chooseAvatar" bindchooseavatar="getAvatar">
    <image src="{{ avatarUrl }}" mode="" />
  </button>
</view>
Page({
  /**
   * 页面的初始数据
   */
  data: {
    avatarUrl: '/assets/tom.png'
  },

  // 获取用户头像信息
  getAvatar(e) {
    // 获取选中的头像
    const { avatarUrl } = e.detail

    // 将获取到的头像赋值给 data 中变量同步给页面结构
    this.setData({
      avatarUrl
    })
  }
 
  // coding...
}

2. 获取用户昵称

知识点:

当小程序需要让用户完善个人资料时,我们可以通过微信提供的头像、昵称填写能力快速完善。如图:

想使用微信提供的昵称填写能力,需要三步:

  1. 通过 form 组件中包裹住 input 以及 form-type 为 submit 的 button 组件

  2. 需要将 input 组件 type 的值设置为 nickname,当用户输入框输入时,键盘上方会展示微信昵称

  3. 给 form 绑定 submit 事件,在事件处理函数中通过事件对象获取用户昵称

落地代码:

<!-- 需要使用 form 组件包裹住 input 以及 button 组件 -->
<form bindsubmit="onSubmit">

  <!-- input 输入框组件的 type 属性设置为 nickname,用户点击输入框,键盘上方才会显示微信昵称 -->
  <!-- 如果添加了 name 属性,form 组件就会自动收集带有 name 属性的表单元素的值 -->
  <input type="nickname" name="nickname" placeholder="请输入昵称" />

  <!-- 如果将 form-type="submit" ,就将按钮变为提交按钮 -->
  <!-- 在点击提交按钮的时候,会触发 表单的 bindsubmit 提交事件 -->
  <button type="primary" plain form-type="submit">点击获取昵称</button>
</form>
Page({


  // 获取微信昵称
  onSubmit (event) {
    // console.log(event.detail.value)
    const { nickname } = event.detail.value
    console.log(nickname)
  }

}

3. 转发功能

转发功能,主要帮助用户更流畅地与好友分享内容和服务

想实现转发功能,有两种方式:

  1. 页面 js 文件 必须声明 onShareAppMessage 事件监听函数,并自定义转发内容。只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮

  2. 通过给 button 组件设置属性 open-type=“share“ ,在用户点击按钮后触发 Page.onShareAppMessage 事件监听函数

官方文档 onShareAppMessage:

落地代码:

<!--pages/cate/cate.wxml-->

<button open-type="share">转发</button>
Page({

  // 监听页面按钮的转发 以及 右上角的转发按钮
  onShareAppMessage (obj) {
    // console.log(obj)

    // 自定义转发内容
    return {
      // 转发标题
      title: '这是一个非常神奇的页面~~~',
      // 转发路径
      path: '/pages/cate/cate',
      // 自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径
      imageUrl: '../../assets/Jerry.png'
    }

  }

})

4. 分享到朋友圈

小程序页面默认不能被分享到朋友圈,开发者需主动设置“分享到朋友圈”才可以,实现分享到朋友圈需满足两个条件:

  1. 页面 必须 设置允许“发送给朋友”,页面 js 文件声明 onShareAppMessage 事件监听函数

  2. 页面 必须 需设置允许“分享到朋友圈”,页面 js 文件声明 onShareTimeline 事件监听函数

官方文档 onShareTimeline:

落地代码:

Page({


  // 监听右上角 分享到朋友圈 按钮
  onShareTimeline () {

    // 自定义分享内容。
    return {
      // 自定义标题,即朋友圈列表页上显示的标题
      title: '帮我砍一刀~~~',
      // 自定义页面路径中携带的参数,如 path?a=1&b=2 的 【 “?” 后面部分 】
      query: 'id=1',
      // 自定义图片路径,可以是本地文件或者网络图片
      imageUrl: '../../assets/Jerry.png'
    }

  }

})

5. 手机号验证组件

手机验证组件,用于帮助开发者向用户发起手机号申请,必须经过用户同意后,才能获得由平台验证后的手机号,进而为用户提供相应服务

  1. 手机号快速验证组件:平台会对号码进行验证,但不保证是实时验证

    <button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">
  2. 手机号实时验证组件:在每次请求时,平台均会对用户选择的手机号进行实时验证

    <button
      open-type="getRealtimePhoneNumber"
      bindgetrealtimephonenumber="getrealtimephonenumber"
    />

📌注意事项:

1.目前该接口针对非个人开发者,且完成了认证的小程序开放(不包含海外主体)

2.两种验证组件需要付费使用,每个小程序账号将有 1000 次体验额度

其他要求和注意事项,参考文档:

手机号快速验证组件

手机号实时验证组件

落地代码:

<!--pages/cart/cart.wxml-->

<button
  type="primary"
  plain
  open-type="getPhoneNumber"
  bindgetphonenumber="getphonenumber"
>快速验证组件</button>


<button
  type="warn"
  plain
  open-type="getRealtimePhoneNumber"
  bindgetrealtimephonenumber="getrealtimephonenumber"
>实时验证组件</button>
Page({

  // 手机号快速验证
  getphonenumber (event) {
    // 通过事件对象,可以看到,在 event.detail 中可以获取到 code
    // code 动态令牌,可以使用 code 换取用户的手机号
    // 需要将 code 发送给后端,后端在接收到 code 以后
    // 也需要调用 API,换取用户的真正手机号
    // 在换取成功以后 ,会将手机号返回给前端
    console.log(event)
  },

  // 手机号实时验证
  getrealtimephonenumber (event) {
    console.log(event)
  }

})

6. 客服能力

小程序为开发者提供了客服能力,同时为客服人员提供移动端、网页端客服工作台便于及时处理消息

使用方式:

  1. 需要将 button 组件 open-type 的值设置为 contact,当用户点击后就会进入客服会话

    <button type="warn" plain open-type="contact">联系客服</button>
  2. 在微信公众后台,绑定后的客服账号,可以登陆 网页端客服 或 移动端小程序 客服接收、发送客服消息

框架接口-getApp

getApp() 用于获取小程序全局唯一的 App 实例,通过小程序应用实例可实现数据或方法的共享

📌 注意事项

  1. 1.不要在 App() 方法中使用 getApp() ,使用 this 就可以拿到 app 实例
  2. 通过 getApp() 获取实例之后,不要私自调用生命周期函数

落地代码:

➡️ app.js

App({

  // 全局共享的数据
  globalData: {
    token: ''
  },

  // 全局共享的方法
  setToken (token) {
    // 如果想获取 token,可以使用 this 的方式进行获取
    this.globalData.token = token

    // 在 App() 方法中如果想获取 App() 实例,可以通过 this 的方式进行获取
    // 不能通过 getApp() 方法获取
  }

})

➡️ pages/index/index.js

// getApp() 方法用来获取全局唯一的 App() 实例
const appInstance = getApp()

Page({

  login () {

    // 不要通过 app 实例调用钩子函数
    console.log(appInstance)

    appInstance.setToken('fghioiuytfghjkoiuytghjoiug')

  }

})

小程序页面间通信

如果一个页面通过 wx.navigateTo 打开一个新页面,这两个页面间将建立一条数据通道

  1. wx.navigateTosuccess 回调中通过 EventChannel 对象发射事件

  2. 被打开的页面可以通过 this.getOpenerEventChannel() 方法获得一个 EventChannel 对象,进行监听、发射事件

  3. wx.navigateTo 方法中可以定义 events 配置项接收被打开页面发射的事件

这两个 EventChannel 对象间可以使用 emiton 方法相互发送、监听事件。

落地代码:

页面 .js 文件

Page({

  // 点击按钮触发的事件处理函数
  handler () {

    wx.navigateTo({
      url: '/pages/list/list',
      events: {
        // key:被打开页面通过 eventChannel 发射的事件
        // value:回调函数
        // 为事件添加一个监听器,获取到被打开页面传递给当前页面的数据
        currentevent: (res) => {
          console.log(res)
        }
      },
      success (res) {
        // console.log(res)
        // 通过 success 回调函数的形参,可以获取 eventChannel 对象
        // eventChannel 对象给提供了 emit 方法,可以发射事件,同时携带参数
        res.eventChannel.emit('myevent', { name: 'tom' })
      }
    })

  }

})

被页面 .js 文件

Page({

  onLoad () {

    // 通过 this.getOpenerEventChannel() 可以获取 EventChannel 对象
    const EventChannel = this.getOpenerEventChannel()

    // 通过 EventChannel 提供的 on 方法监听页面发射的自定义事件
    EventChannel.on('myevent', (res) => {
      console.log(res)
    })

    // 通过 EventChannel 提供的 emit 方法也可以向上一级页面传递数据
    // 需要使用 emit 定义自定义事件,携带需要传递的数据
    EventChannel.emit('currentevent', { age: 10 })

  }

})

自定义导航栏

小程序默认的导航栏与 APP 一样都位于顶部固定位置。但是默认导航栏可能会影响小程序整体风格,且无法满足特定的设计需求,这时候,就需要进行自定义导航栏。

在 app.json 或者 page.json 中,配置 navigationStyle 属性为 custom,即可 自定义导航栏

在设置以后,就会移除默认的导航栏,只保留右上角胶囊按钮

落地代码:

{
  "usingComponents": {},
  "navigationStyle": "custom"
}
<!--pages/cate/cate.wxml-->
<!-- <text>pages/cate/cate.wxml</text> -->

<swiper class="custom-swiper" indicator-dots autoplay interval="2000">
  <swiper-item>
    <image src="../../assets/banner/banner-1.png" mode=""/>
  </swiper-item>

  <swiper-item>
    <image src="../../assets/banner/banner-2.png" mode=""/>
  </swiper-item>

  <swiper-item>
    <image src="../../assets/banner/banner-3.png" mode=""/>
  </swiper-item>
</swiper>
/* pages/cate/cate.wxss */

.custom-swiper {
  height: 440rpx;
}

.custom-swiper image {
  height: 100%;
  width: 100%;
}

上线发布

假设我们目前已经使用微信开发者工具,按照小程序的开发规范完成了小程序的全部的全部开发工作,并且完成了本地测试,

这时候我们需要开发对小程序进行发布,小程序上线的流程如下:

开发版本:点击开发者工具上传后的版本,开发版本只保留每人最新的一份上传的代码,是供开发者和团队测试和调试的版本

体验版本:小程序开发者可以将开发版本转换为体验版本,由测试人员以及产品经理进行测试与体验,确认没问题可提交审核

审核版本:小程序开发者可以将开发版本转换为审核版本,由微信的审核团队进行审核,审核周期为1~7天,审核通过可提交发布

线上版本:通过微信小程序平台审核,并由开发者提交发布的正式版本,线上版本是用户可以正常使用的小程序版本

小程序开发成员在开发者工具中点击 上传 按钮,在弹出的界面中选择更新类型、版本号、项目备注,就能够将小程序代码上传至微信公众号后台审核。

在登录到微信公众后台以后,点击左侧的 管理版本管理,就能查看小程序的四个个版本


文章作者: 时秋
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 时秋 !
  目录