vue中遇到的坑

仅以此记录 vue 中遇到的坑

组件的复用问题

Vue的虚拟DOM中,Vue会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。

然后我遇到了这样的bug:同事做了一个scrollbar的组件,我用在了steps组件中,用了多个。在点击下一步的时候当前step下的组件会销毁,我特意加了v-if,下一个step下的组件会创建,问题来了…上一步的scrollbar组件影响到了当前创建下的scrollbar组件。

原因:Vue的算法会尝试复用组件

解决方案:使用key,它会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。加了key之后,就会重新渲染,而不是复用了。
代码:

1
2
3
4
5
6
7
8
9
10
<div class="stepContent" key="1" v-else-if="currentStep == 1">
<s-scrollbar wrap-class="scrollheight">
...
</s-scrollbar>
</div>
<div class="stepContent" key="2" v-else-if="currentStep == 2">
<s-scrollbar wrap-class="scrollheight">
...
</s-scrollbar>
</div>

结语:我想大多数玩家和我一样,都是在v-for里用到了key,我虽然用到了,但是没有真正理解它,所以才会碰到这个bug不知道原因,还傻乎乎的去看组件的源码,浪费了很长时间。理解最值得重视…

最后附上:key的官方介绍

数据的拷贝和动态组件产生的bug

自己用elementinputtree合成了一个inputTree组件

想使用动态组件来生成不同的表单,代码如下:

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
38
39
40
41
<component :is="getDynamicComp(item)" v-for="(item,index) in list" :key="index"></component>
export default {
data() {
return {
list: [
{
htmlType: 'udf_char_single_line',
value: ''
},{
htmlType: 'udf_char_list',
value: '',
listvalues: [
{value:1},
{value:2}
]
}

]
}
},
methods: {
getDynamicComp(item) {
let dynamicComp = {
data() {
return {
item: item
}
},
template: ''
};
if (item.htmlType === 'udf_char_single_line') {
dynamicComp.template = `<s-input v-model="${item.value}"></s-input>`;
} else if (item.htmlType === 'udf_char_list') {
dynamicComp.template = '<s-select v-model="item.value">' +
'<s-option v-for="(option,index) in item.listvalues" :key="index" :value="option.value"></s-option>' +
'</s-select>';
}
return dynamicComp;
},
}
}

结果:inputselect都生成了,但是改变不了数据
原因:改变inputselect的值时,list数组会变化,这个list其实还在我另外一个组件中使用,另外一个组件监听了这个list的变化,由此getDynamicComp()重新计算,然后重新生成动态组件,动态组件里面的inputselect又恢复到了初始渲染状态,所以改变数据无效

最后:提供对象浅拷贝和深拷贝的方法

  1. 浅拷贝:

    1
    Object.assign({}, obj)
  2. 深拷贝: 参考 这儿

    • 将对象序列化为字符串,然后将其反序列化

      1
      JSON.parse(JSON.stringify(obj))

      非常简单,但是有两个常见的错误:

      • 如果属性是不可序列化值类型,结果并不是期盼的值。比如 函数会被忽略, Date 类型会被 JSON.parse() 解释为字符串
      • 如果有循环引用会报错

      JSON.stringify()拥有三个参数(参考官方描述),第一个错误可以用下面的代码修复:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      JSON.parse(JSON.stringify(json, function(key,val){
      if(typeof val=='function'){
      return val+''//手动将其字符串化
      }
      return val;
      }
      ),function(key,val){
      if(val.indexOf&&val.indexOf('function') !== -1){
      return eval("(function(){return "+val+" })()")
      }
      return val;
      });

      第二个错误只能望而叹之。

    • 递归拷贝:
      可以使用lodash_.cloneDeep(obj)方法,通过学习这个库,咱们可以简单实现这样的一个方法:

      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
      function cloneDeep(value) {
      if(value instanceof Object) {
      var result;
      if(value.constructor === Object) { // 对象
      result = {};
      for (let i in value) {
      result[i] = cloneDeep(value[i]);
      }
      } else if (value.constructor === Array) { // 数组
      result = [];
      value.forEach(function(item, index) {
      result[index] = cloneDeep(item);
      });
      } else if (value.constructor === Function) { // 函数
      // 方式1
      result = new Function("return " + value.toString())();
      // 方式2
      result = function() {
      return value.apply(this, arguments);
      }
      } else if (value instanceof Date) { // new Date() 时间对象
      result = new Date();
      result.setTime(result.getTime());
      } else { // new String...的instanceof Object也为true,这个直接赋值,
      result = value;
      }
      return result;
      } else {
      return value;
      }
      }

      里面对于循环引用没做处理,因为对lodash的封装的Stack还不是很明白…

堂 wechat
欢迎关注我的微信公众号,里面有各种小故事哦!
坚持原创技术分享,您的支持将鼓励我继续创作!