使用proxy实现一个更优雅的vue【推荐】


Posted in Javascript onJune 19, 2018

如果你有读过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,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
javascript 三种编解码方式
Feb 01 Javascript
JavaScript中使用构造函数实现继承的代码
Aug 12 Javascript
jQuery(非HTML5)可编辑表格实现代码
Dec 11 Javascript
使用JavaScript动态设置样式实现代码及演示动画
Jan 25 Javascript
CodeMirror js代码加亮使用总结
Mar 25 Javascript
在vue-cli脚手架中配置一个vue-router前端路由
Jul 03 Javascript
Vue组件间通信方法总结(父子组件、兄弟组件及祖先后代组件间)
Apr 17 Javascript
tracking.js实现前端人脸识别功能
Apr 16 Javascript
如何使用vue slot创建一个模态框的实例代码
May 24 Javascript
js+html+css实现手动轮播和自动轮播
Dec 30 Javascript
从源码角度来回答keep-alive组件的缓存原理
Jan 18 Javascript
JavaScript继承的三种方法实例
May 12 Javascript
微信小程序input框中加入小图标的实现方法
Jun 19 #Javascript
详解基于Vue cli生成的Vue项目的webpack4升级
Jun 19 #Javascript
Vue中"This dependency was not found"问题的解决方法
Jun 19 #Javascript
Vue.js 2.x之组件的定义和注册图文详解
Jun 19 #Javascript
Vue中使用webpack别名的方法实例详解
Jun 19 #Javascript
vue mounted组件的使用
Jun 18 #Javascript
基于rollup的组件库打包体积优化小结
Jun 18 #Javascript
You might like
兼容性比较好的PHP生成缩略图的代码
2011/01/12 PHP
不重新编译PHP为php增加openssl模块的方法
2011/06/14 PHP
PHP中register_globals参数为OFF和ON的区别(register_globals 使用详解)
2012/02/05 PHP
探讨各种PHP字符串函数的总结分析
2013/06/05 PHP
greybox——不开新窗口看新的网页
2007/02/20 Javascript
11款新鲜的jQuery插件[附所有demo下载]
2011/01/24 Javascript
一些实用的jQuery代码片段收集
2011/07/12 Javascript
JavaScript NodeTree导航栏(菜单项JSON类型/自制)
2013/02/01 Javascript
基于jquery实现后台左侧菜单点击上下滑动显示
2013/04/11 Javascript
javascript垃圾收集机制与内存泄漏详细解析
2013/11/11 Javascript
js中的布尔运算符使用介绍
2013/11/20 Javascript
jquery修改属性值实例代码(设置属性值)
2014/01/06 Javascript
jQuery中element选择器用法实例
2014/12/29 Javascript
AngularJS打开页面隐藏显示表达式用法示例
2016/12/25 Javascript
使用jquery+iframe做一个ajax上传效果(实例)
2017/08/24 jQuery
vue下history模式刷新后404错误解决方法
2018/08/18 Javascript
JavaScript 性能提升之路(推荐)
2019/04/10 Javascript
js判断一个对象是数组(函数)的方法实例
2019/12/19 Javascript
Vue项目配置跨域访问和代理proxy设置方式
2020/09/08 Javascript
[05:02][DOTA2]DOTA进化论 第一期
2013/09/27 DOTA
对Python中TKinter模块中的Label组件实例详解
2019/06/14 Python
python中sort和sorted排序的实例方法
2019/08/26 Python
python主线程与子线程的结束顺序实例解析
2019/12/17 Python
Python双链表原理与实现方法详解
2020/02/22 Python
Python接口测试文件上传实例解析
2020/05/22 Python
python文件排序的方法总结
2020/09/13 Python
Python抖音快手代码舞(字符舞)的实现方法
2021/02/07 Python
css3实现超立体3D图片侧翻倾斜效果
2014/04/16 HTML / CSS
html5 viewport使用方法示例详解
2013/12/02 HTML / CSS
Html5页面中的返回实现的方法
2018/02/26 HTML / CSS
HTML5页面嵌入小程序没有返回按钮及返回页面空白的问题
2020/05/28 HTML / CSS
马来西亚最好的婴儿商店:Motherhood
2017/09/14 全球购物
村党的群众路线教育实践活动总结材料
2014/10/31 职场文书
企业与个人合作经营协议书
2014/11/01 职场文书
预备党员党支部意见
2015/06/02 职场文书
电脑无法安装Windows 11怎么办?无法安装Win11的解决方法
2021/11/21 数码科技