Vue 异步事件更新 & 异步Dom更新解决方案
Posted 小章鱼哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue 异步事件更新 & 异步Dom更新解决方案相关的知识,希望对你有一定的参考价值。
Vue 异步事更新件 & 异步Dom更新解决方案
1. Question
最近遇到一个有点别扭的需求。
进入一个子页面,需要四个接口拿到四份数据【a,b,c,d】
。
其中a
,b
正巧父级拿到过,在父级页面上用v-if
指令保证a
和b
存在,再去展示子页面。
现在,c
和d
必须在子页面拿到了。c
依赖浏览器的query pid
,d
依赖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
方法的快慢问题,到底get
和dispatch
谁先执行?有没有一个先后顺序的?
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
快。
现在,起码明白了一个事: 遇到异步事件,你用拦截异步事件的思路是不通的,mounted
和nextTick
都拦不住,用settimeout
延时你又不知道异步事件的延迟具体是多少秒。实际上异步事件执行的时间更靠后。而且远远晚于dom创建和首次展示的时间。
所以两个请求在created
方法时就前后发出,第二个请求又依赖第一个请求的参数,这样的姿势肯定是不正确了。
2.3 还有困惑
get
一定是在created
方法之后?
并不是。。。。
get
和dispatch
方法谁先执行???比如这个get d
和fetch d
都依赖pgid
,pgid
一变,谁先变??也不一定。。。。
老老实实的用数据控制一切吧。
完
参考:
https://juejin.im/post/5a6fdb846fb9a01cc0268618
以上是关于Vue 异步事件更新 & 异步Dom更新解决方案的主要内容,如果未能解决你的问题,请参考以下文章
初识vue 2.0(12):使用$nextTick获取更新后的DOM
vue nextTick深入理解-vue性能优化DOM更新时机事件循环机制