Vue: 一个简单的Vue2.0 v-model双向数据绑定的实现,含源代码,小白也能看懂
首先说一下原理吧
View层(dom元素)的变动如何响应到Model层(Js变量)呢? 通过监听元素的input事件来动态的改变js变量的值,实际上不是改变的js变量的值,而是改变的js变量的getter的返回值
Model层(Js变量)的变动如何响应到View层(dom元素)呢?通过Object.defineProperty API的set回调方法可以劫持JS变量设置的新值newVal,然后将新值newVal设置给dom元素。
定义我们的view层
<input type="text" v-model="msg"><br> <input type="text" v-model="msg"><br> <input type="text" v-model="name">
定义我们的model代码
let data = { msg: 'hello', name: '' }
定义我们的核心代码双向数据绑定
// 用户存放状态更改的新值 let reflect = {}; // 用于存放状态到dom元素的映射 也就是key是data里面的属性,value存的是数组domArr,domArr存放的是v-model等于key的元素 let deep = {}; // 查询到所有的含有v-modle属性的dom元素 document.querySelectorAll("[v-model]").forEach(item => { // 获得dom元素的v-model的值 let model = item.getAttribute("v-model"); // 如果如果值所对应的映射数组为空的话 就初始化 deep[model] = deep[model] || []; // 绑定状态和dom元素的映射关系 deep[model].push(item) // 初始化dom元素的值 和 状态get的值 reflect[model] = item.value = data[model]; //监听dom元素的input事件 item.addEventListener('input', function(){ // 将dom元素的新值赋值给data中定义的状态 data[model] = item.value; },false) }) // 循环遍历所有状态 for(let [key, val] of Object.entries(data)){ // 通过Object.defineProperty来实现监听 Object.defineProperty(data, key, { // 劫持状态更新的新值 set(newVal){ // 将更新的新值设置给get的返回值 reflect[key] = newVal; // 循环将更新的新值设置给关联的的dom元素 deep[key] && deep[key].forEach(item => { item.value = newVal; }) return newVal; }, // 返回状态的新值 get(){ return reflect[key]; } }) }
此时,无论是改变input的值,还是改变data里面定义的状态,数据都会向另一端同步。
PS:这只是简单的实现了一下v-model,与Vue 2中的实现还是有一些差异的,原理都是相同的。