Vue 組件繼承在項(xiàng)目中的應(yīng)用案例

平時(shí)在項(xiàng)目中很少用到Vue的繼承特性,通常是封裝單文件組件,然后在其他組件中引入。繼承是面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)核心概念之一,通過(guò)它可以實(shí)現(xiàn)子類(lèi)對(duì)父類(lèi)的數(shù)據(jù)和功能的復(fù)用,同時(shí)還可以選擇性地覆寫(xiě)父類(lèi)的屬性和方法。那么,前端組件開(kāi)發(fā)如何利用這個(gè)特性呢?

最近剛好碰到一個(gè)應(yīng)用場(chǎng)景,某個(gè)業(yè)務(wù)模塊的各個(gè)頁(yè)面在結(jié)構(gòu)上幾乎完全一致,區(qū)別只在于里面的數(shù)據(jù)。首先想到的實(shí)現(xiàn)方式是封裝成一個(gè)組件,然后在各個(gè)頁(yè)面中引入,通過(guò)props傳遞不同的數(shù)據(jù)來(lái)區(qū)分不同行為。但這種方式需要在組件里做各種判斷,每多一個(gè)頁(yè)面類(lèi)型就需要增加一個(gè)條件分支,這樣就違背了面向?qū)ο笤O(shè)計(jì)里的“開(kāi)發(fā)-封閉”原則,不利于以后的擴(kuò)展。

頁(yè)面結(jié)構(gòu)

該業(yè)務(wù)模塊是要展示多個(gè)統(tǒng)計(jì)指標(biāo)的趨勢(shì),頁(yè)面包含一個(gè)走勢(shì)圖和報(bào)表,以及一些條件篩選控件。每個(gè)指標(biāo)的頁(yè)面幾乎都是一樣的。使用繼承,剛好可以解決這個(gè)問(wèn)題。定義一個(gè)基類(lèi)頁(yè)面,包含頁(yè)面模板和公共屬性和方法,各數(shù)據(jù)指標(biāo)頁(yè)面作為子類(lèi)繼承該基類(lèi),這樣就擁有了同樣的頁(yè)面模板和事件綁定,只需要在子類(lèi)覆寫(xiě)事件處理方法就能實(shí)現(xiàn)不同統(tǒng)計(jì)指標(biāo)展示不同數(shù)據(jù)。

Vue 支持組件的繼承,在單文件組件里指定extends即可:

<!-- 某統(tǒng)計(jì)指標(biāo)報(bào)表 -->
<script>
import ReportBase from './ReportBase.vue';
export default {
  extends: ReportBase,
  data() {
    return {
      title: '數(shù)據(jù)指標(biāo)標(biāo)題',
      tableTitle: '數(shù)據(jù)指標(biāo)表格標(biāo)題',
      chartTitle: '數(shù)據(jù)指標(biāo)趨勢(shì)圖標(biāo)題',
      columns: [
        {
          label: '日期',
          prop: 'date',
        },
        {
          label: '總數(shù)(臺(tái))',
          prop: 'total',
        },
        {
          label: '故障數(shù)量(臺(tái))',
          prop: 'failure',
        },
        {
          label: '占比(%)',
          prop: 'ratio',
        },
      ],
    };
  },
  methods: {
    // 覆寫(xiě)基類(lèi)的事件處理方法,根據(jù)該統(tǒng)計(jì)指標(biāo)請(qǐng)求不同的業(yè)務(wù)接口
    onFactoryChange() {},
    onChartTimeChange() {},
    onTableDateChange() {},
  },
};
</script>
<style lang="scss" scoped>
</style>

基類(lèi)組件代碼大致如下:

<!-- ReportBase.vue -->
<template>
  <div class="report-page router-page widget">
    <div class="widget-header">
      <div class="widget-header-title">{{title}}</div>
    </div>
    <div class="widget-body">
      <div class="filter flexbox">
        <span class="filter-name">{{ $t('pool.farm') }}</span>
        <el-radio-group class="plain" size="mini" v-model="factoryId" @change="onFactoryChange">
          <el-radio-button
            v-for="(item, index) in factoryList"
            :label="item.id"
            :key="index"
          >{{ item.name }}</el-radio-button>
        </el-radio-group>
      </div>
      <section class="chart-area section">
        <div class="flexbox align-center">
          <div class="chart-title">{{chartTitle}}</div>
          <el-radio-group size="mini" v-model="timespan" @change="onChartTimeChange">
            <el-radio-button label="week">周</el-radio-button>
            <el-radio-button label="month">月</el-radio-button>
            <el-radio-button label="year">年</el-radio-button>
          </el-radio-group>
        </div>
      </section>
      <section class="grid-area section">
        <div class="flexbox space-bt">
          <div class="grid-title">{{tableTitle}}</div>
          <el-date-picker
            v-model="month"
            type="month"
            placeholder="選擇月"
            @change="onTableDateChange"
          ></el-date-picker>
        </div>
        <el-table :data="tableData" style="width: 100%">
          <el-table-column v-bind="item" v-for="(item, index) in columns" :key="index"></el-table-column>
        </el-table>
      </section>
    </div>
  </div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
  data() {
    return {
      title: '',
      factoryId: 0,
      month: '',
      tableTitle: '',
      columns: [],
      tableData: [],
      timespan: 'week',
    };
  },
  watch: {
    factoryList(value) {
      if (value.length > 0) {
        this.factoryId = value[0].id;
        this.onFactoryChange(this.factoryId);
      }
    },
  },
  computed: {
    ...mapGetters(['factoryList']),
  },
  methods: {
    onFactoryChange() {},
    onChartTimeChange() {},
    onTableDateChange() {},
  },
  created() {
    if (this.factoryList.length > 0) {
      this.factoryId = this.factoryList[0].id;
      this.onFactoryChange(this.factoryId);
    }
  },
};
</script>
<style lang="scss">

這樣,后續(xù)如果有更多的統(tǒng)計(jì)指標(biāo)頁(yè)面,只需要繼承和覆寫(xiě)方法就行,而基類(lèi)幾乎不用改動(dòng)。當(dāng)然,Vue組件的這種繼承方式也不是完美的,比如 HTML模板要么完全繼承,要么完全重寫(xiě),不能按需繼承某個(gè)部分。如果子類(lèi)在結(jié)構(gòu)上跟基類(lèi)有所差異,還是需要在基類(lèi)中做條件判斷。如果模板差異太大,可以重新定義子類(lèi)自己的template,至少還可以重用一部分業(yè)務(wù)邏輯代碼。像本文提到的應(yīng)用場(chǎng)景,就非常適合用繼承來(lái)實(shí)現(xiàn)。

各位看官如果有更好的思路,歡迎在評(píng)論中一起探討!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,663評(píng)論 1 32
  • CSS3是CSS技術(shù)的升級(jí)版本,CSS3語(yǔ)言開(kāi)發(fā)是朝著模塊化發(fā)展的。以前的規(guī)范作為一個(gè)模塊實(shí)在是太龐大而且比較復(fù)雜...
    Naeco閱讀 269評(píng)論 0 2
  • 2009年我步入了夢(mèng)寐以求的大學(xué),終于走出了小鎮(zhèn)子來(lái)到了大城市打拼!那一天我就想未來(lái)的我一定屬于這座城市........
    花殘淚殤閱讀 321評(píng)論 0 0
  • 自認(rèn)為文章寫(xiě)作能力不好,以前每天都會(huì)寫(xiě)寫(xiě)日記什么的,后來(lái)也不怎么寫(xiě),一到想寫(xiě)東西,不是這里卡殼了,就是那里死...
    甘恩閱讀 123評(píng)論 0 0
  • 跑步圣經(jīng) 在希臘奧林匹亞阿爾菲斯河岸的巖壁上,至今還刻著古希臘的一段格言:“如果你想聰明,跑步吧;如果你想強(qiáng)壯,跑...
    一枚冰兒閱讀 667評(píng)論 0 3

友情鏈接更多精彩內(nèi)容