用proxy实现一个更优雅的vue

用proxy实现一个更优雅的vue

前言

如果你有读过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-bindv-modelv-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上还有其他一些关于前端的教程和组件,有兴趣的童鞋可以看看,你们的支持就是我最大的动力。

原文发布时间为:2018年06月15日

原文作者:noahlam


本文来源: 
掘金 
如需转载请联系原作者



版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/101602.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 机器学习模型中的损失函数loss function

    机器学习模型中的损失函数loss functionimportmatplotlib.pyplotaspltimportnumpyasnpxmin,xmax=-4,4xx=np.linspace(xmin,xmax,100)plt.plot([xmin,0,0,xmax],[1,1,0,0],’k-‘,label=”Zero-oneloss”)plt.plot(xx,np.where(x

  • SeasLog 与 monolog 日志系统的区别,SeasLog安装步骤[通俗易懂]

    SeasLog 与 monolog 日志系统的区别,SeasLog安装步骤

  • 死人现在在做死事「建议收藏」

    死人现在在做死事「建议收藏」现在和死人没什么差别了,今晚要对6000条数据进行整理,对这些数据进行分类本来应该是属于一个商业智能分类的事情,可公司里没一个会数据挖掘的,都对这堆数据没有办法,现在只能靠我们这些死人来死扛着,人工在对它进行分类,看来真是要得去西天看米罗佛去了看下这个数据,吓死你们客户来电反映本机在7811145/03…

  • WIN10系统开机一个WIFI都找不到,网络适配器里没有WLAN驱动,连接不了网络问题【耗时3天测试10多种方法】

    WIN10系统开机一个WIFI都找不到,网络适配器里没有WLAN驱动,连接不了网络问题【耗时3天测试10多种方法】目录1.前言2.出现的问题情况2.1找不到WIFI,连不了网。2.2网络适配器里没有WLAN选项3.解决方法(推荐按顺序进行)。1.先检查WLAN是否存在2.修改服务设置3.直接重启4.通过驱动软件修复电脑网络5.通过ccleaner修复注册表6.进入bios系统修改wireless设置7.重装系统(最好不要)8.博主问题的解决1.前言最近每天打开电脑时都会出现这样的情况,每次折腾半天,有时候运气好重启一下就…

  • 使用nginx实现动静分离「建议收藏」

    使用nginx实现动静分离「建议收藏」一、什么是动静分离动静分离是指在web服务器架构中,将静态页面与动态页面或者静态内容接口和动态内容接口分开不同系统访问的架构设计方法,进而提升整个服务访问性能和可维护性。nginx的动静分离,指的是由nginx将客户端请求进行分类转发,静态资源请求(如html、css、图片等)由静态资源服务器处理,动态资源请求(如jsp页面、servlet程序等)由tomcat服务器处理,tomcat本身是用来处理动态资源的,同时tomcat也能处理静态资源,但是tomcat本身处理静态资源的

  • web容器和web服务器区别关系_java中间件有哪些

    web容器和web服务器区别关系_java中间件有哪些目录Web服务器中间件容器总结Web开发的选择我们经常会被Web服务器、Web容器和Web中间件这三个概念搞混。因为我们常见的很多网站要么是由IIS搭建,要么是由Apache、Tomcat、Ngnix搭建。所以,我们会把他们都叫成是Web服务器,因为他们都提供了Web服务,可以让我们通过http协议访问到。但是又有很多地方会说Tomcat是Servlet容器,而又有很多地方…

    2022年10月27日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号