现代前端MVVM组件开发的基本理论
此文在写作中。这篇文章意在整理自己目前对MVVM组件开发的理解。在写作过程中我发现,我自以为已经形成了对组件开发的一套理论,但其实这套理论还有很多不完善的地方。最近又翻到了波神分享-*漫谈Web前端的『组件化』*的PPT,深感要形成理论,还是需要数年的积累才行。所以这篇文章就作为我阶段性的成果,不具有太大的参考价值。 如果要给前端开发下一个定义,那就是在浏览器环境下面向对象的GUI客户端程序开发。 既然是基于面向对象的,那就要有类和对象。在Web前端开发中,最基本的类就是组件的基类(比如Vue和React的构造函数),所有的组件都是基类的一个实例。基类有属性和方法,同时还有一些生命周期钩子函数(其实就是基类上定义的一些方法,会在基类初始化的特定时刻被调用,并且允许实例重载这个方法)。 既然是GUI客户端程序开发,那Web开发中自然有着MVC之类的分层。MVC、MVP、MVVM都是经典的GUI客户端程序的设计模式[1]。目前Web开发中最流行的架构就是MVVM,MVVM已经成为了Web前端开发的一个事实标准。 谈到事实标准,客户端的开发,比如Android、iOS和Windows,都需要使用系统自带的原生UI组件为基础,配合相关的库,进行开发。 iOS丰富的原生组件 在GUI开发领域,组件是一种独立的、可复用的交互元素的封装。 Web开发最大的特点就是,因为历史原因,Web前端当初是作为展示文档的一种渠道来设计的,所以Web前端只有非常少数的几个原生组件,比如表单组件,可以使用。其他的组件都是需要开发者自行封装的。历史上出现过基于jQuery和Backbone等等框架/库的组件。这些组件在当时起到了很重要的作用。在Web标准大幅发展的今天,Web平台(浏览器)有没有提供标准的组件API呢? 答案是肯定的。目前Web前端组件的官方标准就是Web Components。Web Components标准由Custom Elements、HTML Templates、Shadow DOM和HTML Imports四部分组成。Web Components解决了组件的封装、组合以及复用问题。 但在组件的逻辑编写上,随着Web应用越来越复杂,jQuery为代表的命令式编程范式已经不能满足开发的需要。Knockout、ember、angular等MVVM框架出现后,使得声明式的编程范式成为可能。这些框架也渐渐成为了前端开发中的主流。 Web Components解决了组件的封装和复用问题,但Web Components的逻辑依然是命令式的。而且Web Components目前的浏览器兼容性还不太好,不能直接用于生产。 当前主流的Web前端框架/库Vue、React和Angular在某种程度上都提供了组件封装和声明式编程范式两个重要特性。这些框架都有自己的组件封装标准,都遵循数据驱动的范式。值得注意的是Web Components标准在某种程度上对这些框架的组件封装和组合方式有一定的影响,特别是Vuejs,在很多地方参考了Web Components,并实现了Web Components中的slot特性。在未来Web Components标准真正落地时,这些框架都可以和Web Components实现无缝的整合。 因此我们可以说,实现了声明式编程范式和组件封装、复用和组合的现代MVVM组件框架,就是目前Web前端开发的事实标准。并且这个标准在未来很长一段时间内都会持续保持稳定。 Web前端没有一套官方的原生UI组件(这里指的是官方提供的组件实现,比如日期选择组件、ListView组件等待,不是指Web Components这样的底层标准),以后也不会有,因为Web是一个开放的平台,不像其他的客户端程序的操作系统由一家公司所控制。但目前在我们的开发中,已经可以总结出一套成熟的组件。比如Ant Design中的众多组件。这些组件都是基于MVVM前端框架开发的。 前端这么多年的发展,到现在已经进入了一个比较成熟的时期了。有了成熟的模块标准和包管理系统,也有了通用的组件模型。所以假设Web也有一个官方的组件标准,那融合了Web Components和声明式编程的MVVM组件已经非常接近了。一个技术想要被称为工业级,只有形成统一的标准。Web前端的组件,虽然不会有统一的标准,但如果在MVVM组件作为事实的标准的前提下去开发,会减少很多不必要的麻烦。 写这篇博客,也是因为想总结一下目前这个时期的Web开发中的一些事实标准。相比于客户端,前端的门槛其实要更高一些,因为我们要使用堪称刀耕火种的方式去应对日益复杂的业务场景。但在当下,对于刚刚进入前端领域的同学来说,可以把一些范式作为前端的标准来学习,形成对现代前端开发的理解。在现在这个时间点进入前端行业的同学,已经没有必要再了解jQuery时代的开发范式了。 本文的题目是现代前端MVVM组件开发的基本理论,下面就分别介绍MVVM组件开发中几个关键的理论。 MVVM Web Components并没有规定开发者应该如何去给一个组件的逻辑分层。一个组件里可能包含数据、表现(UI)和业务逻辑。在编写组件时,这些部分都需要被严格的解耦,并且规定各个部分之间的通信方式。 MVVM下,组件由如下部分构成: 组件 = 视图 + 数据 + 业务逻辑 MVVM分为View、Model和ViewModel三个部分。分别对应视图、数据和业务逻辑三部分。拿Vue举例,Vue的模板和样式属于View层。Vue的组件实例属于ViewModel,Vue的Model层,在没有引入全局Model层的情况下,就是Vue的data属性中的内容。如果开发者引入了全局的Model层,比如Redux或者MobX,那Model就是一个和Vue组件脱离的对象。 MVVM中,各个部分的关系是这样的: 组件的生命周期 客户端的组件会有生命周期函数,比如iOS的ViewController就有viewDidLoad、viewWillDisappear等等声明周期钩子。前端组件和客户端的的组件一样,都有着生命周期。一个前端组件在应用中,会首先初始化(创建一个新的组件实例),接着加载数据并首次渲染,然后进入一个响应数据变化并重新渲染的循环,最后如果这个组件要从应用中移除,那么组件就会被销毁。 组件在各个生命周期阶段会调用一些钩子函数,开发者如果想在组件特定的时刻执行一些逻辑,就可以在组件中实现这些钩子函数。 接下去总结一下MVVM通常都会有的生命周期。 首先,组件进入初始化阶段,在这个阶段主要就是创建组件实例,并调用init钩子函数。 然后进入初始化数据并首次渲染阶段,这个阶段,如果是Push类型的框架(关于Push和Pull在后文会提到),比如Vue,就需要对数据进行处理(对data进行递归遍历,修改getter和setter,然后调用Render Function进行依赖搜集),然后首次渲染(Vriual DOM patch)。如果是Pull类型的框架,Angular和Regular需要遍历View的AST,然后生成Watcher列表,然后进行首次的脏检查,随后View就被渲染到页面。React就启动一次渲染流程,包括调用Render Function和一次patch。最终达到的效果就是组件首次渲染到页面中。一般在此时也会有一个钩子函数被调用,开发者可以在此时执行一些需要确保UI已经渲染作为前提的逻辑。 接着进入响应数据变化并渲染阶段,这个阶段中,组件已经首次渲染了,接下来如果数据发生变化,那组件就会重新渲染,保持组件的UI和组件的状态保持同步。 如果组件的销毁方法被调用,组件就进入销毁阶段。 之所以要先说明这几个阶段,是因为理解组件的生命周期对于理解后文讲的数据侦测、渲染、模板等有着密不可分的关系。 组合 Composition over inheritence[x]。 在UI的开发中我们常常会复用一些代码。 内嵌组件 Mixin 数据驱动 这里有必要解释一下这套MVVM中,各模块之间数据的流动。View不能直接通知Model更新,而是通知ViewModel用户的交互,由ViewModel来修改Model中的数据。Model的数据变化之后,会直接触发View的更新。...