Vue 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。
虚拟DOM (Virtaul DOM): 用 js 对象模拟的,保存当前视图内所有 DOM 节点对象基本描述属性和节点间关系的树结构。用 js 对象,描述每个节点,及其父子关系,形成虚拟 DOM 对象树结构。
简单实现vue双向数据绑定:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <input id="inp" type="text"> <span id="spanEle"></span> </body> <script> let inpEle = document.getElementById('inp'); let spanEle = document.getElementById('spanEle'); inpEle.addEventListener('input', function () { data.name = inpEle.value; }) function render() { spanEle.innerHTML = JSON.stringify(data); } let data = { name: '请输入你的姓名', location: { x: 100, y: 100 } } inpEle.value = data.name; observe(data) function observe(obj) { // 数据劫持 // 判断类型 if (!obj || typeof obj !== 'object') { return } Object.keys(obj).forEach(key => { defineReactive(obj, key, obj[key]) }) function defineReactive(obj, key, value) { // 递归子属性 observe(value) Object.defineProperty(obj, key, { enumerable: true, //可枚举(可以遍历) configurable: true, //可配置(比如可以删除) get: function reactiveGetter() { console.log('get:', value) // 监听 return value }, set: function reactiveSetter(newVal) { observe(newVal) //如果赋值是一个对象,也要递归子属性 if (newVal !== value) { value = newVal render() console.log('set:', newVal) // 监听 } } }) } } setTimeout(() => { data.location = { x: { xx: { xxx: 2020 } }, y: 2020 } console.log(data.name); }, 1000) setTimeout(() => { data.location.x.xx.xxx = 0823; }, 4000) </script> </html>