Vue双向数据绑定
几何心凉 人气:0数据的变化反应到视图
前面我们了解到数据劫持之后,我们可以在数据发生修改之后做任何我们想要做的事情,操作视图当然也是OK的
命令式操作视图
目标:我们通过原始的操作dom的方式让每一次的name的最新值都能显示到p元素内部
<div id="app"> <p></p> </div> <script> let data = { name: '小兰同学', age: 18, height:180 } // 遍历每一个属性 Object.keys(data).forEach((key)=>{ // key 属性名 // data[key] 属性值 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>
声明式操作视图
目标:我们将data中name属性的值作为文本渲染到标记了v-text的p标签内部,在vue中,我们把这种标记式的声明式渲染叫做指令
<div id="app"> <p v-text="name"></p> </div> <script> let data = { name: '小兰同学', age: 18, height: 180 } // 遍历每一个属性 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() } }) } // 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>
小结
不管是指令也好,插值表达式也好,这些都是将数据反应到视图的标记而已,通过标记我们可以把数据的变化响应式的反应到对应的dom位置上去
找标记,把数据绑定到dom的过程,我们称之为binding
视图的变化反应到数据
目标:将data中的message属性对应的值渲染到input上面,同时input值发生修改之后,可以反向修改message的值,在vue系统中,v-model指令就是干这个事情的,下面我们就实现一下v-model的功能
<div id="app"> <input v-model="name" /> </div> <script> let data = { name: '小兰同学', age: 18, height: 170 } // 遍历每一个属性 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) { // 数据发生变化,操作dom进行更新 if (newVal === value) { return } value = newVal compile() } }) } // 编译函数 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 }) } }) } }) } // 首次渲染 compile() </script>
现存的问题
无法做到精准更新
<div id="app"> <p v-text="name"></p> <p v-text="age"></p> <p v-text="name"></p> </div> <script> let data = { name: '小兰同学', age: 18, height: 180 } // 遍历每一个属性 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) { // 数据发生变化,操作dom进行更新 if (newVal === value) { return } value = newVal compile() } }) } // 编译函数 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 Array.from(attrs).forEach(attr => { const dirName = attr.nodeName const dataProp = attr.nodeValue console.log( dirName,dataProp) if (dirName === 'v-text') { console.log(`更新了${dirName}指令,需要更新的属性为${dataProp}`) node.innerText = data[dataProp] } }) } }) } // 首次渲染 compile() </script>
加载全部内容