異步加載在Vue生命週期哪一個階段更合理

react高階面試題中有這麼一道:爲何異步請求數據在didMount階段更合適?同爲MVVM中的翹楚,Vue是否也有相似問題呢?另外,我在平時也無開發過程當中也會發現,每一個人選擇的那個生命週期階段去異步請求數據總會不同,所以引起思考,到底哪一個階段更適合異步請求數據呢?在產品設計和用戶體驗方面又會有哪些影響?本篇記錄就是爲了解決這兩個問題。javascript

1、Vue生命週期vue

首先再老話重提過一下Vue生命週期,以及每一個階段都作了什麼事。java

1. beforeCreated:生成$options選項,並給實例添加生命週期相關屬性。在實例初始化以後,在 數據觀測(data observer) 和event/watcher 事件配置以前被調用,也就是說,data,watcher,methods都不存在這個階段。可是有一個對象存在,那就是$route,所以此階段就能夠根據路由信息進行重定向等操做。node

2. created:初始化與依賴注入相關的操做,會遍歷傳入methods的選項,初始化選項數據,從$options獲取數據選項(vm.$options.data),給數據添加‘觀察器’對象並建立觀察器,定義getter、setter存儲器屬性。在實例建立以後被調用,該階段能夠訪問data,使用watcher、events、methods,也就是說 數據觀測(data observer) 和event/watcher 事件配置 已完成。可是此時dom尚未被掛載。該階段容許執行http請求操做。react

3. beforeMount:將HTML解析生成AST節點,再根據AST節點動態生成渲染函數。相關render函數首次被調用(劃重點)。面試

4. mounted:在掛載完成以後被調用,執行render函數生成虛擬dom,建立真實dom替換虛擬dom,並掛載到實例。能夠操做dom,好比事件監聽ajax

5. beforeUpdate:$vm.data更新以後,虛擬dom從新渲染以前被調用。在這個鉤子能夠修改$vm.data,並不會觸發附加的衝渲染過程。typescript

6. updated:虛擬dom從新渲染後調用,若再次修改$vm.data,會再次觸發beforeUpdate、updated,進入死循環。bash

7. beforeDestroy:實例被銷燬前調用,也就是說在這個階段仍是能夠調用實例的。dom

8. destroyed:實例被銷燬後調用,全部的事件監聽器已被移除,子實例被銷燬。

總結來講,虛擬dom開始渲染是在beforeMount時,dom實例掛載完成在mounted階段顯示。

那麼接下來了解就是render函數。

render示例:export default {
   data () {
       return {
           menu_items: []   // 請求返回如:[{fullname: '頁面一'},{fullname: '頁面二'},{fullname: '頁面三'},{fullname: '頁面四'}]
       }
   },   render (createElement){    return createElement(
          // 1. 第一個參數,要渲染的標籤名稱(必填)
          'ul',
          // 2. 第二個參數,1中要渲染的標籤的屬性,或者文本元素(可選)      {  

               class: {'uk-nav': true},           },          // 3. 第三個參數,1中標籤的子元素,詳情看官方文檔(可選)
        this.menu_items.map(item=>createElement('li',item.fullname)))

       )  }}複製代碼

render函數最終返回的是createNodeDescription(節點描述),即俗稱virtual node(虛擬節點)。用template寫的話,就是下面這樣:

<template>

    <ul>       <li v-for="item in menu_items">         {{ item.fullname }}     </li>  </ul>

</template>複製代碼

這個過程在mounted被調用前完成。詳細參考可移步 這裏

2、異步加載

setTimeout等異步函數

異步函數跟同步函數的不一樣之處,最大的應該就是異步函數會等到全部同步函數執行完成以後再執行。具體的能夠看 事件循環

//data字段有個num 
created: function () {
    console.group('created 建立完畢狀態===============》')
    console.log('%c%s', 'color:red', 'el : ' + this.$el) // undefined
    console.log('%c%s', 'color:red', 'data : ' + this.$data) // 已被初始化
    console.log('%c%s', 'color:red', 'message: ' + this.message) // 已被初始化
    //新增代碼片斷
    setTimeout(() => { //這裏只是爲了偷懶用了ES6的箭頭函數,若是是普通函數請注意this指針修改,vue中請不要濫用箭頭函數,出了問題找都找不到
      this.num ++
      this.num += 2
    }, 0) //注意這裏的延時都是0
    setTimeout(() => {
      this.num -= 5
    }, 0)
 }複製代碼

控制檯答應結果:


(略失真。。。截圖太大稍微壓縮了下!!--)

vue在執行代碼的時候,並無去管定時器裏發生了什麼事情,甚至已經設置了0延時,他依舊會去順序執行其餘生命週期,看起來就像跳過了這些異步加載。所以能夠肯定一點,生命週期中的異步操做不會按照順序執行,而是會等到非異步操做結束後執行。所以書寫這部分代碼的時候請注意裏面的邏輯不要和順序掛鉤,要確保任何異步操做即便最後執行,以前的程序也不會發生異常從而阻塞整個進程。

ajax異步請求

ajax請求是異步操做,回調函數的執行時間是不肯定的。也就是說,即便在created鉤子發送請求,dom被掛載以後請求仍沒有返回結果,就頗有可能致使運行出錯,諸如:


由於此時上述render事例中的menu_items仍是空置。

解決方案

針對ajax異步請求,這樣的錯誤緣由其實就是由於返回結果沒遇上dom節點的渲染。因此能夠從兩方面作修改:一是返回結果的賦值變量上,另外一個就是dom節點的渲染層面。

1. 給予賦值變量初始值,即定義時menu_items:[ {fullname: ''} ]。

這麼作的好處就是頁面節點的渲染不受限於返回結果,靜態文案照樣會被渲染,動態數據則會在數據更新時被填充。給用戶的感受就是,頁面渲染速度不錯。

可是這種方式也有缺陷,後臺返回數據字段不盡相同,要是都這麼寫那就真是麻煩了。

固然若是你使用typescript就沒有這種煩惱,menu_items: { [propName: string]: any } = {}就搞定了。

2. v-if,控制dom節點的掛載,當且僅當menu_items被賦予返回值時,纔開始渲染節點。

這麼作的好處就是靜態和動態文案同步展示在用戶面前,不會有文案跳動,數據從無到有的過程。可是,反作用就是頁面渲染時間、用戶等待時間變長。

那若是dom掛載前請求數據已經返回了,又會是怎樣的結果呢?

咱們能夠用setTimeout來模擬一下這個過程

<span>{{person.name.firstName}}</span> 

data: function () {
    return {
      message: 'hello world',
      add: 1,
      person: {
        name: {}
      }
    }
  },
created: function () {
    console.group('created 建立完畢狀態===============》')
    console.log('%c%s', 'color:red', 'el     : ' + this.$el) // undefined
    console.log('%c%s', 'color:red', 'data   : ' + this.$data) // 已被初始化
    console.log('%c%s', 'color:red', 'message: ' + this.message) // 已被初始化
    //僞裝接口返回了一些信息給你,如一我的,而後你把這些信息賦值給了this實力
    setTimeout(() => {
      this.person = {
        name: {
          lastName: 'carry',
          firstName: 'dong'
        },
        sex: '男'
      }
    }, 0)複製代碼

請求夠早了吧,但仍是報錯了,this.person.name.firstName 是 undefined,不過程序報完錯後仍是再繼續執行了。

3、結論

既然異步函數並不會阻塞vue生命週期整個進程,那麼在哪一個階段請求均可以。若是考慮到用戶體驗方面的影響,但願用戶今早感知頁面已加載,減小空白頁面時間,建議就放在created階段了,而後再處理會出現null、undefined這種狀況就好。畢竟越早獲取數據,在mounted實例掛載的時候渲染也就越及時。

固然即便是這種狀況下,也不排除會觸發updated生命鉤子(data有默認值且已渲染,以後數據被更新),從而致使虛擬dom的從新渲染。

相關文章
相關標籤/搜索