使用非同步元件優化Vue應用程式的效能

megatron發表於2019-04-23

前言

單頁應用其一個問題是首屏屏渲染速度較慢。這是因為頁面首次載入時伺服器將向客戶端傳送大量JavaScript,在螢幕上顯示任何內容之前必須下載並解析。可以想象,隨著應用程式規模的擴大,這個問題對使用者體驗的影響也會越來越突出。

現在幸運的是,當使用Vue CLI構建Vue應用程式時(使用webpack),可以採取一些措施來抵消這種情況。在本文中,我將演示如何在應用程式的初始渲染之後使用非同步元件和webpack的程式碼分割功能載入到頁面的某些部分。這將使初始載入時間降至最低,併為您的應用程式提供更好的使用者體驗。

認識非同步元件

在我們開始建立非同步元件之前,讓我們看一下我們通常如何載入元件。為此,我們將使用一個非常簡單的訊息元件作為例子:

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

現在我們已經建立了我們的Message元件,讓我們將它載入到我們的檔案中並顯示它。我們可以匯入元件並將其新增到元件選項中,以便我們可以在模板中使用它:

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

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

但現在發生了什麼?只要應用程式載入,就會載入Message元件,因此它包含在初始載入過程中。

對於一個簡單的應用程式來說,這看起來可能不是一個大問題,但可以考慮像電商網站這樣複雜的場景。想象一下,使用者將專案新增到購物車,然後想要結帳,因此單擊結帳按鈕會生成一個包含所選商品的所有詳細資訊的框。使用上述方法,此結帳框將包含在初始包中,但我們只需在使用者單擊結帳按鈕時使用該元件。使用者甚至可以在不點選結賬按鈕的情況下瀏覽網站,這意味著在載入這個可能不會使用的元件時浪費資源是沒有意義的。

為了提高應用程式的效率,我們可以結合延遲載入和程式碼分割技術。

Webpack提供的程式碼拆分功能允許您將程式碼拆分為各種捆綁包,然後可以按需載入或稍後並行載入。它只能在需要或使用時載入特定的程式碼片段。

Dynamic Imports(動態匯入)

Vue使用Dynamic Imports解決這種情況。此功能引入了一種新的類似函式的匯入形式,它將返回包含(Vue)元件的Promise。由於import是一個接收字串的函式,我們可以做一些強大的事情,比如使用表示式載入模組。自版本61以來,Chrome中已提供動態匯入。有關這些內容的詳細資訊,請訪問Google Developers網站

程式碼拆分由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
});
複製程式碼

總結

建立和實現非同步元件非常簡單,應該成為標準開發例程的一部分。從使用者體驗的角度來看,儘可能減少初始載入時間以保持使用者的注意力是非常重要。希望本教程可以幫助您構建非同步載入元件。

相關文章