当前位置: 首页 > news >正文

微信小程序开发03 自定义组件:怎么培养组件化思维?

我们已经学习了微信小程序的双线程模型、授权模型,其中双线程模型是底层原理;授权模型是从整体角度(前端+后端)的登录流程,这两讲能帮你从底层和整体更深入地理解小程序。

今天这节课我们进入小程序的纯前端,一起学习自定义组件和它背后的组件化思想。

你要注意,小程序自定义组件的开发细节非常多,我不会事无巨细地全部讲一遍,不然跟文档没什么区别了,我会选择开发自定义组件过程中,你需要重点关注的几个核心点来讲解:

  • 自定义组件的资源管理;

  • 组件的生命周期;

  • 组件间的通信流程。

在这个过程中,我们会结合组件化思想,以及 Web Components 规范辅助理解。Web Components 规范是 W3C 推出的一套用于封装具有复用性、互动性前端组件的技术规范,旨在提供一种标准的组件化模式。

目前流行的几个前端框架( Vue / React / Angular )都在一定程度上遵循了这套规范,微信小程序的自定义组件也是如此,而且,微信小程序渲染自定义组件使用的是 Shadow DOM,这项技术是 Web Components 规范的一部分(这节课的核心并不是讲 Shadow DOM,如果感兴趣的话,你可以在课后学习)。话不多说,我们正式进入今天的学习。

为什么要学习自定义组件?

首先,我想问你一个问题:为什么要学习自定义组件?

作为一名前端开发者,你可能经历过 BootStrap 盛行的年代,也可能是从更悠久的 ExtJS 时代一路走来,就算你不了解这两个框架,肯定不可避免地使用过 React、Vue 或 Angular 这三种框架。React / Vue / Angular 与它们的前辈 BootStrap / ExtJS 有一点是共通的:它们都是前端组件化的推崇者。

你可以把“前端组件化”理解为“面向对象编程思想在前端 UI 领域的具体实践,将部分 UI 内容抽离为独立的可复用的组件”,这样做有这样 3 点优势:

  • 提高代码可复用性

这是组件化最直接的优点,如果不用组件,每次遇到相同的业务场景都需要重新编写代码,而被抽离后的组件可以在其适用的场景内被重复使用,很大程度上降低了开发耗时。

  • 降低代码维护难度

想象一下,假如一个页面的所有 UI 源码都集中在同一个 HTML 文件中,当页面中的导航栏出现 Bug,你需要在上千行甚至上万行的 HTML 文件中找到导航栏对应的 HTML 标签。如果将导航栏抽离为一个组件,那么你仅仅需要在这个组件内寻找。这类案例在工作中普遍存在,通过这个例子可以充分说明组件化在降低代码维护难度方面的优势。

  • 降低系统重构难度

《重构:改善既有代码的设计》讲了:重构并不是当系统复杂度提升到一定程度难以维护时的一次性行为,而是一种高频的、小规模的日常行为。直白点儿说就是:应该不断通过重构来改善系统,不管重构的范围有多小。但是这种实践方式对于代码的可维护性有很大的挑战,这也间接说明了组件化对重构工作的正面影响:通过提高代码的可维护性,间接降低了系统的重构难度。

微信小程序的自定义组件是组件化思想在小程序开发领域的一种具体落地实践,具有上面三个优点。而且不论是工作还是面试,能否开发出优秀的自定义组件是衡量小程序开发者(甚至前端开发者)的核心指标之一。我相信这一点能够成为你学习今天这节课的动力。

那么接下来我们具体来看一下,在开发微信小程序自定义组件的三个核心环节中,你要注意哪些问题。

自定义组件的资源管理

创建微信小程序自定义组件需要使用 Component 构造器,这是微信小程序结构体系内最小粒度的构造器,外层是 Page 构造器,最外层的是 App 构造器,三者的关系如下图:

Drawing 1.png

从外到内依次是 App > Page > Component,每次递进是 1:N 的关系:

  • 1 个 App(也就是 1 个小程序)可包含 N( N >= 1 )个 Page;

  • 1 一个 Page 可包含N(N>=1)个 Component。

每个自定义组件的资源必须包括四个基本文件:

  • 用于描述组件结构的 wxml 文件;

  • 用于描述组件样式的 wxss 文件;

  • 用于描述组件行为的 js 文件;

  • 用于声明组件配置的 json 文件。

这里我多说一句,跟传统前端开发相比,小程序自定义组件的 wxml 和 wxss 文件的编写方式与 HTML 和 CSS 编写基本类似,不要特别关注,差异性主要体现在 js 和 json 文件上。

接着说回来,在 json 文件中必须通过 component 字段声明此组件为自定义组件,如下:

{
  "component": true
}

js 文件中通过 Component 构造器创建组件的逻辑实体,如下:

Component({
  behaviors:[],
  properties:{},
  data: {},
  lifetimes: {},
  pageLifetimes: {},
  methods: {}
});

我们可以对照 Vue 和 React 讲解 Component 构造器的几个属性,这样更容易理解:

  • behaviors 类似于 Vue 和 React 中的 mixins,用于定义多个组件之间的共享逻辑,可以包含一组 properties、data、lifetimes 和 methods 的定义;

  • properties 类似于 Vue 和 React 中的 props ,用于接收外层(父组件)传入的数据;

  • data 类似于 Vue 中的 data 以及 React 中的 state ,用于描述组件的私用数据(状态);

  • lifetimes 用于定义组件自身的生命周期函数,这种写法是从小程序基础库 2.2.3 版本引入的,原本的写法与 Vue 和 React 类似,都是直接挂载到组件的一级属性上(下一小节我们将详细讲解生命周期函数的相关知识);

  • pageLifetimes 是微信小程序自定义组件独创的一套逻辑,用于监听此组件所在页面的生命周期。一般用于在页面特定生命周期时改变组件的状态,比如在页面展示时(show)把组件的状态设置为 A,在页面隐藏时(hide)设置为 B;

  • methods 与 Vue 的 methods 类似,用于定义组件内部的函数。

除 4 个基础文件以外,自定义组件还可以包含一些其他必要的资源,比如图片,下图展示的是自定义组件 chatroom 的资源列表:

Drawing 2.png

你可以看到,除了 wxml/wxss/js/json 文件以外,还有两个图片文件,在 wxml 中可以直接使用相对目录引用,如下:

<image src="./photo.png"></image>

自定义组件的生命周期

而对一个组件来说,生命周期指的是这个组件从被创建到销毁的过程,在这个过程中的里程碑阶段暴露出一些钩子函数,方便开发者针对不同阶段编写逻辑,这些函数就是所谓的“生命周期函数”。微信小程序自定义组件的生命周期函数有以下几个(这张图描述得比较清晰,我就不多说了):

Drawing 4.png

那你要怎么理解这些生命周期呢?

跟 Vue 和 React 相比,小程序自定义组件的生命周期更贴近 Web Components 规范。所以接下来我们结合 Web Components 规范来理解小程序自定义组件的生命周期。

Web Components 规范引入了一个概念:自定义 HTML 元素。目的跟小程序类似,都是为了创建一种自定义的 UI 组件。浏览器环境中,每个 HTML 标签都存在一个对应的类(Class),比如段落节点

对应 HTMLParagraphElement 类,继承这个类所创建的元素便是自定义 HTML 元素,如下代码:

// 创建自定义元素
class MyCustomParagraphElement extends HTMLParagraphElement {
   //...
}
// 注册自定义元素
customElements.define('custom-p', MyCustomParagraphElement);

自定义元素必须被注册(或者叫作定义)之后才可以被使用,上述代码的最后一行便是注册逻辑,第一个参数是该元素被注册后的 HTML 标签名称。注册成功后便可以直接在 HTML 中使用该元素,如下:

<custom-p></custom-p>

这个流程与微信小程序的自定义组件非常相似,只不过注册组件的行为是由小程序底层处理的,开发者仅需要编写组件本身的代码就可以了。

Web Components 规范对于自定义 HTML 元素的生命周期描述为下图所示的流程:

Drawing 6.png

我详细解释一下:

Drawing 7.png

我们对比 Web Components 规范和小程序自定义组件的生命周期,两者有一定相似之处但并不完全一致,总结出这样几点:

  1. 小程序自定义组件的 attached 和 detached 函数分别对应 Web Components 规范的connectedCallback 和 disconnectedCallback,功能上是一致的;

  2. 小程序自定义组件的 moved 函数与 Web Components 规范的 adoptedCallback 类似但作用并不完全相同。由于小程序不支持 iframe,所以不存在组件在文档范畴上的迁移,只能在同一个文档的不同父节点之间迁移。所以也就不存在 adopted 状态,moved 函数可以理解为adopted 的一种变体;

  3. 小程序自定义组件独有的生命周期函数,created、ready 和 error;

  4. Web Components 规范独有的生命周期函数,attributeChangedCallback。

可见小程序自定义组件与 Web Components 规范的主要差异体现在第 3 点和第 4 点。为什么会有这样的差异呢?

差异点一:为什么小程序的自定义组件没有attributeChangedCallback函数?

首先我们要明确 attributeChangedCallback 函数的触发时机,Web Components 规范对这个函数的描述为“当自定义元素的任一属性发生改变(包括新增、删除、更新)时触发”。而更新元素属性这种行为是传统 DOM 编程中常见的,在目前倡导数据驱动 UI 的背景下,绝大多数框架都是通过 VDOM 来间接操作 DOM,所以更新属性在目前的时代背景下非常少见。

微信小程序与 Vue/React 一样,同样不允许直接操作 DOM,从根本上就不可能发生 DOM 属性改变的情况。这就解释了为何小程序自定义组件的生命周期中没有 attributeChangedCallback 函数。

差异点二:Web Components 规范为何没有 created/ready/error 三个函数?

技术规范是一种指导方针,具体的实现方式往往需要根据现实情况决定,这一点对于前端开发者来说再熟悉不过了(比如 CSS 规范在不同浏览器上的各种语法)。

Web Components 规范同样如此,它脱离于业务,单纯从技术的角度提供了最基础的标准和参考,具体到实现层面,Vue/React 之类的框架有各自的理解,微信小程序同样也有独到之处。

之所以有差异,一方面是出于各框架开发者对规范的理解和延伸,另一方面是考虑到实际的业务需要,所以往往会有一些规范未覆盖的“创新”之处,最典型的就是 document.ready 事件。在DOMContentLoad 规范推出之前,jQuery 的 $(document).ready 事件已经在前端技术圈盛行了很久,这个事件发生了 window.onload 之前,此时的文档状态处于渲染未完成但是可交互,所以这个事件在优化网站性能的 FIT(First Load Time,提高加载速度)方面被频繁使用。

回到这个问题本身,小程序自定义组件的 created、ready 和 error 三个函数与 document.ready 有异曲同工之妙,都是结合框架本身特色以及业务需求所开发的超越标准规范之外的“创新”。

总的来说,以上两个差异点的核心原因可以概括为一句话:理论上的规范在实现的时候需要结合现实的客观条件。规范是上层实现的参考标准,但并没有限制和框定上层实现的具体模式。差异点一是由于小程序不存在操作 DOM 的情况,差异点二是由于created、ready 和 error 三个函数是超出规范之外、小程序根据自身技术特色的一种“创新”。

通过上面的学习,理解了自定义组件的资源管理和生命周期之后,你便可以开发出一个优秀的自定义组件了。但是正如上文提到的,一个 Page 中可能存在多个自定义组件,这些组件都是服务于同一个页面,难免会有一些数据上的流通。这时候就会遇到一个组件化领域非常典型的问题:各组件之间如何通信?

组件间的通信流程

与 Vue/React 不同,小程序没有类似 Vuex 或 Redux 数据流管理模块,所以小程序的自定义组件之间的通信流程采用的是比较原始的事件驱动模式,即子组件通过抛出事件将数据传递给父组件,父组件通过 properties 将数据传递给子组件。

假设小程序的某个页面中存在两个组件,两个组件均依赖父组件(Page)的部分属性,这部分属性通过 properties 传递给子组件,如下图所示:

Drawing 9.png

当组件 A 需要与组件 B 进行通信时,会抛出一个事件通知父组件 Page,父组件接收到事件之后提取事件携带的信息,然后通过 properties 传递给组件 B。这样便完成了子组件之间的消息传递。

除了事件驱动的通信方式以外,小程序还提供了一种更加简单粗暴的方法:父组件通过selectComponent 方法直接获取某个子组件的实例对象,然后就可以访问这个子组件的任何属性和方法了。随后将这个子组件的某个属性通过 properties传递个另外一个子组件。相较而言,事件驱动的方法更加优雅,在流程上也更加可控,所以通常建议使用事件驱动的通信方式。

总结

以上就是本节课的全部内容了,这节课我们一起学习了微信小程序自定义组件的一些核心知识点,并没有涉及很多代码和 API的说明,因为这些内容完全可以从官方文档中轻易获取,如果我们的课程只是做搬运文档的工作,那就对不起你花费的时间和金钱了。

所以这节课挑选了在开发自定义组件时需要重点关注的三个环节,包括资源管理、生命周期和组件间的通信流程,并且在其中对照了 Vue/React 框架以及 Web Components 规范。

之所以对照这些内容来讲,是因为这节课跟上一节讲解授权模型的目的一样,理解自定义组件只是第一步,更重要是想教给你这些代码、API、组件背后的思想。希望通过这节课,你不仅能够掌握小程序自定义组件的开发技能,同时又能够理解它背后的组件化思想、Web Components 规范,并且在未来工作中得到实践。

最后留两个课后作业:

  1. 尝试参照小程序的文档实现一个小程序自定义组件;

  2. 尝试参照 Web Components 规范实现一个 Web自定义组件。

下节课我们将深度挖掘微信小程序的开发者工具进而得到一些性能优化方面的启示。


《大前端高薪训练营》
拉勾直推机会+硬核实战干货,6个月助你轻松斩获高薪 offer。


精选评论

**豪:

很好很好,干货。

*佩:

看完如同醍醐灌顶🙏

*刚:

attributeChangedCallback 为什么一定操作DOM呢?如果是操作VDOM,那这个存在是否合理?请老师指点

    讲师回复:

    vdom是一种抽象,不是前端原生的概念。web components规范是一种原生的组件化规范

**帅:

请问小程序设计自己几个生命周期函数到底好在哪了

    讲师回复:

    每个框架都有自己的一套设计理念,没有绝对的好坏之分。小程序的生命周期有优点也有缺点,比如命名没有时态(跟react的生命周期对比一下)。

*宾:

打卡

    讲师回复:

    感谢支持,希望我的内容对你有所帮助。

相关文章:

  • 4.bs4 节点遍历
  • 基于ssm+vue+elementui的二手车交易管理系统
  • 计算机毕业设计springboot+vue基本微信小程序的水库巡检系统
  • 3.BeautifulSoup库
  • 8.cookie的获取
  • 商标注册需要什么材料
  • 什么是布隆过滤器
  • 什么是Quartz
  • 【golang】认识 Go 并发+
  • 你知道你的竞争优势吗?
  • 数据结构刷题:第四天
  • Spring日志
  • VsCode集成Python开发环境
  • 使用platformio+arduino开发自定义板子STM32G070
  • 推荐系统中的特征工程
  • [笔记] php常见简单功能及函数
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • [译] React v16.8: 含有Hooks的版本
  • ESLint简单操作
  • python学习笔记-类对象的信息
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • 编写高质量JavaScript代码之并发
  • 第2章 网络文档
  • 前嗅ForeSpider采集配置界面介绍
  • 嵌入式文件系统
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 手写双向链表LinkedList的几个常用功能
  • 昨天1024程序员节,我故意写了个死循环~
  • ​flutter 代码混淆
  • ​Python 3 新特性:类型注解
  • # 达梦数据库知识点
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • #微信小程序:微信小程序常见的配置传旨
  • (1) caustics\
  • (2020)Java后端开发----(面试题和笔试题)
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (WSI分类)WSI分类文献小综述 2024
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (一) storm的集群安装与配置
  • (一)Neo4j下载安装以及初次使用
  • (译) 函数式 JS #1:简介
  • (转)nsfocus-绿盟科技笔试题目
  • (转载)hibernate缓存
  • .NET 发展历程
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • @JoinTable会自动删除关联表的数据
  • [ 渗透测试面试篇 ] 渗透测试面试题大集合(详解)(十)RCE (远程代码/命令执行漏洞)相关面试题
  • [ 手记 ] 关于tomcat开机启动设置问题
  • [ 转载 ] SharePoint 资料
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...
  • [].slice.call()将类数组转化为真正的数组
  • []串口通信 零星笔记