在本文中我想介绍一些组件相關的设计概念,在进行前端开发时应该考虑这些概念我认为最好的方法是给每个概念一个简洁精炼的名字,然后逐一解释每个概念是什麼以及为什么重要对于比较抽象概念的会举一些例子来帮助理解。
以下这个列表并不是不全面也不完整但我注意到的只有 8 件事情值得┅提,对于那些已经可以编写基本组件但想要提高他们的技术设计技能的人来说所以这是列表:
以下列举的这个列表仅仅是是我注意到嘚 8 个方面,当然组件设计还有其他一些方面在此我只是列举出来我认为值得一提的。
对于已经掌握基本的组件设计并且想要提高自身的組件设计能力的开发者我认为以下 8 项是我认为值得去注意的,当然这并不是组件设计的全部
层次结构和 UML 类图
请注意,前端代码组件化礻例可能有一些小问题或有点人为设计但是它们并不复杂,只是想通过这些例子来帮助更好的理解概念
应用内的组件共同形成组件树, 而在设计过程中将组件树可视化展示可以帮助你全面了解应用程序的布局一个比较好的展示这些的办法就是组件图。
UML 中有一个在 OOP 类设計中经常使用的类型称为 UML 类图。类图中显示了类属性、方法、访问修饰符、类与其他类的关系等虽然 OOP 类设计和前端组件设计差异很大,但是通过图解辅助设计的方法值得参考对于前端组件,该图表可以显示:
因此让我们看一下下面这个基础表组件的组件层次图,该組件的渲染对象是一个数组该组件的功能包括显示总行数、标题行和一些数据行,以及在单击其单元格标题格时对该列进行排序在它嘚 props 中,它将传递列列表(具有属性名称和该属性的人类可读版本)然后传递数据数组。我们可以添加一个可选的’on row click’功能来进行测试
雖然这样的事情可能看起来有点多,但是它具有许多优点并且在大型应用程序开发设计中所需要的。这样会带来的一个比较重要的问题昰它会需要你在开始 codeing 之前就需要考虑到具体细节的实现例如每个组件需要什么类型的数据,需要实现哪些方法所需的状态属性等等。
┅旦你对如何构建一个组件(或一组组件)的整体有大概的思路就会很容易认为当自己真正开始编码实现时,它会如自己所期望的按部僦班的完成但事实上往往会出现一些预料之外的事情, 当然你肯定不希望因此去重构之前的某些部分或者忍受初始设想中的缺点并因此扰乱你的前端代码组件化思路。而这些类图的以下优点可以帮助你有效的规避以上问题优点如下:
一个易于理解的组件组成和关联视圖
一个易于理解的应用程序 UI 层次结构的概述
一个结构数据层次及其流动方式的视图
一个组件功能职责的快照
顺带一提,上图并不是基于某些官方标准比如 UML 类图,它是我基本上创建的一套表达规则例如,在 props 、方法的参数和返回值的数据类型定义声明都是基于 Typescript 语法我还没囿找到书写前端组件类图的官方标准,可能是由于前端 JavaScript 开发的相对较新且生态系统不够完善所致但如果有人知道主流标准,请在回复中告诉我!
在 state 和 props 频繁被 watch 和 update 的情况下如果你有使用嵌套数据,那么你的性能可能会受到影响尤其是在以下场景中,例如一些因为浅对于而觸发的重新渲染;在涉及 immutability 的库中比如 React,你必须创建状态的副本而不是像在 Vue 中那样直接更改它们并且使用嵌套数据这样做可能会创建笨拙,丑陋的前端代码组件化
即使使用展开运算符,这种写法也并不够优雅扁平 props 也可以很好地清除组件正在使用的数据值。如果你传给組件一个对象但是你并不能清楚的知道对象内部的属性值所以找出实际需要的数据值是来自组件具体的属性值则是额外的工作。但如果 props 足够扁平化那么起码会方便使用和维护。
// 我们无法得知 customer 这个对象里面拥有什么属性
// 这个组件需要使用这个对象所有的属性值或者只是需偠其中的一部分
// 如果我想要将这个组件在别处使用,我应该传入什么样的对象
// 下面的这个组件接收的属性就一目了然
我想提醒大家的是:应该更注重以上这些组件设计的原则和你已知的一些最佳实践在实际中的应用虽然你应该尽力维护良好的设计,但是不要为了包装 JIRA ticket 或┅个取消请求而有损前端代码组件化完整性同时总是把理论置于现实世界结果之上的人也往往会让他们的工作受到影响。大型软件项目囿许多活动部分软件工程的许多方面与编码没有特别的关系,但仍然是不可或缺的例如遵守最后期限和处理非技术期望。
虽然充分的准备很重要应该成为任何专业软件设计的一部分,但在现实世界中切实的结果才是最为重要的。当你被雇用来实际创造一些东西时洳果在最后期限到来之前,你有的只是一个如何构建完美产品的惊人计划但却没有实际的成果,你的雇主可能不会太高兴吧此外,软件工程中的东西很少完全按计划进行因此过度具体的计划往往会在时间使用方面得到适得其反的效果。
此外组件规划和设计的概念也適用于组件重构。虽然用了 50 年的时间来计划一切令人难以忍受的细节然后从一开始就完美地编写它就会很好,回到现实世界我们往往會遇到这种情况,即为了赶进度而不能使前端代码组件化达到完美的预期然而,一旦我们有了空闲时间那么一个推荐的做法就是回过頭来重构早期不够理想的的前端代码组件化,这样它就可以作为我们向前发展的坚实基础
在一天结束时,虽然你的直接责任可能是“编寫前端代码组件化”但你不应忽视你的最终目标,即建立一些东西创建产品。为了产生一些你可以引以为豪的东西并帮助别人即使咜在技术上并不完美,永远记得找到一个平衡点不幸的是,在一周内每天 8 小时盯着眼前的前端代码组件化会使得眼界和角度变得更为“狹窄”这个时候你需要的你是退后一步,确保你不要为了一颗树而失去整个森林
模块:按照项目业务内容来划分夶模块;
组件:按照一些小功能的通用性和可复用性来抽象组件;
一般来说模块是由很多小组件组成的。虽然两者侧重点不同但是从包含关系上讲,组件是模块的子集
工作 4 年后再来回答一次——
模块跟组件很像,都像是给你的前端代码组件化重新堆一次积木不过他們的着重点可不一样:
『模块注重的是职责分离,而组件强调的是复用』
上次写完博客后有朋友反应第┅内容有点深,看着迷迷糊糊;第二是感觉没什么使用场景太过业务化,还不如直接写Vue&react的源码分析我感觉这里有必要说下我的认识。
艏先要写源码分析很难,第一是他本来就很难所以一般我们是想了解他实现的思路而不是前端代码组件化;
第二每个开发者有自己发風格,所以你要彻底读懂一个人的前端代码组件化不容易除非你是带着当时作者同样的问题不断的寻找解决方案,不断的重构才可能悝解用户的意图。
我们上一次做的事情其实就是根据自己实际的工作经验做了和外面框架类似的事情虽然前端代码组件化的健壮、优雅程度跟不上,但却和其它作者一样为解决同样的问题思考得出的方案上次做的太晚了,后面就草草结束事实上在我Demo过程中发现一个事實:业务前端代码组件化都是差不多的,只是一些细节点不一样所以决定产品质量的依旧是开发者的业务前端代码组件化能力,框架只昰助力而已
不能了解作者的意图,不能提高本身的编程水平就算用上了React&Vue这类组件化的框架,也组织不好前端代码组件化;事实上这类湔端代码组件化因为是面向大型应用的反而更考验一个人的架构能力,所以大家要多注重内在修养的提升哦
下面我们进入今天的正题,这里依旧提供一些帮助理解的资料:
如果对文中的一些前端代码组件化比较疑惑可以对比着看看这些文章:
因为第一个demo是Vue的,React应该也類似对比之前的前端代码组件化发现一个重要差异是:
对,是完全没有DOM操作了这个是很牛逼的一件事情,因为我觉得有两个地方要摆脫DOM操作很难:
① 我的组件应该放到哪个容器内我需要一个定位的元素,比如:
明确的告诉了组件所属的容器
② 我比较疑惑像这类列表类型的事件该如何处理因为一些必要参数是根据event获取的,比如:
关于这个Vue的作者认为应该将事件处理程序内联做显示声奣:
你可能注意到这种事件监听的方式违背了传统理念 “separation of concern”。不必担心
因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,咜不会导致任何维护困难实际上,使用 v-on 有几个好处:
扫一眼 HTML 模板便能轻松定位在 JavaScript 前端代码组件化里对应的方法
因为你无须在 JavaScript 里手动绑萣事件,你的 ViewModel 前端代码组件化可以是非常纯粹的逻辑和 DOM 完全解耦,更易于测试
当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除伱无须担心如何自己清理它们。
还有种常用的操作比如radioList,点击当前选项便选择项目我们一般的做法是這样的:
这样做比较简单,但是会有一个问题便是数据与dom表现的流程变了,正确的流程是index 变了dom便根据数据做更新,比如Vue:
之前不考虑性能,我们会直接根据数据重新渲染整个列表就为一个简单的选中功能,而Vue&React却做到了局部渲染这个是否牛逼,我相信这个将会是一个核心算法部分后面有时间一定要深入了解。
根据以上局部解读我们得到一个结论,只要达成两个条件就能擺脱DOM操作:
PS:我们这里是很简单的一环,没有考虑组件嵌套组件通信等过于复杂的问题
那么如果达成了以上条件,我们能否做到业务逻輯中不包含dom操作呢我们下面就来试试。
这里真的是demo类尝试思维验证,便不使用之前过于复杂的业务逻辑了这里将me目录拷贝一块出来,依旧以原来的前端代码组件化做底层依赖只要列表与顶部排序部分功能,这里为了简化实现保持前端代码组件化重用,我们这里直接想将entity模块复用要求data中的对象必须是一个entity实例,这里第一步是抽象出来了list module模块于是主控制器变成这样了,事实上这个时候已经没dom操作叻:
这里简单看看列表组件的实现其实就是将原来根View的湔端代码组件化换个位置:
就这种简单的改变貌似便摆脱了DOM操作,页面所有的状态事实上是可以做到由数据控制的但是這里没有形成“标签化”,似乎不太好于是我们来试试是否能改造为标签化的前端代码组件化。
我们这里的业务前端代码组件化(module与entity)沒有什么需要改动的这里主要在底层做改造,这里在我看来是提供了一种“语法糖”的东西这里的具体概念后续阅读Vue源码再深入了解,这里先照着做这里看结果想实现,也是我们常用的一种设计方案首先我们的index编程了这个样子:
PS:里面的js钩子基本无用了
这里标签化帶来的好处是,根View中有一段实例前端代码组件化可以不用与选择器映射了比如这个:
因为处于组件中,其中所处位置已经定了view实例或鍺entity实例全部是跟View显示注入的,这里根View中参考Vue的使用新增一个$components与$entities属性,然后增加一$watch对象
大家写底层框架时,私有属性或者方法使用_method的方式如果是要释放的可以是$method这种,一定要“特殊化”防止被实例或者继承覆盖
他这种做法需要组件在显示后框架底层将刚刚的业务前端玳码组件化实现,使用组件生成的html前端代码组件化将原来标签的占位符给替换掉
这里在组件也需要明示根View需要注入什么给自己:
PS:事实仩这个可以不写,写了对后续属性的计算有好处
//记录需要根View注入的属性
PS:底层什么时候执行替换这个是有一定时机的我们这里暂时放到根View展示后,这里更好的实现后续我们在Vue与React中去找寻
因为我们这里是demo类实现,为降低难度我们为每一个组件动态增加一个div包裹层,于是我们在跟View中,在View展示后我们另外多加一段逻辑:
1 //实例化实体,后面要用
然后将实体与组件的实例化放到框架底层,这里实体的实例化比較简单(如果有特殊数据需求再说这里只考虑最简单情况):
而实例化组件的工作复杂许多,因为他需要将頁面中的自定义标签替换掉还需要完成很多属性注入操作:
于是这个标签便能正常展示了:
后面想要把这段前端代码组件化去掉也十分轻易,峩这里就不进行了:
这里首先根据上次Vue的demo产生了一些思考并且以简单的demo验证了这些思考,楼主在使用过程中发现Vue很多好的点子后续应該会深入研究,并且以实际项目入手这里回到今天的正题,我们使用React实现上次遗留的demo
在我最初接触React的时候,React Native还没出现所以很多人对React嘚关注不高,当时做移动端直接放弃了angular以体量来说,React也不在我们的考虑范围内谁知道野心勃勃的Facebook搞出了React Native,让React彻底的跟着火了一把事實上只要有能力以JavaScript统一Native UI的公司,这个实现就算换个框架依旧会火虽然都已经这么火了,但是React的文档却不怎样我后续也有试水React Native的兴趣,屆时再与各位分享
PS:根据之前的反馈,这次demo稍微做简单点也只包含顶部导航和列表即可:
他这个语法据说是让开发变得更简单了我反正是不喜欢,这里有个不好的地方之前数据实体全部是在根View仩实例化的,然后注入给子ViewReact这里属性完全独享了,现在我触发了状态的改变如何通知到list组件重新渲染排序呢?
这里React子组件之间如何通信暂没有研究出来所以将需要通信的数据做到了父组件中
react的中文文档整理较差很多资料找不到,jsx语法比较怪异不是所有人能接受,我去找模板循环时候压根就没找到所以jsx有个特点,他让你不得不去拆分你的组件在我写React前端代码组件化中,感觉React前端代码组件化控制力度要重一点但是如果没有良好的架构能力,我可以毫不夸张的说你依旧写不好业务前端代码组件化。
至于React与Vue的优劣这个方面见仁见智吧,好了今天的文嶂到此为止
后续我们可能会深入分析下Vue的实现,在React Native上做深入有兴趣的同学可以持续关注。
文章有任何不足错误请您提出,因为小钗吔是第二次使用React写demo如果使用不当请多包涵