当前位置: 首页 > news >正文

从底层了解Vue3

Vue3带来了什么(新特性)

众所周知,前端的技术一直更新的特别快,而我们作为前端开发者也应该快速适应,主动拥抱变化,推陈出新,与时俱进

在这里插入图片描述

  • 重写了虚拟Dom实现

  • 编译模板的优化

  • 更高效的组件初始化

  • undate性能提高1.3~2倍

  • SSR速度提高了2~3倍

在这里插入图片描述

  • 增强可维护性
    TypeScript + modularized internals 基于TS语言编写和模块化更强的核心代码

  • 性能提升
    Proxy-based Reactivity System 基于代理的响应式系统
    Compiler-informed Virtual DOM & SSR 编译推断的虚拟Dom和服务器渲染

  • 更小体积
    Tree-shaking 按需裁剪
    Compile-time flags 编译时标志

  • Scales better 更好的可伸缩性
    组合式API

  • Better DX New Single-file Component improvements 新的单文件组件性能改进
    Modularized Internals 模块化内核

更快

1. 重写虚拟dom,diff算法优化

回顾Vue2的vdom:

  1. 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
  2. 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
  3. 把所记录的差异应用到所构建的真正的DOM树上,视图就更新了

vue2.x的diff算法叫做全量比较,顾名思义,就是当数据改变的时候,会从头到尾的进行vDom对比,即使有些内容是永恒固定不变的,它也会一一遍历。当模板中节点数量不断增加时(例如静态节点有上千个),此时所消耗的性能也会增加,特别是对静态节点的遍历,做了‘无用功’。

所以vue3 针对此问题,diff算法使用了一个全新的静态标记(PatchFlag),在此基础上做出了优化:

  1. 标记模板中的静态内容,区别了模板中的静态和动态节点
  2. 更新时,只diff操作动态的内容
  3. Vue3中会首先区分出以上模板的静态节点和动态节点,当视图更新时,只对动态节点部分进行diff运算,减少了资源的损耗

以下是Vue3遍历节点后编译的虚拟dom
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
我们发现创建动态 dom 元素的时候,Vdom 除了模拟出来了它的基本信息之外,还给它加了一个标记: 1 /* TEXT */

export const enum PatchFlags {
  TEXT = 1,// 动态文本节点
  CLASS = 1 << 1, // 2  // 动态 class
  STYLE = 1 << 2, // 4 // 动态 style
  PROPS = 1 << 3, // 8 // 动态属性,但不包含类名和样式
  FULL_PROPS = 1 << 4, // 16 // 具有动态 key 属性,当 key 改变时,需要进行完整的 diff 比较。
  HYDRATE_EVENTS = 1 << 5, // 32 // 带有监听事件的节点
  STABLE_FRAGMENT = 1 << 6, // 64 // 一个不会改变子节点顺序的 fragment
  KEYED_FRAGMENT = 1 << 7, // 128 // 带有 key 属性的 fragment 或部分子字节有 key
  UNKEYED_FRAGMENT = 1 << 8, // 256 // 子节点没有 key 的 fragment
  NEED_PATCH = 1 << 9, // 512 // 一个节点只会进行非 props 比较
  DYNAMIC_SLOTS = 1 << 10, // 1024 // 动态 slot
  HOISTED = -1, // 静态节点
  // 指示在 diff 过程应该要退出优化模式
  BAIL = -2
}

这个标记就叫做 patch flag(补丁标记),patch flag 的强大之处在于,当你的 diff 算法走到 _createBlock 函数的时候,会忽略所有的静态节点,只对有标记的动态节点进行对比,而且在多层的嵌套下依然有效。

这个也叫做Vue3的静态提升,它包括两部分(静态树的提升,静态属性的提升)

同样的,静态属性的含义也是如此:
在这里插入图片描述

在这里插入图片描述
我们会发现原本的标记变成了 9 /* TEXT, PROPS */,而且后边多了一个数组 [“id”]

这里的 patch flag 中的注释的内容告诉我们,这一个 dom 元素不止有内容 TEXT 会变化,它的属性 PROPS 也会变化。而后边的数组中的内容则是有可能发生变化的属性。

事件侦听器缓存
对于事件绑定来说,在默认情况下onClick会被视为动态绑定会使得vue每次都会跟踪他的变化,但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可

关闭 cacheHandlers

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", _hoisted_1, [
    _createVNode("button", { onClick: _ctx.confirmHandler }, "确认", 8 /* PROPS */, ["onClick"]),
    _createVNode("span", null, _toDisplayString(_ctx.vue3), 1 /* TEXT */)
  ]))
}
// 取消了缓存

开启 cacheHandlers

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", _hoisted_1, [
    _createVNode("button", {
      onClick: _cache[1] || (_cache[1] = $event => (_ctx.confirmHandler($event)))
    }, "确认"),
    _createVNode("span", null, _toDisplayString(_ctx.vue3), 1 /* TEXT */)
  ]))
}
// 首次渲染时, 将事件缓存在_cache[1]中
// 再次调用时, 判断是否有缓存,如果有直接从缓存中获取事件,无需再次创建更新,减少消耗 

2. 使用Proxy代替Object.defineProperty

vue2利用Object.defineProperty来劫持data数据的getter和setter操作。这使得data在被访问或赋值时,动态更新绑定的template模块,来达到数据双向绑定的目的

vue2中Object.defineProperty的缺陷:

  1. 无法原生监听数组的变化,需要特殊处理
  2. 必须遍历对象的每个属性(当示例初始化的时,Object.definePropety是从data的根节点遍历到末节点。一次性便利全部)
  3. 无法监听属性的新增删除操作(VUE提供Vue.set Vue.delete API,原因就是因为Object.definePropety无法监听新增删除操作)
let p = new Proxy(target, handler);

Proxy对象是ES2015(ES6)引入的一个原生对象,proxy在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截。因此它能监听数组,监听新增删除操作,深层遍历嵌套的对象等。
Proxy构造函数的第一个参数是原始数据target,是用Proxy包装的被代理对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
第二个参数是一个叫handler的处理器对象。其声明了代理target 的一些操作,其属性是当执行一个操作时定义代理的行为的函数。Handler是一系列的代理方法集合,它的作用是拦截所有发生在data数据上的操作。
p是代理后的对象,当外界每次对 p 进行操作时,就会执行 handler 对象上的一些方法。Proxy有多达13种劫持操作。包括常用的get()和set()是最常用的两个方法,分别代理访问和赋值两个操作。

但因为浏览器兼容所限,使用proxy会放弃对于IE的支持,vue只能使用Object.defineProperty实现双向绑定。这在当时是一种很前卫的设计,不过随着浏览器的不断迭代,这种技术在api和性能上愈发跟不上时代的步调。
到目前为止,各大主流移动端浏览器,PC端浏览器,包括新一代window产品内置Microsoft Edge都满足对于Vue3的支持。其次,若真的需要满足支持IE及极少数低版本浏览器,还可以使用垫片工具(es6-proxy-polyfill)。

更小

Tree shaking

什么是Tree shaking?

Treeshaking是一个术语,通常用于描述移除JavaScript上下文中的未引用代码(dead-code),就像一棵大树,将那些无用的叶子都摇掉。
它依赖于ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具rollup。

Tree shaking的原理:

使用Tree shaking的原理是ES6的模块静态分析引入,这就可以在编译时正确判断到底加载了什么代码,但是要注意import 和export是ES6原生的而不是通过babel或者webpack转化的

Tree shaking 与 按需导入的区别?

按需引入是在babel编译过程中,按需只引入相关代码其本质是在babel编译阶段将部分代码做了替换
Tree shaking是在webpack打包阶段,移除 JavaScript 上下文中的未引用代码

Tree shaking在Vue3.0中的使用:
我们在vue3的写法上可以发现,当我们要使用到一些API方法时,我们需要不断重复的引入这些方法

import { reactive, onMounted, ref, toRefs } from 'vue'

在Vue3中,对代码结构进行了优化,让其更加符合Tree shaking的结构,Vue3.x中的核心API都支持tree-shaking,这些API都是通过包引入的方式而不是直接在实例化时就注入,只会对使用到的功能或特性进行打包(按需打包),这意味着更多的功能和更小的体积。

更易维护

组合式API(Composition API)

从Options API(选项式API)到Composition API (组合式API)(函数式API)
在这里插入图片描述
composition api 把复杂组件的逻辑抽地更紧凑,而且可以将公共逻辑进行抽取,提高代码逻辑的可复用性,从而实现与模板的无关性;同时使代码的可压缩性更强。另外,把 Reactivity 模块独立开来,意味着 Vue3.0 的响应式模块可以与其他框架相组合。

setup
①setup()函数是vue3中专门新增的方法,可以理解为Composition Api的入口;
②setup在组件创建之前调用的,也就是在beforeCreate、created之前创建,因此没有this;
③setup 函数在创建组件之前被调用,所以在 setup 被执行时,组件实例并没有被创建;因此在 setup 函数中,我们将 没有办法 获取到 this 。

options api 与 composition api 取舍:
两者能实现共存,能混合写在setup中

Composition API 除了在逻辑复用方面有优势,也会有更好的类型支持,因为它们都是一些函数,在调用函数时,自然所有的类型就被推导出来了,不像 Options API 所有的东西使用 this。另外,Composition API 对 tree-shaking 友好,代码也更容易压缩。vue3的Composition API会将某个逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去

更好的TypeScript支持

Vue3.x采用TypeScript重写,开发者使用Vue3.x时可以充分体验TS给编码带来的便利。

经过对于原有项目的vue3升级后,我进行了打包和运行的测试,如下:

在这里插入图片描述
在这里插入图片描述
因为都是使用到webpack打包工具,所以打包出的大小差别无异,后续我们能测试下Vue3的黄金搭档vite2的能力

Vue2 运行速度:

在这里插入图片描述
Vue3运行速度:
在这里插入图片描述
从整个页面完成时间来看,由1.58S到1.39S
从DOM文档结构加载完毕时间来看,由327ms到148ms

vue3推出时给出的数据是更新相比于vue2有1.3~2倍的性能优势
特别是在dom更新的速度上,突出更为明显

相关文章:

  • 六大设计原则(读书笔记)
  • BZOJ1588 营业额统计 (Splay)
  • 小团队的PM和开发方法
  • 第二个商业设想
  • 如何让.net 2003中的Panel正常实现Dock
  • innodb引擎redo文件维护
  • 清理
  • Xcode8.2 继续使用插件
  • Native C++死了吗?
  • SIP协议的常见命令
  • Spring的注解@Repository@Service@Controller和@Component
  • NHiberate的set
  • c#启动windows服务问题总结
  • IE不能开新窗口的解决方法
  • Hadoop工作流--JobControl(五)
  • 【译】JS基础算法脚本:字符串结尾
  • 【技术性】Search知识
  • Android系统模拟器绘制实现概述
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • HTTP--网络协议分层,http历史(二)
  • iOS编译提示和导航提示
  • Java应用性能调优
  • js对象的深浅拷贝
  • mysql中InnoDB引擎中页的概念
  • SQLServer插入数据
  • vue的全局变量和全局拦截请求器
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 聊聊springcloud的EurekaClientAutoConfiguration
  • 每天10道Java面试题,跟我走,offer有!
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 智能合约开发环境搭建及Hello World合约
  • 转载:[译] 内容加速黑科技趣谈
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​低代码平台的核心价值与优势
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #Linux(make工具和makefile文件以及makefile语法)
  • (20050108)又读《平凡的世界》
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (笔试题)分解质因式
  • (分类)KNN算法- 参数调优
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • .net core 控制台应用程序读取配置文件app.config
  • .NET Framework 服务实现监控可观测性最佳实践
  • .NET 事件模型教程(二)
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc
  • @RequestBody详解:用于获取请求体中的Json格式参数
  • @RequestMapping处理请求异常