当前位置: 首页 > 科技观察

前端代码的三种设计模式

时间:2023-03-15 22:36:39 科技观察

为了便于理解,以下代码示例均采用React+rdeco编写。设计模式本身是高度抽象的,不局限于特定类型的框架组件模式。组件模式是我们用得最多的模式,或者说是目前大家唯一能理解的模式。组件模式的特点是给每个组件一个独立的上下文,组件之间有严格的代码隔离。通常,组件不考虑全局变量的影响。它们之间没有潜在的相互作用。constTable=createComponent({name:'table',state:{data:[],},view:{render(){return(

{this.state.data.map(d=>{returnd})
)}}})constPage=createComponent({name:'page',view:{render(){return()}}})copycode这种模式我们都很熟悉,Page和Table是两个具有独立上下文的组件。不同的UI框架有不同的组件交互方式。在React中,如果Page需要与Table进行交互,可以使用props来传递,也可以使用Context共享部分上下文。但是这种模式在很多场景下并不是完全有效的。只有当我们非常清楚两个组件之间的界限时,模式和实际情况才是一致的。例如,考虑这样一个场景:constHeadTitle=({text})=>{return(

{text}}

)}constPage=createComponent({name:'page',state:{text:'page',},view:{render(){}}})复制代码这个例子,乍一看好像没什么问题。通常我们会把一些无状态的UI提取到无状态的功能组件中,但是实践之后你会发现,其实HeadTitle很可能只是为Page服务的,也就是说HeadTitle并不是提取出来复用,更多的是因为需要大组件的文件拆解缩小体积,降低管理难度。但是,以此为目的的组件拆解,会破坏原有组件的完整性,导致大量的参数传递,这其实是我们将代码过度抽取成函数的结果。functionprint(name){console.log(name)}functionmain(){constname='main'print(name)}//如果print在main函数内部,就不需要再传namefunctionmain(){constname='main'functionprint(){console.log(name)}print(name)}//所以print是main的独立函数?,或代码片段?重复代码为了解决组件抽取带来的上下文隔离问题,我们实践了一种模式,我们称之为组合模式。与组件模式相比,复合模式是一种轻量级的解决方案,两者与组件模式相比有明显的区别。组件模式有一个独立的上下文。组件和组件组合起来形成新的组件。需要上下文传递,而组合模式只是组件的一个片段。几种组合形成一个完整的组件,组件之间共享上下文。不需要额外的传递,但是组合本身实现了相关逻辑的内聚。由于上下文隔离,组件可以有相同的内部成员,而组件只是组件的一个片段,组件之间不能使用相同的内部成员。如果有实例,则需要对其进行命名和标记;如果组合没有实例,则不需要命名和标记。参考上面的区别,我们来看一下代码示例:consttable=createCompose({view:{renderTable(){return(
{this.state.data.map(d=>{returnd})
)}}})consthead=createCompose({state:{text:'page'},view:{renderHead(){return(

{text}

)}}})constPage=createComponent(compose({name:'page',state:{data:[]},view:{render(){<>{this.view.renderHead()}{this.view.renderTable()}}}},[table,head]))复制代码现在head和table都变成了一个组合,通过组合成为页面的一部分,所以它们可以共享彼此的context,而不需要通过props或Context额外传递或共享范围。除了组合模式,我们还总结了第三种模式,膜模式,我在前面的文章中提到过,今天我们将其简化。与组合模式相比,膜模式有一些共性,比如没有独立的上下文,不需要命名标识符,但两者也有很大的区别。膜是一种抽象模式。与复合模式相比,每张膜只能有一个模板。Compose和membrane可以结合使用。constpageTemplate=()=>{return{state:{name:'',},service:{init(){}},controller:{onMount(){this.service.init()}},view:{render(){return(
{this.state.name}
)}}}constPage1Membrane=createMembrane(pageTemplate(),{name:'page-1-membrane',service:{初始化(){this.setter.name('page-1-membrane')}}})constPage1Membrane=createMembrane(pageTemplate(),{name:'page-1-membrane',service:{init(){this.setter.name('page-2-membrane')}}})constPage1=createComponent(Page1Membrane)//renderPage1name===page-1-membraneconstPage2=createComponent(Page2Membrane)//renderPage2name===page-2-膜拷贝代码如果熟悉面向对象设计,你可能很快会想到膜和抽象类的特性有些相似,但是相对于抽象类,膜可以包含具体的实现,所以两者并不完全等效,但在设计上有一定的共性。在实际实践中,我们结合以上三种模式,像美人鱼一样使用UML图形库,在日常迭代中添加前端设计相关的内容。从实际结果可以看出,Think这些模式有助于改善前端设计的粗糙和不专业,同时可以提高前端代码的标准化,使用UML图更好的替代注释和文本文档来描述业务代码的组成关系