首页>>前端>>Vue->为什么数据不“响应式”了

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

时间:2023-11-30 本站 点击:1

响应式原理是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就会被触发,监听器监听到变化,会使与其关联的组件重新渲染。

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

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

响应式

<template><div><inputtype="text"v-model="value1"><div>{{value1}}</div></div></template><script>exportdefault{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><inputtype="text"v-model="value1"><div>{{value1}}</div><inputtype="text"v-model="value2"><div>{{value2}}</div><inputtype="text"v-model="obj.value3"><div>{{obj.value3}}</div></div></template><script>exportdefault{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>

运行结果

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

对于数组

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

举个栗子

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

运行结果

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>exportdefault{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中修改数组的值,页面上也会改变。

运行结果

异步更新

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

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

总结

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

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

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

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

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

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

原文地址:https://juejin.cn/post/7092773119529582605


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Vue/3741.html