前端代码的三种设计模式

更新日期: 2022-04-19阅读: 1.1k标签: 模式

前端作为软件工程长期发展出来的一个独立分支,一直没有属于自己的特定的代码设计模式,最近我们在实践中对一些发源于面向对象的代码设计做了一些总结,总结了三种模式,遂有此文予以分享。

为了便于理解,以下代码示例采用的都是 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 图更好的代替注释和文字文档来描述业务代码的组成关系。

作者:掘金泥石流
链接:https://juejin.cn/post/7081147167653494797

链接: https://fly63.com/article/detial/11362

js设计模式之单例模式,javascript如何将一个对象设计成单例

单例模式是我们开发中一个非常典型的设计模式,js单例模式要保证全局只生成唯一实例,提供一个单一的访问入口,单例的对象不同于静态类,我们可以延迟单例对象的初始化,通常这种情况发生在我们需要等待加载创建单例的依赖。

前端设计模式:从js原始模式开始,去理解Js工厂模式和构造函数模式

工厂模式下的对象我们不能识别它的类型,由于typeof返回的都是object类型,不知道它是那个对象的实例。另外每次造人时都要创建一个独立的person的对象,会造成代码臃肿的情况。

JavaScript设计模式_js实现建造者模式

建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象

html和xhtml,DOCTYPE和DTD,标准模式和兼容模式

主要涉及知识点: HTML与XHTML,HTML与XHTML的区别,DOCTYPE与DTD的概念,DTD的分类以及DOCTYPE的声明方式,标准模式(Standard Mode)和兼容模式(Quircks Mode),标准模式(Standard Mode)和兼容模式(Quircks Mode)的区别

前端四种设计模式_JS常见的4种模式

JavaScript中常见的四种设计模式:工厂模式、单例模式、沙箱模式、发布者订阅模式

javascript 策略模式_理解js中的策略模式

javascript 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。 策略模式利用组合,委托等技术和思想,有效的避免很多if条件语句,策略模式提供了开放-封闭原则,使代码更容易理解和扩展, 策略模式中的代码可以复用。

javascript观察者模式_深入理解js中的观察者模式

javascript观察者模式又叫发布订阅模式,观察者模式的好处:js观察者模式支持简单的广播通信,自动通知所有已经订阅过的对象。存在一种动态关联,增加了灵活性。目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。

Vue中如何使用方法、计算属性或观察者

熟悉 Vue 的都知道 方法methods、计算属性computed、观察者watcher 在 Vue 中有着非常重要的作用,有些时候我们实现一个功能的时候可以使用它们中任何一个都是可以的

我最喜欢的 JavaScript 设计模式

我觉得聊一下我爱用的 JavaScript 设计模式应该很有意思。我是一步一步才定下来的,经过一段时间从各种来源吸收和适应直到达到一个能提供我所需的灵活性的模式。让我给你看看概览,然后再来看它是怎么形成的

Flutter 设计模式 - 简单工厂

在围绕设计模式的话题中,工厂这个词频繁出现,从 简单工厂 模式到 工厂方法 模式,再到 抽象工厂 模式。工厂名称含义是制造产品的工业场所,应用在面向对象中,顺理成章地成为了比较典型的创建型模式

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!