使用異步組件優化Vue應用程序的性能

前言

單頁應用其一個問題是首屏屏渲染速度較慢。這是由於頁面首次加載時服務器將向客戶端發送大量JavaScript,在屏幕上顯示任何內容以前必須下載並解析。能夠想象,隨着應用程序規模的擴大,這個問題對用戶體驗的影響也會愈來愈突出。css

如今幸運的是,當使用Vue CLI構建Vue應用程序時(使用webpack),能夠採起一些措施來抵消這種狀況。在本文中,我將演示如何在應用程序的初始渲染以後使用異步組件和webpack的代碼分割功能加載到頁面的某些部分。這將使初始加載時間降至最低,併爲您的應用程序提供更好的用戶體驗。vue

認識異步組件

在咱們開始建立異步組件以前,讓咱們看一下咱們一般如何加載組件。爲此,咱們將使用一個很是簡單的消息組件做爲例子:webpack

<!-- Message.vue -->
<template>
  <h1>New message!</h1>
</template>
複製代碼

如今咱們已經建立了咱們的Message組件,讓咱們將它加載到咱們的文件中並顯示它。咱們能夠導入組件並將其添加到組件選項中,以便咱們能夠在模板中使用它:git

<!-- App.vue -->
<template>
  <div>
    <message></message>
  </div>
</template>

<script>
import Message from "./Message";
export default {
  components: {
    Message
  }
};
</script>
複製代碼

但如今發生了什麼?只要應用程序加載,就會加載Message組件,所以它包含在初始加載過程當中。github

對於一個簡單的應用程序來講,這看起來可能不是一個大問題,但能夠考慮像電商網站這樣複雜的場景。想象一下,用戶將項目添加到購物車,而後想要結賬,所以單擊結賬按鈕會生成一個包含所選商品的全部詳細信息的框。使用上述方法,此結賬框將包含在初始包中,但咱們只需在用戶單擊結賬按鈕時使用該組件。用戶甚至能夠在不點擊結帳按鈕的狀況下瀏覽網站,這意味着在加載這個可能不會使用的組件時浪費資源是沒有意義的。web

爲了提升應用程序的效率,咱們能夠結合延遲加載和代碼分割技術。npm

Webpack提供的代碼拆分功能容許您將代碼拆分爲各類捆綁包,而後能夠按需加載或稍後並行加載。它只能在須要或使用時加載特定的代碼片斷。數組

Dynamic Imports(動態導入)

Vue使用Dynamic Imports解決這種狀況。此功能引入了一種新的相似函數的導入形式,它將返回包含(Vue)組件的Promise。因爲import是一個接收字符串的函數,咱們能夠作一些強大的事情,好比使用表達式加載模塊。自版本61以來,Chrome中已提供動態導入。有關這些內容的詳細信息,請訪問Google Developers網站bash

代碼拆分由webpack,Rollup或Parcel等捆綁器處理,它們解析動態導入語法併爲每一個動態導入的模塊建立單獨的文件。稍後咱們將在控制檯的網絡選項卡中看到這一點。但首先,咱們來看看靜態和動態導入之間的區別:服務器

// static import
import Message from "./Message";

// dynamic import
import("./Message").then(Message => {
  // Message module is available here...
});
複製代碼

如今,讓咱們將這些知識應用到咱們的Message組件中,咱們將獲得一個以下所示的組件:App.vue

<!-- App.vue -->
<template>
  <div>
    <message></message>
  </div>
</template>

<script>
import Message from "./Message";
export default {
  components: {
    Message: () => import("./Message")
  }
};
</script>
複製代碼

如你所見,函數import()將解析返回組件的Promise,這意味着咱們已成功異步加載組件。若是您查看devtoolsnetwork選項卡,您會注意到一個名爲0.js包含異步組件的文件。

調試信息

根據條件加載異步組件

如今咱們已經掌握了異步組件,讓咱們僅在真正須要時加載它們。在本文的上一節中,我解釋了僅在用戶點擊結賬按鈕時才加載的結賬框的用例。讓咱們把它構建出來。

項目設置

若是您沒有安裝vue/cli,首先應該安裝它:

npm i -g @vue/cli
複製代碼

接下來,使用CLI建立新項目,在出現提示時選擇默認預設便可:

vue create my-store
複製代碼

轉到項目目錄,而後安裝咱們將用於樣式的ant-design-vue庫:

cd my-store
npm i ant-design-vue
複製代碼

接下來,導入Ant設計庫:src/main.js

import 'ant-design-vue/dist/antd.css'
複製代碼

最後咱們在src/comonents裏建立兩個新組件Checkout.vueItems.vue

touch src/components/{Checkout.vue,Items.vue}
複製代碼

寫一個商店的視圖層

打開src/App.vue並用如下代碼替換文件裏代碼:

<template>
  <div id="app">
    <h1>{{ msg }}</h1>
    <items></items>
  </div>
</template>

<script>
import items from "./components/Items"

export default {
  components: {
    items
  },
  name: 'app',
  data () {
    return {
      msg: 'My Fancy T-Shirt Store'
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>
複製代碼

這裏沒有什麼花哨的東西。咱們所作的只是顯示一條消息並渲染一個<items>組件。

接下來,打開src/components/Items.vue並添加如下代碼:

<template>
  <div>
    <div style="padding: 20px;">
      <Row :gutter="16">
        <Col :span="24" style="padding:5px">
          <Icon type="shopping-cart" style="margin-right:5px"/>{{shoppingList.length}} item(s)
          <Button @click="show = true" id="checkout">Checkout</Button>
        </Col>
      </Row>
    </div>
    <div v-if="show">
      <Row :gutter="16" style="margin:0 400px 50px 400px">
        <checkout v-bind:shoppingList="shoppingList"></checkout>
      </Row>
    </div>
    <div style="background-color: #ececec; padding: 20px;">
      <Row :gutter="16">
        <Col :span="6" v-for="(item, key) in items" v-bind:key="key" style="padding:5px">
          <Card v-bind:title="item.msg" v-bind:key="key">
            <Button type="primary" @click="addItem(key)">Buy ${{item.price}}</Button>
          </Card>
        </Col>
      </Row>
    </div>
  </div>
</template>

<script>
import { Card, Col, Row, Button, Icon } from 'ant-design-vue';

export default {
  methods: {
    addItem (key) {
      if(!this.shoppingList.includes(key)) {
        this.shoppingList.push(key);
      }
    }
  },
  components: {
    Card, Col, Row, Button, Icon,
    checkout: () => import('./Checkout')
  },
  data: () => ({
    items: [
      { msg: 'First Product', price: 9.99 },
      { msg: 'Second Product', price: 19.99 },
      { msg: 'Third Product', price: 15.00 },
      { msg: 'Fancy Shirt', price: 137.00 },
      { msg: 'More Fancy', price: 109.99 },
      { msg: 'Extreme', price: 3.00 },
      { msg: 'Super Shirt', price: 109.99 },
      { msg: 'Epic Shirt', price: 3.00 },
    ],
    shoppingList: [],
    show: false
  })
}
</script>
<style>
#checkout {
  background-color:#e55242;
  color:white;
  margin-left: 10px;
}
</style>
複製代碼

在此文件中,咱們顯示一個帶有商品數量的購物車圖標。商品是從items數組中提取的。若是單擊項目的Buy 按鈕,將會調用addItem方法,該方法會將相關商品push到shoppingList數組中。從而增長購物車的總數。

咱們還在頁面中添加了一個Checkout按鈕:

<Button @click="show = true" id="checkout">Checkout</Button>
複製代碼

當用戶點擊這個按鈕,咱們設置的參數showtruetrue是很是重要對於有經過條件地加載咱們的異步組件。

在接下來的幾行中,您能夠找到v-if的聲明,這個語句僅用來顯示咱們checkout組件的<div>,可是咱們只想在用戶點擊Checkout 按鈕時顯示結帳組件,咱們該怎麼辦?

這裏咱們將checkout組件在components選項裏異步加載。這裏v-bind將參數傳遞給組件。正如你看的的這樣,建立條件異步組件是很容易的:

<div v-if="show">
  <checkout v-bind:shoppingList="shoppingList"></checkout>
</div>
複製代碼

讓咱們快速Checkout組件添加下面的代碼在src/components/Checkout.vue裏:

<template>
  <Card title="Checkout Items" key="checkout">
    <p v-for="(k, i) in this.shoppingList" :key="i">
      Item: {{items[Number(k)].msg}} for ${{items[Number(k)].price}}
    </p>
  </Card>
</template>

<script>
import { Card } from 'ant-design-vue';

export default {
  props: ['shoppingList'],
  components: {
    Card
  },
  data: () => ({
    items: [
      { msg: 'First Product', price: 9.99 },
      { msg: 'Second Product', price: 19.99 },
      { msg: 'Third Product', price: 15.00 },
      { msg: 'Fancy Shirt', price: 137.00 },
      { msg: 'More Fancy', price: 109.99 },
      { msg: 'Extreme', price: 3.00 },
      { msg: 'Super Shirt', price: 109.99 },
      { msg: 'Epic Shirt', price: 3.00 },
    ]
  })
}
</script>
複製代碼

在這裏,咱們將接收一個shoppingList並把他輸出到屏幕上。

您可使用該npm run serve命令運行該應用程序。而後導航到http:// localhost:8080。若是一切按計劃進行,你應該會看到以下圖所示的內容。

預覽效果

能夠嘗試打開在network選項卡,點擊Checkout按鈕,能夠發現network裏將異步加載結帳組件

您還能夠在GitHub上查看此演示的代碼

爲異步組件添加加載中和加載錯誤組件

有時異步組件加載過長或加載時。顯示加載動畫或錯誤信息可能頗有用,但要支持這會再次下降應用程序的速度。異步組件應該小並且加載速度快。這是一個例子:

const Message = () => ({
  component: import("./Message"),
  loading: LoadingAnimation,
  error: ErrorComponent
});
複製代碼

總結

建立和實現異步組件很是簡單,應該成爲標準開發例程的一部分。從用戶體驗的角度來看,儘量減小初始加載時間以保持用戶的注意力是很是重要。但願本教程能夠幫助您構建異步加載組件。

相關文章
相關標籤/搜索