vue 实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。
当所一个普通Javascript 对象传给 Vue 实例来作为它的data 选项时,Vue 将遍历它的属性,用Object.defineProperty() 将它们转为 getter/setter。用户看不到getter/setter,但是在内部它们让Vue追踪依赖,在属性被访问和修改时通知变化。
Vue 的数据双向绑定 将 MVVM 作为数据绑定的入口,整合 Observer,Compile和Watcher三者,通过Observer 来监听自己的 model 的数据变化,通过 Compile 来解析编译模板指令(vue 中是用来解析{{}}),最终利用watcher 搭起Observer 种Compile之间的通信桥梁,达到数据变化-->视力图更新;视图交互变化(input)-->数据 model 变更双向绑定效果。
Object.defineProperty()
Object.defineProperty()方法用于在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。注意:只能在Object构造器对象上调用此方法,而不能在任意一个Object类型的实例上调用。
Object.defineProperty(obj,prop,descriptor)
参数
obj:目标对象,就是要在这个对象上面定义属性
prop:要定义或修改的属性的名称或Symbol。
descriptor:要定义或修改的属性描述符。是一个对象通过赋值操作添加的普通属性是可枚举的,(for...in 或 Object.keys 方法),即可以改变这些属性的值,也可以删除这些属性。Object.defineProperty()方法可以通过对属性的配置,实现更精确的控制
关于descriptor:对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。
数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。
存取描述符是由getter函数和setter 函数所描述的属性。
一个描述符只能是这两者其中之一;不能同时是两者。
数据描述符和存取描述符都是对象。它们共享以下可选键值(默认值是指在使用Object.defineProperty()定义属性时的默认值):configurable 是否可以删除目标属性:当且仅当该属性的configurable 键值为 true 时,该属性的描述符才能够被改变,同时改属性也能从对应的对象上被删除。(使用delete删除) 默认为 false。
enumberable
当且仅当该属性的enumerable键值为 true时,该属性才会出现在对象的枚举属性中。默认为 false.
value 该属性对应的值。可以是任何有效的JavaScript值(数值,对象,函数)。
默认为undefined。
writable 该属性的值是否可以被重写:当且仅当该属性的 writable 键值为 true时,属性的值,也就是上面的value,才能被赋值运算符改变。默认为false。
存取描述符还具有以下可选键值,get 属性,是一个函数,是一种获得属性值的方法
如果没有getter,则为undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。默认为undefined。
set 属性,是一个函数,是一种设置属性值的方法
如果没有setter,则为undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的this 对象。默认为undefined。
示例:
- <script>
-
- // 在对象中添加一个设置了存取描述符属性的示例
- var bValue = 38;
- Object.defineProperty(o, "b", {
- // 使用了方法名称缩写(ES2015 特性)
- // 下面两个缩写等价于:
- // get : function() { return bValue; },
- // set : function(newValue) { bValue = newValue; },
- get() {
- console.log('有人访问数据了');
- return bValue;
- },
- set(newValue) {
- console.log('设置了新值');
- bValue = newValue;
- },
- enumerable: true,
- configurable: true
- });
-
- o.b; // 38
- // 对象 o 拥有了属性 b,值为 38
- // 现在,除非重新定义 o.b,o.b 的值总是与 bValue 相同
-
- // 数据描述符和存取描述符不能混合使用
- Object.defineProperty(o, "conflict", {
- value: 0x9f91102,
- get() { return 0xdeadbeef; }
- });
- // 抛出错误 TypeError: value appears only in data descriptors, get appears only in accessor descriptors
-
- </script>
说明:拥有布尔值的键 configurable、enumerable 和 writable 的默认值都是 false。
属性值和函数的键value、get和set 字段的默认值为undefined。
实现vue的双向数据绑定:
- <div id='app'>
-
- </div>
- <script>
- //模仿 Vue操作dom 的语法
- //我们自己创建一个Vue 函数
- const Vue =function({el,data={message:''}}){
- //绑定get 和 set 方法
- Object.defineProperty(this,'message',{
- get:()=>{
- return document.getElementById(el).innerHTML
- },
- set:()=>{
- document.getElementById(el).innerHTML=val
- }
- })
- }
- const v=new Vue({
- el:'app',
- data:{
- message:'hello'
- }
- })
- v.message //返回hello
- v.message='HI' //将div 内的内容变为 HI
- </script>