zl程序教程

您现在的位置是:首页 >  前端

当前栏目

【三十天精通Vue 3】第十六天 Vue 3 的虚拟 DOM 原理详解

Vue原理 详解 精通 虚拟 dom 第十六
2023-09-14 09:14:15 时间

请添加图片描述

引言

Vue 3 的虚拟 DOM 是一种用于优化 Vue 应用程序性能的技术。它通过将组件实例转换为虚拟 DOM,并在组件更新时递归地更新虚拟 DOM,以达到高效的渲染性能。在 Vue 3 中,虚拟 DOM 树由 VNode 组成,VNode 是虚拟 DOM 的基本单元。VNode 具有自己的类型和结构,并且可以通过补丁算法进行更新。

一、Vue 3 的虚拟 DOM 树结构

未命名.png

1.1 VNode 的基本结构

一个 VNode 对象的基本结构如下:

{
  // 节点类型,比如元素节点、文本节点等
  type: String | Function,
  // 节点的 props,比如 class、style、事件等
  props: Object,
  // 节点的子节点,可以是一个 VNode 数组或者是文本内容
  children: Array<VNode> | String | Number | null,
  // 节点的 key 值,用于优化 diff 算法
  key: String | Number | null,
  // 节点的 ref 值,用于访问该节点的引用
  ref: String | Function | null
}

其中,type 表示节点的类型,可以是元素节点的标签名,也可以是组件的构造函数;props 表示节点的属性,包括 class、style、事件等;children 表示节点的子节点,可以是一个 VNode 数组或者是文本内容;key 表示节点的 key 值,用于优化 diff 算法;ref 表示节点的 ref 值,用于访问该节点的引用。

1.2 VNode 的类型

在 Vue 3 中,VNode 的类型可以分为以下几种:

  • 元素节点:表示一个 HTML 元素,比如 <div><p> 等;
  • 组件节点:表示一个 Vue 组件,比如 <MyComponent>
  • 文本节点:表示一个纯文本节点,比如 Hello, world!
  • 注释节点:表示一个注释节点,比如 <!-- 注释内容 -->

不同类型的 VNode 会在渲染时使用不同的逻辑进行处理。

1.3 VNode 的 Patch 补丁算法

在 Vue 3 中,使用了 Patch 补丁算法来比较新旧 VNode 树的差异,并进行 DOM 更新。Patch 补丁算法包括以下几个步骤:

  1. 首先比较新旧 VNode 的类型和 key 值,如果不相同,则直接替换节点;

  2. 如果新旧 VNode 的类型和 key 值相同,则比较它们的属性和子节点:

    • 如果属性不同,则更新节点的属性;
    • 如果子节点不同,则递归比较子节点,并更新 DOM。
  3. 如果旧VNode没有子节点,但新VNode有子节点,则将旧VNode的文本内容清空,并添加新的子节点;

  4. 如果新VNode没有子节点,但旧VNode有子节点,则删除旧的子节点;

  5. 如果新旧 VNode 都是文本节点,则直接更新文本内容;

  6. 如果新旧 VNode 的子节点都是同类型的节点,就会使用 Diff 算法来进一步比较子节点的差异,并进行 DOM 更新。

Patch 补丁算法主要的优点是能够最小化 DOM 操作,减少页面重绘的次数,提高性能。而且它可以在非常高效的时间内对大型的 VNode 树进行更新操作。

下面是一个简单的示例,展示了如何使用 Patch 补丁算法来更新 DOM:

<div id="app"></div>
javascriptCopy code
// 创建旧的 VNode 树
const oldVNode = h('div', { class: 'container' }, [
  h('h1', { style: 'color: red' }, 'Hello World!'),
  h('p', 'This is a paragraph.')
])// 将旧的 VNode 渲染到页面上
render(oldVNode, document.getElementById('app'))// 创建新的 VNode 树
const newVNode = h('div', { class: 'container' }, [
  h('h1', { style: 'color: green' }, 'Hello Vue 3!'),
  h('p', 'This is a new paragraph.')
])// 使用 Patch 补丁算法比较新旧 VNode,更新 DOM
patch(oldVNode, newVNode)

在上面的示例中,我们首先创建了一个旧的 VNode 树,并将它渲染到页面上。然后创建了一个新的 VNode 树,并使用 Patch 补丁算法将新旧 VNode 进行比较和更新。在更新过程中,我们只需要更新了两个节点的属性和文本内容,而不需要重新创建整个 DOM 树,从而提高了性能。

二、Vue 3 的虚拟 DOM 更新过程

2.1 Diff 算法的过程

2.1.1 Diff 算法的核心思想

在更新 DOM 的过程中,Diff 算法的核心思想是找出新旧 VNode 树之间的差异,并只更新差异部分的 DOM,从而避免不必要的 DOM 操作,提高性能。

Diff 算法会对比新旧 VNode 树的节点,找出它们之间的差异。为了提高效率,Diff 算法会采用一些优化策略,如只比较同级节点、尽早终止比较、复用已有节点等。

2.1.2 Diff 算法的实现过程

  1. 比较两个数据结构之间的差异。

    • 差异可以表示为行差异或列差异。
    • 行差异:左数据结构中的行与右数据结构中的行不同。
    • 列差异:左数据结构中的列与右数据结构中的列不同。
  2. 将差异划分为行差异和列差异。

    • 将行差异转换为列差异。
    • 将列差异转换为行差异。
  3. 更新数据结构中的差异部分。

    • 将行差异或列差异更新到数据结构的对应位置。
    • 对于具有相同值的列,可以将其更新为相同的值。
    • 对于具有不同值的列,需要将其更新为新的值。

    下面是 Diff 算法的伪代码实现:

Diff(left, right)  
  if left == right  return []  
  if left.length == 0 or right.length == 0 return [left, right]  
  left_arr = Array(left.length)  
  right_arr = Array(right.length)  
  for i in 0..left.length-1  
    for j in 0..right.length-1  
      if left[i] == right[j]  left_arr[i] = right_arr[j]  
      else  
        left_arr[i] = Diff.diff_row(left[i], right[j])  
        right_arr[j] = Diff.diff_row(right[j], left[i])  
  return Diff.diff_col(left_arr, right_arr)  

2.2 Diff 算法的优化

2.2.1 Diff 算法的时间复杂度

Diff 算法的时间复杂度取决于 VNode 树的结构和节点数量,一般情况下是 O(n^3)。为了提高性能,Vue 3 中实现了一些优化策略,如只比较同级节点、尽早终止比较、复用已有节点等。

2.2.2 Key 值的作用

Key 值不仅能够帮助 Diff 算法建立节点之间的对应关系,还能够帮助优化 DOM 更新的过程。在更新 DOM 的时候,如果新旧 VNode 的 key 值相同,则可以认为它们是同一个节点,此时可以直接复用旧节点的 DOM 元素,而不需要进行 DOM 的删除和创建操作,从而提高 DOM 更新的效率。

此外,如果没有为节点指定 key 值,则 Diff 算法将默认使用节点在 VNode 树中的位置作为其 key 值。这种情况下,如果 VNode 树中的节点顺序发生变化,Diff 算法会误认为这是节点发生了变化,从而导致不必要的 DOM 更新。因此,在开发中,建议为每个节点指定唯一的 key 值,从而避免这种情况的发生。

2.2.3 双端比较算法

在 Diff 算法中,双端比较算法是一种常用的优化策略。该算法的核心思想是,从新旧 VNode 树的两端开始比较节点,如果发现新旧节点不同,则直接退出比较。这种方法可以有效地减少不必要的比较操作,从而提高 Diff 算法的效率。

2.2.4 Diff 算法的边界情况

在使用 Diff 算法时,需要注意一些边界情况,如以下几种情况:

  • 在进行 Diff 算法时,如果新 VNode 树为空,则直接删除旧 VNode 树中的所有节点;
  • 在进行 Diff 算法时,如果旧 VNode 树为空,则直接创建新 VNode 树中的所有节点;
  • 在进行 Diff 算法时,如果新旧 VNode 的类型不同,则直接替换节点;
  • 在进行 Diff 算法时,如果新旧 VNode 的 key 值不同,则认为它们是不同的节点,直接替换节点;
  • 在进行 Diff 算法时,如果新旧 VNode 的属性不同,则直接更新节点的属性;
  • 在进行 Diff 算法时,如果新旧 VNode 的子节点不同,则递归比较子节点,直到更新完所有子节点。

三、Vue 3 的虚拟 DOM 渲染流程

1.png

3.1 模板编译器的作用

3.1.1 模板编译器的过程

在 Vue 3 中,模板编译器的主要作用是将模板字符串转换为渲染函数。渲染函数是一个 JavaScript 函数,用于渲染组件的虚拟 DOM 树。

模板编译器的过程主要包括以下几个步骤:

  1. 解析模板字符串,生成抽象语法树(AST)。
  2. 遍历抽象语法树,生成渲染函数的代码。
  3. 将渲染函数的代码转换为 JavaScript 代码,并编译为可执行的函数。

在 Vue 3 中,模板编译器是可选的,也就是说,你可以使用手写的渲染函数代替模板编译器生成的渲染函数。

3.1.2 模板编译器的性能优化

为了提高模板编译器的性能,Vue 3 引入了以下几种优化方式:

  1. 缓存编译结果:将编译后的渲染函数缓存起来,下次渲染时直接使用缓存的渲染函数。
  2. 静态提升:将静态节点提升为常量,在渲染时只需要创建一次静态节点。
  3. 静态节点提取:将静态节点提取到单独的 VNode 中,避免每次重新渲染时都重新创建静态节点。
  4. 模板 inlining:将小型的模板内联到父级模板中,减少了模板编译器的工作量。

3.2 Vue 3 的渲染流程

3.2.1 Vue 3 的初始化流程

在初始化阶段,Vue 3 会做以下几件事情:

  1. 初始化组件实例:Vue 3 在创建组件实例时,会创建一个渲染上下文(render context)对象,并将其作为组件实例的属性 $vnode 存储起来。
  2. 创建虚拟 DOM 树:Vue 3 会通过调用 render 函数生成一个虚拟 DOM 树,并将其存储在渲染上下文对象中的 $vnode 属性中。
  3. 将虚拟 DOM 树转换成真实 DOM:Vue 3 会将 $vnode 属性中的虚拟 DOM 树转换成真实 DOM 树,并将其挂载到组件的根 DOM 元素上。

3.2.2 Vue 3 的更新流程

在更新阶段,Vue 3 会做以下几件事情:

  1. 判断是否需要更新:Vue 3 会通过比较新旧虚拟 DOM 树来判断组件是否需要更新。
  2. 执行更新:如果需要更新,Vue 3 会执行更新操作。更新操作包括计算出新的虚拟 DOM 树、比较新旧虚拟 DOM 树的差异、应用差异到真实 DOM 树上。
  3. 更新组件实例:更新组件的状态,包括 props 和 data 等属性的更新。

3.2.3 Vue 3 的卸载流程

在卸载阶段,Vue 3 会做以下几件事情:

  1. 执行 beforeUnmount 钩子函数:在组件实例被卸载之前,Vue 3 会执行组件的 beforeUnmount 钩子函数。
  2. 卸载子组件:Vue 3 会递归地卸载所有子组件。
  3. 卸载组件实例:Vue 3 会将组件实例从父组件中移除,并执行组件的 destroyed 钩子函数。同时,Vue 3 会将组件的根 DOM 元素从文档中移除,并销毁与之相关的事件监听器和定时器等资源。

四、Vue 3 的虚拟 DOM 与 React 的比较

2.png

4.1 Vue 3 的虚拟 DOM 与 React 的区别

  1. 模板语法 vs JSX: Vue 3 使用类似于 HTML 的模板语法,而 React 使用 JSX,一种类似于 JavaScript 的语法,需要使用特定的编译器转换为 JavaScript 代码。因此,Vue 3 更适合那些熟悉 HTML 的开发者,而 React 更适合那些更熟悉 JavaScript 的开发者。
  2. 响应式系统: Vue 3 内置了响应式系统,使得当状态发生改变时,组件能够自动地重新渲染。React 中需要使用 state 和 props 来管理组件的状态和属性,但并没有内置响应式系统。
  3. 性能优化: Vue 3 采用了静态分析技术,可以在编译时对模板进行优化,生成高效的渲染函数,从而提高渲染性能。React 使用了虚拟 DOM 技术,通过比较前后两个虚拟 DOM 树的差异,最小化 DOM 操作的次数,从而提高性能。
  4. API 设计: Vue 3 的 API 更加简单明了,通过一些简单的配置和选项,就能完成很多常见的操作,如组件化、路由、状态管理等。React 的 API 设计更加灵活,提供了更多的可定制化和可扩展性。

4.2 Vue 3 的虚拟 DOM 与 React 的共同点

  1. 虚拟 DOM: Vue 3 和 React 都使用虚拟 DOM 技术,通过在内存中构建虚拟 DOM 树来减少 DOM 操作,从而提高性能。
  2. 组件化: Vue 3 和 React 都支持组件化开发,将 UI 拆分为独立的组件,使得代码更加可维护、可重用。
  3. 单向数据流: Vue 3 和 React 都遵循单向数据流的原则,即数据只能从父组件向子组件传递,子组件不能直接修改父组件的数据。这种机制使得应用程序更加可靠,易于调试和维护。
  4. 生命周期函数: Vue 3 和 React 都提供了一些生命周期函数,允许开发者在组件生命周期的不同阶段执行一些操作,如组件挂载、更新、卸载等。这些生命周期函数使得开发者能够更好地管理组件的状态和行为。

五、Vue 3 的虚拟 DOM 的应用

4.png

5.1 Vue 3 的虚拟 DOM 在组件化开发中的应用

在 Vue 3 中,组件是基本的构建块,因此使用虚拟 DOM 的优势在于组件的渲染和更新。每个组件都有自己的虚拟 DOM 树,这使得 Vue 3 在渲染组件时更加高效和快速。当组件的状态发生变化时,Vue 3 将仅更新该组件的虚拟 DOM 树,而不是重新渲染整个页面。

此外,Vue 3 还引入了 Teleport 组件,它可以使组件在 DOM 树中的位置移动而不会影响其状态。这对于需要在页面上移动或动态显示的组件非常有用,例如弹出框或下拉菜单。

5.2 Vue 3 的虚拟 DOM 在动态组件中的应用

在 Vue 3 中,动态组件是一种允许组件动态切换的技术。这使得开发者可以根据应用程序的需要,在不同的组件之间进行快速的切换,而不需要重新加载整个页面。这种技术在构建单页应用程序时非常有用。

Vue 3 的虚拟 DOM 可以非常有效地渲染和更新动态组件,使其在应用程序中具有更高的性能和可靠性。同时,使用 Vue 3 的虚拟 DOM 还可以更轻松地管理动态组件之间的状态,并确保在切换组件时不会丢失状态信息。

六、Vue 3 的虚拟 DOM 的优势和不足

33.png

6.1 Vue 3 的虚拟 DOM 的优势

以下是 Vue 3 的虚拟 DOM 的优势:

  1. 性能提升:Vue 3 的虚拟 DOM 采用了优化策略,使得在更新组件时只更新必要的部分,从而提高了性能。
  2. 更好的可维护性:通过将组件的结构抽象为虚拟 DOM,可以更好地进行组件的维护和管理,也方便进行单元测试。
  3. 更好的跨平台兼容性:通过使用虚拟 DOM,Vue 3 可以将组件的渲染方式抽象为函数调用,从而实现跨平台的渲染兼容性,例如在浏览器、服务器端渲染等环境中都可以使用同样的代码渲染组件。
  4. 更好的动画支持:Vue 3 的虚拟 DOM 支持通过 transition、animation 等方式进行动画渲染,从而提供更好的动画效果。
  5. 更好的开发体验:通过使用虚拟 DOM,开发者可以在开发过程中方便地进行组件的调试和修改,从而提高了开发效率。

6.2 Vue 3 的虚拟 DOM 的不足

以下是 Vue 3 的虚拟 DOM 的不足:

  1. 内存占用较高:由于虚拟 DOM 需要在内存中维护组件树的状态,因此在大型应用中可能会占用较多的内存资源。
  2. 学习成本高:Vue 3 的虚拟 DOM 需要掌握一定的概念和使用方法,因此学习成本可能较高。
  3. 不适用于所有场景:在一些简单的场景下,使用虚拟 DOM 可能会增加代码复杂度,不如直接操作 DOM。

七、Vue 3 的虚拟 DOM 的最佳实践

222.png

7.1 使用响应式数据更新 VNode 树

在 Vue 2 中,当我们更新数据时,需要手动触发更新 DOM 的操作。在 Vue 3 中,我们可以通过使用 data 选项来定义虚拟 DOM 的数据,并通过使用 updateVirtualDOM 方法来更新虚拟 DOM。当我们需要更新虚拟 DOM 时,我们可以使用 updateVirtualDOM 方法,该方法接受两个参数:要更新的虚拟 DOM 树和新的虚拟 DOM 树。

下面是一个使用 updateVirtualDOM 方法更新虚拟 DOM 树的示例:

export default {  
  setup() {  
    const socket = io();return {  
      socketMessage(data) {  
        this.$updateVirtualDOM(  
          data.message,  
          JSON.parse(JSON.stringify(data.message))  
        );  
      },  
    };  
  },  
};  

在上面的示例中,当接收到消息时,我们通过调用 $updateVirtualDOM 方法更新虚拟 DOM 树。这个方法接受两个参数:要更新的虚拟 DOM 树和新的虚拟 DOM 树。在更新虚拟 DOM 树时,我们将新的数据解析成 JSON 字符串,并将其作为第一个参数传递给 updateVirtualDOM 方法。第一个参数指定了要更新的虚拟 DOM 树,第二个参数指定了更新后的虚拟 DOM 树。

7.2 使用 Key 值进行优化

在 Vue 2 中,当我们更新数据时,我们需要手动触发更新 DOM 的操作。这可能会导致性能问题,因为每次数据更新时,Vue 都会重新渲染整个组件。在 Vue 3 中,我们可以通过使用 updateVirtualDOM 方法来更新虚拟 DOM,这可以大大提高性能。然而,仍然存在一些性能问题,特别是在大型组件中。为了进一步提高性能,我们可以使用 key 值对组件进行优化。

在 Vue 3 中,key 值的作用是为组件生成唯一的标识符。当组件被重新渲染时,key 值会发生变化,这使得 Vue 无法直接渲染整个组件,而是只重新渲染需要更新的部分。下面是一个简单的示例:

export default {  
  data() {  
    return {  
      message: 'Hello Vue 3!',  
    };  
  },  
  methods: {  
    updateMessage() {  
      this.message = 'Hello Vue 3!';  
    },  
  },  
  setup() {  
    const socket = io();return {  
      socketMessage(data) {  
        this.$updateVirtualDOM(  
          { message: data.message },  
          JSON.parse(JSON.stringify({ message: data.message })))  
      },  
    };  
  },  
};  

在上面的示例中,当接收到消息时,我们通过调用 $updateVirtualDOM 方法更新虚拟 DOM 树。在这个示例中,我们将新的 message 值作为第一个参数传递给 updateVirtualDOM 方法,并将其作为第二个参数传递给方法 socketMessage。这样,Vue 3 将只重新渲染需要更新的部分,从而提高性能。

在 Vue 3 中,我们可以通过使用 Keep-Alive 组件来缓存组件,从而减少不必要的虚拟 DOM 渲染。Keep-Alive 组件是一个内置组件,它可以将挂载在其上的组件缓存起来,只有在组件主动被卸载或重新挂载时才会真正重新渲染。

下面是一个简单的 Keep-Alive 组件示例:

import { keepAlive } from 'vue';export default {  
  name: 'KeepAliveExample',  
  components: {  
    KeepAlive: keepAlive({  
      cache: true,  
      updateOn: 'load',  
      bind: true,  
    }),  
  },  
};  

在上面的示例中,我们使用 keepAlive 组件来缓存一个组件。注意,缓存组件的 key 应该使用一个唯一的标识符,例如组件名称加上版本号。在组件被重新挂载时,Vue 会检查该组件的缓存是否存在,如果存在,则直接使用缓存,否则重新渲染组件。

使用 Keep-Alive 组件可以有效地减少组件重新渲染的次数,提高页面渲染效率。

7.3 减少不必要的 DOM 操作

在 Vue 3 中,我们可以通过优化组件的生命周期方法来减少不必要的 DOM 操作。在 Vue 3 中,组件的生命周期方法包括:beforeCreate、created、beforeMount、mounted、beforeUnmount 和 destroyed。我们可以在这些生命周期方法中执行一些操作,例如更新数据或更新 DOM,但这些操作并不一定需要在每次渲染时执行。

下面是一个简单的示例:

import { createMount } from 'vue';export default {  
  name: 'MyComponent',  
  setup() {  
    const cache = createMount(this, {  
      data() {  
        return {  
          value: 'initial value',  
        };  
      },  
      props: {  
        value: {  
          type: String,  
          default: '',  
        },  
      },  
      ref: 'my-component',  
    });return {  
      cache,  
    };  
  },  
};  

在上面的示例中,我们创建了一个缓存组件,并在其 setup 方法中使用 createMount 函数来创建缓存组件。注意,在 setup 方法中,我们可以使用缓存组件的 ref 属性来访问缓存组件。这可以让我们在每次渲染时都使用相同的 DOM 元素,而不必每次都创建一个新的 DOM 元素。

通过使用缓存组件和优化组件的生命周期方法,我们可以有效减少不必要的 DOM 操作,从而提高页面渲染效率。

7.4 避免频繁的组件卸载和重新挂载

在 Vue 3 中,我们可以通过避免频繁的组件卸载和重新挂载来提高页面渲染效率。在 Vue 3 中,组件的卸载和重新挂载过程是非常耗时的,因为它们需要重新渲染整个组件。因此,我们应该尽可能避免频繁地使用组件卸载和重新挂载。

下面是一个简单的示例:

import { createMount } from 'vue';export default {  
  name: 'MyComponent',  
  setup() {  
    const cache = createMount(this, {  
      data() {  
        return {  
          value: 'initial value',  
        };  
      },  
      props: {  
        value: {  
          type: String,  
          default: '',  
        },  
      },  
      ref: 'my-component',  
    });// 添加一些定时器,用于在每次渲染后等待一段时间  
    cache.$nextTick(() => {  
      setTimeout(() => {  
        // 执行一些操作,例如更新 DOM 或更新数据  
        cache.$forceUpdate();  
      }, 500);  
    });return {  
      cache,  
    };  
  },  
};  

在上面的示例中,我们创建了一个缓存组件,并在其 setup 方法中使用 createMount 函数来创建缓存组件。我们还在缓存组件中添加了一些定时器,用于在每次渲染后等待一段时间。这可以让我们在每次渲染时都可以有效地避免频繁的组件卸载和重新挂载。

八、Vue 3 的虚拟 DOM 的常见问题及解决方案

8.1 如何提高 VNode 的性能

以下是一些提高 VNode 性能的方法:

  1. 避免不必要的渲染:Vue 3 会根据响应式数据自动重新渲染页面,但是有时候我们并不需要重新渲染整个页面,可以使用 Vue 3 提供的 shouldUpdate 方法来判断是否需要重新渲染组件。
  2. 合理使用计算属性:计算属性可以缓存一些计算结果,避免重复计算,提高性能。
  3. 减少 VNode 的层级:VNode 的层级越深,渲染所需的时间就越长。因此,尽量将组件的嵌套层级降到最低。
  4. 使用函数式组件:函数式组件没有响应式数据,也没有实例,因此渲染速度更快。
  5. 合理使用异步组件:异步组件可以将一些不必要的组件延迟加载,提高页面的加载速度。

2211.png

8.2 如何使用 Key 值进行优化

在渲染列表时,使用 key 值可以帮助 Vue 3 更好地跟踪每个 VNode 的状态,从而提高渲染性能。以下是一些使用 key 值进行优化的方法:

  1. 确保 key 值具有唯一性:每个 key 值都应该是唯一的,这样 Vue 3 才能正确地追踪每个 VNode 的状态。
  2. 不要使用索引作为 key 值:使用索引作为 key 值可能会导致渲染错误,因为当列表的顺序发生变化时,索引也会发生变化,从而导致 key 值不唯一。
  3. 使用动态 key 值:在一些情况下,动态生成 key 值可以更好地满足需求,比如在渲染动态组件时。

8.3 如何使用 Keep-Alive 缓存组件

使用 Keep-Alive 缓存组件可以避免频繁的组件销毁和创建,从而提高页面的性能。以下是一些使用 Keep-Alive 的方法:

  1. 在组件外层包裹一个 Keep-Alive 组件:这样包裹的组件会被缓存起来,当下次需要渲染时,就会直接使用缓存中的组件,而不是重新创建。
  2. 在需要缓存的组件上添加一个 name 属性:这样 Vue 3 才能正确地缓存该组件。
  3. 在需要销毁缓存的组件时,使用 $destroy 方法:这样可以手动销毁缓存的组件,从而释放内存。

在这里插入图片描述