Vue底层数据绑定视图的原理
这是一个含有字符串类型属性 profile 的对象:
let obj = { profile:'' }
不过, 身为对象属性的 profile 仅仅只是个字符串吗?在控制台中使用 Object API 中的 getOwnPropertyDescriptor 方法将其 “ 内在“ 打印出来, 如图所示。
原来, 对象属性内藏乾坤, 我们甚至可以使用 Object API 的 defineProperty 方法对其配置, 属性配置项(描述符)如表所示。
在这里可以停顿一下,想一想, 对象属性被赋值时调用的 set 有何妙用呢?
下面来看一段有关 defineProperty 的代码:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <span id="harry"></span> 9 <input id="trigger" type="text"> 10 <script> 11 let harry = document.getElementById('harry') 12 let trigger = document.getElementById(('trigger')) 13 let key = 'profile' //对象属性键名 14 let store = {} // 辅助get取值 15 let obj = { 16 profile:'' 17 } 18 // 使用object api的defineProperty方法对其属性进行配置 19 Object.defineProperty(obj,key,{ 20 set(value) { /// 函数类型,属性被赋值时调用 21 harry.innerText = value // 重点:修改DOM节点视图,将修改的值传递给span标签 22 store[key] = value // 将值传递给store 23 }, 24 get() { // 返回值将作为属性值 25 return store[key] 26 } 27 }) 28 trigger.addEventListener('keyup',function () { 29 // obj[key] = this.value 30 obj.profile = this.value 31 console.log(obj[key]); 32 }) 33 </script> 34 </body> 35 </html>
上述代码中, 在对象属性的 setter 函数中修改文本节点的值, 所以当 obj.profile被重新赋值时,节点视图也会同步更新;然后对输入框添加事件监听 (addEventListener),当用户事件触发时, 输入值将被赋于 obj.profile。以此方式, 我们实现了数据与视图之间的“双向绑定”,这也是 Vue 数据与视图绑定的实现原理。
代码运行如图所示
在 Vue 中, 当我们把普通的 JavaScript 对象传给 Vue 实例的 data 选项时, Vue 将遍历对象属性,并使用 0bject. defineProperty 将其全部转化为 getter/setter, 并在组件渲染时将属性记录为依赖。 之后当依赖项的 setter 函数被调用时, 会通知 watcher 重新计算并更新其关联的所有组件。 由于 Object.defineProperty 是 ES5 中一个无法 shim (自定义拓展)的特性, 所以 Vue应用无法运行在不支待 Object.defineProperty 的 IE8 及其以下版本浏览器上。