1、代码实现Vue双向绑定与事件绑定,v-bind v-model v-on

DOM结构准备

<body><div id="app"><form><input type="text" v-model="userInfo.name" /><button type="button" v-click="increment">增加</button></form><h3 v-bind="number"></h3><h4 v-bind="userInfo.name"></h4></div>
</body>

myVue创建实例,最终使用示例

window.onload = function () {var app = new myVue({el: '#app',data: {number: 0,userInfo: {name: 0}},methods: {increment: function () {console.log(this)console.log()this.userInfo.name += 1console.log(this.userInfo.name)this.number++}},directives: {}})console.log(app._binding, 'bind')}

创建myVue构造函数,

并初始化,将配置数据挂载到构造函数的原型对象上

  function myVue(options) {this._init(options)}myVue.prototype._init = function (options) {this.$options = options// 存储跟节点DOM对象this.$el = document.querySelector(options.el)// 存储data数据this.$data = options.data// 存储methods所有的方法this.$methods = options.methods// 用于存储data中数据绑定到dom的元素this._binding = {}// 形式如下// {//   number: {//     _directives: [watcher1, watcher2]//   },//   'userInfo.name': {//     _directives: [watcher1, watcher2]//   }// }// 绑定发布者,在set属性里面遍历_binding[key]._directivesthis._obverse(this.$data)// 递归根节点,通过识别节点的属性,识别出不同指令,不同指令进行不同操作,// 如v-bind指令:创建watcher并将其push进_binding// _binding[key]._directives.push(watcher)this._complie(this.$el)}

创建监听类watcher的构造函数,

并在其原型对象上实现update方法,该类将会把DOM中使用了指令的节点与data数据绑定

  function Watcher(name, el, vm, exp, attr) {this.name = name //指令名称,例如文本节点,该值设为"text"this.el = el //指令对应的DOM元素this.vm = vm //指令所属myVue实例this.exp = exp //指令对应的值,本例如"number"this.attr = attr //绑定的属性值,本例为"innerHTML"this.update()}Watcher.prototype.update = function () {// let exps = this.exp.split('.')// let res = this.vm.$data// exps.forEach(item => (res = res[item]))// console.log(res, 'res')this.el[this.attr] = getObjValueByStr(this.vm.$data, this.exp)}

在myVue原型上实现方法_obverse(),

在data数据的 set属性中实现页面数据更新的方法调用

  myVue.prototype._obverse = function (obj, preKey) {for (key in obj) {if (obj.hasOwnProperty(key)) {let curKeyif (preKey && preKey != '') {curKey = preKey + '.' + key} else {curKey = key}console.log(curKey)this._binding[curKey] = {_directives: []}let value = obj[key]console.log(value, '----')let binding = this._binding[curKey]Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function () {console.log(`获取${value}`)return value},set: function (newVal) {if (value !== newVal) {console.log(`更新${newVal}`)value = newValconsole.log(binding, 'binding')binding._directives.forEach(function (item) {item.update()})}}})if (typeof value === 'object') {this._obverse(value, curKey)}}}}

在myVue原型上实现方法_complie(),

遍历根节点中的所有节点,根据节点中设置的属性(即指令),对不同指令实现具体的功能如属性绑定指令 v-bind ,就创建一个监听实例,并放入myVue的binding中,binding[绑定的data中的数据名称]._directives.push(watcher实例)

 myVue.prototype._complie = function (root) {var _this = thisvar nodes = root.childrenfor (var i = 0; i < nodes.length; i++) {var node = nodes[i]if (node.children.length) {this._complie(node)}if (node.hasAttribute('v-click')) {node.onclick = (function () {var attrVal = nodes[i].getAttribute('v-click')return _this.$methods[attrVal].bind(_this.$data)})()}if (node.hasAttribute('v-model') && (node.tagName == 'INPUT' || node.tagName == 'TEXTAREA')) {node.addEventListener('input',(function (key) {var attrVal = node.getAttribute('v-model')_this._binding[attrVal]._directives.push(new Watcher('input',node,_this,attrVal,'value'))return function () {// let arr = attrVal.split('.')// let resAttr = _this.$data// for (let j = 0; j < arr.length - 1; j++) {//   resAttr = resAttr[arr[j]]// }// resAttr[arr[arr.length - 1]] = nodes[key].valueobjValueByStr(_this.$data, attrVal, nodes[key].value)// _this.$data[attrVal] = nodes[key].value}})(i))}if (node.hasAttribute('v-bind')) {var attrVal = node.getAttribute('v-bind')_this._binding[attrVal]._directives.push(new Watcher('text',node,_this,attrVal,'innerHTML'))}}}

2、实现原理说明

  1. obj.hasOwnProperty(key)方法,判断对象中是否具有key属性
  2. Object.defineProperty(对象,属性名,配置项)
  3. Object.defineProperty中配置项说明,属性绑定:data数据修改,动态更新DOM中的数据,主要是通过set属性,在set中更新DOM中数据
属性名 功能说明
configurable: 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
enumerable 表示能否通过for in循环访问属性,默认值为false
writable: 表示能否修改属性的值。默认值为false。
value: 包含这个属性的数据值。默认值为undefined。
get: 在读取属性时调用的函数,默认值是undefined
set: 在写入属性的时候调用的函数,默认值是undefined
  1. _obverse()方法,在data所有数据中设置set,数据改变就把绑定的DOM节点值改变
  2. _complie方法,遍历DOM节点树,并通过识别出节点中的指令,为其实现不同功能

3、完整代码

<!DOCTYPE html><head><title>myVue</title>
</head><style>#app {text-align: center;}
</style><body><div id="app"><form><input type="text" v-model="userInfo.name" /><button type="button" v-click="increment">增加</button></form><h3 v-bind="number"></h3><h4 v-bind="userInfo.name"></h4></div>
</body><script>function myVue(options) {this._init(options)}myVue.prototype._init = function (options) {this.$options = options// 存储跟节点DOM对象this.$el = document.querySelector(options.el)// 存储data数据this.$data = options.data// 存储methods所有的方法this.$methods = options.methods// 用于存储data中数据绑定到dom的元素this._binding = {}// 形式如下// {//   number: {//     _directives: [watcher1, watcher2]//   },//   'userInfo.name': {//     _directives: [watcher1, watcher2]//   }// }// 绑定发布者,在set属性里面遍历_binding[key]._directivesthis._obverse(this.$data)// 递归根节点,通过识别节点的属性,识别出不同指令,不同指令进行不同操作,// 如v-bind指令:创建watcher并将其push进_binding// _binding[key]._directives.push(watcher)this._complie(this.$el)}myVue.prototype._obverse = function (obj, preKey) {for (key in obj) {if (obj.hasOwnProperty(key)) {let curKeyif (preKey && preKey != '') {curKey = preKey + '.' + key} else {curKey = key}console.log(curKey)this._binding[curKey] = {_directives: []}let value = obj[key]console.log(value, '----')let binding = this._binding[curKey]Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function () {console.log(`获取${value}`)return value},set: function (newVal) {if (value !== newVal) {console.log(`更新${newVal}`)value = newValconsole.log(binding, 'binding')binding._directives.forEach(function (item) {item.update()})}}})if (typeof value === 'object') {this._obverse(value, curKey)}}}}myVue.prototype._complie = function (root) {var _this = thisvar nodes = root.childrenfor (var i = 0; i < nodes.length; i++) {var node = nodes[i]if (node.children.length) {this._complie(node)}if (node.hasAttribute('v-click')) {node.onclick = (function () {var attrVal = nodes[i].getAttribute('v-click')return _this.$methods[attrVal].bind(_this.$data)})()}if (node.hasAttribute('v-model') && (node.tagName == 'INPUT' || node.tagName == 'TEXTAREA')) {node.addEventListener('input',(function (key) {var attrVal = node.getAttribute('v-model')_this._binding[attrVal]._directives.push(new Watcher('input',node,_this,attrVal,'value'))return function () {// let arr = attrVal.split('.')// let resAttr = _this.$data// for (let j = 0; j < arr.length - 1; j++) {//   resAttr = resAttr[arr[j]]// }// resAttr[arr[arr.length - 1]] = nodes[key].valueobjValueByStr(_this.$data, attrVal, nodes[key].value)// _this.$data[attrVal] = nodes[key].value}})(i))}if (node.hasAttribute('v-bind')) {var attrVal = node.getAttribute('v-bind')_this._binding[attrVal]._directives.push(new Watcher('text',node,_this,attrVal,'innerHTML'))}}}function Watcher(name, el, vm, exp, attr) {this.name = name //指令名称,例如文本节点,该值设为"text"this.el = el //指令对应的DOM元素this.vm = vm //指令所属myVue实例this.exp = exp //指令对应的值,本例如"number"this.attr = attr //绑定的属性值,本例为"innerHTML"this.update()}Watcher.prototype.update = function () {// let exps = this.exp.split('.')// let res = this.vm.$data// exps.forEach(item => (res = res[item]))// console.log(res, 'res')this.el[this.attr] = getObjValueByStr(this.vm.$data, this.exp)}// 通过字符串形式(userInfo.name),将对象中的属性修改为目标值// target=userInfo: {name: '12'}, objStr = 'userInfo.name', value = '1'function objValueByStr(targetObj, objStr, value) {let arr = objStr.split('.')let resAttr = targetObjfor (let j = 0; j < arr.length - 1; j++) {resAttr = resAttr[arr[j]]}resAttr[arr[arr.length - 1]] = value}// 通过字符串形式('userInfo.name'),获取对象中该属性的值function getObjValueByStr(targetObj, objStr) {let exps = objStr.split('.')let res = targetObjexps.forEach(item => (res = res[item]))return res}window.onload = function () {var app = new myVue({el: '#app',data: {number: 0,userInfo: {name: 0}},methods: {increment: function () {console.log(this)console.log()this.userInfo.name += 1console.log(this.userInfo.name)this.number++}},directives: {}})console.log(app._binding, 'bind')}
</script>

Vue双向绑定原理代码实现相关推荐

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

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

  2. 【vue双向绑定原理浅析】

    vue双向绑定原理浅析 1.什么是双向绑定? ​ 所谓双向绑定,指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据.(数据变化更新视图,视图变 ...

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

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

  4. 浅谈vue双向绑定原理

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

  5. 浅聊vue双向绑定原理Object.defineProperty-/-Proxy

    什么是双向绑定呢?vue又是怎么做的我们接下来就聊一聊 什么是双向绑定? 当数据模型data变化时,页面视图会得到响应更新 vue又是怎么做的? vue其实现原理是对data的getter/sette ...

  6. 通俗易懂了解Vue双向绑定原理及实现

    https://www.cnblogs.com/wangjiachen666/p/9883916.html 亲测可用

  7. Vue基础知识总结 6:vue双向绑定原理

  8. Vue数据双向绑定原理(vue2向vue3的过渡)

    众所周知,Vue的两大重要概念: 数据驱动 组件系统 接下来我们浅析数据双向绑定的原理 一.vue2 1.认识defineProperty vue2中的双向绑定是基于defineProperty的ge ...

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

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

最新文章

  1. 周志华、宋继强谈如何培养高端AI人才,以及深度学习的局限性和未来
  2. 必看!清华大学刘洋教授“浅谈研究生学位论文选题”方法,3月7日1小时视频公开课(附视频PPT下载)...
  3. PHPcms 系统简单使用
  4. 网络性能测试工具Iperf上手指南
  5. Netty之有效规避内存泄漏
  6. 列举python中可变数据类型_python 可变数据类型 和 不可变数据类型
  7. matlab fbb,[求助]请教如何将modelsim仿真的输出结果导入到matlab中分析
  8. PTA15、班级人员信息统计 (10 分)
  9. 深入剖析Linux IO原理和几种零拷贝机制的实现
  10. 监控系统哪家强?eBay 在监控系统上的实践应用!
  11. OpenCV DNN调用训练好的caffe 模型(目标检测)
  12. Sphinx制作htmlhelp手册文档(Manual-chm)
  13. rtl8821cu 驱动编译
  14. java精选视频资源,收藏慢慢看!
  15. 条形码生成软件如何制作A级条码
  16. 大神李沐被曝离职,投身大模型创业!网友:希望不要耽误他解读论文
  17. 树莓派操作及搭建frp实现内网穿透
  18. 视比特“AI+3D视觉”产品系列 | 上料装配工作站
  19. 从巴贝奇、爱达到图灵
  20. 你所需要知道的项目管理知识

热门文章

  1. ​肩负使命,砥砺前行!
  2. spring java 灰度发布_SpringCloud灰度发布实践(附源码)
  3. oceanbase 数据库上所有的数据字典表
  4. web渗透测试-从入门到放弃-04XSS-键盘记录
  5. 折叠屏手机:心比天高,命比纸薄
  6. 360手机开启Log打印
  7. Google图片存储格式WebP增加与PNG类似背景透明效果
  8. Hbuilder真机运行安卓机找不到设备及找到设备后提示手机未安装android_base.apk问题解决办法
  9. 老兵不死——麦克 阿瑟
  10. 05【JS 高级】-【PC端网页特效】元素偏移量 offset 系列, 元素可视区 client 系列, 元素滚动 scroll 系列, 动画函数封装, 常见网页特效案例