Vue3相对于Vue2的提升|区别

news/2025/2/19 17:35:00/文章来源:https://www.cnblogs.com/openmind-ink/p/18716731

https://cn.vuejs.org/guide/best-practices/performance
Vue框架基于MVVM的架构进行开发
M - Model:数据层(数据逻辑处理
V - View: 视图层
VM - ViewModel: 数据的响应和渲染(连接数据层和视图层的桥梁)

Vue通过观察者模式和采用代理的方式进行数据劫持和数据响应,从而实现数据响应式
通过VNode、diff算法以及一些事件的特殊定制,eg: 特殊的指令操作和绑定操作,组件化处理进行模板渲染操作
再结合响应式系统、编译器联合 形成【数据驱动视图】的声明式框架

架构升级

Vue3从架构层面来说(基于跨平台的优化),在渲染器、响应式、编译器、组件化、运行时等进行拆分
渲染器优化:将渲染逻辑进行单独的封装

setup函数 =》 组合式API、生命周期

组合式API相较于Option API,代码逻辑更加聚合便于阅读、封装和复用

生命周期上,去掉create和beforeCreate将其封装到setup函数中执行
在setup中进行Vue的实例化

响应式系统

Vue2中通过Object.defineProperty劫持对象定义时的每一层属性,对于对象后续扩展的属性或数组索引上的变化无法做到响应式处理
Vue3中通过Proxy和Reflect对象对对象进行全面代理,解决Vue2中对象属性添加、数组长度变化、数组索引上变化无法响应式处理的缺点
此外还对ES6新增的两种数据结构Map和Set进行响应式处理
对于computed和watch的功能进行重新定义
在computed中通过缓存和调度器的方式实现懒值的处理
在watch中首先进行首次渲染默认不执行以及首次执行的watchEffect的定义
通过参数进行watch的设定包括回调函数的调度时机和watch数量,返回值可以控制watch的结束,内部进行响应式处理

对于组件之间的通讯进行emit和props的定义,单独封装API进行使用,包括对外暴露功能,通过expose导出,移除冗余的filters功能
在TS方面,Vue3本身使用ts进行编写支持TS语法

渲染器优化

patch函数的diff算法采用快速diff算法通过预处理和寻找最长递增子序列的算法
将在原先的双端diff算法的基础上,提升DOM操作性能
采用fragment片段的方式,通过新增fragment的元素类型,解决Vue2中只能进行一个根节点的操作,提升组件化的效率
新增teleport内置组件和异步组件的方式,提升在渲染方面的效率和多样化处理方式
对于v-if和v-for不能并存问题,由原先的v-for的优先级高于v-if改成v-if大于v-for

编译器优化

对于编译优化,在分析模板的过程中提取关键字信息patchFlag,将其存放在编译好的Vnode中
将节点分为动态节点和静态节点
对于动态节点生成一个block树,
对于静态节点进行静态提升,此外对于静态节点预字符串化和v-once的方法,减少DOM的创建和创建操作Vnode开销
tree-shaking,由于Vue3的拆分以及ESM的方式导入和导出,在打包过程中对未使用的代码进行清除,减小打包体积

Vue3.0性能提升通过那几个方面体现的? ===> 【响应式系统】【编译阶段】【打包体积上】

编译阶段优化

Vue2中每个组件实例都对应一个watch实例,在组件渲染过程中将用到的数据property记录为依赖
当依赖发生改变,触发setter会通知watch,使关联的组件重新渲染

Vue2中的问题案例

<template><div id="content"><p class="text">静态文本</p><p class="text">静态文本</p><p class="text">{{ message }}</p><p class="text">静态文本</p>...<p class="text">静态文本</p></div>
</template>

组件内部仅仅存在一个动态节点其他的都是静态节点,很多diff和遍历都是不需要会造成性能浪费
因此 Vue3在编译阶段做了进一步的优化:

  1. 静态提升
  2. diff算法优化 - PatchFlags
  3. Block Tree
  4. 事件监听缓存
  5. SSR优化
🎈1. 静态提升

Vue3中对不参与更新的元素进行静态提升,只会被创建依次,在渲染时直接复用
静态提升免去重复创建节点,免去重复创建的操作,优化运行时的内存占用

预字符串化(Pre-stringification):处理大量静态内容,可以将静态内容在编译时转换为字符串,以减少运行时的计算和处理
<template><div><h1>{{ title }}</h1><ul><li v-for="item in items" :key="item.id">{{ item.name }}</li></ul></div>
</template><script>
export default {data() {return {title: '静态标题',items: [{ id: 1, name: '静态项1' },{ id: 2, name: '静态项2' },{ id: 3, name: '静态项3' }]};}
};
</script>
在上面的示例中,title 和 items 是静态数据,不会在运行时发生变化。在编译时,Vue 3会将这些静态内容转换为字符串,以减少运行时的计算和处理。
预字符串化的结果如下所示:
const _hoisted_1 = { class: "title" };
const _hoisted_2 = { class: "item" };return (_ctx) => {return (_openBlock(),_createBlock("div", null, [_createVNode("h1", _hoisted_1, _toDisplayString(_ctx.title), 1 /* TEXT */),_createVNode("ul", null, [(_openBlock(true),_createBlock(_Fragment, null, _renderList(_ctx.items, (item) => {return (_openBlock(), _createBlock("li", _hoisted_2, _toDisplayString(item.name), 1 /* TEXT */));}), 256 /* UNKEYED_FRAGMENT */))])]));
};编译结果中,_hoisted_1和_hoisted_2两个常量存储静态内容的字符串化结果,在运行时只需要直接使用这些变量,而不需要进行额外的计算和处理
通过预字符串化,Vue3可以在需要的时候直接使用这些变量而不需要额外的计算和处理

静态提升细节:

  • Vue2中,每次渲染时都会重新创建VNode节点,即使是静态节点也会被重新创建,导致一些非必要得消耗
  • Vue3中,引入静态提升概念,将静态节点在编译阶段提升为常量,避免重复创建的开销
<span>你好</span>
<div>{{ message }}</div>没有做静态提升之前
export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock(_Fragment, null, [_createVNode('span', null, '你好'),_createVNode('div', null, _toDisplayString(_ctx.message), 1)], 64))
}
// 静态提升之后
// 静态内容即不参与更新变化的节点_hoisted_1被放置在render函数外,每次渲染的时候只需要取_hoisted_1即可
// 同时_hoisted_1被打上PatchFlag,静态标记值为-1,特殊标志是负整数表示永远不会用于diff
const _hoisted_1 = _createVNode('span', null, '你好', -1)export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock(_Fragment, null, [_hoisted_1,_createVNode('div', null, _toDisplayString(_ctx.message), 1)],64))
}
🎈2. diff算法优化

Vue3在diff算法中相比于Vue2增加静态标记,
静态标记的作用:为了会发生变化的地方添加一个flag标记,下次发生变化的时候直接找到变化地方进行比较
对于已经标记静态节点的p标签在diff过程中则不会比较,性能进一步提高

  • 静态类型枚举PatchFlags
export const enum PatchFlags {TEXT = 1,// 动态的文本节点CLASS = 1 << 1,  // 2 动态的 classSTYLE = 1 << 2,  // 4 动态的 stylePROPS = 1 << 3,  // 8 动态属性,不包括类名和样式FULL_PROPS = 1 << 4,  // 16 动态 key,当 key 变化时需要完整的 diff 算法做比较HYDRATE_EVENTS = 1 << 5,  // 32 表示带有事件监听器的节点STABLE_FRAGMENT = 1 << 6,   // 64 一个不会改变子节点顺序的 FragmentKEYED_FRAGMENT = 1 << 7, // 128 带有 key 属性的 FragmentUNKEYED_FRAGMENT = 1 << 8, // 256 子节点没有 key 的 FragmentNEED_PATCH = 1 << 9,   // 512DYNAMIC_SLOTS = 1 << 10,  // 动态 soltHOISTED = -1,  // 特殊标志是负整数表示永远不会用作 diffBAIL = -2 // 一个特殊的标志,指代差异算法
}
🎈3. 事件监听缓存

默认情况下绑定事件行为被视为动态绑定,每次都会去追踪它的变化
在Vue2中,每次渲染时都会重新创建事件处理函数,即使相同的事件处理逻辑,会导致一些不必要的性能损耗
在Vue3中,引入缓存事件处理函数的概念,将事件处理函数在编译阶段缓存起来,避免重复创建的开销

  <div><button @click="onClick">点我</button></div>// 未开启事件监听器缓存
export const render = /*#__PURE__*/_withId(function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock("div", null, [_createVNode("button", { onClick: _ctx.onClick }, "点我", 8 /* PROPS */, ["onClick"])// PROPS=1<<3,// 8 //动态属性,但不包含类名和样式]))
})// 开启事件侦听器缓存后
export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock("div", null, [_createVNode("button", {onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args)))}, "点我")]))
}

🎈4. Block Tree ⭐⭐⭐

在Vue2中,模板中的条件循环和循环会导致大量的VNode节点创建和销毁,会影响渲染性能
在Vue3中,引入Block Tree的概念,将条件循环和循环渲染的内容封装为一个单独的Block,避免大量的VNode节点创建和销毁
Vue2和Vue3的编译结果对比

// Vue2的条件渲染
render(){return this.show ? createVNode("h1", null, "Hello World") : null// ...
}// Vue3的Block Tree
const _block_1 = this.show ? (openBlock(), createBlock("h1", null, "Hello World")) : null
function render(){return (_block_1)
}

通过使用Block Tree, Vue3将条件渲染和循环渲染的内容封装为一个单独的Block,避免大量的VNode节点创建和销毁从而提高渲染性能
在Vue2中,模板编译后会生成一个单一的渲染函数,该函数负责处理整个模板的渲染逻辑
每次更新时,整个模板都会重新渲染,即使其中只有一部分内容发生变化
在Vue3中,编译后的模板会被拆分为多个块(Blocks),每个块对应一个节点或一组节点。
这些块可以被独立地更新和渲染,从而避免不必要地渲染操作
Vue3的编译结果使用_createBlock和_createVNode来创建块和节点
这些块和节点可以被缓存起来,只有在需要更新时才会重新渲染
Vue3能够更加精确地追踪和更新变化地部分从而提高渲染性能。
当组件地状态发生变化时,只有受到影响的块和节点被重新渲染而不是整个模板。
Vue3的Block Tree在编译结果上和Vue2有所不同,通过拆分模块为多个块和节点,实现更细粒度的渲染更新从而提升性能和效率

🎈5. SSR优化

当静态内容达到一定量级,会使用createStaticVNode方法在客户端去生成一个static node,这些是静态node会被直接innerHTML
不需要创建对象,然后根据对象渲染

div><div><span>你好</span></div>...  // 很多个静态属性<div><span>{{ message }}</span></div>
</div>// 对于大量的静态节点进行SSR优化
// 编译后
import { mergeProps as _mergeProps } from "vue"
import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from "@vue/server-renderer"export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {const _cssVars = { style: { color: _ctx.color }}_push(`<div${_ssrRenderAttrs(_mergeProps(_attrs, _cssVars))}><div><span>你好</span>...<div><span>你好</span><div><span>${_ssrInterpolate(_ctx.message)}</span></div></div>`)
}

响应式系统优化

Vue2中使用Object.defineProperty劫持定义时的对象,会深度递归遍历对象的每一层属性并添加setter()\getter()方法
在getter()方法中收集依赖,setter()方法中触发依赖更新
Vue3中使用Proxy和Reflect对象来代理整个对象是一种懒代理的方式不需要深度递归遍历同时可以解决Vue2中无法做到对象属性新增\删除、数组长度变化、索引值变化的响应式性
Vue3的响应式系统

  • 可以监听对象动态属性的新增或删除
  • 可以监听数组length属性和索引上值得变化

源码打包体积的优化 - Tree Shaking

相对于V2,V3整体体积变小,移除不常用的API以及利用Tree Shaking移除没有使用到的模块

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/884195.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C 语言编写直接插入排序、冒泡排序、简单选择排 序等排序算法。

1. 直接插入排序#include <stdio.h>void insertionSort(int arr[], int n) {for (int i = 1; i < n; i++) {int key = arr[i];int j = i - 1;// 将大于 key 的元素移动到后面while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];j--;}arr[j + 1] = ke…

最小生成树可并行化的 Sollin(Boruvka)算法

上期回顾:https://www.cnblogs.com/ofnoname/p/18715203 在前文中,我们剖析了最小生成树(MST)问题中的两大经典算法:Kruskal 以“边权平等”为信条,通过排序与并查集自下而上聚合连通分量; Prim 以“中心辐射”为策略,通过优先队列自上而下扩张领土。二者虽路径迥异,却…

pip install shutilwhich 安装shutil

(base) C:\Users\Administrator>pip install shutilLooking in indexes: https://mirrors.aliyun.com/pypi/simpleERROR: Could not find a version that satisfies the requirement shutil (from versions: none)ERROR: No matching distribution found for shutil正确 安装…

图片PDF向量化处理的探索与实践

在构建AI本地知识库时,我们不可避免地需要对PDF文件进行处理。PDF文件大致分为两种:扫描的图片文件和非图片文件。对于非图片类型的PDF,可以直接提取文本并进行向量化处理;但对于图片类型的PDF(如扫描件),处理起来就复杂得多。问题背景 图片类型的PDF文件通常存在以下问…

Android ADB 使用笔记

ADB(Android Debug Bridge)是用于与 Android 设备和模拟器交互的命令行工具。通过客户端、服务器和守护进程的协作,提供设备管理、应用安装与卸载、文件操作、系统设置控制等功能。常用命令包括连接设备、安装 APK、截图、录屏、模拟按键、获取设备信息等,是 Android 开发和…

别再误解了!AI 不是软件且不接受反驳!

是的,今天的 AI 是用软件编写的,但如今的实现实际上是一个计算图,它计算的是虚拟神经元网络的行为。软件仅仅是该网络的一个模拟。 让我解释一下。 就像我说的,神经网络 AI 实际上是一组相互连接的虚拟“神经元”。每个神经元或多或少地模拟了人类神经元的行为。这张图数学…

欧拉角和四元数,3D 游戏开发中“旋转”难题的通俗讲解和应用实战

本文深入浅出地介绍了3D游戏开发中常用的旋转工具——欧拉角和四元数。通过生动的例子和代码演示,详细解释了它们的基本概念、应用场景以及如何解决万向锁、插值和计算效率等问题。无论你是初学者还是有经验的开发者,都能从中获得实用的知识和技巧……春节期间,老牛同学学习…

【Go-bug】go引入自建包名报错 package XXX is not in std

引入 这个问题从昨晚学到Package和Module这块的时候就开始折磨我,这里说道:Go语言也有 Public 和 Private 的概念,粒度是包。如果类型/接口/方法/函数/字段的首字母大写,则是 Public 的,对其他 package 可见,如果首字母小写,则是 Private 的,对其他 package 不可见。于…

[AI/前沿展望/综述] AI大模型的技术生态链全景图

序【特别说明】1、本文主要面向对象:开发者、对AI前沿技术感兴趣、基于AI创业的伙伴们 2、本文是对当前AI大模型的技术生态链的【综述】性文章。(文章有点长,预计阅读时长>10min)3、原作者:阿里云开发者(组织机构)(故,极小部分观点有含暗广的嫌疑。但瑕不掩瑜)关键词AI大模…

大语言模型的解码策略与关键优化总结

本文系统性地阐述了大型语言模型(Large Language Models, LLMs)中的解码策略技术原理及其实践应用。通过深入分析各类解码算法的工作机制、性能特征和优化方法,为研究者和工程师提供了全面的技术参考。主要涵盖贪婪解码、束搜索、采样技术等核心解码方法,以及温度参数、惩罚机…

mini-lsm通关笔记Week3Day3

项目地址:https://github.com/skyzh/mini-lsm 个人实现地址:https://gitee.com/cnyuyang/mini-lsm在本章中,您将:在上一章的基础上完成读路径,以支持快照读。 实现事务API,支持快照读。 引擎恢复过程中能正确恢复已提交时间戳。最后,您的引擎将能够为用户提供存储键(key…

软件测试流程——H模型

1、产品开需求澄清会议 2、开发和测试拿到需求 3、分析需求,编写测试计划 4、编写测试用例 5、评审测试用例 6、评审通过,将用例导入用例管理工具 7、搭建环境 8、开发提交项目代码包,达到准入条件 9、测试将项目代码包部署到环境中 10、开始冒烟测试 (冒烟测试通过,则进入…