vue2与vue3双向数据绑定的区别说明
作者:star@星空
vue2与vue3双向数据绑定区别
新的响应式机制采用了ES6的ProxyApi,抛弃了Object.defineProperty()
Vue2双向数据绑定存在的问题
关于对象: Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对property执行 getter/setter转化,所以property必须在 data对象上存在才能让 Vue 将它转换为响应式的。
关于数组: Vue 不能检测以下数组的变动:
当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
在官网深入响应式原理一章有较详细阐述,针对以上两种情况解决方法,官网也有给出答案,那就是使用set方法。
原理
Vue2.0使用Object.defineProperty对象以及对象属性的劫持+发布订阅模式,只要数据发生变化直接通知变化 并驱动视图更新。
<input type="text" id="in"/> 输入的值为:<span id="out"></span>
var int = document.getElementById('in'); var out = document.getElementById('out'); // 定义一个对象 const data={name:'peak',age:10} // 遍历对象 实现对对象的属性进行劫持 Object.keys(data).forEach((key) => { Object.defineProperty(data, key, { // 当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中 enumerable: true, // 当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除 configurable: true, get: ()=>{ // 一个给属性提供 getter 的方法 console.info(`get ${key}-${val}`) return val; }, set: (newVal)=>{ // 一个给属性提供 setter 的方法 // 当属性值发生变化时我们可以进行额外操作 如调用监听器 if(newVal === val ){ // 如果未发生变化 不做其他操作 return; } console.log(`触发视图更新函数${newVal}`); out.innerHTML = newVal; }, }); }); int.addEventListener('input', function(e) { obj.name = e.target.value; }) data.age=25 // 触发set方法
vue3.0双向数据绑定Proxy
Vue3.0中的响应式采用了ES6中的Proxy方法。
Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)
语法:
const p = new Proxy(target, handler)
参数target表示要使用Proxy包装的对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
参数handler是一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理p 的行为
let obj = { a: 1, b: 2 } const proxy = new Proxy(obj, { get: function(target, prop, receiver) { return prop in target ? target[prop] : 0 }, set: function(target, prop, value, receiver) { target[prop] = 666 } }) console.log(proxy.a) // 1 console.log(proxy.c) // 0 proxy.a = 10 console.log(proxy.a) // 666 obj.b = 10 console.log(proxy.b) // 不是666 而是10
以上代码中obj是我们要代理的目标对象,get,set方法是参数handler的两个属性,具体如下:
handler.get()
接收三个参数,第一个参数target为代理的目标对象,第二个参数prop是代理的目标对象的属性,第三个参数是Proxy或者继承Proxy的对象,通常是proxy本身。handler.set()
接收四个参数,其中三个参数都与get方法相同,唯独多出来一个value表示新的属性值。
上述代码表示当访问proxy的属性时,进行拦截判断,该属性是否是目标对象的属性,如果是那么就将其值返回出来,否则就返回0。
当对proxy上的属性进行重写时,将重写的该属性赋值为666。
注意: 此时对数据的劫持,只是劫持了代理对象proxy,而跟原对象obj没有任何关系,对obj进行操作,也不会监听到。
用proxy实现一个简易版的数据响应:
<body> <h2 id="app"></h2> <input id="input" type="text" /> </body>
let app = document.getElementById('app') let input = document.getElementById('input') let obj = { // 源数据 text:'hello world' } let proxy = new Proxy(obj, { set: function(target, prop, value){ // input事件触发进行劫持,触发update方法 target[prop] = value update(value) } }) function update(value){ // update方法用于同步dom更新 app.innerHTML = value input.value = value } input.addEventListener('input',function(e){ // 监听input数据变化,并修改proxy的值 proxy.text = e.target.value }) proxy.text = obj.text // 初始化源数据
vue语法:数据的双向绑定
1.指令v-model
使用v-model可以为输入元素创建数据双向绑定,他会根据元素的类型自动选取正确的方法来更新元素。下面的代码演示了单行文本框、多行文本框、单选按钮,复选框以及下拉选择框做了一个简单的演示。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>指令v-model</title> </head> <body> <div id="app"> <h3>1.单行文本框</h3> <input type="text" v-model="singleText" style="width: 240px;"> <p>{{singleText}}</p> <h3>2.多行文本框</h3> <textarea v-model="multiText" style="width: 240px;"></textarea> <p>{{multiText}}</p> <h3>3.单选框</h3> <!--由于点击被选中的单选项无法取消其被选中的状态,所以一般没有使用单个选项的场景,设置v-model共用一个变量(radioValue)可以实现RadioGroup的效果--> <input id="ra" type="radio" value="张三" v-model="radioValue"> <label for="ra">A.张三</label> <input id="rb" type="radio" value="李四" v-model="radioValue"> <label for="rb">B.李四</label> <p>{{radioValue}}</p> <h3>4.单个复选框</h3> <!--单个复选框被用于true和false的切换--> <input id="c" type="checkbox" v-model="toggleValue"> <label for="c">哒哒哒</label> <p>{{toggleValue}}</p> <h3>5.多个复选框</h3> <!--多个复选框,v-model接受数组类型的变量--> <input id="ca" type="checkbox" value="AAA" v-model="checkedValues"> <label for="ca">AAAA</label> <input id="cb" type="checkbox" value="BBB" v-model="checkedValues"> <label for="cb">BBBB</label> <input id="cc" type="checkbox" value="CCC" v-model="checkedValues"> <label for="cc">CCCC</label> <p>{{checkedValues.join(',')}}</p> <h3>6.单项下拉选择框</h3> <select v-model="singleSelect"> <!--如果没有设置value值,则option节点的文本值会被当做value值--> <option value="zhangsan">张三</option> <option >李四</option> </select> <p>{{singleSelect}}</p> <h3>7.多项下拉选择框</h3> <select multiple v-modle="multiSelect"> <!--按住Ctrl,可执行多选--> <option value=1>语文</option> <option value=2>数学</option> <option value=3>历史</option> <option :value="4">地理</option> </select> <p>{{multiSelect.join(',')}}</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script> <script> var app = new Vue({ el:'#app', data(){ return{ singleText:'', multiText:'', radioValue:'', toggleValue:false, checkedValues:[], singleSelect:'zhangsan', multiSelect:[1,3] } }, methods:{ } }); </script> </body> </html>
2.v-model与修饰符
修饰符 | 可用版本 | 说明 |
---|---|---|
.lazy | 所有 | 将用户输入的数据赋值于变量的时间有输入时延迟到数据改变时 |
.number | 所有 | 自动转换用户输入为数值类型 |
.trim | 所有 | 自动过滤用户输入的首尾的空白字符 |
下面的代码展示了trim和number的简单用法。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>v-model与修饰符</title> </head> <body> <div id="app"> <input type="text" v-model.trim.number="text" @keyup="handleKeyUp"> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script> <script> var app = new Vue({ el:'#app', data:()=>({ text:'' }), methods:{ handleKeyUp(event) { console.log(this.text,typeof this.text) } } }); </script> </body> </html>
3.v-model与自定义组件
在自定义组件中,value属性和input事件尤为重要,value属性用于接收外部传入的值以更新组件内部的状态,input事件由开发者决定在什么事件调用。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>v-model与修饰符</title> </head> <body> <div id="app"> <!--自定义组件--> <custom-screen v-model="text"></custom-screen><br> <input type="text" v-model.trim.number="text"> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script> <script> Vue.component('custom-screen',{ //使用value属性接收外部传入的值 props:['value'], methods: { handleReset(){ console.log('重置为\'\''); this.$emit('input','');//使用$emit发送input事件,并将目标值作为参数传出 } }, template:'<div>\n' + ' <h2>输入值为:{{value}}</h2>\n' + ' <button @click="handleReset">重置为空</button>\n' + '</div>' }) var app = new Vue({ el:'#app', data:()=>({ text:'' }), methods:{ } }); </script> </body> </html>
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。