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

news2024/11/20 8:44:34

请添加图片描述

引言

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PTA L1-096 谁管谁叫爹 (20 分)

《咱俩谁管谁叫爹》是网上一首搞笑饶舌歌曲&#xff0c;来源于东北酒桌上的助兴游戏。现在我们把这个游戏的难度拔高一点&#xff0c;多耗一些智商。 不妨设游戏中的两个人为 A 和 B。游戏开始后&#xff0c;两人同时报出两个整数 N A N_A NA​​ 和 N B ​ N_B​ NB​​ 。判…

C语言函数大全-- n 开头的函数

C语言函数大全 本篇介绍C语言函数大全-- n 开头的函数 1. nan 1.1 函数说明 函数声明函数功能double nan(const char *tagp);用于返回一个表示 NaN&#xff08;非数值&#xff09;的 double 类型数字 参数&#xff1a; tagp &#xff1a; 指向字符串的指针&#xff1b;用于…

Tomcat 配置与部署

http 协议就是 http 客户端和 http 服务器之间通信的协议 , 而Tomcat 就是 java 圈子中最广泛使用的 http 服务器. 下载Tomcat Tomcat官网 Tomcat 的版本 , 和后续的 servlet 版本是强相关的 , 此处使用 tomcat 8 , 对应的 servlet 就是 3.1 下载一个 zip 压缩包解压缩即可 T…

探索【Stable-Diffusion WEBUI】的插件:骨骼姿态(OpenPose)

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;骨骼姿态&#xff08;OpenPose&#xff09;系列插件&#xff08;二&#xff09;插件&#xff1a;PoseX&#xff08;三&#xff09;插件&#xff1a;Depth Lib&#xff08;四&#xff09;插件&#xff1a;3D …

Spring之IOC和DI入门案例

IOC和DI入门案例 1. IOC入门案例1.1 门案例思路分析1.2 实现步骤1.3 实现代码1.4 运行结果 2. DI入门案例2.1 DI入门案例思路分析2.2 实现步骤2.3 实现代码2.4 图解演示 1. IOC入门案例 问题导入 <bean>标签中id属性和class属性的作用是什么&#xff1f; 1.1 门案例思…

金三银四总计面试碰壁15次,作为一个27岁的测试工程师.....

3年测试经验原来什么都不是&#xff0c;只是给你的简历上画了一笔&#xff0c;一直觉得经验多&#xff0c;无论在哪都能找到满意的工作&#xff0c;但是现实却是给我打了一个大巴掌&#xff01;事后也不会给糖的那种... 先说一下自己的个人情况&#xff0c;普通二本计算机专业…

023:Mapbox GL加载mp4视频文件

第023个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载MP4视频文件。一个视频源。 “urls”值是一个数组。 对于数组中的每个 URL,将创建一个视频元素源。 要支持跨浏览器的视频,请提供多种格式的 URL。“坐标”数组包含按顺时针顺序列出的视频角的 [longi…

【MYSQL】数据库和表的基本操作

目录 1.mysql的工作图&#xff1a; 2.连接mysql服务器 3.mysql的配置文件 4.数据库的操作 5.表的操作 1.mysql的工作图&#xff1a; mysql是一个应用层服务&#xff0c;需要使用安装的mysql客户端&#xff08;也叫mysql&#xff09;连接mysql服务器&#xff08;也叫mysq…

Sa-Token源码简单阅读

一.权限登录模块包括几个基本子模块&#xff1a; 1.登录。 实现方式大致为&#xff1a;先检验用户名密码是否正确&#xff0c;如正确则在缓存中存入用户信息&#xff08;一般必须要有用户标识和访问token&#xff0c;或再加一些附加信息如用户的角色权限&#xff09;&#xf…

国内外4款主流ERP系统评测,哪款最好用?

一、ERP系统的概念 ERP系统&#xff0c;是针对通用各个企业特点研发的ERP软件。由于行业产品结构复杂&#xff0c;导致原料种类众多&#xff0c;制造工艺复杂&#xff0c;外加客户、供应商、物流等不确定因素&#xff0c;传统手工、表格、纸质作业模式难以应对复杂状况&#x…

设计模式之责任链模式(C++)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 一、责任链模式是什么&#xff1f; 责任链模式是一种行为型的软件设计模式&#xff0c;对象内存在对下家的引用&#xff0c;层层连…

【世界读书日】2023年通信好书推荐

今天是世界读书日&#xff08;4月23日&#xff09;。按照老规矩&#xff0c;小编给大家推荐一些通信类的优秀书籍。 过去一年&#xff0c;通信行业的关注热点&#xff0c;主要是&#xff1a;5G-Advanced&#xff08;5.5G&#xff09;、算力网络、东数西算、6G、卫星互联网、智…

历史上的今天大事件查询工具推荐 - 历史上的今天 API

引言 历史上的今天&#xff0c;总会有一些特别的事件发生&#xff0c;这些事件对人类的发展产生了深远的影响。想要了解这些事件&#xff0c;往往需要花费大量的时间和精力去查阅历史资料。但现在&#xff0c;有了历史上的今天 API&#xff0c;一切变得方便了许多。 如果你对…

交友项目【根据id查询单条动态发布评论查询评论列表】

目录 1&#xff1a;根据id查询单条动态 1.1&#xff1a;接口分析 1.2&#xff1a;流程分析 1.3&#xff1a;代码实现 2&#xff1a;发布评论 2.1&#xff1a;接口分析 2.2&#xff1a;流程分析 2.3&#xff1a;代码实现 3&#xff1a;查询评论列表 3.1&#xff1a;接…

Python导出含有中文名文件解决方案

使用Python开发过程中有用到需要导出文件的功能 异常代码 # 代码片段 def return_workbook(self, workbook, model_code, x_io):name “税单.xls”workbook.close()res HttpResponse()res["Content-Type"] "application/octet-stream"res["Conte…

Python 基础(九):列表

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录 一、声明列表二、访问列表元素三、修改列表元素四、添加列表元素4.1、在列表末尾添加元…

京东商品列表数据采集(APP,H5端)

采集场景: 在京东搜索页 https://search.jd.com/Search 输入搜索&#xff0c;搜出后得到的多个商品列表数据。 征地: 商品名称、价格、评论数、店铺名称、店铺链接等字段。 采集结果: 采集结果可导出为Excel&#xff0c;CSV&#xff0c;HTML&#xff0c;数据库等格式。导出…

学习系统编程No.22【消息队列和信号量】

引言&#xff1a; 北京时间&#xff1a;2023/4/20/7:48&#xff0c;闹钟6点和6点30&#xff0c;全部错过&#xff0c;根本起不来&#xff0c;可能是因为感冒还没好&#xff0c;睡不够吧&#xff01;并且今天是星期四&#xff0c;这个星期这是第二篇博客&#xff0c;作为一个日…

Java核心技术 卷1-总结-12

Java核心技术 卷1-总结-12 具体的集合链表数组列表 具体的集合 下表中除了以 Map结尾的类之外&#xff0c; 其他类都实现了 Collection 接口&#xff0c;而以 Map结尾的类实现了 Map 接口。 集合类型描述ArrayList一种可以动态增长和缩减的索引序列LinkedList一种可以在任何位…