如果你有读过Vue的源码,或者有了解过Vue的响应原理,那么你一定知道Object.defineProperty(), 那么你也应该知道,Vue 2.x里,是通过 递归 + 遍历 data 对象来实现对数据的监控的, 你可能还会知道,我们使用的时候,直接通过数组的下标给数组设置值,不能实时响应,是因为Object.defineProperty()
无法监控到数组下标的变化,而我们平常所用的数组方法 push , pop , shift , unshift , splice , sort , reverse
, 其实不是真正的数组方法,而是被修改过的,这些都是因为 Object.defineProperty()
提供的能力有限,无法做到完美。
网上看过很多关于Vue的源码解读或者实现一个简易版的Vue的教程,还都是用 Object.defineProperty (大概是为兼容性考虑吧), 而 Object.defineProperty() 确实存在诸多限制, 据说Vue的3.x版本会改用Proxy,那么今天我们就先来尝尝鲜,用Proxy实现一个简单版的Vue
proxy 介绍
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
以上引用内容来自阮一峰的es6教程的Proxy章节 原文地址请戳这里。
我们来看看如何用Proxy去定义一个监听数据的函数
定义 observe
_observe (data){ var that = this // 把代理器返回的对象存到 this.$data 里面 this.$data = new Proxy(data, { set(target,key,value){ // 利用 Reflect 还原默认的赋值操作 let res = Reflect.set(target,key,value) // 这行就是监控代码了 that.handles[key].map(item => {item.update()}) return res } }) }
当触发set的时候,就会执行 that.handles[key].map(item => {item.update()})
,这句代码的作用就是执行 该属性名下的所有 "监视器"
那么,监视器怎么来的呢? 请继续看以下代码
定义 compile
_compile (root){ const nodes = Array.prototype.slice.call(root.children) let data = this.$data nodes.map(node => { // 如果不是末尾节点,就递归 if(node.children.length > 0) this._complie(node) // 处理 v-bind 指令 if(node.hasAttribute('v-bind')) { let key = node.getAttribute('v-bind') this._pushWatcher(new Watcher(node,'innerHTML',data,key)) } // 处理 v-model 指令 if(node.hasAttribute('v-model')) { let key = node.getAttribute('v-model') this._pushWatcher(new Watcher(node,'value',data,key)) node.addEventListener('input',() => {data[key] = node.value}) } // 处理 v-click 指令 if(node.hasAttribute('v-click')) { let methodName = node.getAttribute('v-click') let mothod = this.$methods[methodName].bind(data) node.addEventListener('click',mothod) } }) }
上面这段代码,看起来很长,可是实际上,只做了意见很简单的事情, 就是 "编译" html 模板 ,把有 v-bind 、 v-model 、 v-click 都给加上对应的 通知 和 监控
1.通知就是 this._pushWatcher(...)
, 相当于是安装一个监听器,这样只要 this.$data 有发生 set 操作的话,就会执行 this._pushWatcher 括号里面传的函数,来通知节点更新数据
2.监控就是 node.addEventListener(...)
监听相应的事件,然后执行对应的 watcher 或者 methods
this._pushWatcher 又做了什么呢?
_pushWatcher (watcher) { if (!this._binding[watcher.key]) this._binding[watcher.key] = [] this._binding[watcher.key].push(watcher) }
这个就更简单了,如果 this._binding[watcher.key]
为空,就初始化,然后向里面添加一个 监听器
最后,我们再来看看,监听器是怎么实现的
定义 Watcher
class Watcher { constructor (node,attr,data,key) { this.node = node this.attr = attr this.data = data this.key = key } update () { this.node[this.attr] = this.data[this.key] } }
Watcher 是一个类,只有一个方法,就是更新数据,怎么知道要更新哪个节点,更新为什么数据呢? 在实例化(new)的时候,传的参数就是定义这些的
这样,我们就实现初步的双向绑定了,整个代码大概只有50行。其实还可以更少, 但是更少的话,就是继续阉割功能了(虽然目前实现的也是严重阉割版的), 但是我觉得实现这些,刚好可以不多不少帮我我们理解vue的本质。
最后
本文最终实现代码已经放在 github 上,想要直接看效果的同学,可以上去直接copy,运行。
如果觉得本文对您有用,请给本文的github 加个star,万分感谢
另外, github 上还有其他一些关于前端的教程和组件,有兴趣的童鞋可以看看,你们的支持就是我最大的动力。
总结
以上所述是小编给大家介绍的使用proxy实现一个更优雅的vue,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的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]