前言
在我们的日常 vue 开发中,有这样几个场景,我有个 echart 对象或者 dom 对象,我在我这个组件的其他方法中,都需要访问这个对象,于是我用一个 data 字段保存他;我有一些配置性数据,比如我页面 table 的表头信息,表单项的标题,我只读一次后续就不需要监听,也不需要通过代码更改,为了能让界面读取我可能又是一个 data 字段保存。两种情况都可能导致页面新增大量的_ob__(观察者),进而造成不必要的性能浪费,本文主要是针对这种浪费,提供笔者已经知道的方法来进行优化。
1. 使用 methods 中的方法返回配置数据
Vue 组件在创建的过程中会自动代理 data 中的数据,但不会对方法的返回变量进行监控,所以通过方法返回一个或一组变量,template 中直接通过这个方法就可以获取变量,并显示就可以减少不必要的代理,如:
<template>
<div class="userInfo">
<div class="info-row" v-for="item in getOption()" :key="item.key">
<div class="info-title">{{item.title}}</div>
<div class="info-content">{{data[item.key]}}</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
data: {
name: 'John Brown',
age: 18,
address: 'New York No. 1 Lake Park',
date: '2016-10-03',
},
};
},
methods: {
getOption() {
return [
{
title: '姓名',
key: 'name',
},
{
title: '年龄',
key: 'age',
},
{
title: '地址',
key: 'address',
},
];
},
},
};
</script>
2. 利用 object.defineProperty 对 data 的字段进行赋值
Vue2 代理的基本原理就是深度遍历一个对象,并利用 object.defineProperty 的 get 和 set 方法对象的每个值类型字段进行代理,经过实践,发现 object.defineProperty 直接对 this 进行赋值的属性,不会被代理。如:
<template>
<div class="home-page">
<div class="home-text">这是测试页面</div>
</div>
</template>
<script>
export default {
name: 'home',
data() {
let object = { proxyObject: [{ name: 'name1' }, { name: 'name2' }] };
Object.defineProperty(object, 'fieldOptions', {
value: [{ name: 'name1' }, { name: 'name2' }],
});
return object;
},
created() {
Object.defineProperty(this, 'fieldOptions', {
value: [{ name: 'name1' }, { name: 'name2' }],
});
},
mounted() {
console.info(this.proxyObject);
console.info(this.fieldOptions);
},
};
</script>
console 的输出结果为:
(2) [{…}, {…}, __ob__: Observer]
(2) [{…}, {…}]
data 对应字段的键名以_或者$开头
这个在 vue 的官网中是这样描述的

然后大胆的尝试了一波
<template>
<div class="home-page">
<div class="home-text">{{$data._dataInfo}}</div>
</div>
</template>
<script>
export default {
name: 'home',
data() {
return {
_dataInfo: { name: 'data' },
$dataInfo: { name: 'data' },
dataInfo: { name: 'data' },
};
},
mounted() {
console.info(this.$data._dataInfo);
console.info(this.$data.$dataInfo);
console.info(this.dataInfo);
},
};
</script>
结果界面输出:
{__ob__: Observer}
{__ob__: Observer}
{__ob__: Observer}
好像没什么用啊,查阅资料发现应该这么玩:
<template>
<div class="home-page">
<span>{{_dataInfo}}</span>
<span>{{$dataInfo}}</span>
<span>{{dataInfo}}</span>
</div>
</template>
<script>
export default {
name: 'home',
data() {
return {
_dataInfo: '',
$dataInfo: '',
dataInfo: '',
};
},
created() {
this._dataInfo = { name: 'data' };
this.$dataInfo = { name: 'data' };
this.dataInfo = { name: 'data' };
},
mounted() {
console.info(this._dataInfo);
console.info(this.$dataInfo);
console.info(this.dataInfo);
},
};
</script>
结果界面在输出
{name: "data"}
{name: "data"}
{__ob__: Observer}
很有意思的是把 data 中的 dataInfo 字段删除后,dataInfo 也不在被代理大概代码如下:
<template>
<div class="home-page">
<span>{{_dataInfo}}</span>
<span>{{$dataInfo}}</span>
<span>{{dataInfo}}</span>
</div>
</template>
<script>
export default {
name: 'home',
data() {
return { };
},
created() {
this._dataInfo = { name: 'data' };
this.$dataInfo = { name: 'data' };
this.dataInfo = { name: 'data' };
},
mounted() {
console.info(this._dataInfo);
console.info(this.$dataInfo);
console.info(this.dataInfo);
},
};
</script>
结果输出
{name: "data"}
{name: "data"}
{name: "data"}
object的frezee、seal或preventExtensions 方法
对象的一个字段可以通过 object.defineProperty 赋值来解决这个字段的 writable,但是一个对象都想设置这个属性怎么办。好像有点知识盲区了,最后通过查阅资料得到 object 的 frezee , seal 和 preventExtensions 方法可以做到 然后实验一波
<template>
<div class="home-page">
<span>{{freezeInfo}}</span>
<span>{{sealInfo}}</span>
<span>{{preventExtensionInfo}}</span>
</div>
</template>
<script>
export default {
name: 'home',
data() {
return {
freezeInfo: Object.freeze({ name: 'data' }),
sealInfo: Object.seal({ name: 'data' }),
preventExtensionInfo: Object.preventExtensions({ name: 'data' }),
};
},
mounted() {
console.info(this.freezeInfo);
console.info(this.sealInfo);
console.info(this.preventExtensionInfo);
},
};
</script>
输出结果
{name: "data"}
{name: "data"}
{name: "data"}
来个总结
以上就是笔者查找到的不代理 data 属性字段的方法,总结一下:
- methods 虽然书写简单,但是在日常的开发中,我们都喜欢 v-bind绑定字段, v-on绑定 方法突然, v-bind绑定方法,略微不雅,而且理解容易产生偏差
- object.defineProperty 这个光看就很丑,是真的丑,声明之前对对象进行设置,既不优雅也不好看,所以不建议使用
- 属性名用_或者$,因为复杂对象会被代理,而且在 template 中读取很不方便,所以并不建议用于要显示的字段,反到是一些不需要代理并且需要全局缓存的属性,如全局要用的dom对象或echarts s用这个在合适不过
- object.frezee,object.seal,object.preventExtensionInfo 方法,可能现目前看起来最合适的方式,特别是下一篇文章动态组件的使用用这两个方法输出的字段,尤其简单好用,只是在代码优雅程度上还是有些欠缺。