Skip to content

微信小程序1

一、小程序基础知识

1. 认识什么是小程序

什么是微信小程序

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

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

特点:

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

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

2. 微信小程序账号注册

📌 注意事项

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

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

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

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

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

3. 完善小程序账号信息

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

📌 注意事项

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

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

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

注意:选择类目的时候不要选择小游戏类目 !!!!

4. 小程序开发者 ID

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

一个完整的开发者 ID 由 ****和一个 ****组成。

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

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

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

5. 开发成员和体验成员

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

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

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

6. 微信开发者工具

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

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

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

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

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

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

📌 注意事项

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

8. 创建小程序项目

1.创建项目

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

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

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

    • 项目名称:输入项目名称
    • 目录:选择小程序开发文件夹,注意:
    • AppID:填写自己申请的小程序 AppID
    • 开发模式:选择小程序
    • 后端服务:选择不使用云服务
    • 模板选择:选择不使用模板
    image-20240611182727579
2.开发者工具设置

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

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

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

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

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

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

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

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

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

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

📌 注意事项

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

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

shell
├─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.jsonpages 选项中新增页面路径

第二种方式(推荐):

app.jsonpages 选项中,新增页面路径即可

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

image-20240611185825237

12. 调试基础库

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

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

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

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

二、小程序配置文件

1. 配置文件介绍

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

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

2. 全局配置-app.json

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

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

2.1 pages

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

json
{
  "entryPagePath": "pages/index/index", // 小程序的初始页面,未设置时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 / whitestringwhite
navigationBarTitleText导航栏标题文字内容string
backgroundColor刷新窗口的背景色stringdark
enablePullDownRefresh是否开启全局的下拉刷新booleanfalse
onReachBottomDistance页面上拉触底事件触发时距页面底部距离单位为 pxnumber50
backgroundTextStyle下拉 loading 的样式,仅支持 dark / light

我们按照下图来配置 window

[Missing Image: image-20240611203645722.png]

json
"window": {
    "navigationBarTitleText": "鑫凡花坊", // 导航栏标题
    "navigationBarBackgroundColor": "#f3514f", // 导航栏背景色
    "enablePullDownRefresh": true, // 是否可以下拉刷新
    "backgroundColor": "#efefef", // 刷新窗口背景色
    "backgroundTextStyle":"light" // 下拉刷新loading样式
  },

详细文档: window 配置项

2.3 tabBar

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

tabBar 配置项

属性描述类型默认值
colortab 上的文字默认颜色,仅支持十六进制颜色HexColor
selectedColortab 上的文字选中时的颜色,仅支持十六进制颜色HexColor
backgroundColortab 的背景色,仅支持十六进制颜色HexColor
borderStyletabbar 上边框的颜色, 仅支持 black / whitestringblack
listtab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab
positiontabBar 的位置,仅支持 bottom / topstringbottom

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

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

📌 注意事项:

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

我们按照下图来配置 tabBar

[Missing Image: image-20240611214955423.png]

json
 "tabBar": {
    "color": "#666", // tabBar 文字颜色
    "selectedColor": "#f3514f", // tabBar 文字选中颜色
    "borderStyle":"black", // tabBar 上边框的颜色
    "position": "bottom", // tabBar 的位置
    "list": [ // tab的列表
      { 
        "text": "首页", // tab上按钮文字
        "pagePath": "pages/index/index", // 点击显示的页面路径
        "iconPath": "./assets/tabbar/index.png", // 图片路径
        "selectedIconPath": "./assets/tabbar/index-active.png" // 选中后图片路径
      },
      {
        "text": "分类",
        "pagePath": "pages/cate/cate",
        "iconPath": "./assets/tabbar/cate.png",
        "selectedIconPath": "./assets/tabbar/cate-active.png"
      },
      {
        "text": "购物车",
        "pagePath": "pages/cart/cart",
        "iconPath": "./assets/tabbar/cart.png",
        "selectedIconPath": "./assets/tabbar/cart-active.png"
      },
      {
        "text": "我的",
        "pagePath": "pages/profile/profile",
        "iconPath": "./assets/tabbar/my.png",
        "selectedIconPath": "./assets/tabbar/my-active.png"
      }
    ]
  },

详细文档: window 配置项

3. 页面配置

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

需要注意的是:页面配置文件的属性和全局配置文件中的 window 属性几乎一致 页面中配置项会覆盖全局配置文件中相同的配置项。

属性描述类型默认值
navigationBarBackgroundColor导航栏背景颜色HexColor#000000
navigationBarTextStyle导航栏标题颜色,仅支持 black / whitestringwhite
navigationBarTitleText导航栏标题文字内容string
backgroundColor下拉窗口的背景色string
enablePullDownRefresh是否开启当前页面的下拉刷新booleanfalse
onReachBottomDistance页面上拉触底事件触发时距页面底部距离单位为 pxnumber50
backgroundTextStyle下拉 loading 的样式,仅支持 dark / lightstringlight
navigationStyle导航栏样式(默认/自定义) defalut/customstringdefault

📌 注意事项

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

[Missing Image: image-20240611221929263.png]

json
cate.json
{
  "usingComponents": {},
  "navigationBarTitleText": "商品分类", // 导航标题文字
  "navigationBarBackgroundColor": "#00af92", // 导航背景色
  "enablePullDownRefresh": true, // 是否支持刷新
  "backgroundTextStyle":"light", // 刷新时加载图标颜色
  "backgroundColor": "#00af92" // 刷新时窗口颜色
}

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。

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

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

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

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

📌 注意:

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

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

6. sitemap.json

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

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

json
{
  "rules": [
    {
      "action": "allow", // 允许被搜索
      "page": "*" // 全部页面
    }
  ]
}

常用配置项:

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

📌 注意事项

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

三、小程序样式与组件

1. 组件和样式介绍

在开发 Web 网站的时候:

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

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

小程序中提供了同样的角色:

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

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

WXML WXSS

2. 样式-尺寸单位 rpx

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

📌 注意事项:

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

🔔 开发建议

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

  • iPhone6 的设计稿一般是 750px(二倍图),小程序的宽是 750rpx

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

​ 原因:

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

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

示例:

cart/cart.wxml

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

cart/cart.scss

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

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

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

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

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

示例:

app.wxss 中定义全局样式

css
/* app.wxss */
text {
  color: lightseagreen;
  font-size: 50rpx;
}

然后在 cate.wxss 中定义局部样式,覆盖全局样式

css
/* 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,也是一个块级元素,占据一行

html
<!-- pages/index/index -->
<!-- 轮播图区域 -->
<view class="swiper"></view>
<!-- 公司信息 -->
<view class="info"></view>
<!-- 商品导航 -->
<view class="good-nav"></view>
<!-- 推荐商品 -->
<view class="good-hot"></view>

设置首页样式

scss
// pages/index/index.scss
// page页面根组件
page {
  height: 100vh; // 视口高度
  background-color: #efefef !important;
  box-sizing: border-box; // 盒子宽度包括padding+content+border
  padding: 16rpx;
  display: flex;
  flex-direction: column; // 弹性盒纵向排列
  > view {
    // 从第2个view开始每个view设置margin-top
    &:nth-child(n + 2) {
      margin-top: 16rpx;
    }
  }
}

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

在小程序中,提供了 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 (默认 5000ms)
circular是否采用衔接滑动boolean (默认 false)
其他属性...

示例:

index/wxml

html
<!-- 轮播图区域 -->
<view class="swiper">
  <!-- 设置轮播图自动切换,时间间隔为2s,展示底部指示点,指示点颜色为白色,指示点激活时颜色为红色,采用衔接滑动 -->
  <swiper 
    autoplay  // 自动播放
    interval="2000" // 间隔时间
    indicator-dots  // 显示指示点
    indicator-color="#fff"  // 指示点颜色为白色
    indicator-active-color="#f3514f" // 指示点激活时的颜色
    circular // 衔接滑动
  >
    <swiper-item>
      1
    </swiper-item>
    <swiper-item>
      2
    </swiper-item>
    <swiper-item>
      3
    </swiper-item>
  </swiper>
</view>

index.scss

scss
// 轮播图区域样式
.swiper {
  swiper {
    height: 360rpx;
    background-color: skyblue;
    swiper-item {
      // &在scss中代表父选择器
      &:first-child {
        background-color: lightsalmon;
      }
      &:last-child {
        background-color: hotpink;
      }
    }
  }
}

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

在小程序中没有 img 标签,添加图片需要使用小程序提供的image组件,image组件常用的属性有:

属性描述类型
src图片资源地址string
mode图片剪裁,缩放的模式string 默认值scaleToFill
show-menu-by-longpress长按图片显示菜单boolean 默认值 false
lazy-load图片懒加载lazy-load 默认值 false

📌 注意事项:

  1. image默认具有宽度和高度,宽是 320px 高度是 240px
  2. image组件不给 src属性设置图片地址,也占据宽高

基础用法:

html
<!-- image 
    src:图片资源地址 
    mode:图片的裁剪和缩放模式 
    show-menu-by-longpress:长按图片显示菜单(有转发给好友,收藏,保存等功能)  
    lazy-load:开启懒加载,在即将进入一定范围(上下三屏)时才开始加载图片
-->
<image src="" data-missing="floor-img.jpg" mode="aspectFit" show-menu-by-longpress lazy-load/>

示例:

index.wxml

html
<view class="swiper">
  <!-- 设置轮播图自动切换,时间间隔为2s,展示底部指示点,指示点颜色为白色,指示点激活时颜色为红色,采用衔接滑动 -->
  <swiper autoplay interval="2000" indicator-dots indicator-color="#fff" indicator-active-color="#f3514f" circular>
    <swiper-item>
      <image src="" data-missing="banner-1.jpg" show-menu-by-longpress />
    </swiper-item>
    <swiper-item>
      <image src="" data-missing="banner-2.jpg" show-menu-by-longpress />
    </swiper-item>
    <swiper-item>
      <image src="" data-missing="banner-3.jpg" show-menu-by-longpress />
    </swiper-item>
  </swiper>
</view>

index.scss

scss
// 轮播图区域样式
.swiper {
  width: 750rpx;
  height: 400rpx;
  swiper {
    // 设置轮播图宽高
    width: 750rpx;
    height: 400rpx;
    // 圆角50rpx
    border-radius: 50rpx;
    // 超出部分隐藏
    overflow: hidden;
    image {
      width: 100%;
      height: 400rpx;
    }
  }
}

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

在小程序中,如果想要渲染文本,需要使用 text组件,常用属性有:

属性描述类型
user-select文本是否可选,用于长按选择文本boolean 默认值 false
space显示连续空格string

基础用法:

html
<!-- text组件 
      user-select:文本是否可选
      space:显示连续空格 ensp:中文字符空格一半大小 emps:中文字符空格大小 nbsp:根据字体设置的空格大小
-->
<text user-select space="nbsp">你好呀    小可爱</text>
<view user-select>我不能被选中</view>
<text>显示<view>不显示</view></text>

📌 注意事项:

  1. 除了文本节点以外其他节点都无法长按选中
  2. text组件内支持 text嵌套

示例:

index.wxml

html
<!-- 公司信息 -->
<view class="info">
  <text user-select>同城配送</text>
  <text user-select>行业龙头</text>
  <text user-select>半小时送达</text>
  <text user-select>100%好评</text>
</view>

index.scss

scss
.info {
  display: flex; // 推荐使用弹性盒布局
  justify-content: space-between;
  background-color: #fff;
  padding: 16rpx;
  border-radius: 10rpx;
  font-size: 26rpx;
}

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

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

但在小程序中没有 HTML 中的 divulli 标签,所以绘制导航区域需要使用小程序提供的的组件

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

示例:

index.wxml

html
<!-- 商品导航 -->
<view class="good-nav">
  <view>
    <image src="" data-missing="cate-1.png" mode="" />
    <text>鲜花玫瑰</text>
  </view>
  <view>
    <image src="" data-missing="cate-2.png" mode="" />
    <text>鲜花玫瑰</text>
  </view>
  <view>
    <image src="" data-missing="cate-3.png" mode="" />
    <text>鲜花玫瑰</text>
  </view>
  <view>
    <image src="" data-missing="cate-4.png" mode="" />
    <text>鲜花玫瑰</text>
  </view>
  <view>
    <image src="" data-missing="cate-5.png" mode="" />
    <text>鲜花玫瑰</text>
  </view>
</view>

index.scss

scss
// 商品分类区域的样式
.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: 20rpx;
      margin-top: 12rpx;
    }
  }
}

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

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

属性描述类型
url当前小程序内的跳转的路径string
open-type跳转方式string 默认值 navigate

open-type :跳转方式

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

基本用法:

html
<!-- navigator 
    open-type="navigate":只能跳转到非tabBar页面(是否有底部导航栏)同时保留上一级页面(可以返回上一级),不能跳转到tabBar页面(默认值)
    open-type="redirect":只能跳转到非tabBar页面(是否有底部导航栏)关闭上一级页面(不可以返回上一级),不能跳转到tabBar页面
    open-type="switchTab":只能跳转到tabBar页面(注意不能传递参数),不能跳转到非tabBar页面,同时关闭其他所有非tabBar页面
    open-type="reLaunch":关闭之前所有页面(不能返回上一级),打开小程序中某一个页面
    open-type="navigateBack" :返回上一页,或返回前几页,默认只能返回上一页
    delta="" 返回的层级,默认是1,如果想返回几级就,写几
    注意:在进行路径跳转时,需要在路径前添加 /
         switchTab: url 不支持 queryString
-->
<navigator url="/pages/list/list" open-type="navigate">到商品列表页面</navigator>
<navigator url="/pages/cate/cate" open-type="navigate">到商品分类页面</navigator> // 不能跳转
<navigator url="/pages/list/list" open-type="redirect">到商品列表页面</navigator>
<navigator url="/pages/cate/cate" open-type="redirect">到商品分类页面</navigator> // 不能跳转
<navigator url="/pages/list/list" open-type="switchTab">到商品列表页面</navigator> // 不能跳转
<navigator url="/pages/cate/cate" open-type="switchTab">到商品分类页面</navigator>
<navigator url="/pages/list/list" open-type="reLaunch">到商品列表页面</navigator>
<navigator url="/pages/cate/cate" open-type="reLaunch">到商品分类页面</navigator>
<navigator url="/pages/list/list?id=10&num=hua">到商品列表页面并传参</navigator>
<navigator url="/pages/cate/cate?id=10&num=hua" open-type="switchTab">到商品分类页面</navigator>
// 返回上一级
<navigator open-type="navigateBack" delta="1">返回上一页</navigator>

📌 注意事项:

  1. 路径后可以带参数。参数与路径之间使用 ? 分隔,参数键与参数值用 = 相连,不同参数用 & 分隔 例如:/list?id=10&name=hua,在 onLoad(options) 生命周期函数中获取传递的参数
  2. 属性 open-type="switchTab" 时不支持传参
  3. 书写路径是要在路径前加上 //pages/list/list (路径即 app.jsonpages下的路径)

示例:

index.wxml

html
<view class="good-nav">
  <view>
    <navigator url="/pages/list/list">
      <image src="" data-missing="cate-1.png" mode="" />
      <text>鲜花玫瑰</text>
    </navigator>
  </view>
  <view>
    <navigator url="/pages/list/list">
      <image src="" data-missing="cate-2.png" mode="" />
      <text>永生玫瑰</text>
    </navigator>
  </view>
  <view>
    <navigator url="/pages/list/list">
      <image src="" data-missing="cate-3.png" mode="" />
      <text>玫瑰珠宝</text>
    </navigator>
  </view>
  <view>
    <navigator url="/pages/list/list">
      <image src="" data-missing="cate-4.png" mode="" />
      <text>爱礼精选</text>
    </navigator>
  </view>
  <view>
    <navigator url="/pages/list/list">
      <image src="" data-missing="cate-5.png" mode="" />
      <text>香水护体</text>
    </navigator>
  </view>
</view>

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

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

3.10.1 scroll-view 横向滚动

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

基本用法:

index.wxml

html
<!-- 横向滚动 -->
<scroll-view class="scroll-x" scroll-x>
  <view>1</view>
  <view>2</view>
  <view>3</view>
</scroll-view>

index.scss

scss
.scroll-x {
  width: 100%;
  white-space: nowrap; // 不允许换行,让宽度超出藏在页面后
  background-color: skyblue;
  view {
    display: inline-block; // 将view变为行内块可以排列在一行
    width: 300rpx; // 设置每一个view的宽度
    height: 80rpx;
    &:last-child { // 用于区分
      background-color: hotpink;
    }
    &:first-child {
      background-color: aqua;
    }
  }
}

3.10.2 scroll-view 纵向滚动

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

基本用法:

index.wxml

html
<!-- 纵向滚动 -->
<scroll-view class="scroll-y" scroll-y>
  <view>1</view>
  <view>2</view>
  <view>3</view>
</scroll-view>

index.scss

scss
.scroll-y {
  height: 300rpx; // 设置固定高度
  background-color: skyblue;
  margin-top: 10rpx;
  view {
    width: 100%;
    height: 200rpx; // 高度超出,显示滚动条
    &:last-child {
      background-color: hotpink;
    }
    &:first-child {
      background-color: aqua;
    }
  }
}

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

index.wxml

html
<!-- 推荐商品 -->
<view class="good-hot">
  <scroll-view scroll-x class="scroll-x">
    <view>
      <view class="good-item">
        <image src="" data-missing="1.png" mode="" />
        <text>鲜花玫瑰</text>
        <text>66</text>
      </view>
    </view>
    <view>
      <view class="good-item">
        <image src="" data-missing="2.png" mode="" />  
        <text>鲜花玫瑰</text>
        <text>100</text>
      </view>
    </view>
    <view>
      <view class="good-item">
        <image src="" data-missing="3.png" mode="" />
        <text>鲜花玫瑰</text>
        <text>88</text>
      </view>
    </view>
    <view>
      <view class="good-item">
        <image src="" data-missing="4.png" mode="" />
        <text>鲜花玫瑰</text>
        <text>99</text>
      </view>
    </view>
  </scroll-view>
</view>

index.scss

scss
.good-hot {
  background-color: #fff;
  padding: 16rpx;
  border-radius: 10rpx;
  .scroll-x {
    width: 100%;
    white-space: nowrap; // 不允许换行
    view {
      display: inline-block; // 变为行内块元素
      width: 320rpx;
      height: 420rpx;
      margin-right: 16rpx;
      &:last-child {
        margin-right: 0;
      }
      .good-item {
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        align-items: center;
        text-align: center;
        image {
          width: 100%;
          height: 320rpx;
          border-radius: 16rpx;
        }
        text{
          font-size: 25rpx;
          &:nth-of-type(1){
            font-weight: bold;
          }
          &:nth-of-type(2){
            color: rgb(228, 15, 15);
          }
        }
      }
    }
  }
}

11. iconfont字体图标的使用

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

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

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

image-20241013201903178

  1. 点击链接,会将生成的 CSS 在新的链接页面进行打开,ctrl + s,将该文件重命名为.iconfont.scss后缀名,然后保存到项目根目录下的 iconfont/ 文件夹下
  1. 如果下载到本地将下载文件夹中的 iconfont.css 复制到项目的 src/styles/文件夹下

    image-20240731192037663

  2. 在全局样式文件app.wxss中导入iconfont.scss字体图标文件,然后查看图标类名,在项目中使用即可,应用于页面(Vue中在main.ts中导入)

    css
    // 导入字体图标的scss样式
    @import "./iconfont/iconfont.scss"; // 注意必须以分号结尾
  3. 使用

    html
    <text class="iconfont 图标类名"> // 小程序
    <i class="iconfont 图标类名"> // Vue

Tips:设置使用时类名不添加 iconfont前缀

  • 修改下载到本地的 iconfont.css文件:

    css
    - //.iconfont {
    + [class^='icon-'],
    + [class*=' icon-'] {
       font-family: '项目名' !important; // font-family:项目名可以自定义
       font-size: 16px;
       font-style: normal;
       -webkit-font-smoothing: antialiased;
       -moz-osx-font-smoothing: grayscale;
    }
  • 使用:

    css
    <text class="icon-handset"></text>

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

image-20240719205932721

解决方案:将字体图标转换成 base64 的格式,再将生成的css复制到 iconfong/iconfont.cscc

image-20240612225908556image-20240612225925937image-20240612230010953

示例:

index.wxml

html
<view class="info">
  <text user-select><text class="iconfont icon-tongchengpeisong"></text>同城配送</text>
  <text user-select><text class="iconfont icon-tese"></text>行业龙头</text>
  <text user-select><text class="iconfont icon-shijian"></text>半小时送达</text>
  <text user-select><text class="iconfont icon-haoping"></text>100%好评</text>
</view>

index.scss

scss
.info {
  display: flex;
  justify-content: space-between;
  background-color: #fff;
  padding: 20rpx 16rpx;
  border-radius: 10rpx;
  font-size: 26rpx;
  // 调整字体图标样式
+  .iconfont{
+    font-size: 25rpx;
+    margin-right: 10rpx;
+    color: #f3514f;
  }
}

12. 背景图片的使用

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

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

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

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

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

  /* 使用网络图片 */
  /* background-image: url('http://8.131.91.46:6677/TomAndJerry.jpg'); */
    
  /* 使用 base64 格式展示图片 */
  /* base64 编码的文件很长 不建议 */
  background-image: url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAeAB4AAD/.....")
  background-position: center;
  background-size: cover;
}

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

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

四、小程序事件系统

1. 事件绑定和事件对象

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

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

html
<button type="primary" bind:tap="handler">绑定事件</button>

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

html
<button type="warn" bindtap="handler">绑定事件</button>

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

js
// pages/cate/cate.js
Page({
  // 事件处理函数需要写到Page方法中,会自动注入事件对象
  handler(event) {
    console.log("事件触发了");
    console.log(event);
  },
});

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

cate.wxml

html
<!-- 在小程序中input输入框默认没有边框,需要自行添加样式 -->
<input type="text" bindinput="getInputvalue"/>

cate.js

js
Page({
  // 获取input输入框的内容
  getInputvalue(event) {
    console.log(event.detail.value);
  },
});

2. 绑定并阻止事件冒泡

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

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

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

html
<view class="catch" bindtap="parentHandler">
  <!-- 会触发事件冒泡 -->
  <button bindtap="btnHandler">按钮</button>
  <!-- 阻止事件冒泡 -->
  <button catchtap="btnHandler">按钮</button>
</view>
js
Page({
  // 事件处理程序
  btnHandler (event) {
    console.log('我是子绑定的事件 ~~~')
  },
  parentHandler () {
    console.log('我是父绑定的事件 ~~~')
  }
})

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-自定义属性名="值"

示例:

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

html
<view class="data" bindtap="dataHandler" data-parentId="1" data-parentName="小明">
  <!-- 使用 data-*的方式传递数据 -->
  <!-- <button type="primary" bindtap="dataHandler" data-id="1" data-name="小明">按钮</button> -->
  <button type="primary"  data-id="1" data-name="小明">按钮</button>
</view>

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

js
Page({
  dataHandler(event) {
    // 获取传递的数据
    // 1.event.currentTarget:事件绑定者,指哪个组件绑定了当前处理函数
    console.log(event.currentTarget.dataset);
    // 2.event.target:事件触发者,指哪个组件触发了当前事件处理函数
    console.log(event.target.dataset);
    // 当事件的绑定者和触发者为同一个组件时,通过currentTarget.dataset或target.dataset都可以获取该组件传递的数据
    // 当事件的绑定者和触发者不是同一个组件时,currentTarget.dataset获取的是事件绑定者传递的数据,target.dataset获取的是事件触发者传递的数据
    // 注意:父组件绑定的事件,可以在子组件中触发
  },
})

📌 注意事项

  1. event.target是指事件的触发者,event.currentTarget是指事件的绑定者

    • 当事件的绑定者和触发者为同一个组件时,通过event.currentTarget.datasetevent.target.dataset都可以获取事件传递的参数
    • 当事件的绑定者和触发者不是同一个组件时,event.currentTarget.dataset获取的是事件绑定者传递的参数,event.target.dataset获取的是事件触发者传递的参数
  2. 在父组件上绑定的事件,在子组件也可以触发

  3. 使用 data- 方法传递参数的写法:

    • 多个单词由连字符 - 连接:连字符写法会转换成驼峰写法

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

    • 多个单词使用驼峰写法:大写字符会自动转成小写字符

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

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

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

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

格式:mark:自定义属性名="值"

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

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

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

示例:

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

html
<view class="mark" bind:tap="markHandler" mark:parentId="1" mark:parent-name="李四">
  <!-- 使用mark进行事件传参 格式:mark:自定义属性名="值" -->
  <!-- <button type="primary" bind:tap="markHandler" mark:id="2" mark:name="张三">传递数据mark:</button> -->
  <button type="primary" mark:id="2" mark:name="张三">传递数据mark:</button>
</view>

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

js
// cart.js
Page({
 markHandler(event){
    // 使用 event.mark 获取事件传递的参数(获取事件绑定者和触发者的传递的参数)
    console.log(event.mark)
  },
})

总结:

data-*mark:*传递事件参数的区别:

  1. 使用 data-*传递参数 event.currentTarget.dataset获取的是事件绑定者传递的参数
  2. 使用 data-*传递参数 event.target.dataset获取的是事件触发者传递的参数
  3. 使用 mark:*传递的参数 event.mark 获取的是事件触发者+事件绑定者传递的参数

五、模板语法

1. 声明和绑定数据

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

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

&#123;&#123; &#125;&#125; 内部可以做一些简单的运算,支持如下几种方式:

  1. 算数运算

  2. 三元运算

  3. 逻辑判断

  4. 其他表达式…

定义数据:

js
Page({
  // 微信小程序中页面中所需的数据均来自 data对象
  data: {
    name: "张三",
    id: 1,
    obj: {
      age: 18,
      sex: "男",
    },
    checked: true,
  }
});

使用数据:

➡️ pages/index/index.wxml

html
<!-- 模板语法 -->
<!-- 1.展示数据 -->
<!-- 如果需要展示数据在 wxml 中需要使用双大括号写法将变量进行包裹 -->
<view>{{ name }},今年{{obj.age}}岁,性别{{obj.sex}}</view>
<!-- 2.动态绑定属性值 属性名="{{ 值 }}"-->
<view id="{{ id }}">绑定属性值</view>
<!-- 3.如果属性值是布尔值,也需要使用双大括号进行包裹 -->
<checkbox checked="{{ false }}" />
<checkbox checked="{{ checked }}" />
<!-- 4.算数运算、三元运算、逻辑判断 -->
<view>{{ id + 1 }}</view> <!-- 2 -->
<view>{{ id - 1 }}</view> <!-- 0 -->
<view>{{ id == 1 ? '等于' : '不等于' }}</view> <!-- 等于 -->
<view>{{ id > 1 }}</view><!-- false -->
<!-- 注意:在双括号内部只能写表达式,不能写语句,也不能调用js方法 -->
<!-- <view>{{ if(id == 1){}}}</view> 报错-->
<!-- <view>{{ for(const i = 0; i<=10; i++){i} }}</view> 报错-->
<!-- <view>{{ obj.name.toUpperCase() }}</view> 无效 -->

📌 注意事项:

​ 在 {{ }} 语法中,只能写表达式,不能写语句,注意:不能调用 js 相关的方法(Vue中的 &#123;&#123;&#125;&#125;内可以调用方法)

2. 声明和修改数据

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

用法:

this.setData({key:value})

  • key: data中要更新的属性名
  • value:新值

作用:

  1. 更新数据
  2. 驱动视图更新

示例:

html
// index.wxml
<view>{{ num }}</view>
<button type="primary" size="mini" bindtap="updateNum">num加一</button>
js
// index.js
Page({
  updateNum() {
    // 访问数据 通过this.data访问data中的数据
    console.log(this.data.num);
    // 通过赋值的方式修改数据(可以修改数据,但不能更新视图)
    // this.data.num += 1;
    // console.log(this.data.num);
    // 使用this.setData({key:value}) 修改数据并更新视图
    this.setData({
      // key: 要更新的数据  value:是最新的值
      num: this.data.num + 1,
    });
  },
})

注意:

  1. js 文件中通过 this.data获取 data中的数据

  2. 通过赋值的方式修改数据 this.data.num += 1; (可以修改data中的数据,但不能触发更新视图,必须使用 this.setData()对数据进行响应式更新)

  3. 可以先对data中的数据进行修改,再通过 this.setData方法更新视图

3. setData 修改对象类型数据

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

  1. 定义一个对象

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

    json
    this.setData({
      'userInfo.name': 'Jerry'  // 对象使用.语法时需要使用引号包裹变为字符串
    })
  3. 修改对象中的多个属性

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

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

    js
    const userInfo = {  // 声明一个变量存储修改后的数据
      ...this.data.userInfo,
      name: 'Jerry',
      age: 100
    }
    // 修改对象中的多个属性,并更新视图
    this.setData({
      userInfo
    })
  5. 使用解构赋值(修改对象属性)

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

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

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

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

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

    js
    delete this.data.userInfo.age; // data中的数据被删除但不驱动视图更新
    this.setData({
        userInfo: this.data.userInfo, // 此时data中的数据已被修改
    });
  8. 删除对象中的多个属性(使用解构 + rest(剩余参数))

    js
    const { age, test, ...rest } = this.data.userInfo;
    this.setData({
        userInfo: rest,
    });

📌 注意事项

小程序的数据绑定机制只能监听到 setData 方法中修改的数据(修改数据 + 触发视同更新)

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

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

  1. 定义一个数组

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

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

    js
    // 使用 concat 方法来合并数组
    const newList = this.data.animalList.concat(['Tyke']) // 返回新数组
    
    // 使用 setData 进行赋值,触发视图更新
    this.setData({
      animalList: newList
    })
  3. 使用数组的 push 方法新增属性

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

    js
    // 使用数组的 push 方法来添加元素
    this.data.animalList.push('Tyke') // 修改了data中的数据但未触发视图的更新
    
    // 使用 setData 进行赋值,触发视图更新
    this.setData({
      animalList: this.data.animalList
    })
  4. 使用 ES6 的展开运算符

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

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

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

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

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

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

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

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

html
<input value="{{ num }}" />
  • 使用 this.setData 来更新 num ,输入框的中的值会被更新
  • 但如果用户修改了输入框里的值,不会改变 data 中的 num

双向数据绑定:

语法:在属性前添加model: 前缀即可,例如:

html
<input model:value="{{ value }}" />
  • 使用 this.setData 来更新 numnum 和输入框的中显示的值都会被更新为值

  • 输入框的值被改变了, data 的数据也会随着改变,同时页面会重新渲染

📌 注意事项:

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

  1. 属性值只能是一个单字段的绑定,错误用法:<input model:value="值为 &#123;&#123;value&#125;&#125;" />

  2. 属性值不支持对象和数组 ,错误用法:<input model:value="&#123;&#123; a.b &#125;&#125;" />

示例:

html
<!-- 单向数据绑定:data中数据改变能影响页面,页面中的数据变化不会影响到data中的数据 -->
<input type="text" value="{{ name }}" />
<!-- 双向数据绑定: 页面中的数据改变,data中的数据也会随之改变-->
<input type="text" model:value="{{ name }}" />
<checkbox model:checked="{{ checked }}" />是否同意该协议
<!-- 注意事项1:属性值只能是一个单字段的绑定 -->
<input type="text" model:value="值为{{ name }}" /> <!-- 无效 -->
<!-- 注意事项2:属性值不支持对象和数组 -->
<input type="text" model:value="{{ obj.age }}" /> <!-- 无效 -->

6. 列表渲染-基本使用

基本使用

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

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

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

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

在使用 wx:for 对数组进行遍历的时候,要加上 wx:key 属性,如不提供 wx:key,会报一个 warning。(提高组件的复用)

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

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

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

📌 注意事项

  1. 在使用 wx:for 对数组进行遍历的时候,建议加上 wx:key 属性,否则控制台会报警告
  2. 在给 wx:key添加属性值时,不需要使用大括号语法,直接使用遍历的 array 中的 item.id 属性(可以省略 item 直接写 wx:key="id"

7. 列表渲染-使用进阶

7.1 修改默认下标名和变量名:

如果需要对默认的下标名和变量名进行修改(wx:for嵌套wx:for

可以使用 wx:for-itemwx:for-index (提升语义化)

  1. 使用 wx:for-item 可以指定数组/对象当前元素/值的变量名
  2. 使用 wx:for-index 可以指定数组/对象当前下标/键的变量名
html
<!-- 数组 index为下标 value:数组每一项 -->
<!-- wx:for-item="fruit":修改item为fruit -->
<!-- wx:for-index="i":修改index为i -->
<view wx:for="{{ fruitList }}" wx:for-item="fruit" wx:for-index="i" wx:key="id">{{ fruit.name }}</view>
<!-- 对象 index为对象的键 value:值 -->
<!-- wx:for-item="value":修改item为value -->
<!-- wx:for-index="key":修改index为key -->
<view wx:for="{{ obj }}" wx:for-index="key" wx:for-item="value" wx:key="key">{{ key }}-{{ value }}</view>
7.2 渲染多节点结构块:

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

html
<block wx:for="{{ fruitList }}" wx:for-item="fruit" wx:key="id">
  <view>水果名字:{{ fruit.name }}</view>
  <view>价格:{{ fruit.price }}</view>
</block>

注意: <block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性 ,相当于 template标签,不会破坏页面结构

8. 条件渲染

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

  1. 使用 wx:ifwx:elifwx:else 属性组

    当属性值为 true时显示结构,false 隐藏结构

html
<!-- wx:if wx:elif wx:else -->
<view wx:if="{{num == 1}}">HTML</view>
<view wx:elif="{{num == 2}}">CSS</view>
<view wx:else>JS</view>
<button type="primary" size="mini" bind:tap="addNum"> num+1 </button>
<!-- 注意:wx:elif 和 wx:else 不能单独使用 必须结合 wx:if使用 -->
  1. 使用 hidden 属性

    hidden属性的属性值 如果为true隐藏结构,false展示结构

html
<view hidden="{{ num == 5 }}">当num为5时隐藏</view>
<button type="primary" size="mini" bind:tap="addNum"> num+1 </button>

wx:ifhidden 二者的区别:

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

注意:

  1. wx:elifwx:else 不能单独使用 必须结合 wx:if 使用 ,wx:if可以单独使用(卸载/挂载组件)
  2. 使用了 wx:if 属性组的组件不能被打断,组件必须连贯才可以

六、生命周期

1. 小程序运行机制

冷启动与热启动:

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

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

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

前台与后台状态:

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

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

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

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

挂起:

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

销毁:

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

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

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

2. 小程序更新机制

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

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

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

1. 启动时同步更新

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

2. 启动时异步更新

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

在启动时异步更新的情况下,如果开发者希望立刻进行版本更新,可以使用 wx.getUpdateManager API (获取全局唯一的版本更新管理器,用于管理小程序更新。返回值 UpdateManager:更新管理器对象)进行处理。在有新版本时提示用户重启小程序更新新版本。

示例:模拟小程序更新,使用wx.getUpdateManager API 提示用户重启更新

js
// app.js
App({
  // onLaunch 是小程序的钩子函数,当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
  // 当小程序冷启动时,会自动向微信后台请求新版本的信息,如果有新版本,会立即进行下载
  onLaunch() {
    // 使用 wx.updateManager 方法监听下载的状态
    const updateManager = wx.getUpdateManager();
    updateManager.onCheckForUpdate(function (res) {
 	 // 请求完新版本信息的回调
  	console.log(res.hasUpdate)
	})
    // 当下载完成新版本以后,会触发 onUpdateReady 回调函数
    updateManager.onUpdateReady(function () {
      // 在回调中给用户提示
      wx.showModal({
        title: "更新提示",
        content: "新版本已经准备好,是否重启应用?",
        success(res) {
          if (res.confirm) {
            // 当用户点击确定 强制当前小程序使用新版本并且会重启当前小程序
            updateManager.applyUpdate();
          }
        },
      });
    });
   updateManager.onUpdateFailed(function () {
 	 // 新版本下载失败
	})
  },
});

使用开发者工具模拟更新

image-20240614200549206image-20240614200634928image-20240614200654512

3. 生命周期介绍

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

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

在打开一个小程序应用的时候都需要经历一系列的初始化步骤,比如页面是否加载完成、页面是否初次渲染完成等等。在此过程中,小程序也会运行被称为生命周期钩子的函数,这些函数由小程序框架本身提供,被称为生命周期函数,生命周期函数会按照顺序依次自动触发调用。帮助程序员在特定的时机执行特定的操作,辅助程序员完成一些比较复杂的逻辑

小程序的生命周期分类三类:应用级别页面级别组件级别 3 种类型

4. 应用级别生命周期

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

应用生命周期函数需要 在 app.js 文件的 App() 方法中定义

当小程序启动成功之后,我们要获取小程序的一些信息,就可以在小程序启动成功时的钩子函数中写代码获取我们想要的信息

应用生命周期钩子:

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

📌 注意事项:

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

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

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

示例:

js
App({
  // 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
  onLaunch: function () {
    // 当进行冷启动时才会触发 onLaunch函数,如果是热启动则不会触发
    console.log("onLaunch函数执行了,小程序初始化完成");
  },
  // 当小程序启动,或从后台进入前台显示,会触发 onShow
  onShow: function (options) {
    console.log("onShow函数执行了,小程序进入前台显示");
  },
  // 当小程序从前台进入后台,会触发 onHide
  onHide: function () {
    console.log("onHide函数执行了,小程序进入后台");
  },
  // 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
  onError: function (msg) {},
});

5. 页面级别生命周期

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

当某个页面加载完毕之后,需要发请求获取当前页面所需的数据,就可以在对应的页面加载完成后的钩子函数中执行发送请求的代码。

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

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

示例:

js
// pages/lifeCycle/lifeCycle.js
Page({
  // 页面的初始数据
  data: {},
  // 生命周期函数--监听页面加载(只会在初次加载时触发一次)
  onLoad(options) {
    console.log("onLoad函数执行了,页面开始加载");
  },
  // 生命周期函数--监听页面显示(页面显示/后台切入时都会触发)
  onShow() {
    console.log("onShow函数执行了,前台页面展示");
  },
  // 生命周期函数--监听页面初次渲染完成(只会在初次渲染时触发一次)
  onReady() {
    console.log("onReady函数执行了,页面渲染完毕");
  },
  // 生命周期函数--监听页面隐藏(页面隐藏/切入后台都会触发)
  onHide() {
    console.log("onHide函数执行了,前台页面隐藏");
  },
  // 生命周期函数--监听页面卸载(只在页面卸载时触发一次)
  onUnload() {
    console.log("onUnload函数执行了,页面销毁");
  }
});
html
// pages/lifeCycle/lifeCycle.wxml
<navigator url="/pages/list/list" open-type="navigate">跳转不销毁当前页面</navigator>
<navigator url="/pages/list/list" open-type="redirect">跳转销毁当前页面</navigator>

注意:

  1. 使用navigator组件 open-type="redirect" 页面跳转时会销毁当前页面,触发 onUnload
  2. 使用 navigator组件 open-type="navigate" 页面跳转时只隐藏当前页面,触发 onHide

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

  1. tabBar 页面之间相互切换,页面不会被销毁(只是被隐藏了会触发 onHide

  2. 在非 tabBar 页面点击左上角返回箭头,返回上一个页面,会销毁当前页面(触发 onUnload

七、小程序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 需要开发者自行封装。

image-20241014095932955image-20241014095959541

小程序 API 介绍 微信小程序 API 文档

2. 网络请求

在微信小程序中,如果需要发请求获取服务器数据需要使用:wx.request(),语法如下:

js
// 如果需要发起网络请求,需要使用 wx.request API
wx.request({
	// url:接口地址
	url: "url",
	// 请求方式
	method: 'GET|POST|PUT|DELETE',
	// 请求参数
	data: {},
	// 请求头
	header: {},
	// API 调用成功后,执行的回调
	success: (res) => {
	  console.log(res);
	},
	// API 调用失败后,执行的回调
	fail: (err) => {
	  console.log(err);
	},
	// 成功或失败都会调用
	complete: (res) => {
 	 console.log(res);
	},
});

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

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

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

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

📌 注意事项:

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

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

示例:

js
Page({
  // 获取数据
  getData() {
    // 如果需要发起网络请求,需要使用 wx.request API
    wx.request({
      // url:接口地址
      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);
      },
      // 成功或失败都会调用
      complete: (res) => {
        console.log(res);
      },
    });
  },
});

Tips:wx.request返回结果会将服务器返回的数据进行二次包装,将服务器返回的数据放在 res.data字段中与 axios类似image-20241014101336622

3. 界面交互

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

3.1 loading 提示框

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

  1. wx.showLoading 显示加载提示框(支持Promise风格调用)
  2. wx.hideLoading 隐藏加载提示框(支持Promise风格调用)

语法如下:

js
wx.showLoading({
  title: '提示内容', // 提示的内容,提示的内容不会自动换行,如果提示的内容较多,多出的内容会被隐藏 (必填)
  mask: true, // 是否显示透明蒙层,防止触摸穿透(即在加载时不允许用户其它操作),默认值为false
  success() {}, // API接口调用成功的回调函数
  fail() {} // API接口调用失败的回调函数
})
js
wx.hideLoading()

官方文档:

wx.showLoading 官方文档 wx.hideLoading 官方文档

示例:

js
Page({
  // 获取数据
  getData() {
    // 用户点击按钮后开启显示加载提示框
+    wx.showLoading({
+      // title:用来显示提示内容,提示的内容不会自动换行,如果提示的内容较多,多出的内容会被隐藏
+      title: "加载中...",
+      // mask:是否显示透明蒙层,防止触摸穿透(即在加载时不允许用户操作其他),默认值为false
+      mask: true,
+    });
    // 如果需要发起网络请求,需要使用 wx.request API
    wx.request({
      // url:接口地址
      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);
      },
      // 成功或失败都会调用
      complete: (res) => {
        console.log(res);
+        // 关闭loading提示框
+        wx.hideLoading()
      },
    });
  },
});

3.2 消息提示框&模态对话框

消息提示框:

wx.showToast() (支持Promise风格调用),根据用户的某些操作来告知操作的结果,如退出成功给用户提示,提示删除成功等,语法如下:

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

模态对话框:wx.showModal()(支持Promise风格调用) ,通常用于向用户询问是否执行一些操作,例如:点击退出登录,显示模态对话框,询问用户是否真的需要退出等等

js
wx.showModal({
  title: '提示', // 提示的标题
  content: '您确定执行该操作吗?', // 提示的内容
  confirmColor: '#f3514f', // 确定按钮字体颜色
  // 接口调用结束的回调函数(调用成功、失败都会执行)
  complete: (res) => {
    if (res.cancel) {
        console.log("用户点击取消!");
    }
    if (res.confirm) {
        console.log("用户点击确定!");
    }, 
})

wx.showModel是一个异步方法可以使用 Promise风格

wx.showToast 官方文档 wx.showModal 官方文档

示例:

js
Page({
  // 删除数据
  async deleteData() {
    // 点击按钮后显示模态对话框
    const res = await wx.showModal({
      title: "提示", // 提示的标题
      content: "是否删除商品数据?", // 提示的内容
      confirmColor: "skyblue", // 确定按钮字体颜色
    });
    console.log(res);
    // 点击取消{errMsg: "showModal:ok", cancel: true, confirm: false}
    // 点击确定{errMsg: "showModal:ok", cancel: false, confirm: true, content: null}
    const { confirm } = res;
    if (confirm) {
      // 用户点击了确定,清空数据
      this.setData({
        list: [],
      });
      // 展示消息提示框
      wx.showToast({
        title: "删除成功", // 提示内容
        mask: true, // 是否显示透明蒙层,防止触摸穿透
        icon: "success", // 提示图标 error、loading、none、success
        duration: 2000, // 提示框显示的时间 单位ms
      });
    } else {
      // 用户点击了取消
      wx.showToast({
        title: "取消删除",
        mask: true,
        icon: "error",
        duration: 2000,
      });
    }
  },
});

4. 本地存储

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

同步 API异步 API(支持Promise风格)作用
wx.setStorageSyncwx.setStorage将数据存储在本地缓存中指定的 key 中
wx.getStorageSyncwx.getStorage从本地缓存中同步获取指定 key 的内容
wx.removeStorageSyncwx.removeStorage从本地缓存中移除指定 key
wx.clearStorageSyncwx.clearStorage清理本地数据缓存
  • 异步方式的 API,在调用的时候都需要传入对象类型的参数

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

📌 注意事项

  1. 每次只可以 存储/读取 一条数据

  2. 小程序中存储的数据可以为任意类型,无需转换为字符串 (浏览器只可以存储字符串类型)

    对象类型的数据,可以直接进行存储,无需使用 JSON.stringify 转换

    对象类型的数据存的时候没有使用转换,因此获取的时候也不需要使用 JSON.parse 转换

示例:

html
<view class="localStorage">
  <button type="primary" plain size="mini" bind:tap="setStorage">存储</button>
  <button type="primary" plain size="mini" bind:tap="getStorage">获取</button>
  <button type="warn" plain size="mini" bind:tap="removeStorage">删除</button>
  <button type="warn" plain size="mini" bind:tap="clearStorage">清空</button>
</view>
js
Page({
  // 数据
  data: {
    list: [1, 2, 3],
    obj: {
      name: "张三",
      age: 18,
    },
  },
 // 将数据存储到本地
  setStorage() {
    // 同步存储:wx.setStorageSync("key", value);
    /* wx.setStorageSync("list", this.data.list);
    wx.setStorageSync("obj", this.data.obj); */
    // 异步存储:wx.setStorage({key:"key",data:data})
    wx.setStorage({
      key: "list",
      data: this.data.list,
    }),
      wx.setStorage({
        key: "obj",
        data: this.data.obj,
      });
    /* 注意:
          每次只可以存储一条数据
          小程序中存储的数据可以为任意类型,无需转换为字符串 */
  },
  // 获取本地存储的数据
  async getStorage() {
    // 同步获取:wx.getStorageSync("key");
    /* const obj = wx.getStorageSync("obj");
    console.log(obj); */
    // 异步获取:wx.getStorage({ key: "key" });
    const obj = await wx.getStorage({ key: "obj" });
    console.log(obj.data);
  },
  // 删除本地存储的数据
  removeStorage() {
    // 同步删除:wx.removeStorageSync('key')
    wx.removeStorageSync("obj");
    // 异步删除:wx.removeStorage({key: 'key',})
    wx.removeStorage({
      key: "obj",
    });
  },
  // 清空本地存储的全部数据
  clearStorage() {
    // 同步清空:wx.clearStorageSync()
    wx.clearStorageSync();
    // 异步清空:wx.clearStorage();
    wx.clearStorage();
  },

5. 路由与通信

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

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

示例:

html
// api.wxml
<view class="router">
  <button type="primary" size="mini" bind:tap="navigateTo">navigateTo跳转到list页面并传参</button>
  <button type="primary" size="mini" bind:tap="redirectTo">redirectTo跳转到list页面并传参</button>
  <button type="primary" size="mini" bind:tap="switchTab">switchTab跳转到样式与组件TabBar页面</button>
  <button type="primary" size="mini" bind:tap="reLaunch">reLaunch跳转到样式与组件页面并传参</button>
</view>
js
// api.js
Page({
// wx.navigateTo({url: "url",}); 保留当前页面跳转到非tabBar页面
  navigateTo() {
    wx.navigateTo({
      url: "/pages/list/list?id=01&sex=1", // 路由跳转并传参
    });
  },
  // wx.redirectTo({url: 'url',}); 关闭当前页面跳转到非tabBar页面
  redirectTo() {
    wx.redirectTo({
      url: "/pages/list/list?id=02&sex=0", // 路由跳转并传参
    });
  },
  // wx.switchTab({url: 'url',}); 只能跳转到tabBar页面
  switchTab() {
    wx.switchTab({
      url: "/pages/index/index",
      // url: "/pages/index/index?id=1", // 注意:不支持携带query参数
    });
  },
  // wx.reLaunch({url: 'url',}); 关闭所有页面跳转到指定页面(任意页面)
  reLaunch() {
    wx.reLaunch({
      url: "/pages/index/index?id=03&sex=1",
      // url: "/pages/list/list?id=03&sex=1",
    });
  },
})
html
// list.wxml
<button type="primary" plain bind:tap="switchTab">switchTab返回tabBar页面</button>
<button type="primary" plain bind:tap="reLaunch">reLaunch返回tabBar页面</button>
<button type="primary" plain bind:tap="navigateBack">navigateBack返回上一个页面</button>
js
// list.js
Page({
  // wx.navigateBack(); 关闭/销毁当前页面返回上一个页面或返回多级页面,默认返回上一级
  navigateBack() {
    // 返回上一级
    wx.navigateBack();
    // 返回多级
    wx.navigateBack({
      delta: 2,
    });
  },
  switchTab() {
    wx.switchTab({
      url: "/pages/api/api",
    });
  },
  reLaunch() {
    wx.reLaunch({
      url: "/pages/api/api",
    });
  },
  // 获取路径传递的参数
  onLoad(options) {
    console.log("跳转到list页面路由传递的参数", options);
  },
});

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

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

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

  1. app.json 或者 page.json 中配置上拉距离页面底部距离为多少时触发 onReachBottom事件:

    json
    "onReachBottomDistance":100;  // 默认50px
  2. page.js 中定义 onReachBottom 事件监听用户上拉加载事件

示例:模拟用户上拉加载更多内容

第一步:

设置上拉加载的距离底部距离触发onReachBottom事件

json
// api.json 
{
  "usingComponents": {},
+  "onReachBottomDistance": 100 // 设置上拉加载的距离底部为100px时触发onReachBottom事件
}

第二步:

页面布局 api.wxml

html
<!-- 展示数据 -->
<view class="dropUp" wx:for="{{ list }}" wx:key="index">{{ item }}</view>

第三步:

页面样式 api.scss

scss
.dropUp {
  height: 400rpx;
  display: flex;
  align-items: center; // 垂直居中
  justify-content: center; // 水平居中
  &:nth-child(2n + 1) {
    background-color: skyblue;
  }
  &:nth-child(2n) {
    background-color: rgb(233, 170, 53);
  }
}

第四步:事件监听当用户上拉加载时增加数据 api.js

js
Page({
  // 数据
  data: {
    list: [1, 2, 3],
  },
  // 页面上拉触底事件的处理函数
  onReachBottom() {
    // 实现上拉加载效果
    wx.showLoading({
      title: "加载中",
      mask: true,
    });
    // 当用户上拉加载时,需要对数字进行累加,每次累加 3 个数字
    // 目前:[1,2,3]  上拉后:[1,2,3,4,5,6] 再次上拉:[1,2,3,4,5,6,7,8,9]
    // 获取目前数组最后一项n 然后进行n+1,n+2,n+3
    setTimeout(() => { // 模拟发请求获取数据
      // 获取数组最后一项
      let lastNum = this.data.list[this.data.list.length - 1];
      // 定义需要追加的元素
      const addLsit = [lastNum + 1, lastNum + 2, lastNum + 3];
      // 数组合并,并驱动页面更新
      this.setData({
        list: this.data.list.concat(addLsit),
      });
      // 隐藏提示框
      wx.hideLoading();
    }, 1500);
  },
});

7. 事件监听-下拉刷新

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

小程序中实现下拉刷新的方式:

  1. app.json 或者 page.json 中开启允许下拉,同时可以配置 窗口、loading 样式等
  2. page.js 中定义 onPullDownRefresh 事件监听用户下拉刷新

示例:

api.json

json
{
  "usingComponents": {},
  "onReachBottomDistance": 100,
+  "enablePullDownRefresh": true, // 设置允许下拉
+  "backgroundColor": "#efefef", // 下拉窗口背景色
+  "backgroundTextStyle": "light" // 下拉窗口加载样式
}

api.js

js
Page({
  data: {
    list: [1, 2, 3]
  },
  // 页面下拉刷新触发的处理函数
  onPullDownRefresh() {
    // 模拟下拉刷新重新获取用户数据
    this.setData({
      list: [1, 2, 3],
    });
    // 注意:在下拉刷新以后loading效果可能不会自动消失,需要手动关闭
    if (this.data.list.length === 3) {
      // 获取到数据后关闭下拉加载框
      wx.stopPullDownRefresh();
    }
  },
})

注意:

​ 在下拉刷新以后loading效果可能不会自动消失,需要手动关闭

​ 使用:wx.stopPullDownRefresh();关闭下拉刷新框

8. 增强 scroll-view

8.1 scroll-view 上拉加载

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

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

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

示例:

html
<scroll-view
  class="scroll-y"
  scroll-y  // 开启纵向滚动
  lower-threshold="100" // 距离底部100px时触发scrolltolower事件
  bindscrolltolower="getMore" // 滚动到底部时触发的事件
  enable-back-to-top // 开启双击标题栏时回到顶部
>
  <view wx:for="{{ arr }}" wx:key="*this">{{ item }}</view>
</scroll-view>
js
// index.js
Page({
  data: {
    arr: [1, 2, 3]
  },
  // 上拉加载更多
  getMore() {
    // 显示加载框
    wx.showLoading({
      title: '数据正在加载中...',
      mask: true
    })
    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 用来关闭下拉效果)

注意:刷新框不会自动回弹,需要通过设置 refresher-triggered属性为 false手动关闭

示例:

html
<scroll-view
  class="scroll-y"
  scroll-y
  lower-threshold="100" // 上拉触发scrolltolower距离底部距离
  bindscrolltolower="getMore" // 上拉加载更多的事件
  enable-back-to-top  // 点击标题栏可以快速回到顶部
  refresher-enabled    // 开启自定义刷新
  refresher-default-style="black"  // 刷新加载图标的颜色
  refresher-background="#f7f7f8"  // 刷新框的背景色
  bindrefresherrefresh="onrefresh"  // 自定义下拉刷新触发的事件
  refresher-triggered="{{ triggered }}" // 用于关闭下拉刷新效果
>
  <view wx:for arr }}" wx:key="*this">{{ item }}</view>
</scroll-view>
js
// 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: 'succes'
      })
      // 数据返回,关闭 scroll-view 下拉刷新效果
      this.setData({
        triggered: false
      })
    }, 1000)
  }
})

8.3 增强 scroll-view 完整代码

html
<scroll-view 
  class="scroll-y" 
  scroll-y 
  lower-threshold="100"  // 默认值为50
  bindscrolltolower="getMore"
  enable-back-to-top
  refresher-enabled
  refresher-default-style="black" // 默认值为black
  refresher-background="#eee"
  bindrefresherrefresh="refreshHandler"
  refresher-triggered="{{ isTriggered }}"
>
<!-- lower-threshold: 触发scrolltolower事件距离底部的距离
     bindscrolltolower:绑定上拉加载事件
     enable-back-to-top:点击标题栏可以返回顶部
     refresher-enabled:允许下拉刷新
     refresher-default-style:设置下拉刷新loading默认样式
     refresher-background:设置下拉刷新的背景色
     bindrefresherrefresh="refreshHandler":下拉刷新触发的事件
     refresher-triggered:控制下拉刷新的状态,是否回弹-->
  <view wx:for="{{list}}" :wx:key="index">{{ item }}</view>
</scroll-view>
scss
/* pages/index/index.wxss */
.scroll-y {
  height: 100vh;
  background-color: #efefef;
  view {
    height: 800rpx;
    display: flex;
    align-items: center; // 垂直居中
    justify-content: center; // 水平居中
    &:nth-child(odd) {
      background-color: skyblue;
    }
    &:nth-child(even){
      background-color: pink;
    }
  }
}
js
Page({
  data: {
    list: [1, 2, 3],
    isTriggered: false
  },
// scroll-view 上拉加载更多的事件处理函数
  getMore() {
    // 显示加载效果
    wx.showLoading({
      title: "加载中",
      mask: true,
    });
    // 模拟获取数据
    setTimeout(() => {
      let lastNum = this.data.list[this.data.list.length - 1];
      const addList = [lastNum + 1, lastNum + 2, lastNum + 3];
      this.setData({
        list: this.data.list.concat(addList),
      });
      // 关闭加载效果
      wx.hideLoading();
    }, 1500);
  },
  // scroll-view 下拉刷新的事件处理函数
  refreshHandler() {
    wx.showToast({
      title: "下拉刷新",
      icon: "success",
    });
    this.setData({
      list: [1, 2, 3],
      isTriggered: false, // 使下拉刷新回弹
    });
  },
})