【Vue2】在vue中灵活赋予对象响应式
昨天工作中遇到多个组件共用父组件传入的一个共同变量(在本文中统一用form指代),多人开发、业务复杂度上升后导致错误频发、难以调查,其中也不少原因是因为公司的低代码平台,导致开发者难以理解vue以及真正地上手vue。
首先简单定义一个sfc。
<template>
<div>
{{object1}}
</div>
</template>
<script>
export default {
data() {
return {
object1:{
a:1,
b:''
},
object3:{
c:1,
d:0
}
}
},
methods: {
},
created() {
},
mounted() {
},
updated() {
console.log("视图发生变化")
}
}
</script>
一、如何动态给对象添加响应式属性
1、单个或少量添加
使用this.$set(object,'x','content')
或Vue.set(object,'x','content')
的方法可以对方法进行少量的添加,无论是在created还是在mounted或是methods方法中,都能生效。
注意:如果已经通过直接字面量的方法修改object对象,再set
一个同名的key value值是不会生效的。
created(){
this.object1.f = 1;
// f会保持值为1,不会变为2,且无响应式
this.$set(this.object1,'f',2);
}
2、批量添加
2.1、新建一个object对象,修改原有对象的引用
适用范围:
- object2为当前组件内新建对象,其他地方没有引用过,或者并不是一个经过vue响应式处理过的对象。
- 如果需要赋值给object1一个外部已经存在的、已经被其他画面进行使用的对象,请看下一种情况。
...
methods:{
changeObject(){
let object2 = {
b:1,
c:'1',
e:{
a:1
}
}
this.object1 = object2
}
}
...
2.2、已存在或已被使用中的对象,需要赋值给当前对象
深拷贝 + 修改对象引用。
适用范围:
- 例如:父画面有一个form数据,你懒得新建一个object进行传值,或者是你必须要绑定一个在父画面使用中的对象。
...
methods:{
changeObject() {
let object2 = {};
// 深拷贝的代码会在文后提供
this.deepCopy(object2,this.object3);
this.object1 = object2;
}
}
...
2.3、保留当前对象的内容以及响应式,且合并传入对象
对于传入对象的值是通过深拷贝,重新建立的响应式,如需在当前环境操作传入对象,且影响传入处的响应式变化,直接对传入对象进行修改。
合并object + 深拷贝 + 修改对象引用
...
methods:{
changeObject() {
let object2 = {};
let index = Object.assign({},this.object1,this.object3);
this.deepCopy(object2,index);
this.object1 = object2;
}
}
...
3、深拷贝的几种写法
3.1、利用JSON api
无法拷贝属性里包括函数、Date对象等类型。
this.object1 = JSON.parse(JSON.stringify(this.object3))
3.2、自定义函数
deepCopy(newObj,oldObj){
for(var k in oldObj) {
var item = oldObj[k]
if(item instanceof Array){
// 数组
newObj[k] = []
this.deepClone(newObj[k],item)
} else if (item instanceof Object){
// 对象
newObj[k] = {}
this.deepClone(newObj[k], item)
} else {
// 基本类型
newObj[k] = item
}
}
}
4、如何判断是否有响应式
例如直接操作一个没有提前在object1中声明的对象属性。
created(){
this.object1.f = 1;
}
大概率是不会报错的,且如果该语句在created生命周期中时,页面上的object1会直接打印出来f的值,且你乍一看会觉得这就是有响应式了。但是当你要对f值进行动态修改的时候,发现视图并不会进行响应。
方法1:在console控制台中进行对象的打印,或者是debugger或者直接$0.__vue__.object1
的方式进行查看。
方法2:在update中设置log,有打印出来就是视图有做更新。
注意:在下种情况下,update也会被触发,要注意。但是当前的响应式是$set
触发的。
created(){
this.object1.f = 1;
this.$set(this.object1,'e',2);
}
5、对于第四点的视图变化解析
如果在crteated进行字面量的修改,视图发生变化(不准确),其实mounted的动作还没进行,所以对对象的修改是work的。
画面插值{{model}}
双向绑定的el显示内容是怎么打印出来的呢?
vue使用对象的toString方法进行打印,所以解释了为什么无响应式的属性也能打印出来。
如果object1的内容换为下述:
object1:{
a:{
b:1
}
}
这样控制台直接toString只会显示a:{b:[Object object]}
为什么画面能显示正常呢?
让我们给object重写一个toString方法,并且打进断点。
object1:{
a:{
b:1,
toString(){
debugger;
return 'test'
}
}
}
这时候重新load画面的时候会在mounted之前进该debugger断点。
这时候进行前后调用栈查看,就可以清晰的看清vue的操作。vue会对data对象进行数据劫持,并且重写object原型上的toString方法。(说的应该不是很对)
vue的tostring方法,对应了对象的toString显示问题。
function toString(val) {
return val == null
? ''
: Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
? JSON.stringify(val, null, 2)
: String(val);
}
转载自:https://juejin.cn/post/7278509213220257849