前言

vue3的 Proxy 最近貌似各大网红公众号都有发,我也来蹭蹭热度写一篇吧!我们也可以结合vue2来看看vue3到底发生了些什么变化,又解决了Vue2.x的哪些痛点。接下来我们一起看看~

目录结构

  • Proxy是什么?

    • 简单用法
  • 尝试案例
    • proxy - target 参数
  • Proxy - handler 参数
    • get()
    • set()
    • handler
  • 什么叫做数据双向绑定?
  • 简单实现数据渲染
  • Proxy实现双向绑定
  • 回顾 Vue2 双向绑定实现
  • Proxy 解决了Vue2的哪些痛点
  • Proxy 的缺陷
  • 延伸阅读

Proxy是什么?

Proxy 翻译过来就是代理的意思,何为代理呢?就是 用 new 创建一个目标对象(traget)的虚拟化对象,然后代理之后就可以拦截JavaScript引擎内部的底层对象操作;这些底层操作被拦截后会触发响应特定操作的陷阱函数。

简单用法

const p = new Proxy(target, handler)

target

要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

handler

一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。


尝试案例

讲得再多,看得再多,不如写写再说 

Proxy - target 参数

// 定义一个空对象let data = {};// 创建一个 Proxy , 将 data 作为目标对象let proxy = new Proxy(data, {});// 修改Proxy 代理对象的name属性proxy.name = '严老湿';console.log(proxy); // { name: '严老湿' }console.log(data); // { name: '严老湿' }

看了上面的案例,现在的你应该已经大概知道这个 Proxy 的目标对象(target)是怎么使用的了

Proxy  - handler 参数

handler 单独抽离出来作为一个大标题是因为里面的内容有点多

handler

handler 对象是一个容纳一批特定属性的占位符对象。它包含有 Proxy 的各个捕获器(trap)。它里面的参数有太多了,我们就拿会用到几个讲讲吧!有像深究的同学可以去看看文档 Proxy handler [1]

参数 作用
handler.get() 属性读取操作的捕捉器。
handler.set() 属性设置操作的捕捉器。

handler.set

handler.set() 方法用于拦截设置属性值的操作。

文档上面呢基本上就是这样写的

// 定义一个对象let data = {  name: "严老湿",  age: '24'};// handler 抽离出来let handler = {  set(target, prop, value) {    console.log(target, prop, value)  }}let p = new Proxy(data, handler);p.age = 18;

个人习惯直接这样写

// 定义一个对象let data = {  name: "严老湿",  age: '24'};// 创建一个 Proxy , 将 data 作为目标对象let p = new Proxy(data, {  set(target, prop, value) {    // target = 目标对象    // prop = 设置的属性    // value = 修改后的值    console.log(target, prop, value)    // { name: '严老湿', age: '24' } 'age' 18  }});// 直接修改p就可以了p.age = 18;console.log(data)// { name: '严老湿', age: '24' }

在我们设置值的时候会触发里面的 set 方法;

我们已经捕捉到修改后的 属性 以及 他的值

但是打印data 并没有发生任何变化,那这还有啥用呢?

请看官方示例handler.set()[2]

在示例中出现了一个 Reflect.set()[3]

return Reflect.set(...arguments);

Reflect

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API  [4]

我们需要在 handler.set()return 一个 Reflect.set(...arguments) 来进行赋值给目标对象。

Reflect.set

Reflect.set方法设置target对象的name属性等于value。如果name属性设置了赋值函数,则赋值函数的this绑定receiver

Reflect.get

Reflect.get方法查找并返回target对象的name属性,如果没有该属性,则返回undefined

let data = {  name: "严老湿",  age: '24'};

let p = new Proxy(data, {  set(target, prop, newV) {    // target = 目标对象    // prop = 设置的属性    // newV = 修改后的值    return Reflect.set(...arguments)  }});p.age = 18;console.log(data)// { name: '严老湿', age: 18 }

就像这样,已经打印成功了

handler.get

刚刚我们已经将 set 理解的已经差不多了,get还会难么?我们来看看

let data = {  name: "严老湿",  age: '24'};

let p = new Proxy(data, {  get(target, prop) {    // target = 目标对象    // prop = 获取的属性    console.log(target, prop)    // { name: '严老湿', age: '24' } 'age'    return Reflect.get(...arguments)    // 这里的 Reflect.get 我们在上面已经讲到了  }});// 获取console.log(p.age)// 24

数据响应式 ↓

图片来源公众号 自律神仙ScarSu

什么叫数据双向绑定?

当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。

上栗子:

html

<h2 class="app">h2>

js

// 获取元素let app = document.querySelector('.app');// 定义 datalet data = {    name: "严老湿",    age: 24};// 替换成data.age 此时我们页面上应该是有个24app.innerHTML = data.age;// 我们在这里修改 age data.age = 21;console.log(data);// {name: "严老湿", age: 21}

这样看确实没啥毛病

但是呢在 vue 中,我们在下面异步修改data中的值,页面上的值不应该是跟着一起变化的么?虽然data 对象已经发生变化,但是它并不能触发一些其他操作;我们来看看vue的双向绑定

<div id="Foo">    <h2>hello {{msg}}h2>    <input type="text" v-model="msg">div><script src="https://cdn.jsdelivr.net/npm/vue@2.6.12">script><script>let vm = new Vue({el: '#Foo',data: {msg: "严家辉"        }    });script>

我们现在对双向绑定有了一个基本的认知。

简单实现数据渲染

等会儿我们实现双向绑定,在此之前我们做一个数据渲染过程,也简单的了解一下其原理

因为内容有点多,所以讲解呢全部在注释里面

html><html lang="en"><head>    <meta charset="UTF-8">    <title>Documenttitle>    <script src="./src/index.js">script>head><body>    <div id="app">{{name}}         <h2>{{age}}h2>    div>    <script>let vm = new Reactive({// 挂载元素            el:"#app",data:{name:"严老湿",age:24            }        });script>body>html>

index.js

class Reactive{    // 接收参数    constructor(options){        this.options = options;        // data 赋值        this.$data = this.options.data;        // 挂载元素        this.el = document.querySelector(this.options.el)        // 调用 compile 函数        this.compile(this.el)    }    // 渲染数据    compile(el){        // 获取el的子元素        let child = el.childNodes;        // 遍历判断是否存在文本        [...child].forEach(node=>{            // 如果node的类型是TEXT_NODE            if(node.nodeType === 3){                // 拿到文本内容                let txt = node.textContent;                // 正则匹配{{}} 空格                let reg = /\{\{\s*([^\s\{\}]+)\s*\}\}/g;                if(reg.test(txt)){                    let $1 = RegExp.$1;                    this.$data[$1] && (node.textContent=txt.replace(reg,this.$data[$1]))                }            // 如果node的类型是ELEMENT_NODE            }else if(node.nodeType === 1){                // 递归执行                this.compile(node)            }        })    }}

一个简单并且潦草一点的的渲染数据功能已经完成了

Proxy实现双向绑定

html><html lang="en"><head>    <meta charset="UTF-8">    <title>Documenttitle>    <script src="./src/index.js">script>head><body>    <div id="app">{{name}}         <h2>{{age}}h2>        <input type="text" v-model="name">        {{name}}    div>    <script>let vm = new Reactive({// 挂载元素            el: "#app",data: {name: "严老湿",age: 24,            }        });script>body>html>

index.js

// EventTarget [6]class Reactive extends EventTarget {  // 接收参数  constructor(options) {    super();    this.options = options;    // data 赋值    this.$data = this.options.data;    // 挂载元素    this.el = document.querySelector(this.options.el);    // 调用 compile 函数    this.compile(this.el);    // 调用双向绑定    this.observe(this.$data);  }  // 双向绑定  observe(data) {    // 备份this    let _this = this;    // 接收目标对象进行代理    this.$data = new Proxy(data, {      set(target, prop, value) {        // 创建一个自定义事件 CustomEvent [5]        // 事件名称使用的是 prop         let event = new CustomEvent(prop, {          // 传入新的值          detail: value        })        // 派发 event 事件        _this.dispatchEvent(event);        return Reflect.set(...arguments);      }    })  }  // 渲染数据  compile(el) {    // 获取el的子元素    let child = el.childNodes;    // 遍历判断是否存在文本    [...child].forEach(node => {      // 如果node的类型是TEXT_NODE      if (node.nodeType === 3) {        // 拿到文本内容        let txt = node.textContent;        // 正则匹配        let reg = /\{\{\s*([^\s\{\}]+)\s*\}\}/g;        if (reg.test(txt)) {          let $1 = RegExp.$1;          this.$data[$1] && (node.textContent = txt.replace(reg, this.$data[$1]))          // 绑定自定义事件          this.addEventListener($1, e => {            // 替换成传进来的 detail            node.textContent = txt.replace(reg, e.detail)          })        }        // 如果node的类型是ELEMENT_NODE      } else if (node.nodeType === 1) {        // 获取attr         let attr = node.attributes;        // 判断是否存在v-model属性        if (attr.hasOwnProperty('v-model')) {          // 获取v-model中绑定的值          let keyName = attr['v-model'].nodeValue;          // 赋值给元素的value          node.value = this.$data[keyName]          // 绑定事件          node.addEventListener('input', e => {            // 当事件触发的时候我们进行赋值            this.$data[keyName] = node.value          })        }        // 递归执行        this.compile(node)      }    })  }}

这样我们就实现了一个双向绑定的小 demo ,当然代码还不够严谨,比如v-model的元素筛选都还不够完善,只是带大家简单的了解一下实现逻辑

回顾 Vue2 双向绑定实现

vue2 大部分同学刷题也经常会碰到 ,我们接下来看看vue2如何实现的呢!

一个超级简陋的双向绑定hhh,简单回顾一下就行了

html><html lang="en"><head>    <meta charset="UTF-8">    <title>title>head><body>    <h2 id="txt">h2>    <input type="text" id="el">    <script>let obj = {};// 获取节点let el = document.querySelector('#el');let txt = document.querySelector('#txt');Object.defineProperty(obj, 'foo', {set(newValue) {// 修改后的值 进行赋值                txt.innerHTML = newValue;            }        });// 绑定事件        el.addEventListener('input', e => {// 赋值给obj数据            obj.foo = e.target.value;        });script>body>html>

Proxy解决了vue2的哪些痛点

  • Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象;
  • Object.defineProperty对新增属性需要手动进行Observe
  • vue2.x无法监控到数组下标的变化,因为vue2放弃了这个特性;
  • Proxy支持13种拦截操作,这是 Objectd.defineProperty所不具有的;

Proxy的缺陷

其他的不想多说,就一个 IE 兼容真的挺难受的,硬伤了


延伸阅读

[1] https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler

[2] https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/set

[3] https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set

[4] https://www.cnblogs.com/zczhangcui/p/6486582.html

[5] https://developer.mozilla.org/zh-CN/docs/Web/API/CustomEvent

[6] https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget

数据双向绑定_手写 Vue3 数据双向绑定 理解Proxy相关推荐

  1. c++socket多个客户端通过不同端口与一个服务端通信_手写RPC,深入底层理解整个RPC通信...

    一.前言 RPC,远程过程调用,调用远程方法像调用本地方法一样.RPC交互分为客户端和服务端,客户端调用服务端方法,服务端接收数据并打印到控制台,并response响应给客户端. RPC和HTTP的联 ...

  2. 个人整理的数据集(手写中文数据、发票数据、快递单数据、车牌数据)

    本人在工作生活中收集了各个方面比较多的真实的数据集如下: 一.手写中文数据集 1.档案类数据: 此数据集为手写档案数据,数量较大,大约128G,图像均未标注.ex: 2.手写作文数据: 此数据集为手写 ...

  3. tensorflow应用:双向LSTM神经网络手写数字识别

    tensorflow应用:双向LSTM神经网络手写数字识别 思路 Python程序1.建模训练保存 Tensorboard检查计算图及训练结果 打开训练好的模型进行预测 思路 将28X28的图片看成2 ...

  4. 一起手写Vue3核心模块源码,掌握阅读源码的正确方法

    最近和一个猎头聊天,说到现在前端供需脱节的境况.一方面用人方招不到想要的中高级前端,另一方面市场上有大量初级前端薪资要不上价. 特别是用 Vue 框架的,因为好上手,所以很多人将 Vue 作为入门框架 ...

  5. 来一起造轮子:手写 Vue3 reactivity 模块

    最近和一个猎头聊天,说到现在前端供需脱节的境况.一方面用人方招不到想要的中高级前端,另一方面市场上有大量初级前端薪资要不上价. 特别是用 Vue 框架的,因为好上手,所以很多人将 Vue 作为入门框架 ...

  6. 来一起造轮子:手写 Vue3 reactivity 模块

    最近和一个猎头聊天,说到现在前端供需脱节的境况.一方面用人方招不到想要的中高级前端,另一方面市场上有大量初级前端薪资要不上价. 特别是用 Vue 框架的,因为好上手,所以很多人将 Vue 作为入门框架 ...

  7. 处理自己的数据集_手写代码实现KDD CUP99数据集的数据归一化处理

    归一化是数据处理的常用方法之一,目的是消除不同评价指标之间的量纲对数据分析结果的影响,使各指标处于同一数量级,以解决数据指标之间的可比性问题. 目前学术界关于归一化和标准化的概念还不统一,常常会把这两 ...

  8. 卷积神经网络mnist手写数字识别代码_搭建经典LeNet5 CNN卷积神经网络对Mnist手写数字数据识别实例与注释讲解,准确率达到97%...

    LeNet-5卷积神经网络是最经典的卷积网络之一,这篇文章就在LeNet-5的基础上加入了一些tensorflow的有趣函数,对LeNet-5做了改动,也是对一些tf函数的实例化笔记吧. 环境 Pyc ...

  9. 全站最详细的Python numpy 搭建全连接神经网络模型教程(理论计算+代码实现)(不止能预测手写数字数据,准确率93.21%)

    1.引言 本文构建的全连接神经网络模型结构图如上.其中中间隐藏层的数量以及各层(输入层.隐藏层.输出层)的神经单元数量均可 自由设置,本文构造的神经网络并不是专门为识别手写数字而写死的,而是可以根据 ...

最新文章

  1. R使用lm构建多变量线性回归模型
  2. python 逻辑回归
  3. RabbitMQ 交换器、持久化
  4. Jenkins学习七:Jenkins的授权和访问控制
  5. 数据竞赛入门-金融风控(贷款违约预测)三、特征工程
  6. 【Flink】Flink 报错 Initial AbstractPagedOutputView Segment may not be null
  7. python 爬虫生成csv文件和图_python爬虫系列(4.2-python操作csv文件)
  8. Nginx 代理设置
  9. 【Qt】仿QQ表情选择控件
  10. 基于Stacey模型的变革小招式:强有力的提议
  11. mysql索引失效怎么办,跳槽薪资翻倍
  12. 微信小程序(微信支付回调函数)
  13. 如何在RK3588上面使用摄像头实时实现物体识别?
  14. 从零开始实现一个量化回测系统(一)
  15. 浅谈Jsch SSH2
  16. Android 10 根文件系统和编译系统(十八):Android.bp语法
  17. jitpack.io 错误提示 handshake_failure 开发
  18. 33岁的程序员,还在一线编码,该怎么办?
  19. Linux 查看系统相关信息命令
  20. lay和lied_怎么区分“lie”,“lie”,“lay”这三个的意思和过去式?

热门文章

  1. Python对象特殊方法及其用法演示
  2. 10a 16a 插座区别_电动汽车小知识(NO·5):电动汽车能否用家里的插座进行充电?...
  3. excel打开空白不显示内容 没有隐藏_这些Excel快捷键你都会吗?
  4. 用js实现一个无限循环的动画
  5. 计算机环形拓扑结构教案,面试真题高中信息技术《网络拓扑结构》教学设计...
  6. 通用计算机和嵌入式专用计算机例子,什么是嵌入式技术?
  7. 柔性太阳能电池pdf_电子科大刘明侦教授团队钙钛矿太阳能电池研究取得重要进展...
  8. android button layoutparams,使用LayoutParams设置布局
  9. linux mkdir 系统调用,Linux Rootkit 系列四:对于系统调用挂钩方法的补充
  10. python调用opencv的速度_OpenCV-Python系列之OpenCV性能衡量与优化方法