Vue2和Vue3数据双向绑定原理的区别及优缺点(下篇)

2023-05-16

上篇我们讲到了Vue2的数据双向绑定原理,如果你没有阅读上篇,建议先阅读一下上篇中的内容:

Vue2和Vue3数据双向绑定原理的区别及优缺点(上篇)

在上篇中我们抛出了一个问题:是不是Vue3中的数据双向绑定原理就优于Vue2的?我们也给大家透露了,并不是这样,是各有利弊的,那么本篇我们就带着这个问题,来感受一下Vue3中的数据双向绑定原理是如何实现的。

一、Vue3数据双向绑定原理的实现

Vue3中最主要的改变就是将Object.defineProperty()替换成为Proxy对象,可以原生支持到数组的响应式,不需要重写数组的原型,还可以直接支持新增和删除属性,比Vue2的Object.defineProperty更加的清晰明了。

那么什么是Proxy,通过查阅MDN我们知道Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。什么意思呢?Proxy 就像一个拦截器一样,它可以在读取对象的属性,修改对象的属性,获取对象属性列表,通过for in循环等等操作的时候,去拦截对象上面的默认行为,然后自己自定义这些行为。以下为Proxy的语法格式:

let proxy = new Proxy(target, handler)

/**

* target: 要兼容的对象,可以是一个对象,数组,函数等等

* handler: 是一个对象,里面包含了可以监听这个对象的行为函数,

* 同时会返回一个新的对象proxy, 为了能够触发handler里面的函数,必须要使用返回值去进行其他操作,比如修改值

*/

那么Vue3是如何实现数据双向绑定原理的呢,其实核心代码非常的少,并且主要是3版本的数据劫持的方式由Object.defineProperty更改为Proxy代理。

具体代码实现过程如下:

定义构造函数

function Vue(option){

this.$el = document.querySelector(option.el); //获取挂载节点

this.$data = option.data;

this.$methods = option.methods;

this.deps = {}; //所有订阅者集合 目标格式(一对多的关系):{msg: [订阅者 1, 订阅者2, 订阅者3], info: [订阅者1, 订阅者2]}

this.observer(this.$data); //调用观察者

this.compile(this.$el); //调用指令解析器

}

定义指令解析器

Vue.prototype.compile = function (el) {

let nodes = el.children; //获取挂载节点的子节点

for (var i = 0; i < nodes.length; i++) {

var node = nodes[i];

if (node.children.length) {

this.compile(node) //递归获取子节点

}

if (node.hasAttribute('l-model')) { //当子节点存在l-model指令

let attrVal = node.getAttribute('l-model'); //获取属性值

node.addEventListener('input', (() => {

this.deps[attrVal].push(new Watcher(node, "value", this, attrVal)); //添加一个订阅者

let thisNode = node;

return () => {

this.$data[attrVal] = thisNode.value //更新数据层的数据

}

})())

}

if (node.hasAttribute('l-html')) {

let attrVal = node.getAttribute('l-html'); //获取属性值

this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //添加一个订阅者

}

if (node.innerHTML.match(/{{([^\{|\}]+)}}/)) {

let attrVal = node.innerHTML.replace(/[{{|}}]/g, ''); //获取插值表达式内容

this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //添加一个订阅者

}

if (node.hasAttribute('l-on:click')) {

let attrVal = node.getAttribute('l-on:click');//获取事件触发的方法名

node.addEventListener('click', this.$methods[attrVal].bind(this.$data)); //将this指向this.$data

}

}

}

定义观察者

Gao.prototype.observer = function (data) {

const that = this;

for (var key in data) {

that.deps[key] = []; //初始化所有订阅者对象{msg: [订阅者], info: []}

}

let handler = {

get(target, property) {

return target[property];

},

set(target, key, value) {

let res = Reflect.set(target, key, value);

var watchers = that.deps[key];

watchers.map(item => {

item.update();

});

return res;

}

}

this.$data = new Proxy(data, handler);

}

定义订阅者

function Watcher(el, attr, vm, attrVal) {

this.el = el;

this.attr = attr;

this.vm = vm;

this.val = attrVal;

this.update(); //更新视图

}

更新视图

Watcher.prototype.update = function () {

this.el[this.attr] = this.vm.$data[this.val]

}

二、两者的区别及优缺点

通过上面的分析,我们也应该了解,两者的区别主要是Vue3的数据劫持的方式由Object.defineProperty更改为Proxy代理。那么他们两者的优点和缺点分别是什么呢?

Object.defineProperty的优点

兼容性好,支持IE9,为什么这么说?只需要继续往下看,看proxy的劣势我们就能明白Object.defineProperty的优点。

Object.defineProperty的缺陷

1、无法检测到对象属性的新增或删除

Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。其只能监听对象已有数据是否被修改,但是不能监听新增属性和删除属性。

2、不能监听数组的变化

我们在Vue的官方文档中可以看到这么一段话:

Vue在实现数组的响应式时,把无法监听数组的情况通过重写数组的部分方法来实现响应式,但是只局限在以下7种方法:

push/pop/shift/unshift/splice/sort/reverse,其他数组方法及数组的使用则无法检测到。

那么怎么解决以上问题呢?答案就是Vue3的proxy。

Proxy

proxy属性是ES6中新增的一个属性,它也是一个构造函数,可以通过new关键字来创建函数。表示修改某些操作的默认行为,等同于在语言层面做出修改。

proxy可以理解为在目标对象之前架设一层拦截,外界对该对象的访问,都必须经过这层拦截,因此提出了一种机制,可以对外界的网文进行过滤和改写,proxy这个词是代理的意思,用来表示由他代理某些操作,可以译为代理器。

Proxy的优点

1、proxy直接代理的是整个对象而不是代理对象属性。

2、可以监听同级结构下的所有属性变化,包括新增的属性和删除的属性,也可以监听数组的变化。

Proxy代理目标对象,是通过操作下面表格中的13种trap来完成的,这与ES6提供的另一个api即Reflect的13种静态方法一一对应。二者一般是配合使用的,在修改proxy代理对象时,一般也需要同步到代理的目标对象上,这个同步就是用Reflect对应方法来完成的。

trap

描述

handler.get

获取对象的属性时拦截

handler.set

设置对象的属性时拦截

handler.has

拦截propName in proxy的操作,返回boolean

handler.apply

拦截proxy实例作为函数调用的操作,proxy(args)、proxy.call(...)、proxy.apply(..)

handler.construct

拦截proxy作为构造函数调用的操作

handler.ownKeys

拦截获取proxy实例属性的操作,包括Object.getOwnPropertyNames、Object.getOwnPropertySymbols、Object.keys、for...in

handler.deleteProperty

拦截delete proxy[propName]操作

handler.defineProperty

拦截Objecet.defineProperty

handler.isExtensible

拦截Object.isExtensible操作

handler.preventExtensions

拦截Object.preventExtensions操作

handler.getPrototypeOf

拦截Object.getPrototypeOf操作

handler.setPrototypeOf

拦截Object.setPrototypeOf操作

handler.getOwnPropertyDescriptor

拦截Object.getOwnPropertyDescriptor操作

Proxy的劣势

最主要的就是兼容性问题,虽然proxy相对于object.defineProperty有很有优势,但是并不是说proxy就是完全的没有劣势,主要表现在以下的两个方面:

1、proxy有兼容性问题,无完全的polyfill:

proxy为ES6新出的API,浏览器对其的支持情况可在w3c规范中查到,通过查找我们可以知道,虽然大部分浏览器支持proxy特性,但是一些浏览器或者低版本不支持proxy,因此proxy有兼容性问题,那能否像ES6其他特性有polyfill解决方案呢?,这时我们通过查询babel文档,发现在使用babel对代码进行降级处理的时候,并没有合适的polyfill。

2、第二个问题就是性能问题:

proxy的性能其实比promise还差,这就需要在性能和简单实用上进行权衡,例如Vue3使用proxy后,其对对象及数组的拦截很容易实现数据的响应式,尤其是数组。虽然proxy有性能和兼容性处理,但是proxy作为新标准将受到浏览器厂商重点持续的性能优化,性能这块相信会逐步得到改善。

总结

到这为止,咱们的Vue2与Vue3的数据双向绑定原理就总结完毕了,强烈建议没有阅读过上篇的同学先去看看上篇中所说内容,通过对比两篇文章咱们发现这两种方式还是各有优劣的,但是咱们发现Vue3的缺点其实是软性缺点,是可以通过后期优化去逐步完善的,所以咱们可以完全相信在不久的将来,Vue2的数据双向绑定原理的实现终将被Vue3所替代。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue2和Vue3数据双向绑定原理的区别及优缺点(下篇) 的相关文章

随机推荐

  • QML自定义的日历控件

    QML中提供了日历的控件Calendar xff0c 但该控件为QtQuick Controls 1中提供的控件 xff0c 因此只能使用QtQuick Controls Styles的方式对该控件进行设置 xff0c 效果如图 xff1a
  • QML地图Map中使用QPainterPath,并显示任意点经纬度位置

    QML地图Map中提供了供绘制图形的组件 xff0c 例如MapPolyline xff0c MapCircle等 xff0c 但是这些组件在绘制复杂轨迹时就显得功能不够全面 xff0c 因此我将QPainterPath在Map中进行使用并
  • QML地图绘制虚线

    QML提供了MapPolyline用于在地图上绘制线段 xff0c 该线段是实线 xff0c 因此我使用Canvas自定义绘制的方式在地图上绘制线段 xff0c 如图 xff1a 鼠标在地图上点击后 xff0c 在点击位置添加图标 xff0
  • QML无边框最大化窗口时遮住了任务栏,程序默认置顶的问题

    在QML窗口使用无边框 xff0c 并且默认程序最大化显示后 xff0c 此时程序显示默认自动变成了全屏显示 xff0c 程序自动遮挡住了系统任务栏 当使用多屏幕显示时 xff0c 切换不同程序 xff0c 该QML程序的界面显示错误 使用
  • 用VScode写C/C++,从下载安装到配置使用

    介绍 编程的过程大致分为编写代码 代码编译 代码执行三步 xff0c vscode可以完成代码编写 xff0c 但是不能进行编译 也就是将代码翻译为计算机可以听懂的话 xff0c MinGW可以完成这个任务 xff0c 二者配合可以实现在v
  • 深度学习训练降低显存指南

    一 小模块API参数inplace设置为True xff08 省一点点 xff09 比如 xff1a Relu 有一个默认参数inplace xff0c 默认设置为False xff0c 当设置为True时 xff0c 计算时的得到的新值不
  • C++工程师学习内容

    C 43 43 是最贴近底层编程语言 在性能方面上 xff0c 有着无可替代的优势特别是对于很多游戏开发公司来说 xff0c C 43 43 尤其适合作为后端服务的开发语言 在一些对于并发性能要求较高的业务上 xff0c C 43 43 也
  • Ubuntu 更新apt出错

    输入sudo apt get update后出现 Err 1 http us archive ubuntu com ubuntu xenial InRelease Temporary failure resolving 39 us arch
  • 使用OpenWrt开发嵌入式Linux(二):先让系统跑起来(使用initramfs)

    安装相关工具 推荐使用ubuntu 16及以上版本 sudo apt install gcc binutils bzip2 flex python perl make diffutils unzip gawk subversion zlib
  • 使用kubeadm从0到1搭建kubernete集群

    目录 概述 安装前提示 安装docker 安装kubeadm 安装kubernete集群master节点 安装 kubeadm kubectl kubelet组件 安装kubernete master节点 安装CNI网络插件 部署集群wor
  • shell基础之变量(2):变量有哪些种类、怎么定义/赋值/取值、不同种类变量的作用域

    通过本文能对shell变量有一个系统性的了解 xff0c 具体的包括 xff1a 变量的种类 xff1a 局部 全局 环境变量变量的定义和操作 xff1a 赋值 取值 取消变量变量的作用域 文章目录 一 变量的种类1 全局变量2 局部变量
  • java 泛型全解 - 绝对最详细

    背景 对于java的泛型我一直属于一知半解的 xff0c 平常真心用的不多 直到阅读 Effect Java 看到很多平常不了解的用法 xff0c 才下定决心 xff0c 需要系统的学习 xff0c 并且记录下来 1 泛型的概述 xff1a
  • Zookeeper数据同步流程

    在服务器启动阶段 xff0c 会进行磁盘数据的恢复 xff0c 完成数据恢复后就会进行Leader选举 一旦选举产生Leader服务器后 xff0c 就立即开始进行集群间的数据同步 xff0c 在整个过程中 xff0c Zookeeper都
  • JS中Ajax的方法和应用

    XMLHttpRequest对象 Ajax技术的核心是XMLHttpRequest对象 xff08 简称XHR xff09 这是有微软率先引入的一个特性 xff0c 其他浏览器提供商后来都提供了相同的实现 但因为IE的兼容性问题 xff0c
  • node.js安装及环境配置

    一 下载nodejs的安装包 xff1a 下载地址 xff1a https nodejs org zh cn download 根据自己电脑系统及位数选择 xff0c 一般都选择windows64位 msi格式安装包 网站上提供的安装包版本
  • 6个常用的React组件库

    Ant Design 项目链接 xff1a Ant Design 包大小 xff08 来自 BundlePhobia xff09 xff1a 缩小后 1 2mB xff0c 缩小 43 gzip 压缩后 349 2kB xff0c 通过摇树
  • 大数据培训课程数据清洗案例实操-简单解析版

    数据清洗 xff08 ETL xff09 在运行核心业务MapReduce程序之前 xff0c 往往要先对数据进行清洗 xff0c 清理掉不符合用户要求的数据 清理的过程往往只需要运行Mapper程序 xff0c 不需要运行Reduce程序
  • 宋红康2023版Java视频发布

    1500万 43 播放量见证经典 xff0c 尚硅谷宋红康老师的Java入门视频堪称神作 xff0c 如今经典再次超级进化 xff0c 新版Java视频教程震撼来袭 xff01 开发环境全新升级 xff1a JDK17 43 IDEA202
  • Java消息队列:消息在什么时候会变成Dead Letter?

    在较为重要的业务队列中 xff0c 确保未被正确消费的消息不被丢弃 xff0c 通过配置死信队列 xff0c 可以让未正确处理的消息暂存到另一个队列中 xff0c 待后续排查清楚问题后 xff0c 编写相应的处理代码来处理死信消息 一 什么
  • Vue2和Vue3数据双向绑定原理的区别及优缺点(下篇)

    上篇我们讲到了Vue2的数据双向绑定原理 xff0c 如果你没有阅读上篇 xff0c 建议先阅读一下上篇中的内容 Vue2和Vue3数据双向绑定原理的区别及优缺点 xff08 上篇 xff09 在上篇中我们抛出了一个问题 xff1a 是不是