vue 数据绑定实现的核心 Object.defineProperty()
vue深入响应式原理
现在是时候深入一下了!Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接,不过理解其工作原理同样重要,这样你可以回避一些常见的问题。在这个章节,我们将进入一些 Vue 响应式系统的底层的细节。
如何追踪变化
当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。
每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

检测变化的注意事项
受现代 JavaScript 的限制 (而且 Object.observe 也已经被废弃),Vue 不能检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。例如:
var vm = new Vue({
data:{
a:1
}
})
// `vm.a` 是响应的
vm.b = 2
// `vm.b` 是非响应的
Vue 不允许在已经创建的实例上动态添加新的根级响应式属性 (root-level reactive property)。然而它可以使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上:
Vue.set(vm.someObject, 'b', 2)
您还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:
this.$set(this.someObject,'b',2)
有时你想向一个已有对象添加多个属性,例如使用 Object.assign() 或 _.extend() 方法来添加属性。但是,这样添加到对象上的新属性不会触发更新。在这种情况下可以创建一个新的对象,让它包含原对象的属性和新的属性:
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
也有一些数组相关的问题,之前已经在列表渲染中讲过。
声明响应式属性
由于 Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明根级响应式属性,哪怕只是一个空值:
var vm = new Vue({
data: {
// 声明 message 为一个空值字符串
message: ''
},
template: '<div>{{ message }}</div>'
})
// 之后设置 `message`
vm.message = 'Hello!'
如果你未在 data 选项中声明 message,Vue 将警告你渲染函数正在试图访问的属性不存在。
这样的限制在背后是有其技术原因的,它消除了在依赖项跟踪系统中的一类边界情况,也使 Vue 实例在类型检查系统的帮助下运行的更高效。而且在代码可维护性方面也有一点重要的考虑:data 对象就像组件状态的概要,提前声明所有的响应式属性,可以让组件代码在以后重新阅读或其他开发人员阅读时更易于被理解。
vue.js的双向数据绑定就是通过Object.defineProperty方法实现的,俗称属性拦截器。
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,
并返回这个对象。“`
// 语法:
/*
* @param: obj:需要定义属性的对象;
* prop:需要定义或修改的属性;
* descriptor:将被定义或修改属性的描述符
*/
Object.defineProperty(obj,prop,descriptor)
对象里目前存在的属性描述符主要有两种形式: 数据描述符和存取描述符.
- 数据描述符: 拥有可写或不可写值的属性*
可选键值:
configurable: 当且仅当configurable为true时,改属性描述符才能够被改变,也能被删除
enumerable: 当其值为true时,该属性才能够出现在对象的枚举属性中,默认为false
writable: 当且仅当该属性的值为true时,该属性才能被赋值运算符改变, 默认为false。
value: 该属性对应的值,可以是任意有效的javascript的值(数值,对象,函数等),默认为undefined
- 存取描述符: 由一对getter-setter函数功能来描述的属性*
可选键值:
configurable: 当且仅当configurable为true时,改属性描述符才能够被改变,也能被删除
enumerable: 当其值为true时,该属性才能够出现在对象的枚举属性中,默认为false
get: 给属性提供getter的方法,如果没有 getter
则为undefined。当我们读取某个属性的时候,其实是在对象内部调用了该 方法,此方法必须要有return语句。该方法返回值被用作属性值。默认为 undefined
set:设置属性值的方法, 如果没有 setter 则为
undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined。也就是说,当我们设置某个属性的时候,实际上是在对象的内部调用了该方法
note:两者不能同时定义, 否则报错==
实例:
var a = {};
Object.defineProperty(a, 'b', {
set:
function(newValue) {
console.log('赋值操作, 赋值' + newValue);
},
get:
function() {
console.log('取值操作');
return
2;
}
});
a.b = 1; // 赋值操作,赋值1
a.b; // 取值操作2
虽然我将a.b的值设置成了1,但是因为我在get方法中始终返回了2,所以a.b的值一直是2。
那么,这就好玩儿了:我们可以在页面监听某个变量,当变量发生变化的时候,我们就更新对应的视图。由数据来驱动视图的更新,是不是很熟悉?是的,vue .js的核心思想就是这个。我们写个小例子:
<!DOCTYPE html>
<html>
<head>
<meta
charset="UTF-8">
<title>test</title>
</head>
<body>
<div
id="test">这是一个测试</div>
<script>
var view = document.getElementById("test");
var data = {};
var i=0;
Object.defineProperty(data, "b", {
set:
function(newValue) {
//当data.b的值改变的时候更新#test的视图
view.textContent=newValue;
},
get: function() {
}
});
setInterval(function(){
i++;
data["b"]
= "data.b的值更新了,我要更新视图"+i;
},1000);
</script>
</body>
</html>
视图的变化过程:
刚开始:
data.b的值更新了,我要更新视图1;
1秒后:
data.b的值更新了,我要更新视图2
2秒后:
data.b的值更新了,我要更新视图3
Object对象有一个freeze的方法,用于实现对象属性和方法的不可更改
// 使用方法:
const arr = [1,2,3,4];
Object.freeze(arr); // 变量arr不可更改
arr.push(5); // 报错:不能添加属性
Object.definePropperty也可以实现规定变量的不可更改
const obj = { key: 'chris', vlaue: 'person'
}; Object.defineProperty(obj, 'key', { configurable: false, // 不可删除
writable: false, // 不可写 });
视图和数据变化绑定原理
对于一个html页面
<div>
<p>你好,<span
id='nickName'></span></p>
<div
id="introduce"></div>
</div>
设置一个数据的属性的getter和setter
//视图控制器
var
userInfo = {};
Object.defineProperty(userInfo,
"nickName", {
get: function(){
return
document.getElementById('nickName').innerHTML;
},
set: function(nick){
document.getElementById('nickName').innerHTML = nick;
}
});
Object.defineProperty(userInfo,
"introduce", {
get: function(){
return
document.getElementById('introduce').innerHTML;
},
set: function(introduce){
document.getElementById('introduce').innerHTML = introduce;
}
})
然后就能愉快地绑定数据交互了。
userInfo.nickName
= "xxx";
userInfo.introduce
= "我是xxx,我来自云南,..."
vue.js的数据变动原理
但是,这个例子只是数据和dom节点的绑定,而vue.js更为复杂一点,它在网页dom和accessor之间会有两层,一层是Wacher,一层是Directive,比如以下代码。
var a = {
b: 1 }
var vm =
new Vue({
data: data
})
把一个普通对象(a={b:1})传给 Vue 实例作为它的 data 选项,Vue.js 将遍历它的属性,用Object.defineProperty 将它们转为 getter/setter,如图绿色的部分所示。
每次用户更改data里的数据的时候,比如a.b =1,setter就会重新通知Watcher进行变动,Watcher再通知Directive对dom节点进行更改。

vue 数据绑定实现的核心 Object.defineProperty()的更多相关文章
- Vue 双向数据绑定原理分析 以及 Object.defineproperty语法
第三方精简版实现 https://github.com/luobotang/simply-vue Object.defineProperty 学习,打开控制台分别输入以下内容调试结果 userInfo ...
- vue原理之-神奇的Object.defineProperty
vue2.0通过defineProperty进行数据双向绑定 例如:(他接受三个参数,都是必填!) var a= {} Object.defineProperty(a,"b",{ ...
- vue双向数据绑定的原理-object.defineProperty() 用法
有关双向数据绑定的原理 关于数据双向绑定的理解:利用了 Object.defineProperty() 这个方法重新给对象定义了新属性,在操作新属性分别为为获取属性值(调用get方法)和设置属性值(调 ...
- 17: VUE数据绑定 与 Object.defineProperty
VUE数据绑定原理:https://segmentfault.com/a/1190000006599500?utm_source=tag-newest Object.defineProperty(): ...
- vue实现双向数据绑定之Object.defineProperty()篇
前言 vue.js中使用ES5的Object.defineProperty()实现数据的双向绑定 Object.defineProperty()原理 Object.defineProperty()可以 ...
- Vue的数据双向绑定和Object.defineProperty()
Vue是前端三大框架之一,也被很多人指责抄袭,说他的两个核心功能,一个数据双向绑定,一个组件化分别抄袭angular的数据双向绑定和react的组件化思想,咱们今天就不谈这种大是大非,当然我也没到达那 ...
- 双向数据绑定实现之Object.defineProperty
vue.js利用的是es5的 defineproperty 特性实现的双向数据绑定,了解一下基本原理. 举例 var person= {}; Object.defineProperty(person, ...
- 【Vue】-- 数据双向绑定的原理 --Object.defineProperty()
Object.defineProperty()方法被许多现代前端框架(如Vue.js,React.js)用于数据双向绑定的实现,当我们在框架Model层设置data时,框架将会通过Object.def ...
- vue Object.defineProperty Proxy 数据双向绑定
Object.defineProperty 虽然已经能够实现双向绑定了,但是他还是有缺陷的. 只能对属性进行数据劫持,所以需要深度遍历整个对象 对于数组不能监听到数据的变化 虽然 Vue 中确实能检测 ...
随机推荐
- linux /mac 下 go环境变量配置
安装了go语言之后,还要设置路径,如果不设置路径,则执行 go 的时候会提示 go: command not found,提示的意思是没有这个命令行.这个是因为还没有设置PATH路径. 设置路径的方式 ...
- Aurelia 创建模板
今天介绍一下Aurelia创建模板的三种方式. 模板既可以作为页面也可以作为组件. 1. View+View Model Aurelia的模板通常由一个html文件和一个同名的ts或js文件组成,文件 ...
- Sql 判断函数是否存在、sql判断表是否存在、sql判断存储过程是否存在、sql判断视图是否存在
--数据库是否存在 IF exists(SELECT * FROM master..sysdatabases WHERE name=N'库名') PRINT 'exists' ELSE PRINT ' ...
- DAY14(PYTHONS)生成器进阶
def average(): sum = 0 count = 0 avg = 0 while True: #循环 num = yield avg #遇到yield就停止,防止一开始除数为0导致崩溃 s ...
- spring4笔记----spring4设值注入
2个接口 package com.ij34.servce; public interface people { public void cut(); } package com.ij34.servce ...
- 单纯linux系统下hadoop2.7.3 eclipse,记一次成功的运行wordcount的注意事项
hadoop要正确安装好 hadoop eclipse plugin要对应相应的eclipse版本 define hadoop location mr master:9000 另一个9001 下面的 ...
- [20181109]12cR2 的pre_page_sga参数
[20181109]12cR2 的pre_page_sga参数.txt --//12CR2改变了参数pre_page_sga设置为True.设置为true有好处也有缺点.--//先看看官方的定义:ht ...
- 获取Bing每日图片API接口
bing图片每日更新,对于这一点感觉挺不错的,如果能够把bing每日图片作为博客背景是不是很不错呢?首先我们进入Bing首页,会发现自动转到中国版.不过这没关系,中国版更符合国情,速度也比国际版快一些 ...
- scrapy实例:爬取中国天气网
1.创建项目 在你存放项目的目录下,按shift+鼠标右键打开命令行,输入命令创建项目: PS F:\ScrapyProject> scrapy startproject weather # w ...
- python简单的监控脚本-利用socket、psutil阻止远程主机运行特定程序
python简单的监控脚本-利用socket.psutil阻止远程主机运行特定程序 psutil是一个跨平台的库(http://code.google.com/p/psutil/),能够轻松的实现获取 ...