微信小程序开发教程

尚硅谷微信小程序开发教程,2024最新版微信小程序项目实战!

一、小程序基础

1. 初始小程序

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

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

使用 App 过程:王者荣耀 → 应用商店 → 下载 + 安装 → 5 v 5 → 卸载…

使用小程序过程:羊了个羊 → 扫、搜 → 直接使用 → 关掉…

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

2. 注册微信小程序

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

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

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

  1. 未被微信公众平台注册
  2. 未被微信开发平台注册
  3. 未被个人微信号绑定过,如果被绑定了需要解绑 或 使用其他邮箱

注:因流程较多,详细步骤可参考笔记https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/getstart.html#%E7%94%B3%E8%AF%B7%E8%B4%A6%E5%8F%B7

3. 完善小程序账号信息

在完成小程序账号的注册后,需要打开微信公众平台对小程序账号进行一些设置,这是因为小程序在 上线阶段 - 提交审核 的时候,小程序账号信息是必填项,因此注册小程序以后,需要补充小程序的基本信息,如名称、图标、类目等。同时需要进行小程序备案和微信认证

4. 项目成员和体验成员

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

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

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

5. 小程序开发者ID

微信小程序账号只要开发者满足开发资质都可以进行注册,并且会获得对应的 开发者 ID;一个完整的开发者 ID 由 小程序ID(AppID)小程序密钥(AppSecret) 组成。

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

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

6. 微信开发者工具下载

微信开发者工具官网:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

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

微信开发者工具包含三个版本:

  1. 稳定版:稳定性高,开发中一般推荐大家使用稳定版本
  2. 预发布版:稳定性尚可,一般包含新的、大的特性,通过了内部测试
  3. 开发版:稳定性差,主要用于尽快修复缺陷和敏捷上线小的特性

注意事项:微信开发者工具必须联网使用

7. 创建小程序项目

  1. 打开微信开发者工具,左侧选择小程序,点击 + 号即可新建项目
  2. 在弹出的新页面,填写项目信息

8. 文件和目录结构介绍

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

主体文件 又称 全局文件,能够作用于整个小程序,影响到小程序的每个页面,主体文件必须放到项目的根目录下

主体文件由三部分组成:

  1. app.js:小程序入口文件
  2. app.json:小程序的全局配置文件
  3. app.wxss:小程序的全局样式

注意事项:主体文件的名字必须是app,app.js 和 app.json 文件是必须的

页面文件 是每个页面所需的文件,小程序页面文件都存放在pages目录下,一个页面一个文件夹


每个页面通常由四个文件组成,每个文件只对当前页面有效:

  1. .js:页面逻辑
  2. .wxml:页面结构
  3. .wxss:页面样式
  4. .json:小页面配置

注意事项:.js 文件和 .wxml文件是必须的

9. 如何新建页面 以及 调试基础库

1)新建页面
  1. 方式一
    在项目目录中找到 pages 文件夹,点击右键选中新建文件夹,输入文件名,再次选中新建的文件名,右键选择新建Page,输入page名称(名字与文件夹名一致即可,不需要后缀名),微信开发者工具会自动生成相应的.js.wxml.wxss.json 等文件。
  2. 方式二
    app.json 中新增对应的路由并保存,对应的文件及文件夹会自动生成。
2) 调试基础库

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

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

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

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

10.如何调试小程序

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


注意事项:微信开发者工具缓存非常重要;如果发现代码和预期不一样,先点击编译;编译后还是没有达到预期的效果,就需要清除缓存,甚至重启项目才可以。

二、配置文件

1. 配置文件介绍

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

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

  1. app.json:小程序全局配置文件,用于配置小程序的一些全局属性和页面路由。
  2. 页面.json:小程序页面配置文件,也称为局部配置文件,用于配置当前页面的窗口样式、页面标题等。
  3. project.config.json:小程序项目的配置文件,用于保存项目的一些配置信息和开发者的个人设置。
  4. sitemap.json:配置小程序及其页面是否允许被微信索引(即被微信搜索),提高小程序在搜索引擎搜索到的概率。
  5. project.private.config.json:可选,用于保存开发者自定义的配置信息,例如小程序的第三方 API 密钥、版本号等敏感信息,这些信息不应该被公开或共享,因此需要保存在本地进行保护。同时,由于这个文件不会被上传到服务器或共享给其他开发者,因此不同开发者之间也可以使用不同的 private.config.json 文件,并且可以独立配置;由于 project.private.config.json 包含敏感信息,一般不会被提交到代码仓库中,而是由开发人员在本地进行管理。因此,在拉取代码时,project.private.config.json 文件可能会出现冲突,因为不同开发人员的私有配置信息可能不同。另外,由于project.private.config.json 文件在本地管理,可能会被误删除或修改,也会导致冲突的出现。开发人员可以在项目中加入一些约定,比如将project.private.config.json 文件放在一个独立的目录下,并在.gitignore文件中将该目录排除在代码仓库之外。这样可以更好地管理私有配置信息,避免冲突的出现。

原文链接:https://blog.csdn.net/snowball_li/article/details/134553156

2. 全局配置 - pages配置

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


在配置 pages字段时,有以下注意事项:

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

3. 全局配置 - window 配置

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


官方文档:window 配置

4. 全局配置 - tabbar 配置

tabbar 字段:定义小程序顶部、底部 tab 栏,用以实现页面之间的快速切换,可以通过 tabBar配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。

注意事项:tab按数组的顺序排序,list 配置最少2个、最多5个tab

官方文档:tabbar 配置

5. 页面配置

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

需要注意的是:页面配置文件的属性和全局配置文件中的 window 属性几乎一致的,只不过这里不需要额外指定 window 字段,因此如果出现相同的配置项,页面中配置项 会覆盖全局配置文件中相同的配置项

官方文档:页面配置

6. 项目配置文件和配置sass

在创建项目的时候,每个项目的根目录生成两个 config.json 文件,用于保存开发者在工具上做的个性化配置,例如和编译有关的配置。

当重新安装微信开发者工具或者换电脑工作时,只要载入同一个项目的代码包,开发者工具就会自动恢复到当时开发项目时的个性化配置。


项目根目录中 project.config.jsonproject.private.config.json 文件都可以对项目进行配置

project.config.json项目配置文件,常用来进行配置公共的配置

project.private.config.json项目私有配置,常用来配置个人的配置

注意事项:

  1. project.private.config.json 写到 .gitignore 避免版本管理的冲突
  2. **与最终编译结果有关的设置 必须设置到 **project.config.json

官方文档:项目配置项文件

7. sitemap.json 文件

sitemap.json 文件:配置小程序及其页面是否允许被微信索引,提高小程序在微信内部被用户搜索到的概率

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

注意事项:

  1. 注:没有 sitemap.json 则默认所有页面都能被索引
  2. { "action": "allow", "page": "*" } 是优先级最低的默认规则,未显示指明 “disallow” 的都默认被索引

三、样式

1. 小程序的样式和组件介绍

在开发 Web 网站的时候:页面的结构由 HTML 进行编写,例如:经常会用到 div、p、span、img、a 等标签

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

但是在小程序中不能使用 HTML 标签,也没有 DOM 和 BOM,CSS 也仅仅支持部分选择器。

小程序提供了 WXML 进行页面结构编写,同时提供了 WXSS 进行页面的样式编写。

WXML 提供了 view、text、image、navigator 等标签来构建页面结构,只不过在小程序中将标签称为 组件

WXSS 对 CSS 扩充和修改,新增了尺寸单位 rpx、提供了全局的样式和局部样式,另外需要注意的是 WXSS 仅支持部分 CSS 选择器

官方文档:选择器

2. 尺寸单位 rpx

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

rpx:是小程序新增的自适应单位,它可以根据不同设备的屏幕宽度进行自适应缩放

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

开发建议:

  1. 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准,iPhone6 的设计稿一般是 750px
  2. 如果 iPhone6 作为视觉稿的标准量取多少 px,直接写多少 rpx 即可,开发起来更方便,也能够适配屏幕的宽度

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

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

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

3. 全局样式和局部样式

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

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

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

四、组件

1. 组件案例演示

小程序常用的组件:

  1. view 组件
  2. swiper 和 swiper-item 组件
  3. image 组件
  4. text 组件
  5. navigator 组件
  6. scroll-view 组件
  7. 字体图标

使用小程序常用的组件实现项目首页的效果图

1.1 轮播图区域绘制

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

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

swiper:滑块视图容器,其中只能放置swiper-item 组件

swiper-item:只可放置在 swiper 组件中,宽高自动设置为100%,代表 swiper 中的每一项

官方文档: swiper 和 swiper-item

<!-- 轮播区域 -->
<view class="swiper"><swiperautoplaycircularindicator-dotsinterval="2000"   indicator-color="#fff"indicator-active-color="#f3514f"><swiper-item>1</swiper-item><swiper-item>2</swiper-item><swiper-item>3</swiper-item></swiper>
</view>
// 轮播图区域样式
.swiper {swiper {height: 360rpx;background-color: skyblue;swiper-item {/*& 在 Sass 中代表的是父选择器,引用的意思相当于:swiper-item:first-child {}*/&:first-child {background-color: lightsalmon;}&:last-child {background-color: lightseagreen;}}}
}
1.2 轮播图图片添加

在小程序中,如果需要渲染图片,需要使用image 组件,常用的属性有4个:

  1. src 属性:图片资源地址
  2. mode:图片裁剪、缩放的模式
  3. show-menu-by-longpress:长按图片显示菜单
  4. lazy-load:图片懒加载

注意事项:

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

官方文档:image 组件

<!-- 轮播区域 -->
<view class="swiper"><swiperautoplaycircularindicator-dotsinterval="2000"   indicator-color="#fff"indicator-active-color="#f3514f"><!-- src:图片的资源地址mode:图片的裁剪和缩放模式show-menu-by-longpress:长按展示菜单,菜单中有转发给好友、收藏、保存等功能lazy-load:图片懒加载功能,在滑动到一定的距离(上下三屏)以后展示图片--><swiper-item><image src="../../assets/banner/banner-1.png" mode="aspectFit" show-menu-by-longpress lazy-load /></swiper-item><swiper-item><image src="../../assets/banner/banner-2.png" mode="aspectFit" show-menu-by-longpress lazy-load /></swiper-item><swiper-item><image src="../../assets/banner/banner-3.png" mode="aspectFit" show-menu-by-longpress lazy-load /></swiper-item></swiper>
</view>
// 轮播图区域样式
.swiper {swiper {height: 360rpx;swiper-item {image{width: 100%;height: 100%;}}}
}
1.3 绘制公司信息区域

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

  1. user-select:文本是否可选,用于长按选择文本
  2. space:显示连续空格

注意事项:

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

官方文档:text 组件

<!-- 公司信息 -->
<view class="info"><!-- text 不可嵌套其他组件user-select:长按以后选中文本 boolean 文本是否可选,该属性会使文本节点显示为 inline-block	space:显示连续空格 可选值:ensp	中文字符空格一半大小 emsp	中文字符空格大小 nbsp	根据字体设置的空格大小--><text space="ensp">同城配送</text><text space="ensp">行业龙头</text><text space="ensp">半小时送达</text><text space="ensp">100% 好评</text>
</view>
page {height: 100vh;background-color: #efefef !important;padding: 16rpx;box-sizing: border-box;display: flex;flex-direction: column;> view {&:nth-child(n+2) {margin-top: 16rpx;}}
}// 轮播图区域样式
.swiper {border-radius: 10rpx;overflow: hidden;swiper {height: 360rpx;swiper-item {image{width: 100%;height: 100%;}}}
}.info {display: flex;justify-content: space-between;background-color: #fff;padding: 16rpx;border-radius: 10rpx;font-size: 24rpx;
}
1.4 商品导航区域
  1. veiw:视图容器
  2. image:图片组件
  3. text:文本组件
<!-- 商品导航 -->
<view class="good-nav"><view class="nav-item"><image src="../../assets/category/cate-1.png" mode=""/><text>鲜花玫瑰</text></view><view class="nav-item"><image src="../../assets/category/cate-2.png" mode=""/><text>鲜花玫瑰</text></view><view class="nav-item"><image src="../../assets/category/cate-3.png" mode=""/><text>鲜花玫瑰</text></view><view class="nav-item"><image src="../../assets/category/cate-4.png" mode=""/><text>鲜花玫瑰</text></view><view class="nav-item"><image src="../../assets/category/cate-5.png" mode=""/><text>鲜花玫瑰</text></view>
</view>
page {height: 100vh;background-color: #efefef !important;padding: 16rpx;box-sizing: border-box;display: flex;flex-direction: column;> view {&:nth-child(n+2) {margin-top: 16rpx;}}
}// 轮播图区域样式
.swiper {border-radius: 10rpx;overflow: hidden;swiper {height: 360rpx;swiper-item {image{width: 100%;height: 100%;}}}
}// 公司信息
.info {display: flex;justify-content: space-between;background-color: #fff;padding: 20rpx 16rpx;border-radius: 10rpx;font-size: 24rpx;
}// 商品导航区域
.good-nav {display: flex;justify-content: space-between;background-color: #fff;padding: 20rpx 16rpx;border-radius: 10rpx;.nav-item {display: flex;flex-direction: column;align-items: center;image {width: 80rpx;height: 80rpx;}text {font-size: 24rpx;margin-top: 12rpx;}}
}
1.5 跳转到商品列表

在小程序中,如果需要进行跳转,需要使用 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" 时不支持传参

官方文档:navigator

<!-- view 小程序提供的容器组件,可以直接当成 div 使用即可 -->
<!-- 轮播区域 -->
<view class="swiper"><swiperautoplaycircularindicator-dotsinterval="2000"   indicator-color="#fff"indicator-active-color="#f3514f"><!-- src:图片的资源地址mode:图片的裁剪和缩放模式show-menu-by-longpress:长按展示菜单,菜单中有转发给好友、收藏、保存等功能lazy-load:图片懒加载功能,在滑动到一定的距离(上下三屏)以后展示图片--><swiper-item><image src="../../assets/banner/banner-1.png" mode="aspectFit" show-menu-by-longpress lazy-load /></swiper-item><swiper-item><image src="../../assets/banner/banner-2.png" mode="aspectFit" show-menu-by-longpress lazy-load /></swiper-item><swiper-item><image src="../../assets/banner/banner-3.png" mode="aspectFit" show-menu-by-longpress lazy-load /></swiper-item></swiper>
</view><!-- 公司信息 -->
<view class="info"><!-- text 不可嵌套其他组件user-select:长按以后选中文本 boolean 文本是否可选,该属性会使文本节点显示为 inline-block	space:显示连续空格 可选值:ensp	中文字符空格一半大小 emsp	中文字符空格大小 nbsp	根据字体设置的空格大小--><text space="ensp">同城配送</text><text space="ensp">行业龙头</text><text space="ensp">半小时送达</text><text space="ensp">100% 好评</text>
</view><!-- 商品导航 -->
<view class="good-nav"><view class="nav-item"><!-- 在进行页面跳转时,需要在路径的前面添加 / 斜线,否则跳转不成功open-type	string类型	默认值 navigate	 跳转方式参数列表:navigate 对应 wx.navigateTo 或 wx.navigateToMiniProgram 的功能只能跳转到非 tabbar 页面,不能跳转到 tabbar 页面,保留上级页面redirect 对应 wx.redirectTo 的功能只能跳转到非 tabbar 页面,不能跳转到 tabbar 页面,关闭上级页面switchTab	对应 wx.switchTab 的功能	只能跳转到 tabbar 页面,不能跳转到非 tabbbar 页面reLaunch	对应 wx.reLaunch 的功能关闭所有页面,打开到应用内的某个页面navigateBack	对应 wx.navigateBack 或 wx.navigateBackMiniProgram (基础库 2.24.4 版本支持)的功能关闭当前页面,返回上一页面或多级页面 默认返回上一页 当返回多级页面时,需要添加 delta 属性,表示返回的层级 默认为 1exit	退出小程序,target="miniProgram"时生效--><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-1.png" mode=""/><text>鲜花玫瑰</text></navigator></view><view class="nav-item"><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-2.png" mode=""/><text>鲜花玫瑰</text></navigator></view><view class="nav-item"><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-3.png" mode=""/><text>鲜花玫瑰</text></navigator></view><view class="nav-item"><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-4.png" mode=""/><text>鲜花玫瑰</text></navigator></view><view class="nav-item"><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-5.png" mode=""/><text>鲜花玫瑰</text></navigator></view>
</view><!-- 推荐商品 -->
<view class="good-hot"></view>
page {height: 100vh;background-color: #efefef !important;padding: 16rpx;box-sizing: border-box;display: flex;flex-direction: column;> view {&:nth-child(n+2) {margin-top: 16rpx;}}
}// 轮播图区域样式
.swiper {border-radius: 10rpx;overflow: hidden;swiper {height: 360rpx;swiper-item {image{width: 100%;height: 100%;}}}
}// 公司信息
.info {display: flex;justify-content: space-between;background-color: #fff;padding: 20rpx 16rpx;border-radius: 10rpx;font-size: 24rpx;
}// 商品导航区域
.good-nav {display: flex;justify-content: space-between;background-color: #fff;padding: 20rpx 16rpx;border-radius: 10rpx;.nav-item {navigator {display: flex;flex-direction: column;align-items: center;image {width: 80rpx;height: 80rpx;}text {font-size: 24rpx;margin-top: 12rpx;}}}
}
1.6 推荐商品区域 - 滚动效果

在微信想小程序中如果想实现内容滚动,需要使用 scroll-view 组件

scroll-view:可滚动视图区域,适用于需要滚动展示内容的场景,用于在小程序中实现类似于网页中的滚动条效果,用户可以通过手指滑动或者点击滚动条来滚动内容。

先来学习两个属性:

  1. scroll-x:允许横向滚动
  2. scroll-y:允许纵向滚动
<!-- 推荐商品 -->
<view class="good-hot"><scroll-view class="scroll-x" scroll-x="true"><view>1</view><view>2</view><view>3</view></scroll-view>
</view>
// 推荐商品
.good-hot {.scroll-x {width: 100%;white-space: nowrap;background-color: skyblue;view {display: inline-block;width: 300rpx;height: 80rpx;&:last-child {background-color: slateblue;}&:first-child {background-color: springgreen;}}}
}

横向滚动时,需要把宽度固定,纵向滚动时,需要把纵向滚动固定

1.7 推荐商品区域 - 实现结构样式
<!-- view 小程序提供的容器组件,可以直接当成 div 使用即可 -->
<!-- 轮播区域 -->
<view class="swiper"><swiperautoplaycircularindicator-dotsinterval="2000"   indicator-color="#fff"indicator-active-color="#f3514f"><!-- src:图片的资源地址mode:图片的裁剪和缩放模式show-menu-by-longpress:长按展示菜单,菜单中有转发给好友、收藏、保存等功能lazy-load:图片懒加载功能,在滑动到一定的距离(上下三屏)以后展示图片--><swiper-item><image src="../../assets/banner/banner-1.png" mode="aspectFit" show-menu-by-longpress lazy-load /></swiper-item><swiper-item><image src="../../assets/banner/banner-2.png" mode="aspectFit" show-menu-by-longpress lazy-load /></swiper-item><swiper-item><image src="../../assets/banner/banner-3.png" mode="aspectFit" show-menu-by-longpress lazy-load /></swiper-item></swiper>
</view><!-- 公司信息 -->
<view class="info"><!-- text 不可嵌套其他组件user-select:长按以后选中文本 boolean 文本是否可选,该属性会使文本节点显示为 inline-block	space:显示连续空格 可选值:ensp	中文字符空格一半大小 emsp	中文字符空格大小 nbsp	根据字体设置的空格大小--><text space="ensp">同城配送</text><text space="ensp">行业龙头</text><text space="ensp">半小时送达</text><text space="ensp">100% 好评</text>
</view><!-- 商品导航 -->
<view class="good-nav"><view class="nav-item"><!-- 在进行页面跳转时,需要在路径的前面添加 / 斜线,否则跳转不成功open-type	string类型	默认值 navigate	 跳转方式参数列表:navigate 对应 wx.navigateTo 或 wx.navigateToMiniProgram 的功能只能跳转到非 tabbar 页面,不能跳转到 tabbar 页面,保留上级页面redirect 对应 wx.redirectTo 的功能只能跳转到非 tabbar 页面,不能跳转到 tabbar 页面,关闭上级页面switchTab	对应 wx.switchTab 的功能	只能跳转到 tabbar 页面,不能跳转到非 tabbbar 页面reLaunch	对应 wx.reLaunch 的功能关闭所有页面,打开到应用内的某个页面navigateBack	对应 wx.navigateBack 或 wx.navigateBackMiniProgram (基础库 2.24.4 版本支持)的功能关闭当前页面,返回上一页面或多级页面 默认返回上一页 当返回多级页面时,需要添加 delta 属性,表示返回的层级 默认为 1exit	退出小程序,target="miniProgram"时生效--><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-1.png" mode=""/><text>鲜花玫瑰</text></navigator></view><view class="nav-item"><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-2.png" mode=""/><text>鲜花玫瑰</text></navigator></view><view class="nav-item"><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-3.png" mode=""/><text>鲜花玫瑰</text></navigator></view><view class="nav-item"><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-4.png" mode=""/><text>鲜花玫瑰</text></navigator></view><view class="nav-item"><navigator url="/pages/list/list?id=10&num=hua"><image src="../../assets/category/cate-5.png" mode=""/><text>鲜花玫瑰</text></navigator></view>
</view><!-- 推荐商品 -->
<view class="good-hot"><scroll-view class="scroll-x" 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>99</text></view></view><view><view class="good-item"><image src="../../assets/floor/3.png" mode=""/><text>鲜花玫瑰</text><text>100</text></view></view><view><view class="good-item"><image src="../../assets/floor/4.png" mode=""/><text>鲜花玫瑰</text><text>105</text></view></view><view><view class="good-item"><image src="../../assets/floor/5.png" mode=""/><text>鲜花玫瑰</text><text>88</text></view></view></scroll-view>
</view>
page {height: 100vh;background-color: #efefef !important;padding: 16rpx;box-sizing: border-box;display: flex;flex-direction: column;> view {&:nth-child(n+2) {margin-top: 16rpx;}}
}// 轮播图区域样式
.swiper {border-radius: 10rpx;overflow: hidden;swiper {height: 360rpx;swiper-item {image{width: 100%;height: 100%;}}}
}// 公司信息
.info {display: flex;justify-content: space-between;background-color: #fff;padding: 20rpx 16rpx;border-radius: 10rpx;font-size: 24rpx;
}// 商品导航区域
.good-nav {display: flex;justify-content: space-between;background-color: #fff;padding: 20rpx 16rpx;border-radius: 10rpx;.nav-item {navigator {display: flex;flex-direction: column;align-items: center;image {width: 80rpx;height: 80rpx;}text {font-size: 24rpx;margin-top: 12rpx;}}}
}// 推荐商品
.good-hot {background-color: #fff;border-radius: 10rpx;padding: 16rpx;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;image {width: 100%;height: 320rpx;}text {&:nth-of-type(1) {font-weight: bold;}}}&:last-child {margin-right: 0;}}}
}
1.8 字体图标的使用

在项目中使用到的小图标,一般由公司设计师进行设计,设计好以后上传到 阿里巴巴矢量图标库,然后方便程序员来进行使用。

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

阿里巴巴矢量图标库 中搜索或上传自己需要的图标,添加到项目,生成对应的样式文件,在微信小程序项目目录创建,字体图标 iconfont 文件夹,然后新建 iconfont.scss 样式文件,导入到 app.scss文件中,即可使用。


样式文件推荐使用 Base64 格式的字体文件,在项目设置中设置,如下图:


iconfont.scss 文件中

@font-face {font-family: "iconfont"; /* Project id 4569815 */src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAYwAAsAAAAAC7gAAAXjAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqKPIh4ATYCJAMUCwwABCAFhGcHUxslClGULlKC7Mdh7DwJwwiNSoHG4pHKh7t33+Ph+WO85776XZbpUfPRaLymgQ2jaOQTKh5VW/RQnTaFPCnlLBTsBoV8foHpyXwBHtS5FK41Ey/D5cMvtwriFxfsRpM3eSvD9VO7xCv///1KJ9L+ANYF8PumqizD/gDVBvDYWnSBDPieFadVwc2lmOo1XiPQbVss5dPU7GKgf40vFwNrqszJIKmOazTJB5qCQ2kQhU5z63pqEQ9AJd1UvgiA+8Hvx0cQGJokNTOvcvZCCgaOPlN6dlql+z8iPwfehIDdEyoy1jfmiuvNgWtUIt16YOiuCqg6PBzVKZFJN3J7Jnp2+v9/Es3RTN3WPzyKLAmiMQ29xD0sFt4oOYrE6AabDKMINgnG01A9i6F6p3RjBIjHAKRTYFrmp9y0ydq4AFeYURpwYFlDoYDX7mhgbTZJlhcUyJwVGZ0K1ZaCz+Jb3a6LpL826BUyUij30HxTvwcZqmFVtKQtM1FVWZM/rXrOVYl/v+Ix3+SJ7F/WUwzBsHNKQIGgz5U1hqGcxlkW/8H7t0lm0Gz7Ek1Taryixq1Vv+QdVD4lKDucJRk6rw8rsOOFi7uHQQZrEAJ0QI8YJtiaKgDI29uRhR1uLyu3Jb5n7eQWADRK+IE2JMzrpZJlUVg0zx4gpoktixoG0vU+sg/GmMCHMJXl1W2/TBQJqkftbkcCCxkHNG8vTlscJatE24clazI93VHMQLy+xNipha41KAZVlYvGGlVAlKAsRLaHHddENkfOzjwa6mBJ76R4Wr7LsofXvOsXXNMZDnoAANpn2dUv8hHvKVpUwJVf+p8EdOQ7v9cYpBlnBQLL4HFyjx/oHbp1hI9TJsLX4NAGwm8GY+ZErzCaB2eeJdmpkjdPmJ4oV+UO+ZQyuN+oZPzcdo05I6DJLcOB4JNBZdp0gk7tt/RBdUapzHNkAInfa7I3nj/QXOpOKqk/NP2mU5ugTQeWeWcOVOZrGcKrJlu09hvNa20x+WAI87VKNinWepCt4d/jLYH+ox8MuSR4Z5rvskqrBzmrsi8lHxQDiUEDio9hqmieNwKMx5rv8sp0TQB/QzBtowkmvbYgmFFvUm8V6PGseHqCjoAysjb6pDa1JnWhc1xV8+nBNe0GS6xYeMHhPTwAWadE1cocnywvryyfnF221SszxrR2F1kzvUhZEGZlHlZZk9Gekd42l97enj7Xlq5tNZfR1pZxol/PSM9+ch3Pc/GekwKfFzG+tp9xLt5nrSh2Zvqry5kV3jNR0zHLmXTwf56qdatynZhwrXKrvWqjjzGmZ9e6Xa3YXL3vfDsTQ0ZHkzHMDxt9jDFN/0N6JBbwKtcfE2vE6f4L/tMNeJrVX7KyBDSuBqsr9ObVvdZ5PYUaobFUI1n98mSNZd5mBd0Pj12wo0ye2aRoLDFcopHiZOT0WHPYcFgzxtrsofkGs0dmdHFXdhd2BIOjmG7RQBdx2Rx6BMXli2gnQ+cNvM3ZW0otKN++JPFvZBplY9sUbjGGDKKQYGvQDOD/Xe5XiQvEDzlPJX5vVEC+AZC/Kl/8nnbzi/4G6wyiv2rq/MfWvegapUjTvdgpwSu+HuP5jrRFI2QsVYOOooO0uquFEt9I3+cSGfZ4GveIKX2hM6PkIekwA1mnWWbhrkLVYxeaTnvQbU3K2T1GcFRE6cKivh+EQQpI+j2CbNAZZuHegWrcc2gG/YVu50Lvcj0WA02S4nw5jkGkBdIoKyZsszxYtBAnG4R8aVm2FJfKaKRhgK9/vpyGi3HpGEfIGslAuZyAhJQVwVTubrhQyEJOyjI4Kvel5HIu0s+PqPoRX5QVgSRSOD45HAZCtIBoKJYY4dHLs3+vEI7UQIhP2pFZ6pOSMXJaHArgy78DM80g7jR0I40yjUiB5OQjQEcnpZIIlCogdEK+wIK46hwDh6oBX9SAHCeSn0VHdDX4Lm8SvWDlf3akeb0aKXKUqNF0XOKzHC0mVS//EDVUyKICQzkrJlEKd2McTssSOwAA') format('woff2'),url('//at.alicdn.com/t/c/font_4569815_u50pb2x14v.woff?t=1717164520149') format('woff'),url('//at.alicdn.com/t/c/font_4569815_u50pb2x14v.ttf?t=1717164520149') format('truetype');
}.iconfont {font-family: "iconfont" !important;font-size: 16px;font-style: normal;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;
}.icon-haoping:before {content: "\e6c6";
}.icon-icon:before {content: "\e66d";
}.icon-clock:before {content: "\e627";
}.icon-tongchengpeisong:before {content: "\e601";
}

app.scss 文件中

// 导入样式文件以后,必须以分号结尾,否则会出现异常
@import "./iconfont/iconfont.scss";

index.scss 文件中

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

index.wxml 文件中

<!-- 公司信息 -->
<view class="info"><!-- text 不可嵌套其他组件user-select:长按以后选中文本 boolean 文本是否可选,该属性会使文本节点显示为 inline-block	space:显示连续空格 可选值:ensp	中文字符空格一半大小 emsp	中文字符空格大小 nbsp	根据字体设置的空格大小--><text space="ensp"><text class="iconfont icon-tongchengpeisong"></text>同城配送</text><text space="ensp"><text class="iconfont icon-icon"></text>行业龙头</text><text space="ensp"><text class="iconfont icon-clock"></text>半小时送达</text><text space="ensp"><text class="iconfont icon-haoping"></text>100% 好评</text>
</view>

注意事项:

使用字体图标可能会报错:[渲染层网络层错误] Failed to load font.………该错误可忽略

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

2. 背景图片的使用

当编写小程序的样式文件时,我们可以使用 background-image 属性来设置元素的背景图像

注意事项:

小程序的 background-image 不支持本地路径! 需要使用网络图片,或者 base64 ,或者使用 <image /> 组件

五、事件系统

1. 事件绑定和事件对象

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

第一种方式:bind:事件名,bind 后面需要跟上冒号,冒号后面跟上事件名,例如:<view bind:tap="fnName"></view>

第二种方式:bind事件名,bind 后面直接跟上事件名,例如:<view bindtap="fnName"></view>

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

<button type="primary" size="mini" bind:tap="handleTap">绑定事件</button>
<button type="warn" size="mini" bindtap="handleTap">绑定事件</button><!-- 微信小程序input输入框默认不带边框 -->
<input type="text" bindinput="getInputValue"/>

微信小程序input输入框默认不带边框

Page({handleTap(event) {console.log(event)},getInputValue(event) {console.log(event.detail.value)}
})

2. 事件分类以及阻止事件冒泡

事件分为 冒泡事件非冒泡事件

冒泡事件:当一个组件的事件被触发后,该事件会向父节点传递

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

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

<view class="catch" bindtap="parentHander"><button catchtap="btnHander">按钮</button>
</view>
Page({parentHander () {console.log("父组件绑定的事件")},btnHander () {console.log("子组件绑定的事件")}
})

以上代码可以组织事件冒泡。

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

事件传参:在触发事件时,将一些数据作为参数传递给事件处理函数的过程,就是事件传参

在微信小程序中,我们经常会在组件上添加一些自定义数据,然后在事件外理函数中获取这些自定义数据,从而完成业务逻辑的开发

在组件上 通过 data-* 的方式 定义需要传递的数据,其中 是自定义的属性,例如:<view data-id=“100"bindtap="handler”/>

然后通过事件对象进行获取自定义数据

<button catchtap="btnHander" data-id="1" data-name="tom">按钮</button><view class="catch" bindtap="parentHander" data-parent-id="1" data-parent-name="tom"><button data-id="1" data-name="tom">按钮</button>
</view>
Page({// 按钮触发的事件处理函数btnHander (event) {/*** currentTarget 事件绑定者 也就是指:哪个组件绑定了当前事件处理函数* target 事件触发者,也就死指:哪个组件触发了当前事件处理函数* currentTarget 和 target 都是指按钮,因为是按钮绑定的事件处理函数,同时点击按钮触发事件处理函数,这时候通过谁来获取数据都可以。*/console.log(event.currentTarget.dataset) // {id: "1", name: "tom"}console.log(event.target.dataset) // {id: "1", name: "tom"}},// view 触发的事件处理函数parentHander (event) {/*** 点击蓝色区域(不点击按钮)* currentTarget 事件绑定者 view* target 事件触发者,view* currentTarget 和 target 都是指 view,通过谁来获取数据都可以。* * 点击按钮(不点击蓝色区域)* currentTarget 事件绑定者 view* target 事件触发者,按钮* 如果想获取view 身上的数据,就必须使用 currentTarget;如果想获取事件本身的数据,就需要使用 target。* * 在传递参数的时候,自定义属性是多个单词:* 如果单词与单词之间使用中划线 - 进行连接,在事件对象中会被转换为小驼峰写法;* 如果使用的是小驼峰写法,在事件对象中会被转为全部小写的*/// 点击蓝色区域时(不能获取按钮的传参)console.log(event.currentTarget.dataset) // {parentId: "2", parentName: "tom"}console.log(event.target.dataset) // {parentId: "2", parentName: "tom"}// 点击按钮时(通过 currentTarget 获取父节点传参)console.log(event.currentTarget.dataset) // {parentId: "2", parentName: "tom"}console.log(event.target.dataset.id)  // {id: "1", name: "tom"}}
})

点击父节点不能获取子节点的参数,点击子节点可以通过 currentTarget 获取父节点的参数。

注意事项:

  1. event.target 是指事件触发者,event.currentTarget 是指事件绑定者
  2. 使用 data- 方法传递参数的时候,多个单词由连字符-连接,连字符写法会转换成驼峰写法
  3. 使用 data- 方法传递参数的时候,而大写字符会自动转成小写字符

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

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

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

在组件上使用 mark:自定义属性 的方式将数据传递给事件处理函数,例如:<view mark:id=“100" bindtap="handler”/>,然后通过事件对象进行获取自定义数据。

mark 和 data-* 很相似,主要区别在于:*

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

currentTarget.dataset 或者 target.dataset 只包含事件绑定者 或者 事件触发者那一个节点的 data-* 值

<!-- 如果需要使用 mark 进行事件传参,需要使用 mark:自定义属性的方式进行参数传递 -->
<button bindtap="markBtnHander" mark:id="1" mark:name="tom">按钮</button><view class="catch" bindtap="markParentHander" mark:parentId="2" mark:parentName="char"><button mark:id="1" mark:name="tom">按钮</button>
</view>
Page({// 使用 mark:自定义属性 传参markBtnHander(event) {console.log(event.mark) // {id: "1", name: "tom"}},markParentHander(event) {/*** 点击蓝色区域(不点击按钮)* 通过事件对象获取的是 view 身上绑定的数据* * 点击按钮(不点击蓝色区域)* 通过事件对象获取的是 触发事件的节点 以及 父节点身上所有的 mark 数据*/console.log(event.mark) // {id: "1", name: "tom", parentId: "2", parentName: "char"}}
})

六、wxml 语法

1. 声明和绑定数据

小程序页面中使用的数据均需要在 Page() 方法的 data 对象中进行声明定义
在将数据声明好以后,在 WXML 使用 Mustache 语法(双大括号{{ }}) 将变量包起来,从而将数据绑定
在 {{ }} 内部可以做一些简单的运算,支持如下几种方式:

  1. 算数运算
  2. 三元运算
  3. 逻辑判断
  4. 其他…

注意事项:在 {{ }} 语法中,只能写表达式,不能写语句,也不能调用 JavaScript 相关的方法

<!-- 如果需要展示数据,在 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><!-- 在双大括号写法内部,只能写表达式,不能写语句,也不能调用 JavaScript 的方法 -->
<!-- 以下写法报错 -->
<!-- <view>{{ if(id === 1) {} }}</view> -->
<!-- <view>{{ for(const i =0; i <= 10; i++) {} }}</view> -->
<!-- 以下代码没有报错,但也不会生效 -->
<!-- <view>{{ obj.name.toUpperCase }}</view> -->
Page({/*** 在小程序页面中所需要使用的数据均来自于 data*/data: {id: 1,isChecked: false,school: "尚硅谷",obj: {name: "tom"}}
})

2. setData() 修改数据

小程序中修改数据不推荐通过赋值的方式进行修改,通过赋值的方式修改数据无法改变页面的数据
而是要通过调用 setData() 方法进行修改,setData() 方法接收对象作为参数,key 是需要修改的数据,value 是最新的值。
setData() 方法有两个作用:

  1. 更新数据
  2. 驱动视图更新
<view>{{ num }}</view>
<button bindtap="handlerUpdata">更新 num</button>
Page({/*** 在小程序页面中所需要使用的数据均来自于 data*/data: {num: 1},handlerUpdata(event) {/*** 通过赋值的方式直接修改数据:* 能够修改数据,但是不能更新页面上的数据,例如:this.data.num += 1* * 通过 this.setData({ key: value }) 方式修改数据:1. 更新数据,2.驱动视图(页面)更新* key:是需要更新的数据* value:是最新的值*/this.setData({ num: this.data.num + 1 })}
})

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

  1. 新增 单个/多个属性
  2. 修改 单个/多个属性
  3. 删除 单个/多个属性
<view>{{ userInfo.name }}</view>
<view>{{ userInfo.age }}</view>
<button bindtap="handlerUpdataUserInfo">更新 userInfo</button>
Page({/*** 在小程序页面中所需要使用的数据均来自于 data*/data: {userInfo: {name: "Tom",age: 10,test: "aaa"}},// 更新userInfohandlerUpdataUserInfo() {/*** 新增单个 / 多个属性* 如果给对象新增属性,可以将 key 写成数据路径的方式 a.b.c*/this.setData({"userInfo.name": "Tom","userInfo.age": 10})/*** 修改单个 / 多个属性* 如果需要修改对象属性,可以将 key 写成数据路径的方式 a.b.c*/this.setData({"userInfo.name": "jerry","userInfo.age": 18})/*** 优化方案* 目前进行新增和修改都是使用数据路径,如果新增和修改的数据量比较小,还可以;* 如果修改的数据很多,每次都写数据路径,就太麻烦了,可以使用 ES6 提供的展开运算符 和 Object.assign()*/// 通过展开运算符,能够将对象中的属性复制给另外一个对象,后面的属性会覆盖前面的属性const userInfo = {...this.data.userInfo,name: "Tom",age: 20}this.setData({userInfo})// Object.assign() 将多个对象合并为一个对象const userInfo = Object.assign(this.data.userInfo, { name: 'jerry', age: 8 })this.setData({ userInfo })// 删除单个属性delete this.data.userInfo.age;this.setData({ userInfo: this.data.userInfo })// 删除多个属性 rest 剩余参数const {age, test, ...rest} = this.data.userInfothis.setData({ userInfo: rest })}
})

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

  1. 新增数组元素
  2. 修改数组元素
  3. 删除数组元素
<!-- setData() 修改数组类型数据 -->
<view wx:for="{{ list }}" wx:key="index">{{ item }}</view>
<button bindtap="handlerUpdataList">修改数组类型数</button>
Page({/*** 在小程序页面中所需要使用的数据均来自于 data*/data: {list: [1, 2, 3]},// 修改数组类型数据handlerUpdataList() {// 新增数组元素// 如果直接使用 push 方法,可以更新 data, 但是不能更新 页面中的数组,需要调用 setData() 更新页面this.data.list.push(4);this.setData({list: this.data.list})const newList = this.data.list.concat(4);this.setData({list: newList})const newList = [...this.data.list, 4];this.setData({list: newList})// 修改数组元素,使用 数据路径的方式修改 如:{ "list[1].name": 6 }this.setData({"list[1]": 6})// 删除数组元素this.data.list.splice(1, 1)this.setData({list: this.data.list})const newList = this.data.list.filter(item => item != 2)this.setData({list: newList})}
})

5. 简易双向数据绑定

在 WXML中,普通属性的绑定是单向的,例如:<input value="{{value}}"/>

如果希望用户输入数据的同时改变 data 中的数据,可以借助简易双向绑定机制。在对应属性之前添加 mode: 前缀即可:

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

注意事项:

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

  1. 只能是一个单一字段的绑定,例如:错误用法:<input model:value="值为{{value}}"/>
  2. 尚不能写 data 路径,也就是不支持数组和对象,例如:错误用法:<input model:value="{{a.b}}"/>
<!-- 单向绑定:数据能够影响页面,但是页面更新不会影响到数据 -->
<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.name }}"/> <!-- 错误写法 -->
Page({/*** 在小程序页面中所需要使用的数据均来自于 data*/data: {isChecked: false,obj: {name: "tom"},list: [1, 2, 3],value: 123}
})

6. 列表渲染 - 基本使用

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

在组件上使用 wx:for 属性绑定一个数组或对象,既可使用每一项数据重复渲染当前组件

每一项的变量名默认为 item,下标变量名默认为 index

在使用 wx:for 进行遍历的时候,建议加上 wx:key 属性wx:key 的值以两种形式提供:

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

注意事项:

  1. 如果不加 wx:key,会报一个 warning,如果明确知道该列表是静态,即以后数据不会改变,或者不必关注其顺序,可以选择忽略。
  2. 在给 wx:key 添加属性值的时候,不需要使用双大括号语法,直接使用遍历的 arrayitem 的某个属性
<!-- 如果需要进行列表渲染,需要使用 wx:for 属性,属性需要使用双大括号进行包裹,每一项的变量名默认是 item,每一项下标的变量名默认是 index -->
<!-- wx:key 提升性能wx:key 属性值有两种添加形式:1. 字符串,需要是遍历的数组中 item 的某个属性,要求该属性是列表中唯一的字符串或者数字,不能进行动态改变2. 保留关键字 *this, *this 代表的是 item 本身,item 本身是唯一的字符串或者数字wx:key 的属性值不需要使用大括号进行包裹,直接写遍历的数组中 item 的某个属性
-->
<!-- 如果渲染的是数组,item:数组的每一项,index:下标 -->
<view wx:for="{{ numList }}" wx:key="*this">{{ item }} - {{ index }}</view>
<!-- 如果渲染的是对象,item:对象的值,index:对象的属性 -->
<view wx:for="{{ obj }}" wx:key="index">{{ item }} - {{ index }}</view>
<view wx:for="{{ fruitList }}" wx:key="index">{{ item.id }} - {{ item.name }}</view>
<view wx:for="{{ fruitList }}" wx:key="id">{{ item.id }} - {{ item.name }}</view>
Page({/*** 在小程序页面中所需要使用的数据均来自于 data*/data: {obj: {name: "tom",age: 12},numList: [1, 2, 3],fruitList: [{ id: 1, name: "🍎" },{ id: 2, name: "🍋" },{ id: 3, name: "🍅" }]}
})

7. 列表渲染 - 进阶用法

  1. 如果需要对默认的变量名和下标进行修改,可以使用 wx:for-itemwx:for-index
    • 使用 wx:for-item 可以指定数组当前元素的变量名
    • 使用 wx:for-index 可以指定数组当前下标的变量名
  2. wx:for 用在 <block /> 标签上,以渲染一个包含多个节点的结构块
    • <block /> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性
    • <block /> 标签在 wxml 中可以用于组织代码结构,支持列表渲染、条件渲染等
<!-- 如果需要修改默认的变量名,需要使用 wx:for-item 属性;如果需要修改默认的下标变量名,需要使用 wx:for-index 属性。两个属性需要和 wx:for 写到同一组件上在重命名、修改以后,需要使用最新的变量名-->
<!-- 数组 -->
<view wx:for="{{ fruitList }}" wx:key="id" wx:for-item="fruitItem" wx:for-index="i">
{{ fruitItem.name }}
</view>
<!-- 对象 -->
<view wx:for="{{ obj }}" wx:key="key" wx:for-item="value" wx:for-index="key">
{{ value }} - {{ key }}
</view><view wx:for="{{ fruitList }}" wx:key="id" wx:for-item="fruitItem" wx:for-index="i"><view>名字:{{ fruitItem.name }}</view><view>价格:{{ fruitItem.price }}</view>
</view><!-- block 不是一个组件,只是渲染元素,也就是只是包装元素,可以组织代码,支持列表渲染,block 不会在页面中做任何渲染,只接受控制属性
-->
<block wx:for="{{ fruitList }}" wx:key="id" wx:for-item="fruitItem" wx:for-index="i"><view>名字:{{ fruitItem.name }}</view><view>价格:{{ fruitItem.price }}</view>
</block>
Page({/*** 在小程序页面中所需要使用的数据均来自于 data*/data: {fruitList: [{ id: 1, name: "🍎", price: 66 },{ id: 2, name: "🍋", price: 77 },{ id: 3, name: "🍅", price: 77 }]}
})

8. 条件渲染

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

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

wx:ifhidden 二者的区别:

  1. wx:if :当条件为 true 时将结构展示出来,否则结构不会进行展示,通过 移除/新增节点 的方式来实现
  2. hidden:当条件为 true 时会将结构隐藏,否则结构会展示出来,通过 display 样式属性 来实现的
<view class="line">条件渲染</view>
<!-- wx:if 属性组,包含 wx:if  wx:elif  wx: else只有对应的条件成立,属性所在的组件才会进行展示 wx:elif  wx: else 不能单独使用,在使用的时候,必须结合 wx:if 使用使用了 wx:if 属性组的组件不能被打断,组件必须连贯才可以,即中间不能参杂没有使用 wx:if 属性组的组件-->
<view wx:if="{{ num === 1 }}">num 等于 {{ num }}</view>
<view wx:elif="{{ num === 2 }}">num 等于 {{ num }}</view>
<view wx:else>num 大于2,目前 num 等于{{ num }}</view><!-- 使用了 wx:if 属性组的组件不能被打断,组件必须连贯才可以,即中间不能参杂没有使用 wx:if 属性组的组件 -->
<!-- <view wx:if="{{ num === 1 }}">num 等于 {{ num }}</view>
<view></view>
<view wx:elif="{{ num === 2 }}">num 等于 {{ num }}</view>
<view wx:else>num 大于2,目前 num 等于{{ num }}</view> --><!-- hidden 属性hidden 属性 属性值,如果是 true,就会隐藏结构,如果是 false,才会展示结构-->
<view hidden="{{ !isFlag }}">如果 isFlag 是 true,展示结构,否则隐藏结构</view><!-- wx:if 和 hidden 的区别:wx:if 控制结构的展示和隐藏,是通过新增和移除结构来实现的hidden 属性控制结构的 展示和隐藏,是通过 css 的 display 属性来实现--><button type="warn" bind:tap="handlerUpdata">更新num</button>
Page({/*** 在小程序页面中所需要使用的数据均来自于 data*/data: {num: 1,isFlag: true},// 更新 numhandlerUpdata(event) {/*** 通过赋值的方式直接修改数据:* 能够修改数据,但是不能更新页面上的数据,例如:this.data.num += 1* * 通过 this.setData({ key: value }) 方式修改数据:1. 更新数据,2.驱动视图(页面)更新* key:是需要更新的数据* value:是最新的值*/this.setData({ num: this.data.num + 1 })}
})

七、生命周期

1. 小程序运行机制


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

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

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

前台 和 后台状态

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

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

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

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

当小程序进入后台并被「挂起」后,如果很长时间(目前是 30 分钟)都未再次进入前台,小程序会被销毁当小程序占用系统资源过高,可能会被系统销毁或被微信客户端主动回收。

2. 小程序更新机制

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

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

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

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

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

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

App({/*** onLaunch 是小程序的钩子函数,这个钩子函数在冷启动时肯定会执行到* 当小程序冷启动时,会自动微信后台请求新版本信息,如果有新版本,会立即进行下载*/onLaunch() {// 使用 wx.getUpdateManager() 方法监听下载的状态const updateMannager = wx.getUpdateManager()// 当下载完成新版本以后,会触发 onUpdateReady 回调函数updateMannager.onUpdateReady(function() {// 在回调函数中给用户提示wx.showModal({title: '更新提示',content: '新版本已经准备好,是否重启应用?',complete: (res) => {if (res.confirm) {// 强制当前小程序使用新版本,并且会重启当前小程序updateMannager.applyUpdate()}}})})}
})

模拟更新设置:

3. 小程序生命周期介绍

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

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

小程序完整的生命周期由 应用生命周期页面生命周期组件生命周期 三部分来组成。

小程序生命周期伴随着一些函数,这些函数由小程序框架本身提供,被称为 生命周期函数,生命周期函数会按照顺序依次触发调用,帮助程序员在特定的时机执行特定的操作,辅助程序员完成一些比较复杂的逻辑。

4. 应用生命周期

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

应用生命周期伴随着一些函数,我们称为 应用生命周期函数,应用生命周期函数需要 app.js 文件的 App() 方法中进行定义,App() 方法必须在 app.js 中进行调用,主要用来注册小程序。

应用生命周期函数由 onLaunchonShowonHide 三个函数组成

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

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

5. 页面生命周期

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

页面生命周期函数需要在 Page() 方法进行定义

<!-- redirect:销毁当前页面,跳转到下一个页面 -->
<navigator url="/pages/list/list" open-type="redirect">跳转到列表页面-redirect</navigator>
<!-- navigate:保留当前页面,跳转到下一个页面 -->
<navigator url="/pages/list/list" open-type="navigate">跳转到列表页面-navigate</navigator>
Page({/*** 页面的初始数据*/data: {},/*** 生命周期函数--监听页面加载--一个页面只会调用只会调用一次*/onLoad: function (options) {console.log("onLoad 页面创建的时候");},/*** 生命周期函数--监听页面初次渲染完成--一个页面只会调用只会调用一次*/onReady: function () {console.log("onReady 页面初次渲染完成,代表页面已经准备妥当,可以和视图层进行交互");},/*** 生命周期函数--监听页面显示--如果从后台进入前台*/onShow: function () {console.log("onShow 页面在前台展示的时候");},/*** 生命周期函数--监听页面隐藏--在当前小程序进入后台是,也会触发执行*/onHide: function () {console.log("onHide 页面隐藏");},/*** 生命周期函数--监听页面卸载*/onUnload: function () {console.log("onHide 页面卸载、销毁");},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh: function () {},/*** 页面上拉触底事件的处理函数*/onReachBottom: function () {},/*** 用户点击右上角分享*/onShareAppMessage: function () {}
})

onLoad、onReady 两个钩子函数,一个页面只会触发一次

6. 生命周期两个细节

  1. tabBar 页面之间相互切换,页面不会被销毁
  2. 点击左上角,返回上一个页面,会销毁当前页面

八、小程序 API

1. 小程序 API介绍

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

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

  1. 当接口参数 Object 对象中不包含 success/fail/complete 时将默认返回 Promise
  2. 部分接口如 request, uploadFile 本身就有返回值,因此不支持 Promise 风格的调用方式,它们的 promisify 需要开发者自行封装。

2. 网络请求

发起网络请求获取服务器的数据,需要使用 wx,request() 接口 API

wx,request() 请求的域名必须在微信公众平台进行配置,如果使用 wx,request() 请求未配置的域名,在控制台会有相应的报错。

跳过域名的校验的开发:

  1. 在微信开发者工具中,点击详情按钮,切换到本地详情,将不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书 勾选上
  2. 在真机上,需要点击胶囊区域的分析按钮,在弹框中选择 开发调试,重启小程序后即可

注意事项:

这两种方式只适用于开发者工具、小程序的开发版和小程序的体验版,项目上线前必须在小程序管理平台进行合法域名的配置。

<button type="warn" bind:tap="getData">获取数据</button>
Page({/*** 页面的初始数据*/data: {list: []},// 获取数据getData() {// 如果需要发起网络情趣,需要使用 wx.request APIwx.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: () => {console.log("complete");}})}
})

3. 界面交互 - loading 提示框

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

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

  1. wx.showLoading() 显示 loading 提示框
  2. wx.hideLoading() 关闭 loading 提示框
Page({/*** 页面的初始数据*/data: {list: []},// 获取数据getData() {// 显示 loading 提示框wx.showLoading({/*** title 用来显示提示的内容* 提示的内容不会自动换行,如果提示的内容比较多,因为在同一行展示,多出来的内容会被隐藏*/title: "数据加载中....",// 是否显示透明蒙层,防止触摸穿透mask: true});// 如果需要发起网络情趣,需要使用 wx.request APIwx.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: () => {console.log("complete");// 关掉 loading 提示框,通常放在 complete 回调函数里面wx.hideLoading()}})}
})

注意:wx.showLoading()wx.hideLoading() 必须结合,配对使用。

4. 界面交互 - 模态对话框 - 消息提示框

wx.showModal():模态对话框,常用于询问用户是否执行一些操作

例如:询问用户是否退出登录、是否删除该商品 等

wx.showToast():消息提示框,根据用户的某些操作来告知操作的结果

例如:退出成功给用户提示,提示删除成功等

<button type="warn" bind:tap="delHandler">删除商品</button>
Page({async delHandler() {// showModal 显示模态对话框 是一个异步函数,返回 一个 Promise 对象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,})}}
})

5. 本地存储

小程序本地存储是指在小程序中使用 API 将数据存储在用户的设备上,以便小程序运行时和下次启动时快速地读取这些数据

注意事项: 对象类型的数据,可以直接进行存储获取,无需使用 JSON.stringfy()JSON.parse() 转换

同步 API 示例:

<button size="mini" plain type="warn" bindtap="setStorage">存储</button>
<button size="mini" plain type="primary" bindtap="getStorage">获取</button>
<button size="mini" plain type="warn" bindtap="removeStorage">删除</button>
<button size="mini" plain type="primary" bindtap="clearStorage">清空</button>
Page({// 将数据存储到本地setStorage() {// 第一个参数:本地存储中指定的 key,第二个参数:需要存储的数据wx.setStorageSync('num', 1)// 在小程序中,如果对象类型的数据,可以直接进行存储获取,无需使用 JSON.stringfy()、JSON.parse() 转换wx.setStorageSync('obj', {name: 'Tom', age: 10})},// 获取本地存储的数据getStorage() {// 从本地存储的数据中获取指定 key 的数据、内容const num = wx.getStorageSync('num')const obj = wx.getStorageSync('obj')console.log(num);console.log(obj);},// 删除本地存储的数据removeStorage() {// 从本地移除指定 key 的数据、内容wx.removeStorageSync('num')},// 清空本地存储的全部数据clearStorage() {wx.clearStorageSync()}
})

异步 API 示例:

<button size="mini" plain type="warn" bindtap="setStorage">存储</button>
<button size="mini" plain type="primary" bindtap="getStorage">获取</button>
<button size="mini" plain type="warn" bindtap="removeStorage">删除</button>
<button size="mini" plain type="primary" bindtap="clearStorage">清空</button>
Page({// 将数据存储到本地setStorage() {/*----------------------------- 同步 API -------------------------------------------*/ // 第一个参数:本地存储中指定的 key,第二个参数:需要存储的数据// wx.setStorageSync('num', 1)// 在小程序中,如果对象类型的数据,可以直接进行存储获取,无需使用 JSON.stringfy()、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() {/*----------------------------- 同步 API -------------------------------------------*/ // 从本地存储的数据中获取指定 key 的数据、内容// const num = wx.getStorageSync('num')// const obj = wx.getStorageSync('obj')/*----------------------------- 异步 API -------------------------------------------*/ const num = await wx.getStorage({ key: 'num' })const obj = await wx.getStorage({ key: 'obj' })console.log(num);console.log(obj);},// 删除本地存储的数据removeStorage() {/*----------------------------- 同步 API -------------------------------------------*/ // 从本地移除指定 key 的数据、内容// wx.removeStorageSync('num')/*----------------------------- 异步 API -------------------------------------------*/ wx.removeStorage({ key: 'num' })},// 清空本地存储的全部数据clearStorage() {/*----------------------------- 同步 API -------------------------------------------*/ // wx.clearStorageSync()/*----------------------------- 异步 API -------------------------------------------*/ wx.clearStorage()}
})

6. 路由与通信

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

  1. 声明式导航:navigator 组件
  2. 编程式导航:使用小程序提供的 API

路径后可以带参数,参数与路劲之间使用 ? 分隔,参数键与参数值用 = 相连,不同参数用 & 分隔,例如:path?key=value&key2=value2 参数需要在跳转到的页面的 onLoad 钩子函数 中通过形参进行接收

<button plain size="mini" type="warn" bindtap="navigateTo">navigateTo</button>
<button plain size="mini" type="primary" bindtap="redirectTo">redirectTo</button>
<button plain size="mini" type="warn" bindtap="switchTo">switchTo</button>
<button plain size="mini" type="primary" bindtap="relaunchTo">relaunchTo</button>
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',})},switchTo() {// 跳转到 tabBar 页面,不能跳转到非 tabBar 页面,路劲后面不能传递参数wx.switchTab({// url: '/pages/list/list',url: '/pages/cate/cate',})},relaunchTo() {// 关闭所用的页面,然后跳转到应用中某一个页面wx.reLaunch({url: '/pages/list/list?id=1&name=Tom'})}
})
<button plain size="mini" type="warn" bindtap="navigateBack">navigateBack</button>
Page({navigateBack() {// 关闭当前页面,返回上一级或者返回多级页面wx.navigateBack({delta: 1})},onLoad(options) {console.log(options);}
})

7. 页面处理函数 - 上拉加载

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

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

  1. app.json 或者 page.json 中配置距离页面底部距离:onReachBottomDistance;默认 50px
  2. 在 页面.js 中定义 onReachBottom 事件监听用户上拉加载
<view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>
{"usingComponents": {},"onReachBottomDistance": 100
}
view {height: 400rpx;display: flex;align-items: center;justify-content: center;
}view:nth-child(odd) {background-color: lightcyan;
}
view:nth-child(even) {background-color: coral;
}
Page({/*** 页面的初始数据*/data: {numList: [1, 2, 3]},/*** 监听用户上拉加载*/onReachBottom() {/*** 产品需求* 当用户上拉,需要数字进行累加* 分析:* 当用户上拉加载时,需要对数字进行累加,每次加 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()}, 3000)}
})

8.页面处理函数 - 下拉刷新

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

  1. 在 app.json 或者 page.json 中开启允许下拉,同时可以配置 窗口、loading 样式等
  2. 在 页面.js 中定义 onPullDownRefresh 事件监听用户下拉刷新
<view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>
{"usingComponents": {},"onReachBottomDistance": 100,"enablePullDownRefresh": true,"backgroundColor": "#efefef","backgroundTextStyle": "light"
}
view {height: 400rpx;display: flex;align-items: center;justify-content: center;
}view:nth-child(odd) {background-color: lightcyan;
}
view:nth-child(even) {background-color: coral;
}
Page({/*** 页面的初始数据*/data: {numList: [1, 2, 3]},/*** 监听用户下拉刷新*/onPullDownRefresh() {/*** 产品需求:* 当用户上拉加载更多以后,如果用户进行了下拉刷新,需要将数据进行重置*/this.setData({numList: [1, 2, 3]})// 在下拉刷新以后,loading 效果有可能不会回弹回去if (this.data.numList.length === 3) {wx.stopPullDownRefresh()}},/*** 监听用户上拉加载*/onReachBottom() {/*** 产品需求* 当用户上拉,需要数字进行累加* 分析:* 当用户上拉加载时,需要对数字进行累加,每次加 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()}, 3000)}
})

9. 增强 scroll-view

使用 scroll-view 实现上拉加载更多和下拉刷新功能

<scroll-viewscroll-yclass="scroll-y"lower-threshold="100"bindscrolltolower="getMore"enable-back-to-toprefresher-enabledrefresher-default-style="white"refresher-background="#f7f7f8"bindrefresherrefresh="refreshHandler"refresher-triggered="{{ isTriggered }}"
><view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>
</scroll-view>
.scroll-y {height: 100vh;background-color: #efefef;
}
view {height: 400rpx;display: flex;align-items: center;justify-content: center;
}view:nth-child(odd) {background-color: lightcyan;
}
view:nth-child(even) {background-color: coral;
}
Page({/*** 页面的初始数据*/data: {numList: [1, 2, 3],isTriggered: false},// scroll-view 上拉加载更多事件的逻辑getMore() {/*** 产品需求* 当用户上拉,需要数字进行累加* 分析:* 当用户上拉加载时,需要对数字进行累加,每次加 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()}, 3000)},refreshHandler() {wx.showToast({title: '下拉刷新...',})this.setData({numList: [1, 2, 3],isTriggered: false})}
})

九、自定义组件

1. 创建和注册组件

小程序目前已经支持组件化开发,可以将页面中的功能模块抽取成自定义组件,以便在不同的页面中重复使用;
也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。
开发中常见的组件有两种:

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

如果是公共组件,建议放在项目根目录的 components 文件夹中
如果是页面组件,建议放在对应页面的目录下

建议:一个组件一个文件夹


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

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

在 usingComponents 中进行组件注册时,需要提供 自定义组件的组件名 和 自定义组件文件路径
在将组件注册好以后,直接将 自定义组件的组件名 当成 组件标签名 使用即可

2. 组件的数据以及方法

组件数据和方法需要在 组件.js 的 Component 方法中进行定义, Component 创建自定义组件

  1. data:定义组件的内部数据
  2. methods:在组件中事件处理程序需要写到 methods 中才可以

3. 组件的属性 - properties

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

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

4. 组件 wxml 的 slot - 插槽

在使用基础组件时,可以在组件中间写子节点,从而将子节点的内容展示到页面中,自定义组件也可以接收子节点
只不过在组件模板中需要定义 <slot /> 节点,用于承载组件中的子节点

默认情况下,一个组件的 wxml 中只能有一个 slot(默认插槽)。需要使用多 slot 时,可以在组件 js 中声明启用。
同时需要给 slot 添加 name 属性来区分不同的 slot(具名插槽),然后给子节点内容添加 slot 属性,属性值是对应 slot 的 name 名称,从而将内容插入到对应的 slot 中。

"usingComponents": {"custom-checkbox": "./components/custom-checkbox/custom-checkbox","custom-slot": "./components/custom-slot/custom-slot"
}
<custom-checkbox lable="我已阅读并同意 用户协议 和 隐私协议" position="right">我已阅读并同意 用户协议 和 隐私协议
</custom-checkbox><custom-checkbox lable="匿名提交" position="left">匿名提交
</custom-checkbox><custom-slot><text slot="slot-top">我需要显示到顶部</text><!-- 默认情况下,自定义组件的子节点内容不会进行展示 --><!-- 如果想内容进行展示,需要在组件模板中定义 slot 节点 -->我是子节点内容<text slot="slot-bottom">我需要显示到底部</text>
</custom-slot>
<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="{{ lable !== '' }}">{{ lable }}</text><slot wx:else /></view></view>
</view>
.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;
}
Component({/*** 组件的属性列表:组件的对外属性,主要用来接收组件使用者传递给组件内部的属性以及数据*/properties: {// 如果需要接收传递的属性,有两种方式:全写、简写// lable: String, // 简写lable: {// 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,/*** 在 JS 中可以访问和获取 properties 中的数据,但一般情况下,不建议修改,因为会造成数据流的混乱*/lable: '在组件内也可以修改 properties 中的数据'})}}
})
<view><!-- 具名插槽 --><slot name="slot-top" /><!-- slot 就是用来接收、承载子节点内容,slot 只是一个占位符,子节点内容会将 slot 进行替换 --><!-- 默认插槽 --><view><slot /></view><!-- 具名插槽 --><slot name="slot-bottom" />
</view>
Component({options: {// 启用多 slot 支持multipleSlots: true}
})

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

自定义组件拥有自己的 wxss 样式,组件 wxss 文件的样式,默认只对当前组件生效
编写组件样式时,需要注意以下几点:

  1. 组件和引用组件的页面不能使用 id 选择器(#a)、属性选择器([a]) 和 标签名选择器,请改用 class 选择器
// 标签名选择器
text {color: lightgreen;
}// id 选择器
#content {color: lightgreen;
}// 属性选择器
[id=content] {color: lightgreen;
}

使用 id 选择器 和 属性选择器 直接不生效,标签名选择器 官方不允许使用,样式会生效。推荐使用 class 选择器

  1. 子元素选择器(.a > .b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况,即子选择器,只能用于 view 和 子组件,用于其他组件可能会出现样式失效问题
  2. 继承样式,如 font、color ,会从组件外继承到组件内,即继承样式,例如:color\font 都会从组件外继承
  3. 除继承样式外,全局中的样式、组件所在页面的的样式对自定义组件无效(除非更改组件样式隔离选项)
  4. 官方不推荐做法,不建议 在 app.wxss页面.wxss 中使用 标签名(view)选择器(或一些其他特殊选择器)设置样式;如果是在 全局样式文件 中设置样式,会影响项目中全部的相同组件;如果是在页面样式文件中设置样式,会影响当前页面所有的相同组件。
  5. 组件和引用组件的页面中使用后代选择器(.a.b)在一些极端情况下会有非预期的表现,如果出现,请避免使用;解决方案:需要具体到使用的位置

6. 组件样式隔离

默认情况下,自定义组件的样式只受自身 wxss 的影响,但是有时我们需要组件使用者的样式能够影响到组件,这时候就需要指定特殊的样式隔离选项 stylelsolation,选择它支持以下取值:

  1. isolated:表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值);
  2. apply-shared:表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;
  3. apply-sharedshared:表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件。
options: {/*** styleIsolation:配置组件样式隔离* * styleIsolation: "isolated",开启样式隔离,默认值;* 在默认情况下,自定义组件和组件使用者如果存在相同的类名,类名不会相互影响;* * styleIsolation: "apply-shared",表示组件使用者、页面的 wxss 样式能够影响到自定义组件,* 但是自定义组件的样式不会影响组件使用者、页面的 wxss 样式;* * styleIsolation: "shared",表示组件使用者、页面的 wxss 样式能够影响到自定义组件,* 自定义组件的样式会影响组件使用者、页面的 wxss 样式和其他使用了 apply-shared 和 shared 属性的自定义组件*/styleIsolation: "shared"},

7. 件样式隔离 - 小程序修改 checkbox 样式

<custom-checkbox lable="我已阅读并同意 用户协议 和 隐私协议" position="right">我已阅读并同意 用户协议 和 隐私协议
</custom-checkbox><view></view><view class="custom"><custom-checkbox lable="匿名提交" position="left">匿名提交</custom-checkbox>
</view>
/* 2.组件使用者也能修改默认样式 */
/* 可以通过在加外层的方式进行提升样式的权重 */
/* 复选框没有选中时默认的样式 */
.custom .custom-checkbox .wx-checkbox-input {border: 1px solid skyblue;
}
/* 复选框选中时默认的样式 */
.custom .custom-checkbox .wx-checkbox-input-checked {background-color: skyblue !important;
}
Component({options: {/*** styleIsolation:配置组件样式隔离* * styleIsolation: "isolated",开启样式隔离,默认值;* 在默认情况下,自定义组件和组件使用者如果存在相同的类名,类名不会相互影响;* * styleIsolation: "apply-shared",表示组件使用者、页面的 wxss 样式能够影响到自定义组件,* 但是自定义组件的样式不会影响组件使用者、页面的 wxss 样式;* * styleIsolation: "shared",表示组件使用者、页面的 wxss 样式能够影响到自定义组件,* 自定义组件的样式会影响组件使用者、页面的 wxss 样式和其他使用了 apply-shared 和 shared 属性的自定义组件*/styleIsolation: "shared"},/*** 组件的属性列表:组件的对外属性,主要用来接收组件使用者传递给组件内部的属性以及数据*/properties: {// 如果需要接收传递的属性,有两种方式:全写、简写// lable: String, // 简写lable: {// 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,/*** 在 JS 中可以访问和获取 properties 中的数据,但一般情况下,不建议修改,因为会造成数据流的混乱*/// lable: '在组件内也可以修改 properties 中的数据'})}}
})
<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="{{ lable !== '' }}">{{ lable }}</text><slot wx:else /></view></view>
</view>
.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;
}.content {font-size: 24rpx;
}/* 复选框组件是公共组件,以后需要在多个页面或者多个项目中使用,所以需要先给复选框组件准备、设置一些默认样式如果在其他页面或者项目中使用的时候,发现样式不符合产品需求,可以进行修改、对默认样式进行修改*//* 1. 需要给复选框设置默认样式,需要先找到小程序给复选框提供的类名,通过小程序给提供的类名才可以修改,需要到小程序开发者文档,找到复选框文档,审查元素,进行查找在自定义组件中,不能直接修改复选框样式,如果需要进行修改,需要设置 styleIsolation: "shared" shared:修改其他页面的样式、组件使用者的样式、以及其他使用了 share 以及 apply-share 的组件如果只想影响当前组件,可以添加命名空间*//* 复选框没有选中时默认的样式 */
.custom-checkbox .wx-checkbox-input {width: 24rpx !important;height: 24rpx !important;border-radius: 50% !important;border: 1px solid #fda007;
}/* 复选框选中时默认的样式 */
.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;
}

8.数据监听器

数据监听器主要用于监听和响应任何属性(properties)和 数据(data)的变化,当数据发生变化时就会触发对应回调函数,从而方便开发者进行业务逻辑的处理
在组件中如果需要进行数据监听 需要使用 observers 字段

<custom-slot label="标题"></custom-slot>
<view><view>{{ num }}</view><view>{{ count }}</view><view>{{ obj.name }}</view><view>{{ arr[1] }}</view><view>{{ label }}</view><button type="warn" plain bindtap="updateData">更新数据</button>
</view>
Component({/*** 组件的属性列表*/properties: {label: {type: String,value: "测试"}},/*** 组件的初始数据*/data: {num: 10,count: 100,obj: { name: 'Tom', age: 22 },arr: [1, 2, 3]},// 用来监听数据以及属性是否发生了变化observers: {/*** 监听单个属性* key: 需要监听的数据* value:就是一个回调函数,形参:最新的数据*/num: function(newNum) {// 对 data 中的数据进行监听,如果数据没有发生改变,监听器不会执行console.log(newNum);},// count: function(newCount) {//   console.log(newCount);// }/*** 同时监听多个数据*/// "num,count": function(newNum, newCount) {//   console.log(newNum);//   console.log(newCount);// }// 支持监听属性以及内部数据的变化// 'obj.name': function(newName) {//   console.log(newName);// },// 'arr[1]': function(newItem) {//   console.log(newItem);// },// 使用通配符监听对象中所有属性的变化// 'obj.**': function(newObj) {//   console.log(newObj);// }label: function(newlabel) {// 组件使用者传递了参数,在监听器中就能获取传递的数据,即监听器立即就执行了console.log(newlabel);},},/*** 组件的方法列表*/methods: {// 更新数据updateData() {this.setData({num: this.data.num + 1,// count: this.data.count - 1// 'obj.name': 'jerry',// 'arr[1]': 666label: "最新的标题"})}}
})

observers:对 data 中的数据进行监听,如果数据没有发生改变,监听器不会执行;组件使用者(父级)传递了参数,在监听器中就能获取传递的数据,即监听器立即就执行了。

9.组件通信 - 父往子传值

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

  1. 在父组件 WXML 中使用 数据绑定 的方式向子组件传递动态数据
  2. 子组件内部使用 properties 接收父组件传递的数据即可
<custom-checkbox lable="我已阅读并同意 用户协议 和 隐私协议" position="right" checked="{{ isChecked }}">我已阅读并同意 用户协议 和 隐私协议
</custom-checkbox><view></view><view class="custom"><custom-checkbox lable="匿名提交" position="left">匿名提交</custom-checkbox>
</view>
Page({data: {isChecked: true}
})
/* 2.组件使用者也能修改默认样式 */
/* 可以通过在加外层的方式进行提升样式的权重 */
/* 复选框没有选中时默认的样式 */
.custom .custom-checkbox .wx-checkbox-input {border: 1px solid skyblue;
}
/* 复选框选中时默认的样式 */
.custom .custom-checkbox .wx-checkbox-input-checked {background-color: skyblue !important;
}
<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="{{ lable !== '' }}">{{ lable }}</text><slot wx:else /></view></view>
</view>
.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;
}.content {font-size: 24rpx;
}/* 复选框组件是公共组件,以后需要在多个页面或者多个项目中使用,所以需要先给复选框组件准备、设置一些默认样式如果在其他页面或者项目中使用的时候,发现样式不符合产品需求,可以进行修改、对默认样式进行修改*//* 1. 需要给复选框设置默认样式,需要先找到小程序给复选框提供的类名,通过小程序给提供的类名才可以修改,需要到小程序开发者文档,找到复选框文档,审查元素,进行查找在自定义组件中,不能直接修改复选框样式,如果需要进行修改,需要设置 styleIsolation: "shared" shared:修改其他页面的样式、组件使用者的样式、以及其他使用了 share 以及 apply-share 的组件如果只想影响当前组件,可以添加命名空间*//* 复选框没有选中时默认的样式 */
.custom-checkbox .wx-checkbox-input {width: 24rpx !important;height: 24rpx !important;border-radius: 50% !important;border: 1px solid #fda007;
}/* 复选框选中时默认的样式 */
.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;
}
Component({options: {/*** styleIsolation:配置组件样式隔离* * styleIsolation: "isolated",开启样式隔离,默认值;* 在默认情况下,自定义组件和组件使用者如果存在相同的类名,类名不会相互影响;* * styleIsolation: "apply-shared",表示组件使用者、页面的 wxss 样式能够影响到自定义组件,* 但是自定义组件的样式不会影响组件使用者、页面的 wxss 样式;* * styleIsolation: "shared",表示组件使用者、页面的 wxss 样式能够影响到自定义组件,* 自定义组件的样式会影响组件使用者、页面的 wxss 样式和其他使用了 apply-shared 和 shared 属性的自定义组件*/styleIsolation: "shared"},/*** 组件的属性列表:组件的对外属性,主要用来接收组件使用者传递给组件内部的属性以及数据*/properties: {// 如果需要接收传递的属性,有两种方式:全写、简写// lable: String, // 简写lable: {// type 组件使用者传递过来的数据类型 包含:String、Number、Boolean、Object、Array,也可以设置为:null 表示不显示类型type: String, value: ''},position: {type: String,value: 'right'},/*** 复选框组件是公共组件* 需要在多个页面、在多个项目中进行使用,在使用的时候,有的地方默认是选中的效果,有的地方是没有选中的效果* 怎么处理?* 首先让复选框默认还是没有被选中的效果* 如果希望复选框默认被选中,传递属性(checked=true)到复选框组件*/checked: {type: Boolean,value: false}},observers: {// 如果需要将 properties 中的数据赋值给 data,可以使用 observers 进行处理checked: function(newChecked) {this.setData({isChecked: newChecked})}},/*** 组件的初始数据:用来定义当前组件内部所需要使用的数据*/data: {isChecked: false},/*** 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中*/methods: {// 更新复选框的状态updateChecked() {this.setData({isChecked: !this.data.isChecked,/*** 在 JS 中可以访问和获取 properties 中的数据,但一般情况下,不建议修改,因为会造成数据流的混乱*/// lable: '在组件内也可以修改 properties 中的数据'})}}
})

10.组件通信 - 子往父传值

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

  1. 自定义组件内部使用 triggerEvent 方法发射一个自定义的事件,同时可以携带数据
  2. 自定义组件标签上通过 bind 方法监听 发射的事件,同时绑定事件处理函数,在事件函数函数中通过事件对象获取传递的数据
<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="{{ lable !== '' }}">{{ lable }}</text><slot wx:else /></view></view>
</view>
Component({options: {/*** styleIsolation:配置组件样式隔离* * styleIsolation: "isolated",开启样式隔离,默认值;* 在默认情况下,自定义组件和组件使用者如果存在相同的类名,类名不会相互影响;* * styleIsolation: "apply-shared",表示组件使用者、页面的 wxss 样式能够影响到自定义组件,* 但是自定义组件的样式不会影响组件使用者、页面的 wxss 样式;* * styleIsolation: "shared",表示组件使用者、页面的 wxss 样式能够影响到自定义组件,* 自定义组件的样式会影响组件使用者、页面的 wxss 样式和其他使用了 apply-shared 和 shared 属性的自定义组件*/styleIsolation: "shared"},/*** 组件的属性列表:组件的对外属性,主要用来接收组件使用者传递给组件内部的属性以及数据*/properties: {// 如果需要接收传递的属性,有两种方式:全写、简写// lable: String, // 简写lable: {// type 组件使用者传递过来的数据类型 包含:String、Number、Boolean、Object、Array,也可以设置为:null 表示不显示类型type: String, value: ''},position: {type: String,value: 'right'},/*** 复选框组件是公共组件* 需要在多个页面、在多个项目中进行使用,在使用的时候,有的地方默认是选中的效果,有的地方是没有选中的效果* 怎么处理?* 首先让复选框默认还是没有被选中的效果* 如果希望复选框默认被选中,传递属性(checked=true)到复选框组件*/checked: {type: Boolean,value: false}},observers: {// 如果需要将 properties 中的数据赋值给 data,可以使用 observers 进行处理checked: function(newChecked) {this.setData({isChecked: newChecked})}},/*** 组件的初始数据:用来定义当前组件内部所需要使用的数据*/data: {isChecked: false},/*** 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中*/methods: {// 更新复选框的状态updateChecked() {this.setData({isChecked: !this.data.isChecked,/*** 在 JS 中可以访问和获取 properties 中的数据,但一般情况下,不建议修改,因为会造成数据流的混乱*/// lable: '在组件内也可以修改 properties 中的数据'})/*** 目前复选框组件的状态是存储在复选框组件内部的、存储在自定义组件内部的,* 在实际开发中,组件使用者、父组件有时候也需要获取到复选框内部的状态,* 需要使用自定义组件内部发射一个自定义事件,如果组件使用者、父组件需要使用数据,绑定自定义事件进行获取即可。* * 将数组传递给父组件* 如果需要将数据传递给父组件,需要使用 triggerEvent 发射自定义事件,第二个参数是携带得参数*/this.triggerEvent("changeChecked", this.data.isChecked)}}
})
<!-- 需要在自定义组件标签上通过 bind 方法绑定自定义事件,同时绑定事件处理函数 -->
<custom-checkboxlable="我已阅读并同意 用户协议 和 隐私协议"position="right"checked="{{ isChecked }}"bind:changeChecked="getData">我已阅读并同意 用户协议 和 隐私协议
</custom-checkbox><view></view><view class="custom"><custom-checkbox lable="匿名提交" position="left">匿名提交</custom-checkbox>
</view>
Page({data: {isChecked: true},getData(event) {// 可以通过 事件对象.detail 获取子组件传递给父组件的数据console.log(event.detail);if (event.detail) {console.log("提交");} else {console.log("请同意协议!");}}
})

11.组件通信 - 获取组件实例

父组件可以 通过 this.selectComponent 方法,获取子组件实例对象,这样就可以直接访问子组件的任意数据和方法
this.selectComponent 方法在调用时需要传入一个匹配选择器 selector

<custom-checkboxclass="child"id="child"lable="我已阅读并同意 用户协议 和 隐私协议"position="right"checked="{{ isChecked }}"bind:changeChecked="getData"
>我已阅读并同意 用户协议 和 隐私协议
</custom-checkbox><button type="primary" plain bindtap="getChild">获取子组件实例对象</button>
Page({data: {isChecked: true},getData(event) {// 可以通过 事件对象.detail 获取子组件传递给父组件的数据console.log(event.detail);if (event.detail) {console.log("提交");} else {console.log("请同意协议!");}},// 获取子组件的实例对象getChild() {/*** this.selectComponent 方法获取子组件实例对象(参数 可传 id 选择器 和 类选择器)* 获取到实例对象以后,就能获取子组件所有的数据,也能获取子组件的方法*/const res = this.selectComponent('#child')console.log(res.data);}
})

12.组件生命周期

组件的生命周期:指的是组件自身的一些钩子函数,这些函数在特定的时间节点时被自动触发
组件的生命周期函数需要在 **lifetimes **字段内进行声明
组件的生命周期函数有 5 个:createdattached、ready、moved、detached
image.png

<custom06 wx:if="{{ num === 1 }}" />
<button type="warn" plain bindtap="handler">销毁自定义组件</button>
Page({data: {num: 1},handler() {this.setData({num: this.data.num + 1})}
})
<text>{{ name }}</text>
Component({data: {name: 'Tom'},// 组件生命周期函数lifetimes: {/*** 组件实例被创建好以后执行*/created() {console.log('组件 created');// 在 created 钩子函数中不能调用 setData,可以给组件添加一些自定义属性,可以通过 this 的方式进行添加// this.setData({//   name: 'Jerry'// })this.test = '测试'},/*** 组件被初始化完毕、模板解析完成,已经把组件挂在到页面上*/attached() {console.log('组件 attached');// 一般页面的交互会在 attached 钩子函数中进行执行this.setData({name: 'Jerry'}),console.log(this.test);},/*** 组件被销毁时*/detached() {console.log('组件 detached');}}
})

注意事项:created 钩子函数中不能调用 setData

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

组件还有一些特殊的生命周期,这类生命周期和组件没有很强的关联
主要用于组件内部监听父组件的展示和隐藏状态,从而方便组件内部执行一些业务逻辑的处理
组件所在页面的生命周期有 4 个:showhide、resize、routeDone,需要在 pageLifeTimes 字段内进行声明
image.png

// 组件所在页面的生命周期
pageLifetimes: {// 监听组件所在的页面展示(后台切前台)状态show() {console.log('组件 show');},// 监听组件所在的页面隐藏(前台切后台,点击 tabbar)状态hide() {console.log('组件 hide');},resize() {},routeDone() {}
}

14.小程序生命周期总结

一个小程序完整的生命周期由 应用生命周期页面生命周期组件生命周期 三部分来组成

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

image.png

  1. 保留当前页面,进入下一个页面,钩子函数执行的顺序
  2. 销毁当前页面,进入下一个页面,钩子函数执行的顺序

image.png

  1. 小程序热启动,钩子函数执行的顺序

image.png

15.拓展 - 使用 Component 构造页面

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

<navigator url="/pages/detail/detail?id=10&title=测试">跳转到详情页面</navigator>
<view>{{ name }}</view>
<button type="warn" plain bind:tap="updateName">更新名字</button>
{"usingComponents": {}
}
Component({/*** 小程序页面也可以使用 Component 方法进行构造* * 为什么需要使用 Component 方法进行构造页面* Component 方法功能比 Page 方法强大很多,如果使用 Component 方法构造页面,可以实现更加复杂的页面逻辑开发,例如:属性监听等方法* * 注意事项:* 1. 要求 .json 文件中必须要有 usingComponents 字段* 2. 里面的配置项需要和 Component 中的配置项保持一致* 3. 页面中 Page 方法有一些钩子函数、事件监听方法,这些钩子函数和事件监听方法必须放到 methods 对象中才可以* 4. 组件的属性 properties 也可以接收页面的参数,在 onLoad 钩子函数中,可以通过 this.data 进行获取*/data: {name: 'Tom'},properties: {id: String,title: String},methods: {onLoad(options) {console.log('Component onload');// 以下三种方式都可以获取上级页面传递过来的参数// console.log(options);// console.log(this.data.id, this.data.title);// console.log(this.properties.id, this.properties.title);},// 更新nameupdateName() {this.setData({name: 'Jerry'})}}})

为什么需要使用 Component 方法进行构造页面?
Component 方法功能比 Page 方法强大很多,如果使用 Component 方法构造页面,可以实现更加复杂的页面逻辑开发,例如:属性监听等方法
注意事项:

  1. 要求对应 .json 文件中包含 usingComponents 字段;
  2. 页面使用 Component 构造器,需要定义语普通组件一样的字段与实例方法;
  3. 页面 Page 中的一些生命周期方法(如 onLoad() 等以“on”开头的方法),在 Component 中要写在 methods 属性中才能生效;
  4. 组件的属性 properties 也可以用于接收页面的参数,在 onLoad() 中可以通过 this.data 拿到对应的页面参数;

16.拓展 - 组件的复用机制 behaviors

小程序的 behaviors 方法是一种代码复用的方式,可以将一些通用的逻辑和方法提取出来,然后再多个组件中复用,从而减少代码冗余,提高代码的可维护性。
如果需要 behavior 复用代码,需要使用 Behavior() 方法,每个 behavior 可以包含一组属性,数据,生命周期函数和方法
组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会再对应世纪被调用
组件和它引用的 behavior 中可以包含同名的字段,对这些字段的处理方法如下:

  1. 如果有同名的属性或方法,采用“就近原则”,组件会覆盖 behavior 中的同名属性或方法
  2. 如果有同名的数据字段且都是对象类型,会进行对象合并,其余情况会 采用“就近原则"进行数据覆盖
  3. 生命周期函数和 observers 不会相互覆盖,会是在对应触发时机被逐个调用,也就是都会被执行
<view>{{label}}</view>
<view>{{name}}</view>
<view>{{obj.name}} - {{obj.age}}</view><button type="primary" plain bind:tap="updateName">更新数据</button>
const behavior = Behavior({/*** 组件的属性列表*/properties: {label: {type: String,value: "我已同意该协议"}},/*** 组件的初始数据*/data: {name: "Tome",obj: {name: "Tyke"}},/*** 组件的方法列表*/methods: {updateName() {this.setData({name: "Jerry"})console.log("我是组建的函数~~~");}},lifetimes: {attached() {console.log("我是组建的生命周期函数~~~");}}
})export default behavior
import behavior from './behavior'
Component({behaviors: [behavior],/*** 在以后的开发中,使用 behaviors 进行代码复用的时候,组件 和 behaviors 可能存在相同的字段*/// 如果存在相同的 properties,采用就近原则,使用组件内部的数据properties: {label: {type: String,value: "匿名提交"}},// 如果存在相同的 data,如果是对象类型,属性会进行合并,如果不是对象类型的数据,就近原则,展示以组件内部为准data: {name: "组件中的 name",obj: {age: 100}},// 如果存在相同的方法,就近原则,展示以组件内部为准methods: {updateName() {console.log("我是组件内部的方法!!!");}},// 如果存在相同的生命周期函数,生命周期函数都会被触发,先执行 behavior 里面的,后执行 组件内里面的lifetimes: {attached() {console.log("我是组件内部调用的生命周期!!!");}}
})

17.拓展 - 外部样式类

默认情况下,组件和组件使用者之间如果存在相同的类名不会相互影响,组件使用者如果想修改组件的样式,就需要解除样式隔离,但是解除样式隔离以后,在极端情况下,会产生样式冲突、CSS 嵌套太深等问题,从而给我们的开发带来了一定的麻烦。
外部样式类:在使用组件时,组件使用者可以给组件传入 CSS 类名,通过传入的类名修改组件的样式。
如果需要使用外部样式类修改组件的样式,在 Component 中需要用 externalClasses 定义若干个外部样式类。
外部样式类的使用步骤:

  1. 在 Component 中用 extemmalClasses 定义段定义若干个外部样式类
  2. 自定义组件标签通过 属性绑定 的方式提供一个样式类,属性是 externalClasses 定义的元素,属性值是传递的类名
  3. 将接受到的样式类用于自定义组件内部

注意事项:在同一个节点上使用普通样式类和外部样式类时,两个类的优先级是未定义的,因此需要添加 !imporant 以保证外部样式类的优先级

<!-- 属性是在 externalClasses 里面定义的属性,属性值必须是一个类名  -->
<custom09 extend-class="my-class" />
.my-class {color: coral !important;
}
<!-- 在同一个节点上,如果存在 外部样式类 和 普通的样式类两个类的优先级是未定义的,建议:在使用外部样式类时,样式通过 !important 添加权重-->
<view class="extend-class box">通过外部样式类修改组件的样式</view>
.box {color: cornflowerblue;
}
Component({// 组件接受的外部样式类externalClasses: ['extend-class']
})

18.完善复选框案例以及总结自定义组件

  1. 组件基本使用:数据、属性、方法、插槽
  2. 组件样式使用:组件样式、注意事项、样式隔离、外部样式类
  3. 组件通信传值:父往子传值、子往父传值、获取组件实例
  4. 组件生命周期:组件的生命周期、组件所在页面的生命周期、总结了小程序全部的生命周期
  5. 组件数据监听器:observers
  6. 组件拓展:使用 Component构造页面、组件复用机制 behaviors 等

如果 styleIsolation 属性值是 shared 时,externalClasses 选项会失效

十、使用 npm 包

1.使用 npm 包

目前小程序已经支持使用 npm 安装第三方包,因为 node modules 目录中的包不会参与小程序项目的编译、上传和打包,因此在小程序项目中要使用的 npm 包,必须走一遍 构建 npm 的过程。
在构建成功以后,默认 会在小程序项目根目录,
也就是
node_modules 同级目录下生成 miniprogram_npm 目录,

里面存放着构建完成以后的 npm 包,也就是小程序运行过程中真正使用的包。
image.png
注意事项:
小程序运行在微信内部,因为运行环境的特殊性,这就导致 并不是所有的包都能够在小程序使用
我们在小程序中提到的包指专为小程序定制的包,简称小程序 npm 包,在使用之前需要先确定该包是否支持小程序
开发者如果需要发布小程序包,需要参考官方规范:npm 支持 | 微信开放文档

2.自定义构建 npm

在实际的开发中,随着项目的功能越来越多、项目越来越复杂,文件目录也变的很繁琐,为了方便进行项目的开发,开发人员通常会对目录结构进行调整优化,例如:将小程序源码放到 miniprogram 目录下
这时候需要开发者在 project.config.json 中 指定 node_moudles 的位置目标 miniprogram_npm 的位置
具体配置如下:

  1. 配置 project.config.json 的 miniprogramRoot 指定小程序源码的目录
  2. 配置 project.config.json 的 setting.packNpmManually 为 true,开启自定义 node_modules 和 miniprogram_npm 位置的构建 npm 方式
  3. 配置 project.config.json 的 seting.packNpmRelationList 项,指定 packagesonPath 和 miniprogramNpmDistDir 的位置

3.Vant Weapp 组件库的使用

Vant Weapp 是有赞前端团队开源的小程序 UI 组件库,基于微信小程序的自定义组件开发,可以用来快速搭建小程序项目。
演示:image 图片组件

{..."usingComponents": {"van-image": "@vant/weapp/image/index","van-loading": "@vant/weapp/loading/index"}
}
<!-- 在使用 van-image 图片组件时,如果需要渲染本地图片是,不能使用 ../,需要相对于小程序源码的目录来查找图片才可以 -->
<van-imageround width="100"height="100" src="/assets/Jerry.png"bind:click="imageHandler"use-loading-slotuse-error-slotcustom-class="custom-class"
><van-loading slot="loading" type="spinner" size="20" vertical /><text slot="error">加载失败</text>
</van-image>
Page({imageHandler() {console.log("点击图片");}
})
.custom-class {border: 10rpx solid lightgreen !important;
}

在使用 Vant 提供的组件时,只需要两个步骤:

  1. 将组件在 app.json 中进行全部注册 或者 index.json 中进行局部注册
  2. 在引入组件后,可以在 wxml 中直接使用组件

注意事项:将 app.json 中的 “style”: “v2” 去除,小程序的新版基础组件强行加上了许多样式,难以覆盖,不关闭将造成部分组件样式混乱。

4.Vant Weapp 组件样式覆盖

Vant Weapp 基于微信小程序的机制,为开发者提供了 3 种修改组件样式的方法:

  1. 解除样式隔离:在页面中使用 Vant Weapp 组件时,可直接在页面的样式文件中覆盖样式
  2. 使用外部样式类:需要注意的是普通样式类和外部样式类的优先级是未定义的,使用时需要添加 !important 保证外部样式类的优先级
  3. 使用 CSS 变量:在页面或全局对多个组件的样式做批量修改以进行主题样式的定制

十一、分包加载

1. 小程序分包加载

小程序的代码通常是由许多页面、组件以及资源等组成,随着小程序功能的增加,代码量也会逐渐增加,体积过大就会导致用户打开速度变慢,影响用户的使用体验。
分包加载是一种小程序优化技术。将小程序不同功能的代码,分别打包成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载,在构建小程序分包项目时,构建会输出一个或多个分包。每个使用分包小程序必定含有一个主包
主包:包含默认启动页面/TabBar 页面 以及 所有分包都需用到公共资源的包
分包:根据开发者的配置进行划分出来的子包
分包后加载顺序:在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,微信客户端会把对应分包下载下来,下载完成后再进行展示。
image.png
目前小程序分包大小有以下限制:

  1. 整个小程序所有分包大小不超过 20MB(大小可能调整)
  2. 单个分包/主包大小不能超过 2MB

2. 配置分包加载以及打包、引用原则

小程序如果需要进行分包加载,需要在 app.json 中,通过 subPackages 或者 subpackages 定义分包结构
每个分包结构含三个常用字段:

  1. root:分包的根目录,该目录下的所有文件都会被打包成一个独立的包
  2. name:分包的别名,用于在代码中引用该分包
  3. pages:指定当前分包中包含哪些页面

例:配置 商品模块 分包,分包包含:商品列表、商品详情两个页面

"subPackages": [{"root": "modules/goodModule","name": "goodModule","pages": ["pages/list/list", "pages/detail/detail"]}],

在跳转到分包页面时,需要加上分包路径:

<navigator url="/modules/goodModule/pages/list/list" open-type="navigate">跳转到列表页面</navigator>

打包原则:

  1. tabBar 页面必须在主包内
  2. 最外层的 pages 字段,属于主包的包含的页面
  3. 按 subpackages 配置路径进行打包,配置路径外的目录将被打包到主包中
  4. 分包之间不能相互嵌套,subpackage 的根目录不能是另外一个 subpackage 内的子目录

引用原则:

  1. 主包不可以引用分包的资源,但分包可以使用主包的公共资源
  2. 分包与分包之间资源无法相互引用,分包异步化时不受此条限制

3. 独立分包

独立分包:是指能够独立于主包和其他分包运行的包
从独立分包中页面进入小程序时,不需要下载主包,当用户进入普通分包或主包内页面时,主包才会被下载
开发者可以将功能相对独立的页面配置到独立分包中,因为独立分包不依赖主包就可以运行,可以很大程度上提升分包页面的启动速度。
给 subPackages 定义的分包结构添加 independent 字段,即可声明对应分包为独立分包。

  "subPackages": [{"root": "modules/goodModule","name": "goodModule","pages": ["pages/list/list", "pages/detail/detail"]},{"root": "modules/markModule","name": "markModule","independent": true,"pages": ["pages/mark/mark"]}],

注意事项:

  1. 独立分包中不能依赖主包和其他分包中的资源
  2. 主包中的 app.wxss 对独立分包无效
  3. App 只能在主包内定义,独立分包中不能定义 App,会造成无法预期的行为

4. 分包预下载

分包预下载是指访问小程序某个页面时,预先下载其他分包中的代码和资源,当用户需要访问分包中的页面时,已经预先下载的代码和资源,因此可以直接使用,从而提高用户的使用体验。
小程序的分包预下载需要在 app.json 中通过 preloadRule 字段设置预下载规则
preloadRule 是一个对象,对象的 key 表示访问哪个路径时进行预加载,value 是进入此页面的预下载配置,具有两个配置项:

  1. packages:进入页面后预下载分包的 root 或 name,APP 表示主包。
  2. network:在指定网络下预下载,可选值为:all(不限网络)、wifi(仅wifi下预下载)
 "preloadRule": {"pages/index/index": {"network": "all","packages": ["goodModule", "modules/markModule"]},"modules/markModule/pages/mark/mark": {"network": "all","packages": ["__APP__"]}},

十二 、开放能力

1. 获取微信头像

当小程序需要让用户完善个人资料时,我们可以通过微信提供的头像、昵称填写能力快速完善
image.png
想使用微信提供的头像填写能力,需要两步:

  1. button 组件 open-type 的值设置为 chooseAvatar
  2. 通过 bindchooseavatar 事件回调获取到头像信息的临时路径。
<view><button class="btn" open-type="chooseAvatar" bindchooseavatar="chooseAvatar"><image class="avatar" src="{{ avatar }}" mode=""/></button>
</view>
.btn {background-color: transparent;
}
.btn::after {border: none;
}.avatar {width: 200rpx;height: 200rpx;border-radius: 50%;
}
Page({/*** 页面的初始数据*/data: {avatar: "../../assets/Jerry.png"},chooseAvatar(event) {// 目前获取的微信头像是临时路径,临时路径是有失效时间的,在实际开发中,需要将临时路径上传到公司的服务器const { avatarUrl } = event.detail;this.setData({avatar: avatarUrl})}
})

2. 获取微信昵称

当小程序需要让用户完善个人资料时,我们可以通过微信提供的头像、昵称填写能力快速完善
image.png
想使用微信提供的昵称填写能力,需要三步:

  1. 通过 form 组件中包裹住 input 以及 form-type 为 submit 的 button 组件
  2. 需要将 input 组件 type 的值设置为 nickname,当用户输入框输入时,键盘上方会展示微信昵称
  3. 给 form 绑定 submit 事件,在事件处理函数中通过事件对象获取用户昵称
<!-- 需要使用 form 组件包裹住 input 以及 button 组件 -->
<form bindsubmit="onSubmit"><!-- input 输入框组件的 type 属性设置为 nickname,用户点击输入框,键盘上方才会显示昵称 --><!-- 如果添加了 name 属性,from 组件就会自动收集 name 属性的表单元素的值--><input type="nickname" name="nickname" placeholder="请输入昵称" /><!-- 如果将 from-type="submit",就将按钮变为提交按钮,在点击提交按钮的时候,会触发 表单的 bindsubmit 提交事件 --><button type="primary" plain form-type="submit">点击获取昵称</button>
</form>
input {border: 1px solid #179c16;margin: 20rpx;height: 60rpx;border-radius: 20rpx;padding-left: 20rpx;
}
Page({// 获取微信昵称onSubmit(event) {// console.log(event);const { nickname } = event.detail.value;console.log(nickname);}
})

3. 转发功能

转发功能,主要帮助用户更流畅地与好友分享内容和服务
想实现转发功能,有两种方式:

  1. 页面is 文件 必须声明 onShareAppMessage 事件监听函数,并自定义转发内容只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮
  2. 通过 给 button 组件设置属性 open-type=“share”,可以在用户点击按钮后触发 Page.onShareAppMessage 事件监听函数

image.png

<!-- 页面设置转发按钮 -->
<button plain type="primary" open-type="share">转发</button>
Page({/*** 监听页面按钮的转发以及右上角的转发按钮*/onShareAppMessage: function (obj) {console.log(obj);return {title: "这是一个神奇的页面~~~",page: "/miniprogram/pages/index/index",imageUrl: "../../assets/Jerry.png"}}
})

通过页面转发,形参 obj 的 target 有值 {from: "button", target: {…}},通过右上角 “转发”按钮转发,形参 obj 的 target 没有值,{from: "menu", target: undefined}

4. 分享到朋友圈

小程序页面默认不能被分享到朋友圈,开发者需主动设置“分享到朋友圈”才可以,实现分享到朋友圈需满足两个条件:

  1. 页面 必须 设置允许“发送给朋友”,页面 js 文件声明 onShareAppMessage 事件监听函数
  2. 页面 必须 需设置允许“分享到朋友圈”,页面 js 文件声明 onShareTimeline 事件监听函数

image.png

Page({/*** 监听页面按钮的转发以及右上角的转发按钮*/onShareAppMessage: function (obj) {console.log(obj);return {title: "这是一个神奇的页面~~~",page: "/miniprogram/pages/index/index",imageUrl: "../../assets/Jerry.png"}},/*** 监听右上角 分享到朋友圈 按钮*/onShareTimeline: function() {return {title: "帮我砍一刀~~",query: "id=1",imageUrl: "../../assets/Jerry.png"}}
})

5. 手机号验证组件

手机验证组件,用于帮助开发者向用户发起手机号申请,必须经过用户同意后,才能获得由平台验证后的手机号,进而为用户提供相应服务
手机号验证组件分为两种:手机号快速验证组件 以及 手机号实时验证组件

  1. 手机号快速验证组件:平台会对号码进行验证,但不保证是实时验证
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber" />
  1. 手机号实时验证组件:在每次请求时,平台均会对用户选择的手机号进行实时验证
<button open-type="getRealtimePhoneNumber" bindgetrealtimephonenumber="getrealtimephonenumber" />

注意事项:

  1. 目前该接口针对非个人开发者,且完成了认证的小程序开放(不包含海外主体)
  2. 两种验证组件需要付费使用,每个小程序账号将有 1000 次体验额度

image.png

<buttontype="primary"plainopen-type="getPhoneNumber"bindgetphonenumber="getPhoneNumber"
>快速验证组件</button>
<buttontype="warn"plainopen-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,当用户点击后就会进入客服会话
  2. 在微信公众后台,绑定后的客服账号,可以登陆 网页端客服 或 移动端小程序 客服接收、发送客服消息

image.png

<button type="warn" plain open-type="contact">联系客服</button>

十三、补充

1. 框架接口 - getApp()

在小程序中,可以通过 getApp() 方法获取到小程序全局唯一的 App 实例,因此在 App() 方法中添加全局共享的数据、方法,从而实现页面、组件的数据传值。

App({// 全局共享的数据globalData: {token: ''},// 全局共享的方法setToken(token) {// 如果想获取 token,可以使用 this 的方式进行获取this.globalData.token = token// 在 App() 方法中,如果想获取 App() 实例,可以通过 this 的方式进行获取,不能通过 getApp() 方法获取}
})
<button type="primary" plain bind:tap="login">登录</button>
// getApp() 方法用来获取全局唯一的 App() 实例
const appInstance = getApp();
Page({// 登录login() {// 不要通过 app 实例 调用钩子函数console.log(appInstance);appInstance.setToken("adadafasdfsfafdafasfdafasfdafaf")}
})
const appInstance = getApp();
Page({onLoad() {console.log(appInstance.globalData.token); // adadafasdfsfafdafasfdafasfdafaf}
})

注意事项:

  1. 不要在 App() 方法中使用 getApp(),使用 this 就可以拿到 app 实例
  2. 通过 getpp() 获取实例之后,不要私自调用生命周期函数

2. eventChannel - 页面间通信

如果一个页面通过 wx.navigateTo 打开一个新页面,这两个页面间将建立-条数据通道

  1. 在 wx.navigateTo 的 success 回调中通过 EventChannel 对象发射事件
  2. 被打开的页面可以通过 this.getopenerEventChannel() 方法来获得一个 Eventchannel 对象,进行监听、发射事件
  3. wx.navigateTo 方法中可以定义 events 配置项接收被打开页面发射的事件

image.png

<button type="warn" plain bind:tap="handler">跳转到列表页面</button>
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' })}})}
})
Page({navigateBack() {// 关闭当前页面,返回上一级或者返回多级页面wx.navigateBack({delta: 1})},onLoad(options) {// 通过 this.getOpenerEventChannel() 可以获取 EventChannel 对象const EventChannel = this.getOpenerEventChannel();// 通过 EventChannel 提供的 on 方法监听页面发射的自定义事件EventChannel.on("myEvent", (res) => {console.log(res);})// 通过 EventChannel 提供的 emit 方法,也可以向上一级页面传递数据,需要使用 emit 定义自定义事件,携带需要传递的数据EventChannel.emit("currentEvent", {age: 10})}
})

3. 小程序组件通信 - 事件总线

随着项目功能的增加,业务逻辑也会变的很复杂,一个页面可能是由多个组件进行构成,并且这些组件之间需要进行数据的传递。这时候如果使用之前学习的组件传值方式进行数据的传递,就会比较麻烦
image.png
事件总线是对发布-订阅模式的一种实现,是一种集中式事件处理机制,允许不同的组件之间进行彼此通信,常用于两个非父子关系组件和兄弟组件之间通讯。我们可以借助第三方的 发布订阅 JS 包,来实现事件总线的功能,PubsubJs

<view><text>父组件,子组件 A 和 子组件 B 是兄弟关系</text><custom06 /><custom09 />
</view>
<view class="son1"><text>子组件 A</text><button bind:tap="setData">传递数据给兄弟</button>
</view>
import PubSub from 'pubsub-js'
Component({data: {name: 'Tom'},methods: {setData() {/*** publish 发布、发射自定义事件* 第一个参数:自定义事件的名称* 第二个参数:需要传递的数据*/PubSub.publish("myEvent", { name: this.data.name, age: 10 })}}
})
<view class="son2"><text>子组件 B</text><view>{{ name }}</view>
</view>
import PubSub from 'pubsub-js'
Component({/*** 组件的初始数据*/data: {name: ''},lifetimes: {attached() {/*** subscribe 订阅、监听自定义事件* 第一个参数:需要订阅、监听的自定义事件名称* 第二个参数:回调函数,回调函数有两个参数:msg:自定义事件的名称  data:传递过来的参数*/PubSub.subscribe("myEvent", (msg, data) => {console.log(msg, data);this.setData({name: data.name})})}}
})

4. 自定义导航栏

小程序默认的导航栏与 APP 一样都位于顶部固定位置。但是默认导航栏可能会影响小程序整体风格,且无法满足特定的设计需求,这时候,就需要进行自定义导航栏。
在 app.json 或者 page.json 中,配置 navigationStyle 属性为 custom,即可 自定义导航栏,在设置以后,就会移除默认的导航栏,只保留右上角胶囊按钮。

{"usingComponents": {},"navigationStyle": "custom"
}
<swiperindicator-dotsautoplayinterval="2000"class="custom-swiper"
><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>
.custom-swiper {height: 440rpx;
}.custom-swiper image {width: 100%;height: 100%;
}

5. 小程序上线发布

假设我们目前已经完成了小程序的开发,并且通过了本地测试,这时候小程序就需要上线发布。
image.png
开发版本:点击开发者工具上传后的版本,开发版本只保留每人最新的一份上传的代码,是供开发者和团队测试和调试的版本
体验版本:小程序开发者可以将开发版本转换为体验版本,由测试人员以及产品经理进行测试与体验,确认没问题可提交审核
审核版本:小程序开发者可以将开发版本转换为审核版本,由微信的审核团队进行审核,审核周期为 1~7 天,审核通过可提交发布
线上版本:通过微信小程序平台审核,并由开发者提交发布的正式版本,线上版本是用户可以正常使用的小程序版本

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/26693.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ARM Cache 及 MMU 系列文章 6.4 -- ARMv8/v9 如何读取 Cache Tag 及分析其数据?】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 Cache Tag 数据读取测试代码Cache Tag 数据读取 在处理器中,缓存是一种快速存储资源,用于减少访问主内存时的延迟。缓存通过存储主内存中经常访问的数据来实现这一点。为了有效地管…

未来的5-10年,哪些行业可能会被AI代替?

在未来的5-10年&#xff0c;多个行业可能会受到AI技术的影响&#xff0c;其中一些工作可能会被AI所代替。以下是对可能被AI替代的行业及工作的一些概述&#xff1a; 客户服务与代表&#xff1a;随着AI技术的发展&#xff0c;特别是自动话术对话和语音生成技术的进步&#xff0…

快捷键专栏 IDEA、Navicat、电脑、Excle、Word等

标题 电脑篇windowsR 配合以下常用命令连上公司网线WiFi速度变慢问题解决Windows10 设置鼠标右键在此处打开cmd和Powershell窗口、关机打开电脑诊断工具系统设置常用设置查看电脑出场日期 systeminfo删除文件显示已在另一个程序打开&#xff1f;找回回收站删除的文件WindowsR输…

Mocha Pro 2024 v11.0.1 Mac版摄像机反求跟踪插件更新:优化AE/PR/OFX/达芬奇工作流程

更新Mac苹果版&#xff0c;原生支持Intel和Apple M芯片&#xff0c;安装很简单。Mocha Pro 是一款世界知名的软件和插件&#xff0c;用于平面运动跟踪、3D 跟踪、动态观察、对象移除、图像稳定和PowerMesh有机扭曲跟踪。得益于集成SynthEyes核心3D跟踪算法的强大功能&#xff0…

linux为什么不是实时操作系统

Linux为什么不是实时操作系统&#xff1f; 从我们接触Linux系统开始&#xff0c;一直听到的都是它是非实时操作系统&#xff0c;怎么理解这个非实时呢&#xff1f; 我的理解&#xff0c;非实时&#xff0c;就是中断响应不及时&#xff0c;任务调度不及时。那么&#xff0c;真…

【数组】【双指针】三数之和

打算冲一把算法类比赛&#xff0c;之前一直对算法提不起兴趣&#xff0c;也有我自己对它的抵触&#xff0c;本身算法也比较菜。 但现在打算勤勤恳恳刷题&#xff0c;踏踏实实总结&#xff0c;冲&#xff01; 数组——双指针 三数之和 该题力扣网址 错误做法 三重循环框架&a…

互联网+智慧运维管理平台建设方案

互联网智慧运维管理平台建设方案 随着信息技术的飞速发展&#xff0c;互联网与各行各业的深度融合已经成为大势所趋。在运维管理领域&#xff0c;传统的运维模式已经无法满足日益复杂的业务需求和技术环境。为了提升运维效率、降低运维成本、保障系统稳定运行&#xff0c;构建…

Adobe Illustrator (AI)小技巧总结

AI2024(64bit) Adobe Illustrator 软件安装包下载地址&#xff1a; 百度网盘下载https://pan.baidu.com/s/1C10-2JVN1rxFF5VFRuV2Yw?pwdSIMS 1.效果-扭曲与变换-变换&#xff0c;两个图形组合&#xff08;CtrlG&#xff09;中心点在中间 例&#xff1a;角度7.5副本24半圆48格…

【轮询负载均衡规则算法设计题】

一、题目描述 给定n台主机&#xff08;编号1~n&#xff09;和某批数据包&#xff0c;数据包格式为&#xff08;抵达主机时刻&#xff0c;负载量&#xff09;。这里数据每个时刻最多只有1条数据到达。负载量表示该主机处理此数据包总耗时。请计算轮询负载均衡规则下&#xff0c…

云平台关键技术 | 使用异步框架 celery 后台执行linux命令的实例

准备工作 (1) 环境 J1 server, CentOS7.9$ which gcc /home/wangjl/soft/gcc-12.3.0/bin/gcc$ which python3 /home/wangjl/soft/python3/python-3.10.14/bin/python3$ gcc --version gcc (GCC) 12.3.0 Copyright (C) 2022 Free Software Foundation, Inc.$ python3 -V Pytho…

计算机msvcp100.dll丢失怎么办,分享5种亲测有效的解决方法

电脑已经成为我们生活中不可或缺的一部分。然而&#xff0c;在使用电脑的过程中&#xff0c;我们常常会遇到一些问题&#xff0c;其中之一就是电脑提示缺失msvcp100.dll。这个问题可能会让我们感到困惑和烦恼&#xff0c;但是只要我们了解其原因并采取相应的解决方法&#xff0…

oracle 自动优化SYS_AUTO_SQL_TUNING_TASK操作

1、查询SYS_AUTO_SQL_TUNING_TASK在执行什么任务 select execution_name,status,execution_start,execution_end from dba_advisor_executions where task_nameSYS_AUTO_SQL_TUNING_TASK order by execution_start desc; select * from DBA_ADVISOR_SQLSTATS where execution_n…

数据仓库学习之hbase-2.2.7分布式搭建

hbase-2.2.7分布式搭建 1、上传解压配置环境变量 # 1、解压 tar -xvf hbase-2.2.7-bin.tar.gz.gz# 2、配置环境变量 vim /etc/profile# 3、在最后增加配置 export HBASE_HOME/usr/local/soft/hbase-2.2.7 export PATH$PATH:$HBASE_HOME/bin# 4、使环境变量剩下 source /etc/p…

Java常见规范及易忘点

文章目录 命名-注释-格式命名注释格式 语法规范易错点 命名-注释-格式 命名 类&#xff0c;接口&#xff0c;枚举&#xff0c;注解名用大驼峰命名 方法名&#xff0c;变量名小驼峰 静态常量全大写&#xff0c;下划线_分割 boolean类型 isError 小驼峰&#xff0c;不能isNo…

利用API推动业务创新

在数字化时代&#xff0c;企业不断创新以求生存和发展。API&#xff08;应用程序编程接口&#xff09;作为一种强大的技术工具&#xff0c;为业务创新提供了无限的可能性。通过正确地开发和应用API&#xff0c;企业能够打造生态系统&#xff0c;开拓新市场&#xff0c;提升用户…

Go AfterFunc 不触发

前言 函数原型为&#xff1a; func AfterFunc(d Duration, f func()) *TimerGo 的 time.AfterFunc 的作用是等待指定的时间间隔&#xff0c;然后在它自己的 goroutine 中调用 f。 现在有一个问题&#xff0c;我明明调用了 AfterFunc&#xff0c;但是它还没调用我指定的函数&…

FPGA+金融|硬件行情加速系统 打造极速交易场景

会议时间&#xff1a;2024年06月20日&#xff08;周四&#xff09;下午13:50 FPGA金融|硬件行情加速系统 打造极速交易场景_中科亿海微_芯有灵犀 智创未来

Python学习笔记12 -- 有关布尔值的详细说明

一、布尔表达式 最终值为true 或者false 二、常见形式&#xff1a; 1、常量&#xff1a;true false 2、比较运算&#xff1a; and &#xff01; 3、复合运算&#xff1a; and and or 4、其他 例&#xff1a;检测闰年&#xff1a; def specialYearMine(year):if (year%4 …

Elasticsearch集群运维,重平衡、分片、宕节点、扩容

个人博客&#xff1a;无奈何杨&#xff08;wnhyang&#xff09; 个人语雀&#xff1a;wnhyang 共享语雀&#xff1a;在线知识共享 Github&#xff1a;wnhyang - Overview 参考 探索集群 Elasticsearch 中文文档 https://www.elastic.co/guide/en/elasticsearch/reference…

方差,标准差,CPK指标数据分析 (使用SQL做数据分析)

以下SQL语句&#xff0c;使用sqlserver数据库 方差的计算公式为&#xff1a; 方差 [(x1 - 平均数)^2 (x2 - 平均数)^2 … (xn - 平均数)^2] / n 其中 x1、x2、…、xn 是样本中的各个数据&#xff0c;n 是样本数量。 SELECT AVG(valu) AS avg_valu,COUNT(valu) AS cnt,SUM…