v-for循环渲染完毕实例化控件

在项目时用到了一些插件,比如这次用了jquery ui的自定义拖拽控件。

控件需要实例化,而列表的数据是通过ajax来获取的,然后在用 v-for 渲染数据,再次遇到了一个问题。

就是等插件实例化完毕,数据却还没渲染完毕,所以这就出现一个bug,拖拽没有实例化。

首先想到的不是vue自带的方法,我先想到的是用settimeout。这里先简单介绍一下setTimeout() 方法:一般用于在指定的毫秒数后调用函数或计算表达式。setTimeout属于异步执行函数,当程序执行时遇到setTimeout会将该函数放入等待队列,等待当前主程序执行完毕后开始执行setTimeout。

第一种解决方法: settimeout

1
2
3
setTimeout(function() {
//实例初始化
},100)

但是这种方法有一个缺点,就是不确定数据什么时候渲染完毕。

第一种情况: 假设10毫秒渲染完毕,但是setTimeout需要等100毫秒,浪费了90毫秒。

第二种情况:假设数据需要 200 毫秒执行完毕,但是 100毫秒就执行了 实例初始化,BUG又出现了。

所以,这种方法不是我们想要答案。

第二种解决方法: watch + vm.nextTick

这两种方法是 vue 的属性和方法。

watch: 监听某一个data数据发生变化就执行方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
vm = new Vue({
el:'.app',
data: {
arr: [],
},
watch: {
arr: function() {
console.log('arr数组的数据发生变化' + this.arr);
}
},
mounted: {
this.arr=[1,2,3,4,5];
}
})

data 里面的arr数组发生了变化变成成了[1,2,3,4,5],就触发了watch的a方法。console.log(arr的数据发生变化[1,2,3,4,5]);

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div @click="ceshi()">{{ a }}</div>
</body>
<script type="text/javascript">
vm = new Vue({
el:'.app',
data: {
a: '1',
},
methods:{
ceshi(){
a = 2;
/*DOM还没更新*/
this.$nextTick(function(){
/*DOM更新了*/
})
}
}
})
</script>
</html>

$nextTick 里面DOM更新是指页面上的数据是最新的数据。而不是data的a数据更新了。

知道这两种属性之后。我们开始解决一下BUG吧。

先贴完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li v-for="(item,key) in arr" :key="key">可拖拽列表{{item}}</li>
</ul>
</body>
<script type="text/javascript">
vm = new Vue({
el:'.app',
data: {
arr: [],
},
watch:{
arr: function() {
this.$nextTick(function(){
/*现在数据已经渲染完毕*/
})
}
},
mounted:function() {
var that = this;
axios.get('url',{
params:{
link: '',
}
}).then(function(res){
that.arr = res;
})
}
})
</script>
</html>

上面就是解决bug的完整代码范例。 解释一下什么意思:

在 axios 请求数据是 this.arr被赋值了,watch监听到了 arr 数据发生变化执行arr方法。到了this.$nextTick 它需要等DOM 渲染完毕才执行(也就是等arr在DOM渲染完毕)。

这个方法完美解决需求,既不浪费时间又不会出现数据还没渲染完毕就执行实例初始化。

2018年5月6日补充:

还有另外一种方法忘记说明了,今天刚好写东西的时候注意了一下:

就是我们要做的逻辑依然在mounted执行我们需要的逻辑,就是利用v-if在需要数据渲染的地方进行判断,这样mounted就会在v-if数据存在并且渲染完成后再执行逻辑,当然,如果v-if判断无数据不需要渲染照常执行就不用多说了。

上面的例子可以写成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul v-if="arr.length">
<li v-for="(item,key) in arr" :key="key">可拖拽列表{{item}}</li>
</ul>
</body>
<script type="text/javascript">
vm = new Vue({
el:'.app',
data: {
arr: [],
},
created:function() {
var that = this;
axios.get('url',{
params:{
link: '',
}
}).then(function(res){
that.arr = res;
})
},
mounted: function() {
this.$nextTick(function(){
/*现在数据已经渲染完毕*/
})
}
})
</script>
</html>



完~