一個不爲人知的高性能組件註冊及實現組件排序技巧

背景

在使用Vue的路途中,你必定知道如何去註冊並調用一個組件前端

一般咱們會經過三個步驟來實現調用組件的一整個流程vue

  • 經過import引入組件
  • 在父組件的組件對象components中將導入的子組件註冊
  • 在父組件中使用該組件
<template>
  <div>
    <Child msg="Hello World!"/>
  </div>
</template>

<script>
import Child from "../components/Child";

export default {
  name: "page",
  components: {
    Child
  }
};
</script>
複製代碼

這多是80%Vue友最多見的一個使用子組件的模式,沒錯,我也常常這麼幹...若是你用膩了這種方式,想換個新奇的,接着往下走,咱們再學習兩個不經常使用的但又很高效的使用方式,提高開發逼格。webpack

場景預設1

想象一下一個會員中心組件,有一部分區域用來展現會員與會員差別的相關展現模塊,可是這兩個只會展現其中一個,會員與非會員。假設咱們如今有MemberInfo以及UserInfo兩個組件,咱們想根據用戶身份進行對應模塊顯示git

實現方案1

這多是大部分前端玩家會想到的實現方式,包括我本身也經常會如此(捂臉)github

<template>
  <div class="container">
    <button @click="isMember = !isMember">{{isMember?'我不想要會員了,哼':'我要成爲會員'}}</button>
    <UserInfo v-if="!isMember"/>
    <MemberInfo v-else/>
  </div>
</template>

<script>
import UserInfo from "../components/UserInfo";
import MemberInfo from "../components/MemberInfo";

export default {
  name: "user",
  data() {
    return {
      isMember: false
    };
  },
  components: {
    UserInfo,
    MemberInfo
  }
};
</script>
複製代碼

實現方案2

經過閱讀官方文檔,咱們會發現Vue有提供一個內置組件 component ,渲染一個「元組件」爲動態組件。根據 is 的值,來決定哪一個組件被渲染。若是你對此還不太瞭解,文末會奉上官網傳送門.web

<template>
  <div class="container">
    <button @click="isMember = !isMember">{{isMember?'我不想要會員了,哼':'我要成爲會員'}}</button>
    <component :is="userComponentName" title="component就是好用喲"/>
  </div>
</template>

<script>
import UserInfo from "../components/UserInfo";
import MemberInfo from "../components/MemberInfo";

export default {
  name: "userComponent",
  data() {
    return {
      isMember: false
    };
  },
  components: {
    UserInfo,
    MemberInfo
  },
  computed: {
    userComponentName() {
      let { isMember } = this;
      return isMember ? "member-info" : "user-info";
    }
  }
};
</script>
複製代碼

從方案1過渡到方案2 ,很明顯咱們經過計算屬性配合內置組件component完美剔除了v-if/v-else的邏輯判斷,也能夠正常作數據傳遞後端

只是從如今看來,除了使用方法改變了,沒有啥差別了。這並非本次想介紹的。現有的兩個模塊組件,咱們仍然必須先導入並註冊才能完成調用。如今,咱們就不想提早註冊好所需使用的子組件,由於太麻煩了而且浪費性能,咱們想嘗試動態導入註冊使用,讓咱們繼續往下,沖沖衝!api

實現方案3

<template>
  <div class="container">
    <button @click="isMember = !isMember">{{isMember?'我不想要會員了,哼':'我要成爲會員'}}</button>
    <component :is="userComponentImstance" title="component就是好用喲"/>
  </div>
</template>

<script>
export default {
  name: "userImport",
  data() {
    return {
      isMember: false
    };
  },
  computed: {
    userComponentImstance() {
      let { isMember } = this;
      let pathName = isMember ? "MemberInfo" : "UserInfo";
      //經過import動態導入組件 配合webpack實現組件分離
      return () => import(`../components/${pathName}`);
    }
  }
};
</script>
複製代碼

能夠看到,咱們再也不是提早導入註冊組件的形式來調用了,component直接給其提供一個完整的組件對象 經過閱讀官方文檔咱們能夠看到 is接收兩種選項之一:緩存

  • 註冊的組件名稱
  • 組件對象

如今,咱們經過動態import的形式導入了子組件,配合computed按條件渲染對應的子組件,誰用誰知道。只有在程序運行的時候纔會進行,就省去了咱們預先導入和註冊組件的形式,這樣的效果是否是跟使用第三方組件的時候按需加載有個似曾相識的味道..嗯是的,包括咱們爲了對SPA性能優化的時候router懶加載有着殊途同歸之妙。性能優化

當咱們改變isMember這個變量就能夠實現動態切換組件了。這樣作的好處在於,當咱們使用動態導入的時候,webpack會將與導入函數匹配的每一個文件單首創建一個chunk,也就是咱們常說的分包加載,而不會一次性加載所有組件。當前樣例並不能看出具體有多大的性能提高,但實際開發中,這個優點會很是明顯。

若是你熟悉webpack你能夠很好的利用這點對代碼進行拆分和DIY屬於本身程序的一個加載策略。具體能夠經過瞭解Code Splitting

場景預設2

以前遇到過一個需求,就是給商城首頁的幾個活動模塊根據後端返回順序依次渲染,當時沒想明白怎麼實現。而如今,當你看到這裏的時候,我想你大概有一點點小思路了吧。(若是沒有,那必定是個人錯,文章寫的不夠清晰)。如今知道了內置組件component以及咱們的動態加載,咱們徹底能夠很輕鬆的實現。

假設咱們父組件爲Home主頁面,可能存在Seckill秒殺、Group團購、Limit限購、Bargain砍價四個活動模塊,模塊展現及排序均由數據決定,而再也不是咱們hard coded

<template>
  <div>
    <p>僞裝這是一個商城首頁</p>
    <button @click="shuffle">隨機切換模塊順序</button>
    <component :is="item.imstance" v-for="(item ,i) in componentImstances" :info="item" :key="i"/>
  </div>
</template>

<script>
export default {
  name: "home",
  data() {
    return {
      moduleList: [
        {
          type: "Bargain",
          title: "砍價",
          startTime: "2019-12-01",
          endTime: "2019-01-01"
        },
        {
          type: "Seckill",

          title: "秒殺",
          startTime: "2019-12-05",
          endTime: "2019-01-01"
        },
        {
          type: "Limit",

          title: "限購",
          startTime: "2019-12-07",
          endTime: "2019-01-01"
        },
        {
          type: "Group",
          title: "團購",
          startTime: "2019-12-11",
          endTime: "2019-01-01"
        }
      ]
    };
  },
  components: {},
  methods: {
    shuffle() {
      let { moduleList } = this;
      let resultArr = [];
      while (moduleList.length > 0) {
        var index = Math.floor(Math.random() * moduleList.length);
        resultArr.push(moduleList[index]);
        moduleList.splice(index, 1);
      }
      this.moduleList = resultArr;
    }
  },
  computed: {
    componentImstances() {
      let { moduleList } = this;
      return moduleList.map(item => {
        item.imstance = () => import(`../components/${item.type}`);
        return item;
      });
    }
  }
};
</script>
複製代碼

這樣就實現了咱們的活動模塊的自定義排序了,可是目前咱們的模塊動態導入是根據後端返回數據來加載的,沒有人會知道中間會不會出現什麼幺蛾子,爲了不動態導入的組件在未知狀況下加載失敗,咱們能夠去作一個異常模板提示。 將上面中的componentImstances稍做修改:

computed: {
    componentImstances() {
      let { moduleList } = this;
      return moduleList.map(item => {
        item.imstance = () => {
          return new Promise((resove, reject) => {
            let imstance = import(`../components/${item.type}`);
            imstance.then(res => {
              resove(res);
            });
            imstance.catch(e => {
              resove(import("../components/Error"));
            });
          });
        };
        return item;
      });
    }
  }
複製代碼

咱們將動態導入異常的組件捕獲並輸出默認模板,用戶體驗嘛確定是你的夙願

參考:

Vue內置組件

webpack

總結

靈活運用component以及import,若是一個組件有許多不一樣的試圖,那這是頗有用的,易於擴展。由於它是異步的,僅在須要的時候才加載模板,使代碼更加輕量。題外話,component配合keep-alive來緩存組件,效果也是頂呱呱噢,本文再也不贅述。相關演示源碼已更新到Github傳送門

❤️ 最後

若是你以爲這篇內容對你挺有啓發:

分享出去讓更多的人也能看到這篇內容(歡迎點贊關注😊)

關注公衆號「前端公蝦米」,每週分享前端乾貨

相關文章
相關標籤/搜索