首页 前端 Vue.js 正文

为什么数据不“响应式”了


响应式原理是Vue的特性之一,正因为有此特性,在使用Vue开发时就能实现“数据”和“视图”的实时交互,即数据驱动视图,视图可改变数据。掌握响应式是深入了解Vue的第一步

原理

首先需要先回顾一下Vue的生命周期。

  • beforeCreate:创建实例前,基本上不做操作

  • created:Vue实例初始化完成,完成响应式绑定;可访问和调用data、methods中的属性和方法,但是尚未开始渲染模板

  • beforeMount:编译模板,调用render生成vdom,但是还没开始dom渲染

  • mounted:完成dom渲染,组件创建完成。组件开始由创建阶段进入运行阶段

  • beforeUpdate:data数据变化后,但是dom尚未更新

  • updated:data数据发生变化,并且dom更新完成。注意不要在updated里面修改data中的数据,否则可能会导致死循环

  • beforeDestroy: 组件销毁前(尚未销毁,还可以正常使用)。可在此阶段移除、解绑一些全局事件、自定义事件

  • destroyed:组件被销毁,子组件也被销毁

created阶段,Vue会遍历data对象中的所有属性property,并使用Object.defineProperty将这些属性全部转换为对应的getter/setter。这个转换过程对于用户来说是不可见的,转换完成后每一个组件实例都会有一个watcher监听器实例。当某一个属性被修改时,其setter就会被触发,监听器监听到变化,会使与其关联的组件重新渲染。

为什么数据不“响应式”了  第1张

为什么属性不“响应式”了

怎么理解响应式,简单的说就是数据和视图相关联。修改数据,视图会发生改变;同理,修改视图,数据也会变化

响应式

<template>
  <div>
    <input type="text" v-model="value1">
    <div>{{value1}}</div>
  </div>
</template>
<script>
export default {
  name: 'indexPage',
  data() {
    return {
      value1: '', // 响应式
    }
  },
  methods: {},
  mounted() {
    this.value1 = 'hello'
  }
}
</script>


输入框的值更改后,页面上的值也会跟着变;同理,也可以直接修改value1的值,输入框的value也会跟着变。

对于对象

Vue无法检测对象属性的添加或删除,原因就在于Vue是在created阶段就对属性property做了转换,并与watcher绑定,后续直接新增或删除属性没有执行getter/setter转换,watcher也就无法监听到变化。

如果要添加响应式的property属性,需要注意以下:

  • 不能直接在data上面添加或删除property

  • 可使用Vue.set(object, propertyName, value)向对象添加响应式property,但是不能是根对象,也就是data

  • 如果有多个属性需要添加为响应式,则需要使用Object.assign(),将需要添加为响应式的属性混合进原对象。需要注意,Object.assign()不是深拷贝,因此最好是新创建一个对象,而不是直接在原对象上面修改

举个栗子

<template>
  <div>
    <input type="text" v-model="value1">
    <div>{{value1}}</div>
    <input type="text" v-model="value2">
    <div>{{value2}}</div>
    <input type="text" v-model="obj.value3">
    <div>{{obj.value3}}</div>
  </div>
</template>
<script>
export default {
  name: 'indexPage',
  data() {
    return {
      value1: '',
      obj: {}
    }
  },
  methods: {},
  mounted() {
    this.value1 = '响应式的'; // 响应式的
    this.value2 = '非响应式的'; // 非响应式的
    // 对象增加属性
    // this.obj.value3 = '非响应式的'; // 非响应式的
    // this.$set(this.obj, 'value3', '响应式的'); // 响应式的
    this.obj = Object.assign({}, this.obj, {value3: '响应式的'});
  }
}
</script>


运行结果

为什么数据不“响应式”了  第2张

ps:由于部分属性未定义,因此在渲染时可能会抛出警告

对于数组

Vue不能检测根据索引值直接添加或修改一个数组项,也不能检测数组直接修改数组长度。实际开发中,直接添加和删除对象的属性不常见,常见的是修改了数组的某一项数据,但是页面未更新。

举个栗子

<template>
  <div>
    <div>list[0]:{{list[0]}}</div>
    <div>list.length:{{list.length}}</div>
  </div>
</template>
<script>
export default {
  name: 'indexPage',
  data() {
    return {
      list: [1,2,3]
    }
  },
  methods: {},
  mounted() {
    this.list[0] = 100; // 直接根据索引修改数组项
    this.list.length = 10; // 直接修改数组长度
  }
}
</script>


运行结果

为什么数据不“响应式”了  第3张

ps:如果数组里面的值不是基本数据类型, 而是引用类型,直接根据索引修改数组对象的值,页面上也是会实时更新的。但是这不是因为响应式,而是因为数组项引用的是内存中的对象

解决方案

使用$set和splice修改某个数组项的值,可实现响应式

this.$set(this.list, index, value);
this.list.splice(index, 1, value); // splice会修改原数组


使用splice修改数组的长度,可实现响应式

splice是在原数组上截取指定长度的数据,会直接修改原数组

this.list.splice(newLength);


完整代码

<template>
  <div>
    <div>list[0]:{{list[0]}}</div>
    <div>list.length:{{list.length}}</div>
  </div>
</template>
<script>
export default {
  name: 'indexPage',
  data() {
    return {
      list: [1,2,3]
    }
  },
  methods: {},
  mounted() {
    this.$set(this.list, 0, 100); // $set修改数组项
    // this.list.splice(0, 1, 100);
    this.list.splice(2); // splice修改数组长度
  }
}
</script>


在mounted中修改数组的值,页面上也会改变。

运行结果

为什么数据不“响应式”了  第4张

异步更新

Vue在更新DOM时是异步执行的。如果watcher监听到数据变化,则会开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。即使同一个watcher被触发多次,也只会被推入到队列中一次。然后在下一个事件循环中,Vue刷新队列并执行实际的更新工作。

常见的,如果我们要在DOM更新后做一些操作,直接写可能会有$el还未渲染的报错,那么就可以考虑在$nextTick()中来执行我们的一下针对DOM的操作。

总结

  • 在Vue初始化时,尽量将需要用到的响应式属性写在data中,便于Vue处理成响应式的;未申明属性就是用,会抛出警告甚至错误

  • 如果要给对象添加响应式属性,则需要使用$setObject.assign()两种方式

  • 如果要响应式修改数组的值,则需要使用$setsplice()两种方式

  • 如果修改数组的引用属性,页面更新的效果不是因为响应式,而是“引用”的特点

  • $nextTick()返回的是一个Promise对象,可使用async/awaitawait this.$nextTick(() => {})

  • 如果数据发生改变,而视图未更新,虽然可以使用$forceUpdate()强制刷新,但是还是应该从根本上解决问题,滥用$forceUpdate()就不能体现响应式的优势。

打赏
海报

本文转载自互联网,旨在分享有价值的内容,文章如有侵权请联系删除,部分文章如未署名作者来源请联系我们及时备注,感谢您的支持。

转载请注明本文地址:https://www.shouxicto.com/article/3382.html

相关推荐

Vue3 中如何加载动态菜单?

Vue3 中如何加载动态菜单?

响应式原理是Vue的特性之一,正因为有此特性,在使用Vue开发时就能实现“数据”和“视图”的实时交互,即数据驱动视图,视图可改变数据。掌...

Vue.js 2022.07.20 0 460

vue-amap引入高德JS API的原理

vue-amap引入高德JS API的原理

响应式原理是Vue的特性之一,正因为有此特性,在使用Vue开发时就能实现“数据”和“视图”的实时交互,即数据驱动视图,视图可改变数据。掌...

Vue.js 2022.06.01 0 747

Vue3 新特性

Vue3 新特性

响应式原理是Vue的特性之一,正因为有此特性,在使用Vue开发时就能实现“数据”和“视图”的实时交互,即数据驱动视图,视图可改变数据。掌...

Vue.js 2022.06.01 0 728

50+Vue经典面试题源码级详解(24)

   Vue 3.0的设计目标是什么?做了哪些优化?    分析    还是问新特性,陈述典型新特性,分析其给你带来的变化即可。    思路    从以...

Vue.js 2022.06.01 0 712

评论列表
转载请注明原文地址:https://juejin.cn/post/7092773119529582605
2022-06-13 20:06:48 回复

ainiaobaibaibaibaobaobeishangbishibizuichiguachijingchongjingdahaqiandaliandangaodw_dogedw_erhadw_miaodw_tuzidw_xiongmaodw_zhutouganbeigeiliguiguolaiguzhanghahahahashoushihaixiuhanheixianhenghorse2huaixiaohuatonghuaxinhufenjiayoujiyankeaikeliankouzhaokukuloukunkuxiaolandelinileimuliwulxhainiolxhlikelxhqiuguanzhulxhtouxiaolxhwahahalxhzanningwennonuokpinganqianqiaoqinqinquantouruoshayanshengbingshiwangshuaishuijiaosikaostar0star2star3taikaixintanshoutianpingtouxiaotuwabiweifengweiquweiwuweixiaowenhaowoshouwuxiangjixianhuaxiaoerbuyuxiaokuxiaoxinxinxinxinsuixixixuyeyinxianyinyueyouhenghengyuebingyueliangyunzanzhajizhongguozanzhoumazhuakuangzuohenghengzuoyi
支付宝
微信
赞助本站