浅谈 Vue3 静态提升和预字符串化

前言

很多朋友在看到 Vue3静态提升 的时候很不理解,不明白这句话到底是什么意思,今天我们就通过这篇日记来搞明白。如果有什么地方描述不正确,请多多指正

静态类型(前置信息)

判断节点是否为静态类型,是根据如下标识进行判断的,可以对照这个进行查看。

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 算法做比较
  NEED_HYDRATION = 1 << 5,  	// 32 表示带有事件监听器的节点
  STABLE_FRAGMENT = 1 << 6,   	// 64 一个不会改变子节点顺序的 Fragment
  KEYED_FRAGMENT = 1 << 7, 		// 128 带有 key 属性的 Fragment
  UNKEYED_FRAGMENT = 1 << 8, 	// 256 子节点没有 key 的 Fragment,这是一个特殊的节点类型,用于包裹多个子节点而不引入额外的 DOM 元素
  NEED_PATCH = 1 << 9,   		// 512 NEED_PATCH 是一个常量,用于表示是否需要对组件进行更新(patch)。可以帮助 Vue 在更新组件时进行更高效的判断和处理,提高性能并减少不必要的渲染操作。
  DYNAMIC_SLOTS = 1 << 10,  	// 1024 动态 solt(动态插槽),允许组件在渲染时动态地向插槽中传递内容
  DEV_ROOT_FRAGMENT = 1 << 11,  // 2048 用于开发环境的特殊标记,不会出现在生产环境中,用于表示根片段(root fragment)。使用 DEV_ROOT_FRAGMENT 来帮助开发者调试和跟踪组件的渲染过程,以便更好地定位问题和优化性能
  HOISTED = -1,  	// 特殊标志是负整数表示永远不会用作 diff
  BAIL = -2 		// 一个特殊的标志,指代差异算法
}

具体源码查看地址请看这里

Vue3基础示例

首先我们来看下面的这段代码,对于看过Vue的童鞋来讲是不陌生的吧,即使没有看过,看到如下的代码是不是觉得也很好理解。

<script setup>
	import { ref } from 'vue';
	const message = ref('努力搬砖,做大做强');
</script>

<template>
	<!-- 这是静态节点 -->
	<span>你好啊,帕努</span>
	
	<!-- 这是动态节点 -->
	<span>{{message}}</span>
</template>

将上面的代码转换成 虚拟DOM 后的图示。
在这里插入图片描述

对于上面的代码,vue3在创建 vNode 的时候,对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用,这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操作,优化了运行时候的内存占用

创建vNode

针对上面的示例代码,在进行创建 虚拟DOM 的时候,下面的代码分别是 vue2vue3 版本下各自的vNode,从这就能够看出一些差别。

Vue2 没有做静态提升

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock(_Fragment, null, [
    _createVNode("span", null, "你好啊,帕努"),  // 静态虚拟节点
    _createVNode("span", null, _toDisplayString(_ctx.message), 1 /* TEXT */)
  ], 64 /* STABLE_FRAGMENT */))
}

Vue3 做静态提升


// 把静态节点提出到外部定义,用一个变量存储,便于后面直接取值使用
const SPAN_V_NODE = _createVNode("span", null, "你好啊,帕努");


export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock(_Fragment, null, [
    SPAN_V_NODE, // 静态虚拟节点
    _createVNode("span", null, _toDisplayString(_ctx.message), 1 /* TEXT */)
  ], 64 /* STABLE_FRAGMENT */))
}

将静态虚拟节点提到外层,便于直接使用(只创建一次),下次就直接获取值放到对应的位置即可。避免了每次执行 render 函数的时候,都要去再次执行一次 _createVNode 函数。

如果是比较简单的项目,在性能上看不大出来,如果是比较大型的项目,那就能够很明显的看到界面卡顿(渲染慢),严重的甚至可能会造成 CPU 的使用率急速上升等明显问题。

通过上面的示例可能也看不出多大的效果(具体有多大的提升),下面将通过一段简单代码就能看出他们有多大的差距。

另辟蹊径解释

如果对于上面的代码如果还不能理解的话,下面再通过另外一个示例来讲一下。

在一个循环中,获取一个计算结果,然后通过这个结果再去计算打印,然后就能够看到两者差别。

没做静态提升

// 静态值,只会返回固定的18,
function getAgeFn() {
    console.log('被调用获取值')
    return  18;
}

function render() {
    console.log('开始时间', new Date().getTime()) // 开始时间 1709628094030
    for (let i = 0; i < 10000; i++){
    
         console.log('获取到的值',i, i+getAgeFn() )
    }
    console.log('结束时间', new Date().getTime()) // 结束时间 1709628096175
}

render()

总运行时长:1709628096175 - 1709628094030 = 2145

在这里插入图片描述

做了静态提升

function getAgeFn() {
    console.log('被调用获取值')
    return  18;
}


function render() {
    console.log('开始时间', new Date().getTime()) // 1709627967983
    const age = getAgeFn();
    for (let i = 0; i < 10000; i++){
        
         console.log('获取到的值',i, i+age )
    }
    console.log('结束时间', new Date().getTime()) // 1709627968909
}


render()

总运行时长: 1709627968909 - 1709627967983= 926;

在这里插入图片描述

通过上面两段代码运行时间差就能够明显的看出效果,没做静态提升运行时长是做了静态提升的 2.32倍(四舍五入)。

预字符串化

另外提到了静态提升,那么就不得不提一下 Vue3的 预字符串化,那这是什么呢?有什么作用呢?

我们在实际开发中,经常会有大量的静态节点,是不需要动态更改的。那这一部分能不能再进行优化一下呢?答案是可行的。

<div>
	<ul>
	    <li>aaaaaa</li>
	    <li>aaaaaa</li>
	    <li>aaaaaa</li>
	    <li>aaaaaa</li>
	    <li>aaaaaa</li>
	    <li>aaaaaa</li>
	  </ul>
	
	  <div class="state">
	    <span>{{state}}</span>
	  </div>
  <div>

在vue2中,每个元素都会变成虚拟节点,这样就会出现一大堆的静态的虚拟节点。

在vue3中它会智能地发现这一点,如下图所示,我们可以很明显的感受到vue3性能的显著提升。

在这里插入图片描述

注意:必须是 大量连续 的静态内容才可以预字符串化哦,切记!目前是连续 20个静态节点 才会预字符串化

总结

静态提升

Vue 2 并没有像 Vue 3 中那样实现静态提升(Static Tree Hoisting)。在 Vue 2 中,模板编译后的渲染函数是动态创建的,每次渲染都需要重新生成一次。这导致了一些性能上的损失,特别是在大型应用中频繁重新渲染时。

Vue 3 引入了静态提升的概念,通过将模板编译为更具静态特性的代码,可以在编译阶段优化并提升静态节点,减少运行时的开销,从而提高性能。这是 Vue 3 在性能方面的一个重要改进之一。

预字符串化

在编译阶段会对模板进行分析和优化,将动态部分和静态部分分离,并将静态部分转换为字符串常量,以减少运行时的开销。

预字符串化可以帮助 Vue 3 在渲染过程中更快地生成虚拟 DOM,并减少不必要的计算,从而提高整体的渲染性能。这个优化技术有助于减少应用的启动时间和运行时的性能消耗,使得 Vue 3 在处理大型复杂应用时表现更加出色。

这些骚操作总结一句话就是【提高性能】

最后

如果这篇文章对你有所帮助,请咚咚大家的 发财黄金手指点赞,收藏

如果文章有描述错误或者有更好解决方案,也请大家多多 评论

  • 30
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值