31-mini-vue 更新element的children

更新element的children

  1. 实现的功能:
    1. 我们更新为 Text,原来的节点是Array
    2. 我们更新为 Text,原来的节点是Text
    3. 我们更新为 Array, 原来的节点是Text
    4. 我们更新为 Array, 原来的节点是Array
  2. 初始化的文件,我们在 this 上挂载一个变量布尔值,后续通过控制台动态修改该变量的值,来实现更新功能
    1. index.html
    2. main.js
    3. App.js
js 复制代码
     // App.js
     import {
         h,
         ref
     } from "../../lib/guide-mini-vue.esm.js";
     import {
         ArrayToText
     } from "./ArrayToText.js";
     export const App = {
         name: "App",
         render() {
             return h("div", {},
                 [
                     h('p', {}, '主页'),
                     h(ArrayToText)
                 ]
             )
         },
         setup() {
             return {}
         },
     }
js 复制代码
     // ArrayToText.js
     import {
         h,
         ref
     } from '../../lib/guide-mini-vue.esm.js'

     const nextChildren = "newChildren"
     const prevChildren = [h('div', {}, "A"), h('div', {}, "B")]

     export const ArrayToText = {
         name: "ArrayToText",
         setup() {
             const isChange = ref(false); // 挂载布尔值
             window.isChange = isChange;
             return {
                 isChange,
             }
         },
         render() {
             const self = this;
             return self.isChange === true // 使用布尔值来进行条件判断,方便控制台控制,进行更新为 text
                 ?
                 h("div", {}, nextChildren) : h("div", {}, prevChildren);
         }
     }
js 复制代码
    // TextToArray.js

    import {
        h,
        ref
    } from '../../lib/guide-mini-vue.esm.js'

    const prevChildren = "oldChildren"
    const nextChildren = [h('div', {}, "A"), h('div', {}, "B")]

    export const TextToArray = {
        name: "ArrayToText",
        setup() {
            const isChange = ref(false);
            window.isChange = isChange;
            return {
                isChange,
            }
        },
        render() {
            const self = this;
            return self.isChange === true ?
                h("div", {}, nextChildren) : h("div", {}, prevChildren);
        }
    }
js 复制代码
      import {
          h,
          ref
      } from '../../lib/guide-mini-vue.esm.js'

      const nextChildren = "newChildren"
      const prevChildren = "oldChildren"

      export const TextToText = {
          name: "TextToText",
          setup() {
              const isChange = ref(false);
              window.isChange = isChange;
              return {
                  isChange,
              }
          },
          render() {
              const self = this;
              return self.isChange === true ?
                  h("div", {}, nextChildren) : h("div", {}, prevChildren);
          }
      }
  1. 实现更新 我们更新为 Text,原来的节点是Array
js 复制代码
      // renderer.ts
      // 处理元素节点:如果 n1 不存在则说明是初次挂载(mount),否则为更新(patch)
      // parentComponent 用于在挂载子节点时传递上下文(例如组件实例)
      function processElement(n1, n2, container, parentComponent) {
          if (!n1) {
              // 这里进行初次挂载,将 n2 渲染为真实 DOM 并插入 container
              mountElement(n2, container, parentComponent)
          } else {
              // 已存在则执行补丁流程,将 n1(旧 vnode)更新为 n2(新 vnode)
              patchElement(n1, n2, container)
          }
      }

      function patchElement(n1, n2, container) {

          // 提取旧 props 和 新 props,保证为对象(避免 undefined)
          let oldProps = n1.props || EMPTY_OBJECT
          let newProps = n2.props || EMPTY_OBJECT

          // 复用旧 vnode 的真实元素 el 给新 vnode,后续直接在 el 上进行更新
          let el = n2.el = n1.el
          // 先更新 children 再更新 props(顺序可以影响 DOM 操作和性能)
          patchChildren(n1, n2, el) // ✅    
          // 最后对 props 进行差异更新
          patchProps(el, oldProps, newProps)
      }

      function patchChildren(n1, n2, container) { // ✅  
          // 处理 children 的更新:根据 shapeFlag 判断之前/之后 children 的类型(文本或数组)
          const {
              shapeFlag: prevShapeFlag,
          } = n1
          const {
              shapeFlag: nextShapeFlag,
              children: c2
          } = n2

          // 情况:旧的是数组,新的是文本(Array -> Text)
          if (nextShapeFlag & shapeFlags.TEXT_CHILDREN) {
              if (prevShapeFlag & shapeFlags.ARRAY_CHILDREN) {
                  // 卸载旧的数组子节点(移除对应 DOM)
                  unmountChildren(n1.children)
                  // 将容器设置为新的文本内容
                  hostSetElementText(container, c2)
              }
          }
      }

      function unmountChildren(children) { // ✅  
          // 卸载一组子节点:遍历 children,调用宿主平台的删除方法移除对应的真实元素
          children.forEach(child => {
              const el = child.el
              hostPatchRemove(el) // ✅  
          })
      }
  1. 我们更新为 Text,原来的节点是Text,
js 复制代码
  // renderer.ts
  // 情况:新的是文本(Text)
  function patchChildren(n1, n2, container) {
      const {
          shapeFlag: prevShapeFlag,
          children: c1
      } = n1
      const {
          shapeFlag: nextShapeFlag,
          children: c2
      } = n2

      if (nextShapeFlag & shapeFlags.TEXT_CHILDREN) {
          if (prevShapeFlag & shapeFlags.ARRAY_CHILDREN) {
              // 如果旧的是数组,需要先卸载数组子节点再设置文本
              unmountChildren(n1.children)
              hostSetElementText(container, c2)
          } else { // ✅  旧的也是文本的情况
              // 只有在新旧文本内容不一致时,才替换元素文本,避免不必要的 DOM 操作
              if (c1 !== c2) {
                  hostSetElementText(container, c2)
              }
          }
      }
  }
  // 上面代码的优化版本:将重复逻辑拆成更通用的步骤,先处理数组->其他情况的卸载,再统一处理文本替换
  function patchChildren(n1, n2, container, parentComponent) {
      const {
          shapeFlag: prevShapeFlag,
          children: c1
      } = n1
      const {
          shapeFlag: nextShapeFlag,
          children: c2
      } = n2

      if (nextShapeFlag & shapeFlags.TEXT_CHILDREN) {
          // 如果之前是数组,则先卸载旧数组子节点
          if (prevShapeFlag & shapeFlags.ARRAY_CHILDREN) {
              unmountChildren(n1.children)
          }
          // 只有新旧文本不同,才进行文本替换,减少 DOM 操作
          if (c1 !== c2) {
              hostSetElementText(container, c2)
          }
      }
  }
  1. 我们更新为 Array, 原来的节点是Text
js 复制代码
  // renderer.ts
  function patchChildren(n1, n2, container, parentComponent) {
      const {
          shapeFlag: prevShapeFlag,
          children: c1
      } = n1
      const {
          shapeFlag: nextShapeFlag,
          children: c2
      } = n2

      if (nextShapeFlag & shapeFlags.TEXT_CHILDREN) {
          // 新的是文本的情况(Text)
          if (prevShapeFlag & shapeFlags.ARRAY_CHILDREN) {
              // 旧的是数组 -> 卸载旧子节点
              unmountChildren(n1.children)
          }
          if (c1 !== c2) {
              // 新旧文本内容不同时替换文本
              hostSetElementText(container, c2)
          }
      } else {
          // ✅ 新的是数组类型(Array)
          if (prevShapeFlag & shapeFlags.TEXT_CHILDREN) {
              // 旧的是文本 -> 先清空容器的文本,再挂载新的数组子节点
              hostSetElementText(container, "")
              mountChildren(c2, container, parentComponent)
          } else {
              // 旧的也是数组:这里应当进行更复杂的数组  diff 和更新逻辑(后续实现)
          }
      }
  }
  1. 我们更新为 Array, 原来的节点是Array,对比比较复杂,使用到了 diff 算法,下一节更新
相关推荐
choke2334 分钟前
[特殊字符] Python 文件与路径操作
java·前端·javascript
云飞云共享云桌面7 分钟前
高性能图形工作站的资源如何共享给10个SolidWorks研发设计用
linux·运维·服务器·前端·网络·数据库·人工智能
wqq631085516 分钟前
Python基于Vue的实验室管理系统 django flask pycharm
vue.js·python·django
Deng94520131419 分钟前
Vue + Flask 前后端分离项目实战:从零搭建一个完整博客系统
前端·vue.js·flask
威迪斯特21 分钟前
Flask:轻量级Web框架的技术本质与工程实践
前端·数据库·后端·python·flask·开发框架·核心架构
Hello.Reader1 小时前
Flink 文件系统通用配置默认文件系统与连接数限制实战
vue.js·flink·npm
wuhen_n1 小时前
JavaScript内置数据结构
开发语言·前端·javascript·数据结构
大鱼前端1 小时前
为什么我说CSS-in-JS是前端“最佳”的糟粕设计?
前端
不爱吃糖的程序媛1 小时前
Capacitor:跨平台Web原生应用开发利器,现已全面适配鸿蒙
前端·华为·harmonyos
AC赳赳老秦1 小时前
2026国产算力新周期:DeepSeek实战适配英伟达H200,引领大模型训练效率跃升
大数据·前端·人工智能·算法·tidb·memcache·deepseek