Vue 异步事更新件 & 异步Dom更新解决方案

1. Question

这里写图片描述

最近遇到一个有点别扭的需求。

进入一个子页面,需要四个接口拿到四份数据【a,b,c,d】
其中a,b正巧父级拿到过,在父级页面上用v-if指令保证ab存在,再去展示子页面。

现在,cd必须在子页面拿到了。c依赖浏览器的query pidd依赖c的一个字段c.pgid

画一个图,大概是这样的需求:
这里写图片描述

然后我的旧实现思路是这样的:

// using typescript

  private created() {
  // 请求c后立刻请求d
   this.fetchC().then(this.fetchD);
  }

  // 后台请求c
  private fetchC() {
   this.$store.dispatch('resource/c', this.pid);
  }

  // 从store获取c
  private get c() {
    return this.$store.getters['resource/c'](this.pid);
  }

  private get pid(): number {
    return Number(this.$route.query.pid);
  }

  private get pgid() {
    if (!this.c) {
      return null;
    }

    return this.c.pgid;
  }

// 后台请求d
  private fetchD() {
    if (!this.pgid) {
      return;
    }

    return this.$store.dispatch('resource/d', this.pgid);
  }

 // store拿d
  private get d() {
    return this.$store.getters['resource/d'](this.pgid);
  }

dispacth c之后立即then一下dispatch d。这个时候,get c()并不能保证一定已经get到,this.pgid更不一定能拿到,dispatch d方法就会报错。

这里写图片描述

当时有点蒙圈,其实很简单一个事,只要保证this.pgid存在的时候再去dispatch d就可以了。

2. Action

2.1 正确姿势

稍微改一点点代码:

// using typescript

  private created() {
  // 请求c后不去立刻请求d了
   this.fetchC();    //  此行改了
  }

  // 后台请求c
  private fetchC() {
   this.$store.dispatch('resource/c', this.pid);
  }

  // 从store获取c
  private get c() {
    return this.$store.getters['resource/c'](this.pid);
  }

  private get pid(): number {
    return Number(this.$route.query.pid);
  }

  private get pgid() {
    if (!this.c) {
      return null;
    }

    return this.c.pgid;
  }

// 后台请求d,通过监听pgid的值,存在之后再去fetchD
  @Watch('pgid')    // 此行改了
  private fetchD() {
    if (!this.pgid) {
      return;
    }

    return this.$store.dispatch('resource/d', this.pgid);
  }

 // store拿d
  private get d() {
    if (!this.pgid) { return; }  // 此行改了
    return this.$store.getters['resource/d'](this.pgid);
  }

跑起来,一切都很完美。两个接口的数据都拿到了。

这是一个思路,把重心放在数据有没有拿到上,用watch监听,等数据拿到之后,再去发送第二个请求。

这里写图片描述

但是有点慌啊,这一块感觉还是有点迷啊。

我如果不把思路放在数据上,我就去控制请求(感觉这样子更符合开发的思路,代码更容易理解),有没有哪个生命周期的时刻,第一个请求回来,get的第一个数据也成功拿到,在这个生命周期时刻,第二个请求才开始呢?

第二个请求放在$nextTick里面行不行?
不论dispatch方法的快慢问题,到底getdispatch谁先执行?有没有一个先后顺序的?

这里写图片描述

2.2 一个试验

settimeout当作dispatch测试一下。

<html>

<head>
  <title>test</title>
</head>

<body>
  <div id="app">
    <span>{{c}}</span>
    <span>{{d}}</span>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <script>
    var app = new Vue({
      el: '#app',
      data: {
        pgid : 1
      },
      computed: {
        c: function () {
          if (!this.pgid) {
            return ;
          }
          console.log('c computed ' + this.pgid);
          return ++this.pgid;
        },
        d: function () {
          if (!this.c) {
            return ;
          }
          console.log('d computed ' + this.c);
          return ++this.c;
        }
      },
      created: function () {
         // 模拟c的请求,这里我不知道这个请求返回需要多少秒,假设1s
        setTimeout(() => {   
          console.log('time out');
          this.pgid = 100;
        }, 1000);
        this.$nextTick(() => {
          console.log('next tick');
          this.pgid = 10;
        })
        console.log('created');
      },
      mounted: function () {
        console.log('mounted');
      }
    });
  </script>
</body>
</html>

运行结果:

这里写图片描述

有图有真相了,看图学习:

官方对$nextTick的解释是:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

官方对created方法的解释是:

在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。

官方对mounted方法的解释是:

el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。

注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted:

mounted: function () {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
  })
}

哦,也就是说在vue的created方法结束之后,mounted方法开始,紧接着执行$nextTick这个方法。

呸,我不信!
这里写图片描述

我把延迟改成0,我的请求巨快!

created: function () {
        // 我把这个改成0
        setTimeout(() => {
          this.pgid = 100;
          console.log('time out');
        }, 0);
        this.$nextTick(() => {
          console.log('next tick');
          this.pgid = 10;
        })
        console.log('created');
      }

结果:

这里写图片描述

好吧,并没有什么变化,还是$nextTick快。

现在,起码明白了一个事: 遇到异步事件,你用拦截异步事件的思路是不通的,mountednextTick都拦不住,用settimeout延时你又不知道异步事件的延迟具体是多少秒。实际上异步事件执行的时间更靠后。而且远远晚于dom创建和首次展示的时间。

所以两个请求在created方法时就前后发出,第二个请求又依赖第一个请求的参数,这样的姿势肯定是不正确了。

2.3 还有困惑

get一定是在created方法之后?

并不是。。。。

这里写图片描述

getdispatch方法谁先执行???比如这个get dfetch d 都依赖pgidpgid一变,谁先变??也不一定。。。。

老老实实的用数据控制一切吧。

这里写图片描述

参考:
https://juejin.im/post/5a6fdb846fb9a01cc0268618

Logo

前往低代码交流专区

更多推荐