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

请添加图片描述

引言

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 方法:这样可以手动销毁缓存的组件,从而释放内存。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

新黑马头条项目经验(黑马)

swagger (1)简介 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(API Documentation & Design Tools for Teams | Swagger)。 它的主要作用是&#xff1a; 使得前后端分离开发更加方便&#xff0c;有利于团队协作 接…

HCIP之RSTP、MSTP

目录 RSTP 相较于802.1D改进 改进1&#xff1a;变更了端口角色 改进点2&#xff1a;修改了端口的状态类型 改进3&#xff1a;对配置BPDU的报文内容进行修改 改进点4&#xff1a;对配置BPDU的处理 改进点5&#xff1a;快速收敛机制 改进点6&#xff1a;拓扑变更机制的改进…

学电路设计时,你遇到过什么有趣的事?

说几个学生时代的傻x事&#xff1a; 1、以前对DC-DC懂得少&#xff0c;而且一般开关电源芯片小&#xff0c;还有一堆外围&#xff0c;手焊很麻烦&#xff0c;就觉得三端稳压器碉堡了啊&#xff0c;一个就能得到想要的电压啊&#xff0c;有木有。然后就各种用三端稳压器。那玩意…

无源滤波器为什么能滤波?

滤波器能够滤波的本质是利用构造特定的阻抗特性引起反射和损耗来实现对频率的选择。 如果从能量守恒的角度来讲&#xff0c;被抑制掉的信号去哪里了&#xff1f;​ 我们先看一下基本电路原理&#xff0c;上图中&#xff0c;负载接收的功率为 我们知道&#xff0c;最大功率传输…

【大数据之Hadoop】十八、MapReduce之压缩

1 概述 优点&#xff1a;减少磁盘IO、减少磁盘存储空间。 缺点&#xff1a;因为压缩解压缩都需要cpu处理&#xff0c;所以增加CPU开销。 原则&#xff1a;运算密集型的Job&#xff0c;少用压缩&#xff1b;IO密集型的Job&#xff0c;多用压缩。 2 压缩算法对比 压缩方式选择时…

广州蓝景分享—快速了解Typescript 5.0 中重要的新功能

作为一种在开发人员中越来越受欢迎的编程语言&#xff0c;TypeScript 不断发展&#xff0c;带来了大量的改进和新功能。在本文中&#xff0c;我们将深入研究 TypeScript 的最新迭代版本 5.0&#xff0c;并探索其最值得注意的更新。 1.装饰器 TypeScript 5.0 引入了改进的装饰…

AI绘画——Checkpoint模型Dark Sushi Mix 大颗寿司Mix

目录 版本解析 模型简介 模型特性 模型演示&#xff08;多图预警&#xff09; Picture One 正面tag&#xff1a; 负面tag&#xff1a; Checkpoint模型darkSushiMixMix无Vae Checkpoint模型darkSushiMixMixVae模型kl-f8-anime2.ckpt Picture Two 正面tag&#xff1a;…

什么是OADM光分插复用器

文章导读&#xff1a; 什么是OADM光分插复用器 光分插复用器的功能 光分插复用器的类型&#xff08;FOADM, TOADM&#xff09; OADM的应用 1、什么是OADM光分插复用器 由不同的光通道进出单模光纤。 它的主要功能是在不影响其他波长信道传输的情况下&#xff0c;选择性地下载或…

python 的 object 与type的关系

python 的 object 与type的关系 是并列关系&#xff0c;两种是相互依赖的 查询父类 type.__bases__ object.__bases__(<class ‘object’>,) () 查询类型 type(type) type(object)<class ‘type’> <class ‘type’> 在python中&#xff0c;type用于描述…

开放原子训练营(第一季)铜锁探密:基于铜锁构建在线在线加密工具箱

基于铜锁构建Web在线加密工具库 搭建运行环境 实验⼿册中的实验都是以 docker 和 docker-compose 环境为主&#xff0c;基于 Ubuntu 20.04 容器镜像。 初始化项目 首先利用 IDE 创建一个 tongsuo_web 的空项目&#xff0c;接下来我们所有的文件都会创建在该项目中&#xff0…

ThinkPHP模型操作下

ThinkPHP模型操作下 前言1. 模型设置1.name(数据表除去前后缀的名字&#xff0c;默认是当前model的类名)2.table(完整的数据表名)3.pk 改变主键名称4.schema 设置模型对应数据表字段及类型5.disuse 数据表废弃字段&#xff08;数组&#xff09;6.模型的其他属性 2. 模型的主要功…

E5EAA HENF105240R1将用于工业生产过程的测量、控制和管理

​E5EAA HENF105240R1将用于工业生产过程的测量、控制和管理 工业控制计算机是工业自动化控制系统的核心设备 工业控制计算机是工业自动化设备和信息产业基础设备的核心。传统意义上&#xff0c;将用于工业生产过程的测量、控制和管理的计算机统称为工业控制计算机&#xff0c;…

JVM学习(八):运行时数据区——虚拟机栈(字节码程度深入剖析)

目录 一、概述 1.1 基于栈结构的虚拟机 1.2 栈和堆 二、虚拟机栈&#xff08;Java Virtual Machine Stack&#xff09;详述 2.1 虚拟机栈介绍 2.2 虚拟机栈作用 2.3 虚拟机栈特点 三、栈中常见的异常 3.1 StackOverflowError异常 3.2 OutOfMemoryError异常 四、…

Linux驱动开发:uboot启动流程详解

前言&#xff1a;uboot作为Linux驱动开发的 “三巨头” 之一&#xff0c;绝对是一座绕不开的大山。当然&#xff0c;即使不去细致了解uboot启动流程依旧不影响开发者对uboot的简单移植。但秉持着知其然知其所以然的学习态度&#xff0c;作者将给读者朋友细致化的过一遍uboot启动…

UE4 架构初识(二)

目录 UE4 引擎学习 一、架构基础 1. Pawn &#xff08;1&#xff09;DefaultPawn &#xff08;2&#xff09;SpectatorPawn &#xff08;3&#xff09;Character 2. AController 3. APlayerState 4. 总结 UE4 引擎学习 一、架构基础 1. Pawn UE也是从Actor中再派生…

【小程序】input输入双向数据绑定

小程序中&#xff0c;input标签中的数据为单向绑定&#xff1a; <inputtype"number"bindinput"inputRealmoney"value"{{ amount }}"placeholder"请输入金额" />如上代码&#xff0c;我们绑定了输入框的数据amount&#xff0c;并…

JavaSE-06 [面向对象OOP + 封装]

JavaSE-06 [面向对象OOP 封装] 第一章 面向对象思想 1.1 面向过程和面向对象 面向过程&#xff1a; 面向过程就是分析出解决问题所需要的步骤&#xff0c;然后用函数把这些步骤一步一步实现&#xff0c;使用的时候一个一个依次调用就可以了面向对象&#xff1a; 面向对象是…

MITA触摸屏维修WP4053米塔工控机控制屏维修

MITA-TEKNIK米塔触摸屏维修工控机工控屏控制器维修DISPLAY 2COM全系列型号 Mita-Teknik触摸屏维修常见故障&#xff1a;上电无显示&#xff0c;运行报故障&#xff0c;无法与电脑通讯&#xff0c;触摸无反应&#xff0c;触控板破裂&#xff0c;触摸玻璃&#xff0c;上电黑屏&a…

云原生|kubernetes|rancher-2.6.4安装部署简明手册

前言: rancher是一个比较特殊的开源的kubernetes管理工具&#xff0c;特殊在它是一个名称为k3s的简单kubernetes集群&#xff0c;而该集群是在kubernetes集群内的。 OK&#xff0c;本文将讲述如何在centos7服务器上&#xff0c;在已有的kubernetes-1.23.15集群内&#xff0c;…

springboot,Flowable 流程实例的激活与挂起(二)

一.简介 接上一篇 springboot&#xff0c;Flowable 流程实例的激活与挂起&#xff08;一&#xff09; 二.流程实例的挂起与激活 1.流程实例的挂起 挂起一个流程实例的代码如下&#xff1a; Test void test08() {List<ProcessDefinition> list repositoryService.cr…