文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

前端代码的三种设计模式

2024-12-02 01:07

关注

为了便于理解,以下代码示例采用的都是 React + rdeco 编写,设计模式本身是高度抽象的,并不局限于某一类特定的框架

组件模式

组件模式是我们用的最多的或者说目前大家都唯一能够理解的模式,组件模式的特点是,予以每个组件独立的上下文,组件和组件之间有严格的代码隔离,通常在不考虑全局变量的影响下组件之间是完全无潜在交互的。

const Table = createComponent({
name:'table',
state:{
data:[],
},
view:{
render(){
return(
<div>{this.state.data.map(d=>{
return d
})}<div>
)
}
}
})
const Page = createComponent({
name:'page',
view:{
render(){
return(
<Table />
)
}
}
})
复制代码

这种模式我们都很熟悉,Page 和 Table 是两个拥有独立上下文的组件,在不同的 UI 框架里有不同的组件交互方式,在 React 中,Page 如果要和 Table 进行交互,可以使用 props 传递,或者借助 Context 来共享一部分上下文。

但是这种模式在很多场景下并不是完全有效的,只有当我们非常明确两个组件之间的边界时,模式和实际情况才是相符合的,例如考虑这样一种场景:

const HeadTitle = ({text})=>{
return(
<p>{text}</p>
)
}
const Page = createComponent({
name:'page',
state:{
text:'page',
},
view:{
render(){
<HeadTtile text={this.state.text}>
}
}
})
复制代码

在这个示例中,乍看是没啥问题,平时我们都会将一些无状态的 UI 提取为无状态的函数组件,但经过实践你会发现实际上,HeadTitle 大概率仅服务于 Page,也就是说 HeadTitle 并不是为了复用而被提取,更多是因为大型组件的文件需要拆解从而减小体积,降低管理难度。

但是以此为目的进行组件化拆解会破坏原有组件的完整性,导致大量的参数传递,这和我们过度提取代码到函数其实是一个效果。

function print(name){
console.log(name)
}
function main(){
const name = 'main'
print(name)
}
// 如果 print 在 main 函数内部则不需要再次传递 name
function main(){
const name = 'main'
function print(){
console.log(name)
}
print(name)
}
// 因此对于 main 来说 print 是一个独立函数?,还是一个代码片段?
复制代码

为了解决组件提取导致的上下文隔离问题,我们实践了一种模式,我们称之为组合模式。

组合模式

和组件模式相比,组合模式是一种轻量化的方案,相比组件模式两者有明显的区别。

  1. 组件模式拥有独立的上下文,组件和组件之间组合成新的组件需要进行上下文的传递,而组合模式则只是组件的一个片段,若干个组合体组成了一个完整组件,组合体之间共享上下文,不需要额外传递,但组合体本身实现了相关逻辑的内聚。
  2. 组件之间因为上下文隔离,因此可以拥有相同的内部成员,组合体只是组件的一个片段,组合体之间不能用相同的内部成员。
  3. 有实例,需要命名标识,组合体没有实例,不需要命名标识。

参照以上区别我们来看看的代码示例:

const table = createCompose({
view:{
renderTable(){
return(
<div>{this.state.data.map(d=>{
return d
})}<div>
)
}
}
})
const head = createCompose({
state:{
text:'page'
},
view:{
renderHead(){
return(
<p>{text}</p>
)
}
}
})
const Page = createComponent(compose({
name:'page',
state:{
data:[]
},
view:{
render(){
<>
{this.view.renderHead()}
{this.view.renderTable()}
</>
}
}
},[table, head]))
复制代码

现在 head 和 table 都成了组合体,通过组合变成了 page 的一部分,为此他们可以共享彼此的上下文,而不用额外通过 props 或者 Context 来传递或者共享参数。

除了组合模式,我们还总结了第三种模式,membrane 模式,这种模式我在早期的文章中有提到过,今天我们将其简化。

Membrane 模式

和组合模式相比,membrane 模式具有一些共通性,例如同样没有独立的上下文,不需要命名标识,不过两者也有极大的区别。

  1. membrane 是一种抽象模式,和组合模式相比,每个 membrane 只能有一个模板。
  2. compose 和 membrane 可以联合使用。
const pageTemplate = () => {
return {
state:{
name:'',
},
service:{
init(){}
},
controller:{
onMount(){
this.service.init()
}
},
view:{
render(){
return(
<div>{this.state.name}</div>
)
}
}
}
}
const Page1Membrane = createMembrane(pageTemplate(), {
name:'page-1-membrane',
service:{
init(){
this.setter.name('page-1-membrane')
}
}
})
const Page1Membrane = createMembrane(pageTemplate(), {
name:'page-1-membrane',
service:{
init(){
this.setter.name('page-2-membrane')
}
}
})
const Page1 = createComponent(Page1Membrane)
// render Page1 name === page-1-membrane
const Page2 = createComponent(Page2Membrane)
// render Page2 name === page-2-membrane
复制代码

如果你熟悉面向对象设计,那么可能会很快联想到 membrane 和 抽象类的特性有些相似,不过相比抽象类,membrane 可以包含具体的实现,因此两者也不完全等价,但是从设计上是有一定的共通性的

在实际实践中,我们结合上述三种模式,借助类似 mermaid 这样的 UML 图形库,在日常迭代中增加了前端设计相关的内容,从实际结果看我们认为这些模式有助于改善前端设计的粗糙和非专业性,同时可以改善前端代码的标准化程度,利用 UML 图更好的代替注释和文字文档来描述业务代码的组成关系。

来源:web前端营内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯