使用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 相关文章推荐
该如何加载google-analytics(或其他第三方)的JS
May 13 Javascript
jquery 插件开发备注
Aug 27 Javascript
『JavaScript』限制Input只能输入数字实现思路及代码
Apr 22 Javascript
为什么要在引入的css或者js文件后面加参数的详细讲解
May 03 Javascript
JavaScript人脸识别技术及脸部识别JavaScript类库Tracking.js
Sep 14 Javascript
JS不用正则验证输入的字符串是否为空(包含空格)的实现代码
Jun 14 Javascript
浅谈angularjs module返回对象的坑(推荐)
Oct 21 Javascript
js指定步长实现单方向匀速运动
Jul 17 Javascript
使用express搭建一个简单的查询服务器的方法
Feb 09 Javascript
angular6根据environments配置文件更改开发所需要的环境的方法
Mar 06 Javascript
解决layer弹出层自适应页面大小的问题
Sep 16 Javascript
js实现飞机大战游戏
Aug 26 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获取当前url路径的函数以及服务器变量
2013/06/29 PHP
PHP批量删除jQuery操作
2017/07/23 PHP
关于javascript中this关键字(翻译+自我理解)
2010/10/20 Javascript
判断用户的在线状态 onbeforeunload事件
2011/03/05 Javascript
遍历jquery对象的代码分享
2011/11/02 Javascript
使用jQuery同时控制四张图片的伸缩实现代码
2013/04/19 Javascript
判断js中各种数据的类型方法之typeof与0bject.prototype.toString讲解
2013/11/07 Javascript
写JQuery插件的基本知识
2013/11/25 Javascript
利用js正则表达式验证手机号,email地址,邮政编码
2014/01/23 Javascript
在JavaScript中重写jQuery对象的方法实例教程
2014/08/25 Javascript
jQuery实现隔行背景色变色
2014/11/24 Javascript
使用js画图之圆、弧、扇形
2015/01/12 Javascript
js实现圆盘记速表
2015/08/03 Javascript
js带缩略图的图片轮播效果代码分享
2015/09/14 Javascript
javascript图片延迟加载实现方法及思路
2015/12/31 Javascript
js调用webservice构造SOAP进行身份验证
2016/04/27 Javascript
jQuery扩展+xml实现表单验证功能的方法
2016/12/25 Javascript
使用JavaScript判断用户输入的是否为正整数(两种方法)
2017/02/05 Javascript
webpack配置打包后图片路径出错的解决
2018/04/26 Javascript
vue项目中添加单元测试的方法
2018/07/21 Javascript
分享vue里swiper的一些坑
2018/08/30 Javascript
vue axios基于常见业务场景的二次封装的实现
2018/09/21 Javascript
vue实现顶部菜单栏
2020/11/08 Javascript
[03:09]2014DOTA2国际邀请赛 赛场上的美丽风景线 中国Coser也爱DOTA2
2014/07/20 DOTA
Python3下错误AttributeError: ‘dict’ object has no attribute’iteritems‘的分析与解决
2017/07/06 Python
Python实现连接两个无规则列表后删除重复元素并升序排序的方法
2018/02/05 Python
django orm 通过related_name反向查询的方法
2018/12/15 Python
Python中类的创建和实例化操作示例
2019/02/27 Python
Python中socket网络通信是干嘛的
2020/05/27 Python
html5实现图片转圈的动画效果——让页面动起来
2017/10/16 HTML / CSS
西班牙购买行李箱和背包网站:Maletas Greenwich
2019/10/08 全球购物
村官个人总结范文
2015/03/03 职场文书
违纪开除通知书
2015/04/25 职场文书
运动会通讯稿100字
2015/07/20 职场文书
2016新年晚会开场白
2015/12/03 职场文书
简单总结SpringMVC拦截器的使用方法
2021/06/28 Java/Android