转载声明:原文链接:https://blog.csdn.net/weixin_45792953/article/details/112472338
数据响应式是vue的特性之一,在面试过程中也会常常被问起响应式原理,现在就让我们深入了解一下vue2.0中如何实现响应式,
下图是Vue2.0中对响应式原理的描述,其核心就是使用Object.defineProperty中的get/set进行数据劫持,
虽然Vue3.0中使用Proxy(代理)去实现响应式,其实原理都差不多,在3.0中主要是使用Proxy的get和set实现响应式,如果理解defineProperty,Proxy也会很快理解的
Object.defineProperty是什么?
define是定义的意思 Property是属性/描述的意思
其实defineProperty就是定义属性
主要有value、writable、configurable、enumerable、get、set这几个配置项
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | let object1={ property1:0 } //例如 Object.defineProperty(object1, 'property1' , { value: 42, writable: false , //是否可写 configurable: false , //是否可删除 enumerable: false , //是否可枚举(遍历) }); //get set配置项 Object.defineProperty(object1, 'property1' , { get() { //get方法在进行获取的时候触发 return property1 }, set(newVal) { //get方法在进行设置的时候触发,主要在此实现数据劫持 property1 = newVal } }); //我这边常用一种复数形式Object.defineProperties Object.defineProperties(object1, { property1: { get() { //get方法在进行获取的时候触发 return property1 }, set(newVal) { //get方法在进行设置的时候触发,主要在此实现数据劫持 property1 = newVal } } }) |
使用上述的内容实现Vue2.0中的双向绑定
实现出来的效果
下面使用Object.defineProperty实现一个响应式计算器,
核心部分在:数据被修改(set)时重新计算结果
html部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>响应式计算器</title> <style> .btnGroup > button.active{ background-color: orange; color: #fff; } </style> </head> <body> <div id= "computed" > <div class= "result" >0</div> <div class= "inputGroup" > <input type= "number" class= "ipt1" value= "0" /> <br/> <input type= "number" class= "ipt2" value= "0" /> </div> <div class= "btnGroup" > <button data-field= "add" class= "active" >+</button> <button data-field= "sub" >-</button> <button data-field= "mul" >*</button> <button data-field= "div" >/</button> </div> </div> <!-- 引入js文件 --> <script src= "./js/index.js" ></script> </body> </html> |
js部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | class Arithmetic { //定义算法类 add(a, b) { return a + b } sub(a, b) { return a - b } mul(a, b) { return a * b } div(a, b) { return a / b } } class Computed extends Arithmetic { constructor() { super (); this .result = document.getElementsByClassName( 'result' )[0]; //获取结果dom this .ipt1 = document.getElementsByClassName( 'ipt1' )[0]; //获取输入框 this .ipt2 = document.getElementsByClassName( 'ipt2' )[0]; this .btnGroup = document.getElementsByClassName( 'btnGroup' )[0]; this .btnItems = this .btnGroup.getElementsByTagName( 'button' ); this .data = this .defineData(); this .btnIndex = 0; //记录选中btn索引 } // 1.进行初始化 init() { this .bindEvent(); } // 2.绑定事件 bindEvent() { this .btnGroup.addEventListener( 'click' , this .onFieldBtnClick.bind( this ), false ) this .ipt1.addEventListener( 'input' , this .onNumberIpt.bind( this ), false ) this .ipt2.addEventListener( 'input' , this .onNumberIpt.bind( this ), false ) } // 6.使用Object.defineProperty进行数据劫持 defineData() { let _obj = {}, field = 'add' , //保存计算方法字段 fNumber = 0, //inp1的value sNumber = 0; //inp2的value let that = this ; // 进行数据劫持 Object.defineProperties(_obj, { fNumber: { get() { return fNumber }, set(newVal) { fNumber = newVal // 重点:在fNumber改变之后计算结果,此处vue使用的是Dom diff算法 that.computedResult(fNumber, sNumber, field); } }, sNumber: { get() { return sNumber }, set(newVal) { sNumber = newVal that.computedResult(fNumber, sNumber, field); } }, field: { get() { return field }, set(newVal) { field = newVal that.computedResult(fNumber, sNumber, field); } } }) return _obj } // 3.btn点击事件 onFieldBtnClick(ev) { let e = ev || window.event, tar = e.target || e.srcElement, tarName = tar.tagName.toLowerCase(); tarName === 'button' && this .update(tar); } // 4.btn进行选项卡切换并更改field update(target) { this .btnItems[ this .btnIndex].className = '' ; this .btnIndex = [].indexOf.call( this .btnItems, target); target.className += ' active' ; this .data.field = target.getAttribute( 'data-field' ); } // 5.ipt改变(input)事件 onNumberIpt(ev) { let e = ev || window.event, tar = e.target || e.srcElement, className = tar.className, val = Number(tar.value.replace(/\s+/g, '' )) || 0; //去除input的空格 switch (className) { case 'ipt1' : // 改变fNumber触发set()将重新计算结果 this .data.fNumber = val; break ; case 'ipt2' : // 改变sNumber触发set()将重新计算结果 this .data.sNumber = val; break ; default : break ; } } // 7.使用继承算法类的方法进行结果计算 computedResult(fNumber, sNumber, field) { this .result.innerHTML = this [field](fNumber, sNumber) } } new Computed().init(); |
帖子还没人回复快来抢沙发