异步加载在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的从新渲染。

相关文章
相关标签/搜索