目录
1.数据响应式
1.1响应式是什么
数据发生变化时,立刻做出反应并完成一些想做的事。例如数据交互,网络请求。
1.2如何实现
两种方案:在vue2.x中,通过Object.defineProperty对对象属性进行拦截(数据劫持)
在vue3.x中,通过Proxy对对象整体代理
1.3实现拦截
无论用哪种方式,道理都是相通的。
let data = {
name:'老师'
}
object.defineproperty对象拦截
Object.defineProperty(data,'name',{
// 访问name属性就会执行此方法 返回值就是获取到的值
get(){
console.log('name属性被获取了')
return '老师'
},
// 设置新值就会执行此方法 newVal就是设置的新值
set(newVal){
console.log('name属性被设置新值了')
console.log(newVal)
}
})
若运行实例,会发现get方法中返回值始终是老师,那么如何取得最新值?就是创建一个中间变量,实现get/set之间的联动。建议验证,体会一下。
let _name = '老师'
Object.defineProperty(data,'name',{
// 访问name属性就会执行此方法 返回值就是获取到的值
get(){
return _name
},
// 设置新值就会执行此方法 newVal就是设置的新值
set(newVal){
_name = newVal
}
})
1.4优化
若是对象中所有属性都变成响应式,怎么办?利用闭包的特性,自己看吧。
let data = {
name: '老师',
age: 26,
height:175
}
// 遍历每一个属性
Object.keys(data).forEach((key)=>{
// key 属性名
// data[key] 属性值
// data 原对象
defineReactive(data,key,data[key])
})
// 响应式转化方法
function defineReactive(data,key,value){
Object.defineProperty(data,key,{
get(){
return value
},
set(newVal){
value = newVal
}
})
}
所谓响应式其实就是拦截对象属性的访问和设置,插入一些自己想做的事。
2.数据变化反应到视图
数据变化后,操作视图。还是离不开dom操作。分两种,自行体会。
命令式操作视图。
<div id="app">
<p></p>
</div>
<script>
let data = {
name: '老师',
age: 26,
height:175
}
// 遍历每一个属性
Object.keys(data).forEach((key)=>{
// key 属性名
// data[key] 属性值
// data 原对象
defineReactive(data,key,data[key])
})
function defineReactive(data,key,value){
Object.defineProperty(data,key,{
get(){
return value
},
set(newVal){
value = newVal
// 数据发生变化,操作dom进行更新
document.querySelector('#app p').innerHTML = data.name
}
})
}
// 首次渲染
document.querySelector('#app p').innerHTML = data.name
</script>
声明式操作视图。
<div id="app">
<p v-text="name"></p>
</div>
<script>
let data = {
name: '老师',
age: 26,
height: 175
}
// 遍历每一个属性
Object.keys(data).forEach((key) => {
// key 属性名
// data[key] 属性值
// data 原对象
defineReactive(data, key, data[key])
})
function defineReactive(data, key, value) {
Object.defineProperty(data, key, {
get() {
return value
},
set(newVal) {
value = newVal
// 数据发生变化,操作dom进行更新
compile()
}
})
}
// 获取并操作dom
function compile() {
let app = document.getElementById('app')
// 1.拿到app下所有的子元素
const nodes = app.childNodes // [text, input, text]
//2.遍历所有的子元素
nodes.forEach(node => {
// nodeType为1为元素节点
if (node.nodeType === 1) {
const attrs = node.attributes
// 遍历所有的attrubites找到 v-model
Array.from(attrs).forEach(attr => {
const dirName = attr.nodeName
const dataProp = attr.nodeValue
if (dirName === 'v-text') {
node.innerText = data[dataProp]
}
})
}
})
}
// 首次渲染
compile()
</script>
3.视图变化反映到数据
将视图变化通过事件监听实现反向修改。自行体会。
// 编译函数
function compile() {
let app = document.getElementById('app')
// 1.拿到app下所有的子元素
const nodes = app.childNodes // [text, input, text]
//2.遍历所有的子元素
nodes.forEach(node => {
// nodeType为1为元素节点
if (node.nodeType === 1) {
const attrs = node.attributes
// 遍历所有的attrubites找到 v-model
Array.from(attrs).forEach(attr => {
const dirName = attr.nodeName
const dataProp = attr.nodeValue
if (dirName === 'v-model') {
node.value = data[dataProp]
// 视图变化反应到数据 无非是事件监听反向修改
node.addEventListener('input', (e) => {
data[dataProp] = e.target.value
})
}
})
}
})
}
到此,响应式结束。但还有一个问题就是无法做到精准更新,进一步优化则是发布订阅模式。(无论在前端还是后端,都是经典。)
<div id="app">
<p v-text="name"></p>
<p v-text="age"></p>
<p v-text="name"></p>
</div>