响应式原理的介绍
Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。Vue是声明式框架,
手写一个双向绑定的实现
<input id="input"/>
<p><span>输入框的内容:</span><span id="inputText"></span></p>
<button id="btn">重置数据</button>
js部分
// 获取input button span标签
const input = document.getElementById('input')
const inputText = document.getElementById('inputText')
const btn = document.getElementById('btn')
// 给input绑定input事件
input.addEvevtListener('input',function(e){
// 1当用户在input输入框中输入,需要对数据进行修改
data.inputText = e.target.value
// 2通过e.target.value获取输入值,赋值给data的inputText属性
})
// 给button绑定click事件
btn.addEventListener('click',function(){
})
// 响应式数据
const data = {}
let it = data.inputText// 5定义变量存储属性值
// 给data添加一个'inputText'属性
// 第二个参数:想要监听的属性
// 第三个参数是相关配置项
Object.defineProperty(data,'inputText',{
get(){// 当用户在input输入框中输入,需要对数据进行修改
// 6不能直接返回data.inputText,返回时会调用get,陷入死循环
return it
}
set(val){// 3修改后需要在span中显示
// 4数据进行修改后会引起此处set的响应
inputText.innerText = val// 用原生js实现
it = val// 7对it赋值
}
})
- 响应式数据定义在后面,在修改数据时会影响页面的显示,在页面输入时也会影响数据的显示,这样才是响应式处理
- 响应式数据核心:数据获取于数据修改时,能够监听到变化
- Object.defineProperty(),会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象;参数:1、obj:要定义的属性对象;2、prop:要定义或修改的属性名或Symbol;3、descriptor:要定义或修改的属性描述符;返回值:被传递给函数的对象
- 使用
Object.defineProperty()
方法定义(绑定)的属性,与直接在对象中声明的属性不同,它无法被Object.keys方法枚举出来。如果想要其能够被枚举出来,可以在第三参数的配置项中添加一个属性enumerable
,值置为true。此时有以上方法绑定来的属性就可以被枚举出来 - 第三参数配置项,可以自定义设置get、set方法
- 第一部分的逻辑:在input中输入数据,修改data下的inputText属性,这会被其中的set监听到,然后进行set中的赋值操作,当要获取数据时,就可以直接获取到get中it这个变量
点击按钮时,可以修改数据,通过修改数据影响输入框的内容
// 获取input button span标签
const input = document.getElementById('input')
const inputText = document.getElementById('inputText')
const btn = document.getElementById('btn')
// 给input绑定input事件
input.addEvevtListener('input',function(e){
// 当用户在input输入框中输入,需要对数据进行修改
data.inputText = e.target.value
// 通过e.target.value获取输入值,赋值给data的inputText属性
})
// 给button绑定click事件
btn.addEventListener('click',function(){
data.inputText = '重置数据'// 1点击按钮时修改data下的数据
})
// 响应式数据
const data = {}
let it = data.inputText// 定义变量存储属性值
// 给data添加一个'inputText'属性
// 第二个参数:想要监听的属性
// 第三个参数是相关配置项
Object.defineProperty(data,'inputText',{
get(){// 当用户在input输入框中输入,需要对数据进行修改
// 不能直接返回data.inputText,返回时会调用get,陷入死循环
return it
}
set(val){// 修改后需要在span中显示
// 数据进行修改后会引起此处set的响应
inputText.innerText = val// 用原生js实现
it = val// 对it赋值
// 在前面修改属性时会触发此处的set
// 在set中修改输入框的内容
input.value = val// 找到input的dom,直接赋值
}
})
上面的代码基本完成了需求,但是其中在全局定义了一个变量it,这会造成全局的污染,需要优化
除了避免全局污染,还得要能观察到data的数据
// 获取input button span标签
const input = document.getElementById('input')
const inputText = document.getElementById('inputText')
const btn = document.getElementById('btn')
// 给input绑定input事件
input.addEvevtListener('input',function(e){
// 当用户在input输入框中输入,需要对数据进行修改
data.inputText = e.target.value
// 通过e.target.value获取输入值,赋值给data的inputText属性
})
// 给button绑定click事件
btn.addEventListener('click',function(){
data.inputText = '重置数据'// 1点击按钮时修改data下的数据
})
// 响应式数据
const data = {
inputText:''
}
// 在把data数据转变为getter和setter时,应该遍历对象的每一个属性
// 所以对于data,应该先枚举出其所有的键,并遍历,同时对数据与键进行处理
Object.keys(data).forEach(// 2当响应式数据定义完后,需要对这里的函数进行调用
key=>defineReactive(data,key,data[key])
)
// 1定义响应式处理的函数
function defineReactive(data,key,value){
/*
data:当前对象
key:当前键
value:当前值
*/
// 获取后进行拦截
Object.defineProperty(data,key,{
get(){// 之前用全局变量接收
// 这里就可以直接用value来代替,因为value拿到的就是对应的值
return value
},
set(val){
inputText.innerText = val
value = val
input.value = val
}
})
}