认识vm
大约 4 分钟
vue实例提供了很多属性和方法(API)。
- 所有以
$开始的属性,可以看做是公开的属性,这些属性是供程序员使用的。 - 所有以
_开始的属性,可以看做是私有的属性,这些属性是Vue框架底层使用的。一般我们程序员很少使用(不建议操作)。

- Vue框架底层使用了数据代理机制,可以以调用属性的形式代理访问选项值对象的属性。
let dataObj = {
msg : 'Hello Vue!'
}
// vue实例
const vm = new Vue({
el : '#app',
data : dataObj
})
vm.msg // 代理访问 dataObj.msg- 为了避免和底层冲突,Vue实例不会给以
_和$开始的属性名做数据代理。
Vue源码解读
Vue框架源代码中关键性代码:
var data = vm.$options.data;
注意:这是获取data。程序执行到这里的时候vm上还没有 _data 属性。
data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
程序执行完这个代码之后,vm对象上多了一个_data这样的属性。
通过以上源码解读,可以得知data不一定是一个{},也可以是一个函数。
- 如果data是函数,则调用
getData(data, vm)来获取data。 - 如果data不是函数,则直接将data返回,给data变量。并且同时将data赋值给
vm._data属性了。
_data属性,以"_"开始,足以说明,这个属性是Vue框架底层需要访问的。 vm._data 这个属性直接指向了底层真实的data对象。通过_data访问是不会走getter和setter方法的。
注意:对于Vue实例vm来说,不仅有_data这个属性,还有一个$data这个属性。
_data 是框架内部使用的,可以看做私有的。
$data 这是Vue框架对外公开的一个属性,是给我们程序员使用。
重点函数:
function isReserved(str) { var c = (str + '').charCodeAt(0); return c === 0x24 || c === 0x5f; }这个函数是用来判断字符串是否以
_或$开始的。判断属性是否进行代理。proxy(vm, "_data", key);通过这行代码直接进入代理机制(数据代理)。重点函数proxy
function proxy(target, sourceKey, key) { // target是vm,sourceKey是"_data" sharedPropertyDefinition.get = function proxyGetter() { return this[sourceKey][key]; }; sharedPropertyDefinition.set = function proxySetter(val) { this[sourceKey][key] = val; }; Object.defineProperty(target, key, sharedPropertyDefinition); }
如果不想走代理的方式读取data,想直接读取data当中的数据,可以通过
_data和$data属性来访问。(建议使用$data)data选项值也可以是一个函数,这个函数返回一个
{}对象。data(){ return { msg : 'Hello Zhangsan!' } }
Vue的响应式
响应式:指修改 data 后,页面自动改变/刷新。
就像我们在使用 excel 的时候,修改一个单元格中的数据, 其它单元格的数据会联动更新,这也是响应式。
Vue2 的响应式基于 Object.defineProperty 实现。
- Vue 会给 data 中所有的属性,以及子属性,都会添加响应式。
- 如果后期直接再给vm添加属性(
vm.newPro = 'VM'),则不会走响应式。需要后期添加响应式的方式才行。
后期添加响应式
// 二者等价,成员方法和静态方法的区别
Vue.set(目标对象, '属性名', 值)
vm.$set(目标对象, '属性名', 值)
// 目标对象['属性名'] = 值
Vue.set(vm.$data.email, '属性名', 值)
Vue.set(vm.email, '属性名', 值)
// 避免在运行时向Vue实例或其根$data添加响应式
// 不能直接给 vm、vm.$data 追加响应式属性。只能在声明时提前定义好。数组的响应式
Vue.set(数组对象, '下标', 值)
vm.$set(数组对象, '下标', 值)
// 数组对象调用这些方法也可以实现响应式
push()
pop()
reverse()
splice()
shift()
unshift()
sort()
// vm.user.push("jack")Vue数据代理与劫持伪码
vue实例进行数据代理的伪码:
// 定义一个Vue类
class Vue {
// 定义构造函数
// options是一个简单的纯粹的JS对象:{}
// options对象中有一个data配置项
constructor(options){
// 获取所有的属性名
Object.keys(options.data).forEach((propertyName, index) => {
//console.log(typeof propertyName, propertyName, index)
let firstChar = propertyName.charAt(0)
if(firstChar != '_' && firstChar != '$'){
Object.defineProperty(this, propertyName, {
get(){ // 添加数据代理
...
return options.data[propertyName]
},
set(val){ // 添加数据劫持
...
options.data[propertyName] = val
}
})
}
})
// 获取所有的方法名
Object.keys(options.methods).forEach((methodName, index) => {
// 给当前的Vue实例扩展一个方法
this[methodName] = options.methods[methodName]
})
}
}