V-model的原理
在学习v-model的过程中手贱自己用原生JS实现了一遍v-model的双向绑定。。。
首先,v-model只是个语法糖,实际上原理给表单元素用绑定值和input或change事件,举个例子:

<input v-model="value" />
//实际上等于
<input v-bind:value="value" @input="value = $event.target.value" />

至于表单元素的用法可以自行查看Vue官网文档。
简单阐述一下,
input元素和textarea元素是绑定value属性和input事件,
checkbox和radio则是绑定checked属性和change事件,
select,option则是绑定selected属性和change事件。
接下来开始原生JS的实现过程。。。。
先贴结构
(ps:p元素仅作展示使用)

     <label for="text"><input type="text" id="text" /></label><p></p><label for="male">男<input type="radio" name="sex" value="boy" id="male" /></label><label for="female">女<input type="radio" name="sex" value="girl" id="female" /></label><p></p><input type="checkbox" name="hobby" value="足球" />足球<input type="checkbox" name="hobby" value="篮球" />篮球<input type="checkbox" name="hobby" value="排球" />排球<p></p><select><option value="" disabled>请选择水果</option><option value="苹果">苹果</option><option value="芒果">芒果</option><option value="火龙果">火龙果</option></select><p></p>

一开始的实现是这样的

      let radio = document.querySelectorAll("input[type='radio']")let checkbox = document.querySelectorAll("input[type='checkbox']")let text = document.getElementById("text")let p = document.querySelectorAll('p')let div = document.querySelector('div')let option = document.querySelectorAll('option')let select = document.querySelector('select')let inputText = 'hhh'let sex = 'boy'let checkArr = ['足球' , '篮球' , '排球']let selected = ''// select//遍历option节点,判断选中哪个节点,初始化到DOM上for(let i = 0 ; i < option.length ; i++) {if(option[i].value === selected) option[i].selected = true}//给select绑定change事件,当触发事件时更新展示区域和变量值select.onchange = function(event) {// console.log(event.target.selectedIndex);selected = option[event.target.selectedIndex].valuediv.innerHTML = selected}//input//初始化input框的值text.value = inputText//给input框绑定input事件,触发时更新展示区域和变量值text.oninput = function(event) {inputText = event.target.valuep[0].innerHTML = inputText }//radio//遍历radio节点,判断是否选中,并绑定change事件for(let i=0 ; i<radio.length ; i++) {if(radio[i].getAttribute('value') === sex) radio[i].checked = trueradio[i].onchange = function(event) {sex = event.target.valuep[1].innerHTML = sex}}//checkbox//遍历checkArr数组,判断是否选中,初始化DOMcheckArr.forEach((item) => {for(let i=0 ; i<checkbox.length ; i++) {if(checkbox[i].getAttribute('value') === item) checkbox[i].checked = true}        })//遍历checbox节点,绑定change事件for(let i=0 ; i<checkbox.length ; i++) {checkbox[i].onchange = function(event) {if(event.target.checked) {checkArr.push(event.target.value)}else {checkArr.forEach((item , index) => {if(checkbox[i].getAttribute('value') === item)checkArr.splice(index , 1)})}        p[2].innerHTML = checkArr}}

很麻烦的实现了一遍,后来一想每次都要写for循环很麻烦,就写了个函数把获取到的节点类数组转成数组,同时将所有的变量写成一个对象并做了一层数据代理

         function selectElem(selectStr) {return Array.from(document.querySelectorAll(selectStr))}let radio = selectElem("input[type='radio']")let checkbox = selectElem("input[type='checkbox']")let option = selectElem('option')let p = selectElem('p')let select = document.querySelector('select')let text = document.getElementById("text")let obj = {inputText: 'hhh',sex: 'boy',checkArr: ['足球', '篮球', '排球'],selected: ''}let proxyObj = {}for (let key in obj) {Object.defineProperty(proxyObj, key, {get() {return obj[key]},set(newVal) {obj[key] = newVal}})}p[0].innerHTML = proxyObj.inputTextp[1].innerHTML = proxyObj.sexp[2].innerHTML = proxyObj.checkArrp[3].innerHTML = proxyObj.selected

然后就变成了这样

         text.value = proxyObj.inputTexttext.oninput = function(event) {proxyObj.inputText = event.target.valuep[0].innerHTML = proxyObj.inputText}//radioradio.forEach(item => {if (item.getAttribute('value') === proxyObj.sex) item.checked = trueitem.onchange = function(event) {proxyObj.sex = event.target.valuep[1].innerHTML = proxyObj.sex}})//checkboxproxyObj.checkArr.forEach((item) => {for (let i = 0; i < checkbox.length; i++) {if (checkbox[i].getAttribute('value') === item) checkbox[i].checked = true}})checkbox.forEach(item => {item.onchange = function(event) {if (event.target.checked) {proxyObj.checkArr.push(event.target.value)} else {proxyObj.checkArr.forEach((checkItem, index) => {if (item.getAttribute('value') === checkItem)proxyObj.checkArr.splice(index, 1)})}p[2].innerHTML = proxyObj.checkArr}})//selectoption.forEach((item) => {if (item.value === proxyObj.selected) item.selected = true})select.onchange = function(event) {// console.log(event.target.selectedIndex);proxyObj.selected = option[event.target.selectedIndex].valuep[3].innerHTML = proxyObj.selected}

但是仔细一想,这样似乎只实现了DOM到数据的绑定,没有实现数据到DOM的绑定,很操蛋啊,而且对于checkbox元素,Vue在实现的时候可以是一个布尔值或者数组,而我这样实现只有数组的形式,所以又在这个基础上又完善了一遍。。。。

      let obj = {inputText : 'hhh',sex : 'boy',check : ['足球' , '篮球' , '排球'],// check: true,selected : ''}let proxyObj = {}for(let key in obj) {Object.defineProperty(proxyObj , key , {get() {return obj[key]},set(newVal) {obj[key] = newValupdate()//数据变化时触发更新}})}//初始化函数function init() {        setInput()bindInput()setRadio()bindRadio()setCheckbox()bindCheckbox()setOption()bindSelect()}//选择元素function selectElem(selectStr) {return Array.from(document.querySelectorAll(selectStr))}//更新函数function update() {setInput()setRadio()setCheckbox()setOption()}//设置input框,初始化和更新时使用function setInput() {p[0].innerHTML = proxyObj.inputTexttext.value = proxyObj.inputText}// 给input框绑定事件function bindInput() {text.oninput = function(event) {proxyObj.inputText = event.target.value}}// 设置radiofunction setRadio() {radio.forEach(item => {if(proxyObj.sex) {if(item.getAttribute('value') === proxyObj.sex) item.checked = true}else {item.checked = false}   })p[1].innerHTML = proxyObj.sex}// 绑定radiofunction bindRadio() {radio.forEach(item => {item.onchange = function(event) {proxyObj.sex = event.target.value          }})}//判断是否为数组function isArr(obj) {return Array.isArray(obj)}// 判断数组是否为空function isEmpty(check) {return check.length === 0}// 设置checkbox的checkedfunction setCheckItem(bool) {checkbox.forEach(item => {item.checked = bool})}// 设置checkboxfunction setCheckbox() {if(isArr(proxyObj.check)) {if(!isEmpty(proxyObj.check)) {proxyObj.check.forEach((item) => {checkbox.forEach(checkItem => {if(checkItem.getAttribute('value') === item)checkItem.checked = true})})}else {setCheckItem(false)}}else {if(proxyObj.check) {setCheckItem(true)}else {setCheckItem(false)}}p[2].innerHTML = proxyObj.check}// 绑定checkboxfunction bindCheckbox() {checkbox.forEach(checkItem => {checkItem.onchange = function(event) {if(isArr(proxyObj.check)) {if(event.target.checked) {proxyObj.check.push(event.target.value)}else {proxyObj.check.forEach((item , index) => {if(checkItem.getAttribute('value') === item)proxyObj.check.splice(index , 1)})}p[2].innerHTML = proxyObj.check}else {proxyObj.check = event.target.checked}}})}// 设置optionfunction setOption() {option.forEach((item) => {if(item.value === proxyObj.selected) item.selected = true})p[3].innerHTML = proxyObj.selected}// 绑定selectfunction bindSelect() {select.onchange = function(event) {// console.log(event.target.selectedIndex);proxyObj.selected = option[event.target.selectedIndex].value          }}init()

在这里不得不提一下,给checkbox元素绑定时的逻辑最为复杂。。。首先得判断绑定的值是否为数组,其次在判断数组是否为空,为空时就默认都不选中,不为空时就遍历checkbox节点选中value值为checkArr中的对应值,如果不是数组就判断传入的是true还是false(或者为空)如果是true就默认都选中,false就默认都不选中。在绑定事件时也要进一步判断。。。
然后我又要把它封装到一个类中,如下

 class Bind {_options = {}constructor(options) {this.options = optionsthis.dataProxy()this.init()}init() {this.setInput()this.bindInput()this.setRadio()this.bindRadio()this.setCheckbox()this.bindCheckbox()this.setOption()this.bindSelect()}dataProxy() {let self = thisfor(let key in this.options) {Object.defineProperty(self._options , key , {get() {return self.options[key]},set(newValue) {self.options[key] = newValueself.update()}})}}update() {this.setInput()this.setRadio()this.setCheckbox()this.setOption()}setInput() {this._options.p[0].textContent = this._options.inputValuethis._options.input.value = this._options.inputValue}bindInput() {this._options.input.oninput = event => {this._options.inputValue = event.target.value}}setRadio() {this._options.radio.forEach(item => {if(this._options.radioValue) {if(item.getAttribute('value') === this._options.radioValue) item.checked = true}else {item.checked = false}   })p[1].textContent = this._options.radioValue}bindRadio() {this._options.radio.forEach(item => {item.onchange = event => {this._options.radioValue = event.target.value          }})}setOption() {this._options.options.forEach(item => {if(item.value === this._options.selectValue) item.selected = true})p[3].textContent = this._options.selectValue}bindSelect() {this._options.select.onchange = event => {// console.log(event.target.selectedIndex);this._options.selectValue = options[event.target.selectedIndex].value          }}isArr(obj) {return Array.isArray(obj)}isEmpty(check) {return check.length === 0}setCheckItem(bool) {this._options.checkbox.forEach(item => {item.checked = bool})}setCheckbox() {if(this.isArr(this._options.checkValue)) {if(!this.isEmpty(this._options.checkValue)) {this._options.checkValue.forEach((item) => {this._options.checkbox.forEach(checkItem => {if(checkItem.getAttribute('value') === item)checkItem.checked = true})})}else {this.setCheckItem(false)}}else {if(this._options.checkValue) {this.setCheckItem(true)}else {this.setCheckItem(false)}}this._options.p[2].textContent = this._options.checkValue}bindCheckbox() {this._options.checkbox.forEach(checkItem => {checkItem.onchange = event => {if(this.isArr(this._options.checkValue)) {if(event.target.checked) {this._options.checkValue.push(event.target.value)}else {this._options.checkValue.forEach((item , index) => {if(checkItem.getAttribute('value') === item)this._options.checkValue.splice(index , 1)})}this._options.p[2].textContent = this._options.checkValue}else {this._options.checkValue = event.target.checked}}})}}

使用如下:

      let radio = selectElem("input[type='radio']")let checkbox = selectElem("input[type='checkbox']")let options = selectElem('option')let p = selectElem('p')let select = document.querySelector('select')let input = document.getElementById("text")let bind = new Bind({input,inputValue: 'hhh',radio,radioValue: '',select,options,selectValue:'',checkbox,checkValue: [],p})

很简陋,而且只能传一个元素,new一个对象只能处理一个input,一个radio数组,一个checkbox数组,和一个select元素。。。而且本来应该在类中进一步判断new Bind({options})时是否传入对应值,再执行对应的初始化和绑定。。。Vue底层是用发布者订阅者模式进行监听的,当数据发生变化就通知对应的订阅者触发更新。
最后,想说Vue是真的好用,一个v-model解决的事情我硬生生写了这么多代码,还只是个粗糙版,也印证了那句话,你用起来越简单的东西,一定是别人在底层帮你做了无数的事情。。。卒,希望努力提升自己的水平,不要做一个卑微的前端捞仔。。。

V-model的双向绑定原理相关推荐

  1. 基于Vue2.0数据双向绑定原理-详解

    在线使用-线上测试-源码 //代码: <div id="app"><input v-model="name" type="text& ...

  2. 前端技巧|vue双向绑定原理,助你面试成功

    在面试一些大厂的时候,面试官可能会问到你vue双向数据绑定的原理是什么?有些小伙伴不知道是什么东西,这样你在面试官的眼里就大打折扣了.今天小千就来给大家介绍一下vue的双向绑定原理,千万不要错过啦. ...

  3. 西安电话面试:谈谈Vue数据双向绑定原理,看看你的回答能打几分

    最近我参加了一次来自西安的电话面试(第二轮,技术面),是大厂还是小作坊我在这里按下不表,先来说说这次电面给我留下印象较深的几道面试题,这次先来谈谈Vue的数据双向绑定原理. 情景再现: 当我手机铃声响 ...

  4. vue双向绑定原理及实现

    vue双向绑定原理及实现 一.MVC模式 二.MVVM模式 三.双向绑定原理 1.实现一个Observer 2.实现一个Watcher 3.实现一个Compile 4.实现一个MVVM 四.最后写一个 ...

  5. 浅谈vue双向绑定原理

    简析mvvm框架 目前angular,reat和vue都是mvvm类型的框架 以vue为例 这里的vm 就是vue框架,它相当于中间枢纽的作用,连接着model 和view. 当前台显示的view发生 ...

  6. vue的数据双向绑定原理

    前言: 什么是数据双向绑定? vue是一个mvvm框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化,数据也会跟着同步变化.这也算是vue的精髓之处了.单项数据绑定是使用状 ...

  7. angular的双向绑定原理

    http://sentsin.com/web/779.html AngularJS是一款优秀的前端JS框架,已经被用于Google的多款产品当中.AngularJS有着诸多特性,最为核心的是:MVVM ...

  8. Android DataBinding双向绑定原理

    Android中的双向绑定是指:将Model设置给View之后,当Model中的数据发生改变时,View显示的内容也会随之改变:当View发生变化时,Model中的数据也会随之改变.双向绑定可以让开发 ...

  9. v-model双向绑定原理_Vue数据绑定

    这是一篇简单的学习笔记.在学习一段时间Vue后,尝试实现一下Vue的数据绑定. 相关源码:https://github.com/buchuitoudegou/Data-Binding-demo Vue ...

  10. vue的双向绑定原理及实现

    前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图 ...

最新文章

  1. Unity3D中的函数方法及解释
  2. python学习笔记(开课介绍1)
  3. 【备忘】linux shell 字符串操作(长度,查找,替换,匹配)详解
  4. 爬虫-基于bs4库的HTML内容查找方法
  5. springboot 不同环境不同的配置
  6. linux大端小端命令,linux的大小端、网络字节序问题
  7. insert---插入记录
  8. Oracle修改SID(实例名)
  9. 数据库-MySQL-JDBC框架
  10. 编程面试问题越难越好?!
  11. mysql优化方法_mysql优化方案总结
  12. 计算机视觉:关于Graph cuts的简介及相关资源
  13. Atitit  记录方法调用参数上下文arguments
  14. IDEA 设置背景颜色为黑色
  15. TCP/IP网络编程项目式教程(微课版)
  16. port bridge enable命令导致的环路
  17. 浅谈LigerUi Tree(树)
  18. 云呐|固定资产管理的目的,固定资产管理办法的目的
  19. 2D Pixel Perfect:使用Unity创建任天堂红白机风格复古游戏
  20. 使用ftp服务上传文件时553报错的解决(绝对有用)

热门文章

  1. 康佳的“顺势”与“逆商”
  2. linux syscall 输出函数,Golang:如何在Linux上使用syscall.Syscall?
  3. iMovie教程:如何给视频进行防抖动处理?
  4. 用css做(花的绽放)
  5. 数据库操作语言——DML语言
  6. Unity下落式音游实现——(4)鼓盘动画及敲击判定
  7. T300最长递增子序列
  8. 动易DIV+CSS官方模板发布时附加的介绍性文章
  9. iText+freemarker 生成PDF 使用ftl模板
  10. android手机底噪,[RK3399][Android7.1] 调试笔记 --- Codec播放音乐会有底噪