設計模式在vue中的應用(四)

前言

目錄整理:
設計模式在vue中的應用(一)
設計模式在vue中的應用(二)
設計模式在vue中的應用(三)
設計模式在vue中的應用(四)
設計模式在vue中的應用(五)
設計模式在vue中的應用(六)
設計模式在vue中的應用(七)vue

爲何要寫這些文章呢。正如設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、通過分類的、代碼設計經驗的總結(來自百度百科)同樣,也是想經過分享一些工做中的積累與你們探討設計模式的魅力所在。
在這個系列文章中爲了輔助說明引入的應用場景都是工做中真實的應用場景,固然沒法覆蓋全面,但以此類推也覆蓋到了常見的業務場景react



今天的主角應該是咱們在使用框架(如:vue、react)開發中使用最多的設計模式——模板方法模式
定義(來自網絡):算法

模板方法模式在一個方法中定義一個算法的骨架,而將一些步驟的實現延遲到子類中。模板方法使得子類能夠在不改變算法結構的狀況下,從新定義算法中某些步驟的具體實現編程

1、OOP實現

一、算法骨架

一個動物身體結構算法有:頭部、身體、腳設計模式

// template class
// 注意:JavaScript裏面沒有接口的概念,與傳統的OOP實現有必定的缺陷
class AnimalTemplate {
  constructor() {}
  head () {}
  body () {}
  foot () {}
  render () {
    // 算法過程 ———— 身體部位排列順序頭、身體、腳
    // render方法不能被子類重寫,沒法經過JavaScript自己實現需人爲遵照
    this.head()
    this.body()
    this.foot()
  }
}
複製代碼

二、子類算法實現

// 有一頭豬
class Pig extends AnimalTemplate {
  head () {
    console.log('head from pig')
  }
  body () {
   console.log('body from pig')
  }
  foot () {
    console.log('foot from pig')
  }
}

const pig1 = new Pig()
pig1.render() // 執行算法

// 有一隻雞
class Chicken extends AnimalTemplate {
  head () {
    console.log('head from chicken')
  }
  body () {
   console.log('body from chicken')
  }
  foot () {
    console.log('foot from chicken')
  }
}
const chicken1 = new Chicken()
chicken1.render() // 執行算法
複製代碼

2、react實現

react中有class組件採用上面OOP的實現徹底沒問題,可是繼承的方式好像在react中不怎麼受歡迎,更流行是函數式編程(props傳遞)bash

一、來一個很常見的場景

class Parent {
  constructor() {}
  render () {
    <div>
      <div name="tom"></div>
      <!-- 算法過程:children要渲染在name爲joe的div中 -->
      <div name="joe">{this.props.children}</div>
    </div> 
  }
}
class Stage {
  constructor() {}
  render () {
    // 在parent中已經設定了children的渲染位置算法
    <Parent>
      // children的具體實現
      <div>child</div>
    </Parent> 
  }  
}
複製代碼

因此咱們在寫jax時就已是在使用模板方法模式網絡

二、再次實現OOP中完成的例子

// 算法模板
class AnimalTemplate {
  constructor() {
    ...
  }
  render () {
    // 算法過程————頭、身體、腳的順序
    return (
      <div>
        {this.props.renderHead()}
        {this.props.renderBody()}
        {this.props.renderFoot()}
      </div>
    )
  } 
}
// 具體需求
class Stage {
  constructor() {}
  pigRenderHead () {
    return <div>pig head</div>
  }
  pigRenderBody () {
    return <div>pig body</div>
  }
  pigRenderFoot () {
    return <div>pig foot</div>
  }
  chickenRenderHead () {
    ...
  }
  chickenRenderBody () {
    ...
  }
  chickenRenderFoot () {
    ...
  }
  render () {
    <div>
      <AnimalTemplate
        renderHead={this.pigRenderHead}
        renderBody={this.pigRenderBody}
        renderFoot={this.pigRenderFoot}
      />
      <AnimalTemplate
        renderHead={this.chickenRenderHead}
        renderBody={this.chickenRenderBody}
        renderFoot={this.chickenRenderFoot}
      />
    </div>
    
  }  
}
複製代碼

總結

經過這節相信你們對react的render props有了更清晰的認識數據結構

3、vue實現

有了上面兩步的推導要理解模板方法模式在vue中的應用就比較簡單了。框架

1,children在name爲joe的div中

// parent.vue
<template>
  <div>
    <div name="tom"></div>
    <div name="joe">
      <!--vue中的插槽渲染children-->
      <slot />
    </div>
  </div>
</template>
複製代碼
// stage.vue
<template>
  <div>
    <parent>
      <!-- children的具體實現 -->
      <div>child</div>
    </parent>
  </div>
</template>
複製代碼

二、再次實現OOP中完成的例子

// AnimalTemplate.vue
<template>
  <div>
    <slot name="head"></slot>
    <slot name="body"></slot>
    <slot name="foot"></slot>
  </div>
</template>
複製代碼
// stage.vue
<template>
  <div>
    <animal-template>
      <div slot="head">pig head</div>
      <div slot="body">pig body</div>
      <div slot="foot">pig foot</div>
    </animal-template>
    <animal-template>
      <div slot="head">chicken head</div>
      <div slot="body">chicken body</div>
      <div slot="foot">chicken foot</div>
    </animal-template>
  </div>
</template>
複製代碼

總結

從OOP實現到react實現再推導出vue實現,如今你們應該能很輕鬆的理解在vue中模板方法模式的應用是怎樣的函數式編程

4、搞點事情

經過上面的介紹咱們應該是掌握了模板方法模式的理論知識,有了新技能就應該應用下。

場景:
列表渲染應該是常見操做,不論是什麼列表都會有一下幾個特色:請求數據時顯示loading狀態、沒有數據時給出提示、有數據就渲染列表

1,算法封裝

根據模板方法模式的理論咱們首先須要封裝列表渲染的算法:

  • 請求數據 ——> loading
  • 數據爲空 --> 空數據提示
  • 獲取到數據 ——> 渲染列表
// renderList.vue
<template>
  <div>
    <img v-if="isLoading" src="./loading.gif" />
    <div v-if="isEmpty">數據爲空</div>
    <div v-else>
      <div v-for="(item, index) in data" :key="index">
        <!-- vue中的做用域插槽 -->
        <slot name="item" :data="item" />
      </div>
    </div>
  </div>
</template>
<script>
  export default {
    name: 'RenderList',
    props: {
      isLoading: Boolean,
      data: Array,
    },
    computed: {
      isEmpty () {
        return this.data.length < 1
      }
    }
  }
</script>
複製代碼

2,定義某些步驟的實現(列表項)

// stage.vue
<template>
  <div>
   <render-list :isLoading="isStudentsLoding" :data="students">
     <!-- 學生信息的渲染 -->
     <div slot="item" slot-scope="{ data }">
       <div>名字:{{ data.name }}</div>
       <div>年齡:{{ data.age }}</div>
     </div>
   </render-list>
   <render-list :isLoading="isFamilyLoading" :data="family">
     <!-- 家庭成員的渲染 -->
     <div slot="item" slot-scope="{ data }">
       <!-- 不一樣的DOM結構 -->
       <div>
         <span>稱呼:{{ data.role }}</span>
         名字:{{ data.name }}
       </div>
     </div>
   </render-list>
  </div>
</template>
<script>
  import RenderList form './renderList'
  export default {
    data () {
      return {
        isStudentsLoding: false,
        isFamilyLoading: false,
        students: [
          { name: 'aa', age: '18' },
          { name: 'bb', age: '19' },
          { name: 'cc', age: '120' }
        ],
        family: [
          { name: 'aaaa', role: '爸爸' },
          { name: 'bbbb', role: '媽媽' },
          { name: 'cccc', role: '本人' },
        ]
      }
    },
    components: {
      RenderList   
    }
  }
</script>
複製代碼

3,更復雜點

在上面渲染學生列表時我想將學生每科的成績也展現出來,數據結構以下:

export default {
  ...
  data () {
    return {
      students: [
	    { name: '小明',
	      age: 16, 
	      score: [
	        { course: '語文', value: 86 },
	        { course: '數學', value: 88 }
	      ]
	    },
	    { name: '小李',
	      age: 16, 
	      score: [
	        { course: '語文', value: 90 },
	        { course: '數學', value: 85 }
	      ]
	    }
      ]
    }
  }
  ...
}
複製代碼
在children中手動實現?
<render-list :isLoading="isStudentsLoding" :data="students">
 <div slot="item" slot-scope="{ data }">
   <div>名字:{{ data.name }}</div>
   <div>年齡:{{ data.age }}</div>
   <!-- 咱們能拿到每一個學生的信息,手動渲染成績列表 -->
   <div v-for="score in data.score" :key="score.course">
     <slot name="score" :data="score" />
   </div>
 </div>
</render-list>
複製代碼
調整咱們的列表算法

上面的方式或許能夠達到咱們目的,可是要渲染列表對使用者來講要關心的事太多了,須要調整咱們的算法模板

// renderList.vue
<template>
  <div>
    <img v-if="isLoading" src="./loading.gif" />
    <div v-if="isEmpty">數據爲空</div>
    <div v-else>
      <div v-for="(item, index) in source" :key="index">
        <!-- 學生信息 -->
        <slot name="item" :data="item"></slot>
        <!-- 學生分數 -->
        <div class="score">
          <div v-for="score in item.score" :key="score.course">
            <slot name="score" :data="score" />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
複製代碼
// stage.vue
<template>
  <div>
   <render-list :isLoading="isStudentsLoding" :data="students">
     <!-- 學生信息渲染規則 -->
     <div slot="item" slot-scope="{ data }">
       <div>名字:{{ data.name }}</div>
       <div>年齡:{{ data.age }}</div>
     </div>
     <!-- 學生成績渲染規則 -->
     <div slot="score" slot-scope="{ data }">
       <div>——課程:{{ data.course }}</div>
       <div>——成績:{{ data.value }}</div>
     </div>
   </render-list>
  </div>
</template>
複製代碼

總結

這一篇主要分享了vue中slot的設計模式理念,結合列表渲染的場景向你們展現了模板方法模式的應用。
以前在網上看到有人用各類騷操做實現vue的render props,如今看看有必要麼,slot無論怎麼看都比較順眼(在我寫這篇文章時slot的API有點捉急,v2.6.0 很好解決了這個問題)

爲何開頭說模板方法模式是vue、react開發中使用最多的設計模式?

經過上面的介紹相信你們已經感覺到模板方法模式的特色:
1,封裝一個算法模板
2,用戶負責某一步的具體實現

如今想一想vue,react內部封裝好了diff算法、狀態改變觸發更新的機制、生命週期,咱們在使用框架時只
須要按照語法規則使用它們的API完成咱們的業務邏輯,這也就是模板方法模式
複製代碼

本文實現一樣適用於react,爲何文章以vue作題?vue的template讓咱們在理解一些概念的時候可能會有點不適應,而react的jsx能夠看作就是在寫JavaScript對各類概念實現更靈活 友情提示:設計模式在vue中的應用應該會寫一個系列,喜歡的同窗記得關注下

相關文章
相關標籤/搜索