导言: 本菜鸡在Vue2没多久,结果就Vue3发布了。赶紧学习和体验了一番Vue3,发现和Vue2有较大不同。其中最让我印象深刻的是他们有一个叫ref和reactive的用来绑定和更新数据。然后再略微调查原理之后发现Vue3是用的Proxy来实现数据绑定。我之前对JS语法的Proxy基本完全不懂,正好趁这个机会,深入理解一下Proxy和Vue3的数据绑定。希望对于同样是初学者的你,看完我的文章,能对这两部分知识有更深入的认识。

数据绑定

我们进行网页设计,除了需要设计各个页面组件之外,另外最重要的部分应该就属于数据交互了。经常我们要面对的需求是获取用户的数据或者将一些处理过的数据发送给用户。比如我们用原生代码展示一下上将数据发送给用户:

<p></p>
const data = { value: 'hello' }
document.querySelector('p').innerText = data.value;

等到我们使用Vue之后,Vue引入了一种设计模式称为MVVM,我们不再和DOM直接进行交互。

MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。


上图是一个经验的MVVM的示意图,我们在使用Vue的时候,只设计UI以及数据处理的Model。如何将数据渲染在页面上,我们不再关心了。交给Vue来处理。

可以再看一下,我们最开始举得简单代码的例子。之前我们是View和Model直接进行通信的,现在多了一个ViewModel层。就像假设你之前出入小区可以随便出入。现在因为疫情,门口多了一个站岗的保安。你能不能出入不再是由你决定了,而是由保安决定。这样的保安或者ViewModel,可以称之为一种代理。

类似于交易不是双方直接达成,而是要经过中间商才能达成交易。类似于你们之前是直接现金交易,现在都需要用支付宝转到对方账户才行。支付宝就是这样的中间商或者称为代理。有了代理就可以设置一些处理规则,或者拦截等。比如支付宝可以定义一种代理规则:只要是通过我转账到对方账户,我就要收取1%的手续费。

Proxy

我们理解了代理这个概念之间,下面就可以开始讲Proxy。首先看一下MDN关于Proxy的定义:

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)

总之,Proxy就是干代理的事。就是前文讲的那个保安或者支付宝。

关于Proxy最简单的讲解,我就不去贴MDN的内容了,如果对Proxy具体语法完全不了解请移步Proxy,Proxy的handler对象方法虽然很多,但最最常用的还是get和set。如果完全不了解Proxy的,别的对象方法可以暂时不看,一定要把这两个方法看一下。

下面我举一些Proxy的通俗例子,并逐渐过渡到Vue3的Proxy中。

首先举一个简单的例子:

let o = {name: 'xiaoming',age: 20
}let handler = {get(obj, key) {return key in obj ? obj[key] : ''}
}let p = new Proxy(o, handler)console.log(p.from)

这个代码是想表达如果 o 对象有这个 key-value 则直接返回,如果没有一律返回空。这里的p的结果就相当于是一个代理过后的结果了,o的数据不是直接传给p,而是经过经过handler代理逻辑之后才传过去的。对于对象o而言,如果直接访问o.from会报错。对于p而言,因为中间有一个代理程序帮我们处理了一下,所以不会报错,而是返回空。

通过上面的例子,我们初步了解了Proxy的代理特性,下面我们来看一下更复杂一点的例子。

let user = {name: 'xiaoming',age: 20,_password: '***'
}
user = new Proxy(user, {get(target, prop) {if (prop.startsWith('_')) {throw new Error('不可访问')} else {return target[prop]}},set(target, prop, val) {if (prop.startsWith('_')) {throw new Error('不可访问')} else {target[prop] = valreturn true}},deleteProperty(target, prop) { // 拦截删除if (prop.startsWith('_')) {throw new Error('不可删除')} else {delete target[prop]return true}},ownKeys(target) {return Object.keys(target).filter(key => !key.startsWith('_'))}
})
console.log(user.age)
console.log(user._password)
user.age = 18
console.log(user.age)
try {user._password = 'xxx'
} catch (e) {console.log(e.message)
}try {// delete user.agedelete user._password
} catch (e) {console.log(e.message)
}
console.log(user.age)for (let key in user) {console.log(key)
}

大家可以执行一下代码看看输出效果。关于Proxy的基本介绍就先到这里,下面我们来看看Vue3中是如何使用Proxy实现数据绑定的。

Vue3中的Proxy

在Vue3中我们使用ref和reactive来实现数据绑定。更改ref或者reactive实例化对象的值,会导致页面中的数据也发生对应更改。之所以能够实现更改ref或者reactive对应的值使得页面的值发生对应变化是因为我们实际操作的数据是经过Proxy代理过后的,而在Proxy代理中,我们有具体定义页面数据刷新的代码。我用代码举一个很简单的例子:

let obj = {name: 'xiaoming', age: 20}
let state = new Proxy(obj, {get (obj, key) {return obj[key]},set (obj, key, value) {obj[key] = valueconsole.log('更新UI界面')// set方法必须通过返回值告诉Proxy此次操作是否成功return true}
})console.log(state.name)
state.age = 22

你可以执行一下上述代码看看执行效果。当我们去设置一个值的时候,就会具体执行更能UI界面的操作。通过Proxy就可以实现ref和reactive的页面更新和数据绑定,这也是ref和reactive背后的基本原理。

我们下面来谈谈reactive和ref的Proxy具体实现。

我们在使用Vue3的时候,通常使用ref来监听一些简单数据类型如数字、字符串、布尔等。你可能注意到一种现象,是更改ref的值的时候,需要使用ref.value的形式,(在template中使用ref时,Vue自动帮我们添加.value了,所以不需要我们手动添加),看起来很神奇,为什么会有一个.value呢?是因为ref本质上是一个包装过后的reactive。在你去定义ref的时候,Vue会内部帮我们包装一下。

let age = ref(18)
// 等价于
let age = reactive({value: 18})

对于reactive,使用Proxy来完成对象监听要更复杂一些,因为对象里面可能还嵌套对象,比如下面这种形式的对象:

let arr = [{id:1, name: '鲁班'}, {id:2, name: '虞姬'}]
let obj = {a:{id:1, name: '鲁班'}, b:{id:2, name: '虞姬'}}

所以我们需要对每一层的对象都监听到,才能做到当数据发生变化时,实现对应的更新。要实现一层一层的监听,往往就会想到使用递归来实现。下面是代码演示:

function reactive(obj) {if (typeof obj === 'object') {if (obj instanceof Array) {// 如果是一个数组,则取出数组中每一个元素,// 判断每一个元素是否是一个对象,如果又是一个对象,则也需要包装成Proxyobj.forEach((item, index) => {if (typeof item === 'object') {obj[index] = reactive(item)}})} else {// 如果是一个对象,则取出对象中的每一个属性,// 判断属性是否又是一个对象,如果又是一个对象,则也需要包装成Proxyfor (let key in obj) {let item = obj[key]if (typeof item === 'object') {obj[key] = reactive(item)}}}return new Proxy(obj, {get (obj, key) {return obj[key]},set (obj, key, val) {obj[key] = valconsole.log('更新UI界面')return true}})} else {console.warn(`${obj} is not object`)}
}

你可以执行一些测试看看效果,看看会输出什么。

let state = reactive(arr)
state[0].name = 'zhangsan'
state[1].id = 3

对于ref而言,因为ref是包装过的reactive,所以使用reactive就可以定义ref:

function ref(val) {return reactive({value: val})
}
参考资料

[1] Vue双向数据绑定
[2] Vue面试题总结
[3] Proxy
[4] Vue3 的 Proxy 和 defineProperty 的比较
[5] Vue3.0教程

从Proxy到Vue3数据绑定相关推荐

  1. vue3数据绑定显示列表数据局

    文章目录 一.第一种ref 1. 案例代码 2.页面使用 二.第一种reactive,toRef 2.1. 案例代码 2.2. 页面使用 一.第一种ref 1. 案例代码 <script lan ...

  2. Vue2和Vue3的双向数据绑定原理

    目录 前言: vue2.x 是如何实现响应式系统的: defineProperty 的痛点: Object.defineProperty 代码的使用 Proxy 方法的理解 Proxy 代码的使用: ...

  3. 【Vue知识点】——Vue2和Vue3的数据劫持

    文章目录 数据劫持 为什么在data中定义的数据会自动携带get.set属性方法呢? 数据劫持 data与_data Object.DefineProperty Object.DefineProper ...

  4. Javascript基础:代理器(proxy)

    一.proxy的概念 Proxy 可以理解成,在目标对象之前架设一层"拦截",外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写.Prox ...

  5. 基于Object.defineProperty实现双向数据绑定

    双向数据绑定可算是前端领域经久不衰的热词,不管是前端开发还是面试都会有所涉及.而且不同的框架也想尽一切办法去实现这一特性,比如: Knockout / Backbone --- 发布-订阅模式 Ang ...

  6. proxy connect abort处理方法_Vue 3.0 初探 - Proxy

    前言 4 月 17 日,尤大在微博上宣布 Vue 3.0 beta 版本正式发布. 在尤大发布的< Vue3 设计过程>文章中提到之所以重构 Vue 一个考量就是JavaScript新的语 ...

  7. vue3+ElementPlus后台管理搭建

    搭建一个vite项目 https://vitejs.cn/guide/#scaffolding-your-first-vite-project 文章有点长,有写的不对的地方,请各位大佬指点,有问题一起 ...

  8. Object.defineProperty 和 Proxy 的区别

    Object.defineProperty是一个用来定义对象的属性或者修改对象现有的属性的函数,,而 Proxy 是一个用来包装普通对象的对象的对象. 张子俊改 Object.defineProper ...

  9. 原生 遍历_细品原生JS从初级到高级知识点汇总(三)

    作者:火狼1 转发链接:https://juejin.im/post/5daeefc8e51d4524f007fb15 目录 细品原生JS从初级到高级知识点汇总(一) 细品原生JS从初级到高级知识点汇 ...

最新文章

  1. 到2030年AI会变成怎样?专家给出10大预测
  2. 教你如何成为数据科学家(六)
  3. DCMTK:DCMTK文档
  4. Python-opencv在线帮助
  5. Redis操作Set类型
  6. IDEA 实用功能Auto Import:自动优化导包(自动删除、导入包)
  7. VBA 打开文件对话框
  8. 一、Arcgis api js -- 基本概念
  9. java murmurhash实现_一致性哈希算法与Java实现
  10. java 抽象类、接口使用
  11. 【【★★★★★★CSS兼容IE6,IE7,FF的技巧 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★】】...
  12. 微型计算机原理偏移地址,微机原理及应用复习
  13. oracle学习札记94
  14. git mertool使用kdiff3解决冲突合并
  15. html5学生dw网页设计大作业,hbuilder华谊网页设计成品模板,静态网页设计定制
  16. python模拟浏览器访问企查查_python爬虫另辟蹊径绕过企查查的登录验证,我太冇财了...
  17. 世界各国大城市GDP排名
  18. echarts 实现柱状图左右横向对比显示
  19. 网站建设SEO推广说明
  20. AngularJS 双向绑定 input type='file'中文件名,文件内容

热门文章

  1. [译] 最佳安全实践:在 Java 和 Android 中使用 AES 进行对称加密
  2. iOS多线程编程:线程同步总结 NSCondtion
  3. [repost]Xcode因为证书问题经常报的那些错
  4. 移动网页设计9大原则——第1部分
  5. JS函数重载解决方案
  6. 获取内容第一张图片地址的函数
  7. Hadoop多次format格式化会导致节点的clusterID不一致
  8. Google BERT模型提取句子Token特征
  9. Leetcode 113. 路径总和 II 解题思路及C++实现
  10. Android应用小工具(窗口小部件)