本文实例讲述了js实现视图和数据双向绑定的方法。分享给大家供大家参考,具体如下:
前言
视图和数据绑定,使视图和逻辑层分离,使视图层变为数据驱动是前端的一大进步。由此诞生了mvvm类的前端框架,大大提升了开发的效率。
那么在使用旧有的项目中,如何使用更加先进的设计模式来替换掉大量的面向过程编程。
各大框架对于数据绑定的实现都有各自的方式,这里不做深入只是简单介绍一下。
Vue使用了es5 Object.defineProperty的特性来实现对数据读取和设置的监听,是一种元编程的方式。个人感觉,比之angularJS的脏检查数据比对更新机制确实要科学一点。
Object.defineProperty实现了对数据get和set的重新编程,能让我们在做最简单的 = 赋值操作时来做一些事,具体的实现就不放在这了。这不是今天要讨论的重点。
在低版本浏览器中显然对于这点的支持也不够友好,那么我可以想一种替代性的方案来达到和Object.defineProperty所类似的效果。在微信小程序中设置数据需要使用set方法,因为小程序的视图层本质上是分离于js引擎的。这样等于手动告诉视图层某个数据更新了。那么我也可以使用这种方法来实现对数据更新的监听。
实现
首先我们需要一个对象来保存数据。并且在其数据变更时做一些事情。可以定义一个构造函数来获得这个对象。
因为get和set方法很显然是公共的,所以可以定义在原型对象上。
var $vm = function(obj) { this.data = obj.data } $vm.prototype.get = function(prop) { //返回当前值 return this.data[prop] } $vm.prototype.set = function (prop, val) { //赋值操作 this.data[prop] = val }
如果这时候实例化一个这个构造函数的对象,这个对象上就会存在get和set方法,看代码可以知道他对这个对象上的data生效。
这样一个简单的get set方法就设置好了。一个是获取当前对象属性的值,一个是对其设置新的值。
如果在设置的时候我们再去触发相应的视图层的操作,那么一个简单的绑定就实现了。
var vm = new $vm({ // 绑定的变量值 data: { info: true } })
取值
vm.get('info')
存值
vm.set('info', false)
如果我们在set方法里添加console.log()
那么每次数据变动都会被打印出来。
使用set方法来替代=号的赋值操作可以一定意义上代替Object.defineProperty
的效果。并且兼容性更好。
对视图数据进行绑定是一个很大的问题,怎样使数据的变动在视图上体现。
这里一个最简单的替代实现就是去手动绑定数据和视图。用jq的方式。
比如在set里面执行对应这个属性变动的回掉函数。
例如
$vm.prototype.set = function (prop, val) { this.data[prop] = val if (this.$$fn[prop]) { this.$$fn[prop](val, oldVal) } }
可以看到如果当前对象上$$fn属性上如果存在同名的函数,会执行。
这样我们可以把绑定dom的操作来放到里面显示。
这种写法显然可能不太利于维护,于是我想可以参照vue框架的watch观察者来实现。
在vue中观察对象上某个值的改变可以do someThing。
所以在此可以借鉴。
// 存值 $vm.prototype.set = function (prop, val) { var oldVal = this.data[prop] this.data[prop] = val //如果发现被列入观察者 执行函数并注入修改后的值 if (this.watch[prop]) { this.watch[prop](val, oldVal) } }
鸽了,这里省略3000字。哈哈哈哈,因为写了好多代码但是没写博客,懒得写了直接跳过吧,有兴趣的童鞋直接看源码。虚拟dom和{{}}
表达式,观察者模式,计算属性等等。
突发奇想,想要对数据层绑定还有个简单的方案。利用html data的自定义属性来绑定相应的属性,利用jq选择器来找到对应的节点进行更新。这也是一种替代的方案。
JQ选择器,选择属性=某值的dom节点。利用这个选择器可以来获得所有绑定了某属性的节点。
<div data-vm="info"> </div>
$('[data-vm="info"]').text(300)
这个节点的值变为了300。
所以利用自定义属性和jq的选择器,只要在dom节点上写上data-vm(取的名字)然后等于要绑定的值,那么就可以实现对视图的绑定了。
// 存值 $vm.prototype.set = function (prop, val) { try { var oldVal = this.data[prop] this.data[prop] = val //如果发现被列入观察者 执行函数并注入修改后的值 if (this.watch[prop]) { this.watch[prop](val, oldVal) } //查询是否有订阅值 if (this.$$fn[prop]) { this.$$fn[prop](val, oldVal) } //查询是否有依赖于此项的计算属性 if (this.$$count[prop]) { // 获得所有依赖此值的计算属性 var arr = this.$$count[prop] //循环遍历每个计算属性并重新计算它的值 for (var i = 0; i < arr.length; i++) { var item = arr[i] // 获得返回的值 this.data[item] = this.computed[item]() } } // 如果节点绑定了此属性 更新节点 var dom = $('[data-vm="' + prop + '"]') if (dom) { dom.text(val) } // this.updateView() } catch(e) { console.log('error setData' + prop) } }
实现成功!
目前绑定的各种方式我改了蛮多,具体的代码在github上,有兴趣的童鞋可以看一看。
项目github地址https://github.com/unjust-life/mvvm
更多关于JavaScript相关内容可查看本站专题:《JavaScript操作DOM技巧总结》、《JavaScript页面元素操作技巧总结》、《JavaScript事件相关操作与技巧大全》、《JavaScript查找算法技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript错误与调试技巧总结》
希望本文所述对大家JavaScript程序设计有所帮助。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]