首页 > 其他分享 >[vue3] patchFlags与位运算

[vue3] patchFlags与位运算

时间:2024-08-03 18:28:08浏览次数:8  
标签:__ const 运算 静态 return export vue3 msg patchFlags

Vue3在编译template的过程中会分析模板中的动态部分和静态部分,并标记相应的flag,用于在运行时优化虚拟DOM的更新。

  • Parse:将模板字符串解析成AST
  • Transform:对AST进行转换和优化,包括识别动态节点和静态节点;
  • CodeGeneration:将转换后的AST生成渲染函数,这个阶段会生成patchFlags

diff过程中,遇到包含dynamicChildren的块时,diff算法会进入优化模式,跳过对静态节点的处理从而优化了diff的执行效率。

flag的种类

源码位置core/packages/shared/src/patchFlags.ts at main · vuejs/core (github.com)

export enum PatchFlags {
  TEXT = 1,
  CLASS = 1 << 1,
  STYLE = 1 << 2,
  PROPS = 1 << 3,
  FULL_PROPS = 1 << 4,
  NEED_HYDRATION = 1 << 5,
  STABLE_FRAGMENT = 1 << 6,
  KEYED_FRAGMENT = 1 << 7,
  UNKEYED_FRAGMENT = 1 << 8,
  NEED_PATCH = 1 << 9,
  DYNAMIC_SLOTS = 1 << 10,
  DEV_ROOT_FRAGMENT = 1 << 11,
    
  HOISTED = -1,
  BAIL = -2,
}

可以看到flag使用二进制格式记录的,并且每个标志仅有一位为1,这样可以通过位运算获知一个复合状态里包含哪些状态。

flag含义

  • TEXT:表示元素具有动态的 textContent

    <div>{{ dynamicText }}</div>
    
  • CLASS:表示元素具有动态的类绑定。

    <template>
      <div :class="dynamicClass">Content</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          dynamicClass: 'active'
        }
      }
    }
    </script>
    
  • STYLE:表示元素具有动态样式。

    <template>
      <div :style="dynamicStyle">Content</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          dynamicStyle: {
            color: 'red'
          }
        }
      }
    }
    </script>
    
  • PROPS:表示元素具有非 class/style 的动态属性。也可以用于具有任何动态属性的组件。

    <template>
      <input :value="dynamicValue" />
    </template>
    
    <script>
    export default {
      data() {
        return {
          dynamicValue: 'Hello'
        }
      }
    }
    </script>
    
  • FULL_PROPS:表示具有动态键属性的元素。当键变化时,总是需要完全差异检查。

    <template>
      <div v-bind:[dynamicProp]="dynamicValue">Content</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          dynamicProp: 'id',
          dynamicValue: 'uniqueId'
        }
      }
    }
    </script>
    
  • NEED_HYDRATION:表示该元素在客户端渲染时,需要将属性从静态 HTML转换为动态绑定。hydration是指从服务器端渲染(SSR)的静态内容中恢复出动态行为和状态的过程。该元素不需要常规的虚拟 DOM 属性更新,只需要在初始化时处理特定的属性以恢复其动态行为。

    案例(事件监听器):如 @click="handler",在服务器端渲染时,事件绑定不会被实际添加,客户端加载后需要将事件监听器正确绑定到元素上。

    <template>
      <button @click="handleClick">Click me</button>
    </template>
    
    <script>
    export default {
      methods: {
        handleClick() {
          console.log('Button clicked!')
        }
      }
    }
    </script>
    
  • STABLE_FRAGMENT:表示子元素顺序不变的片段。

  • KEYED_FRAGMENT:表示子元素的都带有或部分带有key标注。

  • UNKEYED_FRAGMENT:表示子元素没有key标注的片段。

  • NEED_PATCH:表示不涉及classstyleprops但仍需触发更新的情况,通常对应ref、指令等使用场景。

  • DYNAMIC_SLOTS:主要用于标识那些插槽内容或插槽名称是动态变化的组件。带有此标志的组件在更新时会被强制更新,以确保插槽内容或名称的变化能够正确反映到 DOM 中。

    <template>
      <parent-component>
        <template :slot="dynamicSlotName">
          <child-component :data="someData" />
        </template>
      </parent-component>
    </template>
    
    <script>
    export default {
      data() {
        return {
          dynamicSlotName: 'defaultSlot',
          someData: { message: 'Hello, World!' }
        }
      }
    }
    </script>
    
  • DEV_ROOT_FRAGMENT:表示用户在template的顶层写了注释而创建的flag。仅用于开发环境,因为生产中会去除注释。

    <template>
      <!-- Root level comment -->
      <div>Content</div>
    </template>
    
  • HOISTED:表示提升的静态虚拟节点。patch过程可以跳过整个子树,因为静态内容永远不需要更新。

    <p>Static content</p>
    
  • BAIL:表示diff算法应退出优化模式,通常是对应用户使用h函数自定义渲染函数的情况。

示例代码

vue3有提供一个playground可以查看编译后的结果:Vue SFC Playground (vuejs.org)

简单的代码案例

<script setup>
import { ref } from 'vue'

const msg = ref('Hello World!')
</script>

<template>
  <p>static content</p>
  <h1>{{ msg }}</h1>
  <input v-model="msg" />
</template>

编译后的JS

import { ref } from 'vue'


const __sfc__ = {
  __name: 'App',
  setup(__props, { expose: __expose }) {
  __expose();

const msg = ref('Hello World!')

const __returned__ = { msg, ref }
Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })
return __returned__
}

};
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, vModelText as _vModelText, withDirectives as _withDirectives, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

const _hoisted_1 = /*#__PURE__*/_createElementVNode("p", null, "static content", -1 /* HOISTED */)
function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock(_Fragment, null, [
    _hoisted_1,
    _createElementVNode("h1", null, _toDisplayString($setup.msg), 1 /* TEXT */),
    _withDirectives(_createElementVNode("input", {
      "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (($setup.msg) = $event))
    }, null, 512 /* NEED_PATCH */), [
      [_vModelText, $setup.msg]
    ])
  ], 64 /* STABLE_FRAGMENT */))
}
__sfc__.render = render
__sfc__.__file = "src/App.vue"
export default __sfc__
  • 可以看到<p>static content</p>被应用了静态提升(Vue3优化策略之一):

(静态的flag-1

const _hoisted_1 = /*#__PURE__*/_createElementVNode("p", null, "static content", -1 /* HOISTED */)
  • <h1>{{ msg }}</h1>由于有动态文本,被标记为TEXT
  • <input v-model="msg" />使用了v-model指令,被标记了NEED_PATCH
  • Vue2template内部只能存在一个顶级节点,如果有多个要使用一个标签囊括其中;Vue3支持template内部多个顶级节点,其实是框架帮我们套了一个fragment;在上述代码中由于这个fragment内部元素的顺序是固定的,因此被标记为STABLE_FRAGMENT

静态提升

静态提升是Vue3的一种性能优化手段。如果有VNode被标记为静态节点,说明它的内容是固定不变的。那么它的构建函数会被提升到渲染函数的外部,即只会被运行一次。

位运算的应用

Vue3中,这些flags都是只有一位为1,在这个前提下,可以通过位运算实现下面两种操作:

组合标志

通过或运算组合标志:

const combinedFlag = PatchFlags.TEXT | PatchFlags.STYLE; // 0001 | 0100 = 0101

检查标志

通过与运算检查混合标志是否存在某个base flag

const hasText = combinedFlag & PatchFlags.TEXT; // 0101 & 0001 = 0001 (truthy)
const hasClass = combinedFlag & PatchFlags.CLASS; // 0101 & 0010 = 0000 (falsy)

可以在源码中看到patchFlag和与运算的相关代码:core/packages/runtime-core/src/renderer.ts at main · vuejs/core (github.com)

image-20240803181837861

标签:__,const,运算,静态,return,export,vue3,msg,patchFlags
From: https://www.cnblogs.com/feixianxing/p/18340881/vue-3-vnode-patch-flags

相关文章

  • 47 集合操作与运算
    1增加与删除集合元素集合对象的add()方法可以增加新元素,如果该元素已存在则忽略该操作,不会抛出异常;update()方法合并另外一个集合中的元素到当前集合中,并自动去除重复元素。s={1,2,3}print(s)s.add(3)#增加元素,重复元素自动忽略s.update({3,4,5})#更新......
  • VUE3学习路线
    以下是一份详细的Vue3学习路线,涵盖从基础到进阶的各个方面,以帮助你系统掌握Vue3开发。第一阶段:基础知识理解前端基础HTML:了解文档结构,常用标签,语义化HTML。CSS:学习选择器、布局、Flexbox和Grid,基本的样式应用。JavaScript:理解基本语法、DOM操作、事件处......
  • C语言运算符优先级口诀
    口诀内容(优先级自上而下递减;由逗号分隔的,优先级自左到右递减。)圆方括号,箭头句号。单目增减非反负,针强址长,从右。乘除求模,加减,位移,大小,等不等。位与异或,逻辑与或。条件赋值均右。真逗。解释(斜体字是补全;加粗字是对整行的说明;代码块即是所对应操作符。)圆括号()方括号[]......
  • C语言运算符优先级口诀
    口诀内容(优先级自上而下递减;由逗号分隔的,优先级自左到右递减。)圆方括号,箭头句号。单目增减非反负,针强址长,从右。乘除求模,加减,位移,大小,等不等。位与异或,逻辑与或。条件赋值均右。真逗。解释(斜体字是补全;加粗字是对整行的说明;代码块即是所对应操作符。)圆括号()方括号[]......
  • c语言位运算符和位运算,位运算举例,位段
    位运算符1.按位与运算符(&)按位与运算符对两个整数的每一位进行“与”操作,只有当两个位都是1时,结果才为1,否则为0。inta=5; //二进制:00000101intb=3; //二进制:00000011intresult=a&b; //结果:00000001(1)2.按位或运算符(|)按位或运算符对......
  • 使用SpringBoot+Vue3来实现前后端登录注册功能(新手入门,带你一步一步实现)
    目录一.所用技术栈:二.前端创建工程: 1.使用elementplus展开前端页面格式布局:2.基于Vue3的使用来实现登录与注册:(1)定义数据模型:(2):model绑定表单:(3)表单数据校验:①定义表单校验规则:②给表单绑定校验规则:(4)前端注册功能开发: ①创建request.js请求工具:②创建user.js来调用......
  • 第五章 向量运算(知识点及笔记)
    以下是第五章(向量运算)的知识点,笔记也给大家整理好了......
  • 共用体、typedef、位运算
    共用体定义:共用体是一个用户自定义的类型,包含多个不同类型的成员,但在同一时间只能存储其中一个成员的值。共用体的声明类似于结构体,但使用关键字union。unionExample{inti;floatf;charc;}; 内存分配:共用体的大小等于其最大成员的大小。例如,如果......
  • 【C++】运算符重载
    一、示例如果我想实现以下代码,按照下面的写法是不能正常运行的。classPerson{public:intm_A;intm_B;};Personp1;p1.m_A=10;p1.m_B=10;Personp2;p2.m_A=10;p2.m_B=10;Personp3;p3=p1+p2;按照以上学过的内容,可以自己写成员函数,实......
  • 链表尾插法、头删、尾删,共用体、位运算。
    一、链表1、尾插程序:2、头删3、尾删4、清空链表二、共用体1、定义:union 共用体名(首字母大写。所占字节大小:结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。共用体变量所占的内存长度等于最长的成员的长度。但是整体大......