vue源码解析——vue如何将template转换为render函数

Vue 将模板(template)转换为渲染函数(render function)是 Vue 编译器的核心功能,它是 Vue 实现响应式和虚拟 DOM 的关键步骤。在 Vue 中,模板(template)是开发者编写的类似 HTML 的代码,用于描述页面的结构和交互逻辑。渲染函数(render function)是 Vue 编译器将模板转换为JavaScript 函数,用于生成虚拟 DOM,并最终渲染到页面上。所以render的作用是生成虚拟dom。你可能会想,为什么vue要写template而不是html?为什么写了template通过render将模板生成虚拟dom,而不是直接生成html呢?请往下看

 vue模板渲染是个很复杂的过程,牵扯vue的响应式和虚拟dom。本文只抓核心流程,不关心具体实现。纵向深入,不做横向扩展,避免罗里吧嗦又抓不住重点。

vue中的template模板

在Vue中,模板(template)指的是开发者编写的类似HTML的代码,用于描述页面的结构、样式和交互逻辑。模板中可以包含Vue特有的指令、数据绑定、事件处理等内容,通过Vue的编译器将模板转换为渲染函数,最终渲染到页面上。

vue2的template

以下是一个简单的Vue2模板示例:比较简单的Vue模板,描述了页面的结构和交互逻辑,通过Vue的数据绑定和事件处理机制实现了动态更新页面内容的功能

<template>
  <div>
    <h1>{{ message }}</h1>
    <p>计数:{{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Vue!',
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
    }
  }
};
</script>

<style>
h1 {
  color: blue;
}
p {
  font-size: 16px;
}
button {
  background-color: green;
  color: white;
  padding: 5px 10px;
  cursor: pointer;
}
</style>

在上面的示例中,<template> 标签内包含了页面的结构,包括一个标题、一个段落和一个按钮。模板中使用了双大括号语法 {{ }} 进行数据绑定,将 message 和 count 的值动态显示在页面上。按钮上使用了 @click 指令绑定了 increment 方法,实现点击按钮增加计数的功能。

vue3的template

以下是一个简单的Vue3模板示例:使用 <script setup> 语法的Vue 3模板,通过更简洁的语法实现了相同的功能。

<template>
  <div>
    <h1>{{ message }}</h1>
    <p>计数:{{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

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

const message = ref('Hello, Vue 3!');
const count = ref(0);

const increment = () => {
  count.value++;
};
</script>

<style>
h1 {
  color: blue;
}
p {
  font-size: 16px;
}
button {
  background-color: green;
  color: white;
  padding: 5px 10px;
  cursor: pointer;
}
</style>

在这个示例中,我们使用 <script setup> 语法来定义组件的逻辑部分。通过 import { ref } from 'vue'; 引入 ref 函数,然后直接在 <script setup> 中定义 message 和 count 两个响应式数据,以及 increment 方法。这样可以更加简洁地管理组件的状态和逻辑。

 思考:为什么vue写成template形式而不是html

Vue.js 使用 template 而不是直接使用 HTML主要考虑了以下几个原因:

  1. 数据绑定:Vue.js 使用了数据绑定技术,可以动态地渲染和更新视图。如果直接使用 HTML,则需要使用一些特殊的语法来实现数据绑定,这会使 HTML 变得复杂和难以维护。
  2. 组件化:Vue.js 是一个基于组件的框架,可以将复杂的应用分解成多个小的组件。如果直接使用 HTML,则无法实现组件化的特性,因为 HTML 本身不支持组件的概念。
  3. 虚拟 DOM:Vue.js 使用了虚拟 DOM 技术,可以更高效地渲染和更新视图。如果直接使用 HTML,则需要使用一些特殊的语法来实现虚拟 DOM,这会使 HTML 变得复杂和难以维护。
  4. 编译优化:Vue.js 在编译期间可以对 template 进行优化,例如将相同的元素合并成一个元素,提高渲染性能。如果直接使用 HTML,则无法进行这种优化。

因此,Vue.js 使用 template 可以更好地支持数据绑定、组件化、虚拟 DOM 和编译优化等特性,同时可以让我们使用简单直观的方式来定义组件的结构和内容。

为什么render不直接将template转成html,而是转成了虚拟dom?

当我们使用 Vue ,往往会将页面拆分为各种组件,通过拼装组件来形成页面和应用,就像搭积木一样。模板编译后组件所产出的内容并不是 html 字符串,而是大家所熟知的 Virtual DOM。

 

组件最核心的东西是 render 函数,剩余的其他内容,如 datacompoutedprops 等都是为 render 函数提供数据来源服务的。render 函数本可以直接产出 html 字符串,但却产出了 Virtual DOMVirtual DOM 终究要渲染真实 DOM,这个过程就可以理解为模板引擎年代的完全替换 html,只不过它采用的不是完全替换,我们通常把这个过程叫做 patch。当数据变更时,组件会产出新的 VNode,我们只需再次调用 patch 函数即可

 为何组件要从直接产出 html 变成产出 Virtual DOM 呢?

其原因是 Virtual DOM 带来了 分层设计,它对渲染过程的抽象,使得框架可以渲染到 web(浏览器) 以外的平台,以及能够实现 SSR 等。至于 Virtual DOM 相比原生 DOM 操作的性能,这并非 Virtual DOM 的目标,确切地说,如果要比较二者的性能是要“控制变量”的,例如:页面的大小、数据变化量等

组件从直接产出 HTML 变成产出 Virtual DOM 的主要原因包括以下几点:

  1. 性能优化:Virtual DOM 可以作为一个轻量级的内存数据结构存在于内存中,通过对 Virtual DOM 进行比对,可以最小化对实际 DOM 的操作,从而提高性能。相比直接操作实际 DOM,Virtual DOM 的比对操作更高效,可以减少不必要的 DOM 操作,提升页面渲染性能。

  2. 跨平台兼容:Virtual DOM 的抽象层可以使得组件的渲染逻辑与具体平台无关,从而实现跨平台兼容。通过 Virtual DOM,可以将组件的渲染逻辑统一抽象,使得组件可以在不同平台上进行渲染,提高了组件的复用性和可移植性。

  3. 方便的状态管理:Virtual DOM 可以轻松地与状态管理库(如 Vuex、Redux 等)结合使用,实现组件状态的管理和更新。通过 Virtual DOM,可以更方便地管理组件的状态变化,实现数据驱动的视图更新。

  4. 简化复杂的 DOM 操作:直接操作实际 DOM 可能会涉及复杂的 DOM 操作,而 Virtual DOM 可以将这些操作抽象成简单的数据结构,使得组件的开发和维护更加简单和高效。

  5. 提高开发效率:通过 Virtual DOM,开发者可以更加专注于组件的逻辑和交互,而不需要过多关注底层的 DOM 操作细节。这样可以提高开发效率,减少开发成本。

综上所述,组件从直接产出 HTML 变成产出 Virtual DOM 主要是为了提高性能、跨平台兼容、方便的状态管理、简化复杂的 DOM 操作以及提高开发效率等方面的考虑。通过 Virtual DOM,可以更好地实现组件化开发和优化页面性能,提升用户体验。

编译VS运行时render

在大多数情况下,Vue会在编译阶段将模板(template)转换为渲染函数。这意味着Vue的编译器会在构建时将模板解析成渲染函数的静态代码,然后将这些静态代码打包到最终的构建文件中。编译时生成的渲染函数可以提高性能,因为它们是预先生成的静态代码,不需要在运行时进行解析和编译。

部分render函数在运行时生成

  • 在某些情况下,特别是在使用Vue的单文件组件(.vue文件)时,渲染函数可能会在运行时动态生成。这种情况下,Vue会在运行时解析模板并生成渲染函数。
  • 在运行时动态生成渲染函数的过程中,Vue会利用编译器将模板转换为可执行的JavaScript代码,以便在每次组件渲染时动态生成虚拟DOM。这种方式相对于编译时生成静态渲染函数会带来一些性能开销,因为需要在每次渲染时进行模板解析和代码生成。

注:使用npm run dev,开发模式下虽然不会生成最终的生产构建文件,但是在使用 npm run dev 启动开发服务器时,Vue 项目中的代码仍然会经历编译和预处理过程,以便在开发服务器上实时编译和加载。

  vue2的模板编译

实现模板编译共有三个阶段:解析、优化和生成

在vue2中,通过重写$mount方法,使得在调用原始的 $mount 函数之前,从 template 选项中获取模板字符串或 DOM 元素,并将其编译为 render 函数和 staticRenderFns 数组。确保当前实例在调用 $mount 函数时已经具有 render 函数,从而可以正确地渲染到页面上。编译时生成的 render 函数会被挂载到组件实例的 $options 对象上的 render 属性中。这样,在组件实例化时,Vue 就可以直接从 $options.render 中获取到预先编译好的 render 函数,而不需要每次都重新编译。 

 过程具体如下:

首先,Vue的编译器会将Vue模板(template)字符串解析为抽象语法树(Abstract Syntax Tree,AST)。AST表示了模板的结构和内容。编译器会对AST进行静态分析,识别模板中的静态内容(不会改变的部分)和动态内容(会改变的部分)。在静态分析的基础上,编译器会进行一些优化操作,例如静态节点提升(Static Node Hoisting)和静态属性提升(Static Props Hoisting),以减少渲染时的开销。根据AST和优化后的结果,编译器会生成对应的渲染函数代码。静态内容会被转换为静态的JavaScript代码,而动态内容会被转换为动态的JavaScript表达式。最终生成的渲染函数代码会被包含在最终的构建文件中,用于在组件渲染时生成虚拟DOM并更新页面。

render伪代码

重写$mount方法

const mount = Vue.prototype.$mount;//记录原$mount
Vue.prototype.$mount = function (el) {
  const vm = this;
  const options = vm.$options;
  el = document.querySelector(el);

  // 如果没有render方法
  if (!options.render) {
    let template = options.template;
    // 如果没有模板但是有el
    if (!template && el) {
      template = el.outerHTML;
    }
    const render = compileToFunctions(template);
    // 将render函数挂载到options上。
    options.render = render;
  }
  mount.call(this,..)//调用原$mount方法
}

Vue.prototype._init = function (options) {
  const vm = this;
  vm.$options = options;
  // 初始化状态
  initState(vm);
  // 页面挂载
  if (vm.$options.el) {
    vm.$mount(vm.$options.el);
  }
}

compileToFunctions

export function compileToFunctions(template) {
  const root = parseHTML(template);
  let code = generate(root);
  let render = `with(this){return ${code}}`;
  let renderFn = new Function(render);
  return renderFn
}

vue2源码实现

 重写$mount方法

 首先,重写的$mount函数会检查 el 参数是否为字符串或 DOM 元素,如果不是,则会将其转换为字符串或 DOM 元素。如果 el 参数为 document.bodydocument.documentElement,则会打印一个警告信息,并返回当前实例。

接下来,这个$mount函数会检查当前实例的 $options 对象是否包含 render 函数,如果不包含,则会尝试从 template 选项中获取模板字符串或 DOM 元素,并将其编译为 render 函数。如果 template 选项为字符串,则会将其转换为模板字符串或 ID 选择器,并查找对应的 DOM 元素。如果 template 选项为 DOM 元素,则会获取其 innerHTML 属性。如果 template 选项不存在,则会尝试从 el 参数获取模板字符串或 DOM 元素。

如果 template 选项为字符串或 DOM 元素,则会使用 compileToFunctions 函数将其编译为 render 函数和 staticRenderFns 数组。并且render 函数和 staticRenderFns 数组挂载到当前实例的 $options 对象上

compileToFunctions 函数是 Vue.js 中的编译器函数,它可以将模板字符串或 DOM 元素编译为 render 函数和 staticRenderFns 数组,并将其返回。

最后,这个函数会调用原始的 $mount 函数,并将 elhydrating 参数传递给它。

compileToFunctions方法

compileToFunctions 函数,它是将模板编译为 render 函数的入口函数。在这个函数中,Vue 的编译器会将模板解析成抽象语法树(AST),然后根据 AST 生成对应的 render 函数。

 通过render函数反向去找怎么生成的

在 Vue.js 的源码中,createCompiler 函数是用于创建一个新的编译器的函数。createCompiler 函数调用createCompilerCreator函数,接收一个名为 baseCompile 的函数作为参数,并返回一个新的编译器函数。

当调用 createCompilerCreator函数时,会返回一个新的编译器函数,该函数包含 compilecompileToFunctions 两个方法

compile 方法用于编译模板,并返回一个包含渲染函数和抽象语法树(AST)的对象。渲染函数是一个可以直接在渲染过程中使用的函数,而 AST 是模板的抽象语法树,可以用于进一步优化和代码生成。

compileToFunctions 方法也用于编译模板,但返回的是一个包含渲染函数和静态渲染函数数组的对象。这个方法可以用于将模板编译为可以直接在浏览器中运行的 JavaScript 函数。

baseCompile函数

baseCompile 是一个函数,在 createCompiler 函数中被用作参数,返回一个新的编译器函数。baseCompile 函数的作用是将模板编译为抽象语法树(AST)并对其进行优化和代码生成。具体来说,编译器会遍历模板的 AST,根据不同的节点类型生成相应的代码片段,最终拼接成一个完整的 render 函数。这个 render 函数会在组件实例化时被调用,用于生成虚拟 DOM。

baseCompile 函数接收两个参数:

  1. template:要编译的模板字符串。
  2. options:编译选项。

baseCompile 函数首先调用 parse 函数将模板解析为 AST,然后对 AST 进行优化,最后调用 generate 函数生成渲染函数和静态渲染函数数组。

parse 函数中,模板被解析为一个包含元素、指令和表达式的 AST。在 optimize 函数中,对 AST 进行优化以提高渲染性能。在 generate 函数中,根据 AST 生成渲染函数和静态渲染函数数组。

每个阶段具体如下:

 1.模板解析-ast语法树

首先,Vue的编译器会将Vue模板(template)字符串解析为抽象语法树(Abstract Syntax Tree,AST)。AST表示了模板的结构和内容。

解析html的parse代码很长,几百行,它的作用就是将模板转换为ast语法树。给个示例看下ast语法树的样子吧

在AST explorer这个网站,可以看vue的模板转换为ast语法树的效果

AST (Abstract Syntax Tree),抽象语法树,记录了源代码的结构和语法信息。具体来说,AST 记录了以下信息:

  1. 节点类型:每个节点都有一个类型,例如表达式、函数、变量声明等。
  2. 节点内容:每个节点都有具体的内容,例如表达式的值、函数的名称和参数等。
  3. 子节点:每个节点可能有多个子节点,例如函数可能有多个参数,表达式可能有多个操作数。
  4. 位置信息:每个节点都有位置信息,包括行号和列号,用于定位源代码中的位置。

在 Vue.js 中,AST 记录了模板的结构和语法信息,包括以下内容:

  1. 元素:每个AST节点对应一个模板中的元素,包括标签、文本和注释。
  2. 属性:每个元素可能有多个属性,包括普通属性和指令属性。
  3. 表达式:元素和属性可能包含表达式,例如 v-if 指令中的表达式。
  4. 位置信息:每个AST节点都有位置信息,包括行号和列号,用于定位模板中的位置。

通过记录这些信息,可以更好地理解和分析模板,并进行优化和代码生成,以提高渲染性能。

2.静态分析

编译器会对AST进行静态分析,识别模板中的静态内容(不会改变的部分)和动态内容(会改变的部分)。

  

3.优化

在静态分析的基础上,编译器会进行一些优化操作,例如静态节点提升(Static Node Hoisting)和静态属性提升(Static Props Hoisting)。优化器的作用是在AST中找出静态子树并打上标记。静态子树是在AST中永远不变的节点,如纯文本节 点,以减少渲染时的开销。

标记静态子树的好处:

  • 每次重新渲染,不需要为静态子树创建新节点
  • 虚拟DOM中patch时,可以跳过静态子树

4.代码生成

根据AST和优化后的结果,编译器会生成对应的渲染函数代码。静态内容会被转换为静态的JavaScript代码,而动态内容会被转换为动态的JavaScript表达式。

5.渲染函数输出

最终生成的渲染函数代码会被包含在最终的构建文件中,用于在组件渲染时生成虚拟DOM并更新页面。

vue2运行时动态生成render函数

在 Vue 2 中,当组件没有提供 render 函数在编译阶段没有编译好的 render 函数时,那么在组件实例化时会动态生成 render 函数。这个 render 函数会在运行时生成,并且会被挂载到组件实例的 $options 对象上的 render 属性中。

具体来说,Vue 会检查组件选项中的 render 方法。如果存在 render 方法,Vue 会调用该方法来动态生成 render 函数。这个 render 方法可以返回一个用于渲染组件的虚拟 DOM 树。

因此,在运行时动态生成 render 函数时,Vue 会调用组件实例的 $options.render 方法来生成 render 函数。这个方法的返回值将用于渲染组件的内容。

vue3的模板编译

Vue 3 在 render 函数方面相比 Vue 2 进行了一些改进和优化,在性能优化方面主要有两点改变

  1. 静态树提升(Static Tree Hoisting): Vue 3 在编译阶段会对模板进行静态分析,将静态节点提升为常量,减少渲染时的节点比对和更新操作,提高性能。
  2. 事件侦听器缓存(Event listener caching): Vue 3 会对事件侦听器进行缓存,避免每次渲染都重新创建事件侦听器,减少性能开销

vue3模板编译过程

  1. parse 模板解析: 首先会调用 parse 方法,将模板源码解析为 AST(抽象语法树)的树形结构。AST 是对模板的抽象表示,方便后续的处理和转换。

  2. transform AST 转换: 接着可能会调用一系列的 transform 方法,对 AST 进行一些转换和优化操作。这些转换可以包括静态节点提升、插槽处理、指令转换等。

  3. generate 代码生成: 经过 AST 转换后,会调用 generate 方法,将经过处理后的 AST 节点转换为渲染函数的代码字符串。这个过程会将 AST 节点转换为可执行的 JavaScript 代码。

  4. 优化代码: 可能会对生成的代码进行一些优化,例如进行尾部优化、静态节点提升等,以提高渲染函数的性能。

  5. 返回 CodegenResult: 最终将生成的代码字符串以及其他相关信息(如错误信息、提示等)封装在 CodegenResult 对象中返回,供后续使用。

 render伪代码

compileToFunction伪代码

function compileToFunction(template, options) {
  const key = template;
  const cache = new Map();

  if (cache.has(key)) {
    return cache.get(key);
  }

  const { code } = compile(template, options); // 假设 compile 函数可以将模板编译成代码

  const render = new Function('Vue', code)(Vue); // 假设 Vue 是运行时的 Vue 实例

  // 标记函数为已编译
  render._rc = true;

  cache.set(key, render);

  return render;
}
function baseCompiler(source,options){
    const ast = baseParse(source,options);//生成ast语法树
    transform(ast,...)//对ast进行一个优化
    return generate(ast,options);//返回generate生成code
}

vue3源码实现

源码中,compileToFunction方法的实现位于core-main\packages\vue\src\index.ts文件中

compileToFunction方法

vue3模板编译的核心仍然是compileToFunction 函数,将template模板转换成render函数。

整个过程可以分为以下几个步骤: 

  1. 传入模板和选项参数: 函数接受两个参数,一个是模板 template,另一个是选项参数 options
  2. 生成缓存键值: 将传入的模板作为键值 key,用于缓存已经编译过的模板函数。
  3. 检查缓存: 使用 cache Map 对象来检查是否已经缓存了该模板对应的函数,如果有,则直接返回缓存的函数。
  4. 模板编译: 调用 compile 函数对传入的模板进行编译,得到编译后的代码 code。这里假设 compile 函数是一个能够将模板编译成代码的函数。
  5. 创建函数: 使用 new Function 构造函数,将编译后的代码作为函数体,生成一个新的函数 render。这个函数在运行时将会接收一个 Vue 实例作为参数。
  6. 执行函数: 调用生成的函数 render,并传入 Vue 实例作为参数,得到最终的渲染函数。
  7. 标记函数: 将生成的渲染函数标记为已编译,以便下次可以直接从缓存中获取。
  8. 缓存函数: 将生成的渲染函数存入缓存中,以备下次使用。
  9. 返回函数: 返回生成的渲染函数。

核心调用了compile方法,compile又调了baseCompile方法

baseCompiler方法

关键方法还是看baseCompile方法。在 Vue 3 的 baseCompile 函数中,主要完成了将模板源码编译为渲染函数的过程。

整个过程可以分为以下几个步骤: 

parse 模板解析

首先会调用 parse 方法,将模板源码解析为 AST(抽象语法树)的树形结构。AST 是对模板的抽象表示,方便后续的处理和转换。

transform AST 转换

接着可能会调用一系列的 transform 方法,对 AST 进行一些转换和优化操作。这些转换可以包括静态节点提升、插槽处理、指令转换等。

generate 代码生成

经过 AST 转换后,会调用 generate 方法,将经过处理后的 AST 节点转换为渲染函数的代码字符串。这个过程会将 AST 节点转换为可执行的 JavaScript 代码。

优化代码

可能会对生成的代码进行一些优化,例如进行尾部优化、静态节点提升等,以提高渲染函数的性能。

返回 CodegenResult

最终将生成的代码字符串以及其他相关信息(如错误信息、提示等)封装在 CodegenResult 对象中返回,供后续使用。

如何查看打包后的render函数?

在 Vue 项目中,不论是编译时还是运行时,打包后的构建文件通常都会包含 render 函数。区别在于编译时生成的 render 函数是静态的,而运行时生成的 render 函数是动态的。

具体来说:

  1. 编译时生成的 render 函数:在编译阶段,Vue 的编译器会将模板(template)编译为静态的 render 函数。这个静态 render 函数会被包含在最终的构建文件中,用于在组件渲染时生成虚拟 DOM。

  2. 运行时生成的 render 函数:有时候,Vue 组件可能会在运行时动态生成 render 函数,特别是在动态组件或函数式组件的情况下。这些动态生成的 render 函数通常不会在编译时静态生成,而是在组件实例化或渲染时动态生成。

打包 Vue 项目时,通常会将 Vue 组件中的模板(template)转换为渲染函数(render function),然后将这些渲染函数打包到最终的构建文件中。如果想查看 render 函数被打包到哪里了,可以按照以下步骤进行:

  1. 查看构建输出文件

    • 查看生成的构建文件,通常在 dist 目录下。
    • 打开生成的构建文件,查找包含 Vue 组件代码的文件,通常是经过处理的 JavaScript 文件。
  2. 搜索渲染函数代码

    • 在构建文件中搜索 Vue 组件的代码,特别是包含 render 函数的部分。
    • 渲染函数通常会以函数的形式存在,搜索关键字如 render: function 或 render() { 来找到渲染函数所在的位置

思考:一个template对应一个render函数吗?

在 Vue 中,一个 template 可以对应多个 render 函数。在 Vue 的编译过程中,一个 template 会被编译为一个 render 函数,这个 render 函数用于生成组件的虚拟 DOM。然而,有时候一个 template 也可以对应多个 render 函数,这取决于 Vue 组件的定义方式和使用场景。

  1. 单文件组件:在 Vue 单文件组件中,通常一个 template 会对应一个 render 函数。这是因为单文件组件中的 template 会被编译为一个 render 函数,并且在组件的定义中只会有一个 render 函数。

  2. 动态组件:在某些情况下,一个 template 可能会对应多个 render 函数,特别是在动态组件的情况下。动态组件可以根据不同的条件或状态渲染不同的组件,每个组件可能有不同的 template 和 render 函数。

  3. 函数式组件:在函数式组件中,通常不会有 template,而是直接使用 render 函数来定义组件的渲染逻辑。

思考:打包后的每个.js文件都会有render函数吗?

在 Vue 项目中,经过编译和打包后的每个 .js 文件并不一定都会包含 render 函数。Render 函数通常是在 Vue 单文件组件中定义的,然后经过编译器编译为 JavaScript 代码,并最终打包到构建文件中。

具体来说:

  1. 单文件组件:在 Vue 单文件组件中,通常会包含 template 和 render 函数。编译器会将 template 编译为 render 函数,并将这个 render 函数包含在最终的构建文件中。

  2. 普通 JavaScript 文件:在普通的 JavaScript 文件中,如果没有定义 Vue 组件或没有使用 render 函数,那么这些文件通常不会包含 render 函数。

  3. 动态组件和函数式组件:动态组件和函数式组件可能会在运行时动态生成 render 函数,这些 render 函数可能不会在打包后的每个 .js 文件中静态存在,而是根据需要动态生成。

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

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

相关文章

uni-app项目打包步骤和踩过的坑(二)

书接上回&#xff0c;上一篇文章写道我利用Android Studio打包uni-app的项目&#xff0c;不知道填写那个数据签证的问题&#xff0c;而且即使能成功打包出的apk在运行时候一直报未配置appkey或配置错误 期间尝试了多种网络上的方式都出现问题&#xff0c;而且我还切换Android S…

【数据库】锁表原因及处理

文章目录 什么是数据库锁表&#xff1f;数据库锁表可能会导致什么问题&#xff1f;死锁问题的原因分析如何避免数据库锁表&#xff1f;解决死锁问题的常用策略解决死锁问题mysql锁表处理ORACEL数据库锁表处理SQL Server数据库锁表处理 来源 什么是数据库锁表&#xff1f; 答&a…

【LeetCode热题100】124.二叉树的最大路径和(二叉树)

一.题目要求 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 root …

百度行驶证C++离线SDK V1.1 C#接入

百度行驶证C离线SDK V1.1 C#接入 目录 说明 效果 项目 代码 说明 自己根据SDK封装了动态库&#xff0c;然后C#调用。 SDK包结构 效果 项目 代码 using Newtonsoft.Json; using System; using System.Drawing; using System.Runtime.InteropServices; using System.Text;…

Python基础之pandas:文件读取与数据处理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、文件读取1.以pd.read_csv()为例&#xff1a;2.数据查看 二、数据离散化、排序1.pd.cut()离散化&#xff0c;以按范围加标签为例2. pd.qcut()实现离散化3.排序4.…

走进《与凤行》感受维达棉韧联名魅力

随着《与凤行》的热播&#xff0c;维达也陆续推出了各个纸抽系列的联名&#xff0c;棉韧联名就是这样一款结合了维达品牌优质棉韧面巾纸和《与凤行》IP元素的产品。这款软抽不仅在质地上保持了维达棉韧系列的柔软舒适&#xff0c;还融入了《与凤行》的设计元素&#xff0c;为用…

L2-036 网红点打卡攻略 ( 模拟题 )

本题链接&#xff1a;PTA | 程序设计类实验辅助教学平台 题目&#xff1a; 样例&#xff1a; 输入 6 13 0 5 2 6 2 2 6 0 1 3 4 2 1 5 2 2 5 1 3 1 1 4 1 2 1 6 1 6 3 2 1 2 1 4 5 3 2 0 2 7 6 5 1 4 3 6 2 6 5 2 1 6 3 4 8 6 2 1 6 3 4 5 2 3 2 1 5 6 6 1 3 4 5 2 7 6 2 1 3…

ubuntu20.04 运行 lio-sam 流程记录

ubuntu20.04 运行 lio-sam 一、安装和编译1.1、安装 ROS11.2、安装 gtsam1.3、安装依赖1.4、下载源码1.5、修改文件1.6、编译和运行 二、官方数据集的运行2.1、casual_walk_2.bag2.2、outdoor.bag、west.bag2.3、park.bag 三、一些比较好的参考链接 记录流程&#xff0c;方便自…

管理项目有哪些好用的系统?

不论在公司是什么角色&#xff0c;不过不管是负责哪一块&#xff0c;项目型公司的管理难点都会经历过&#xff0c;特别是中小型的做建筑装饰类的业务。 一般都会存在合同进度统计难、项目成本管控难、上下游结算难等问题&#xff0c;除了资金方面的原因&#xff0c;也有数据核…

c++对象指针

对象指针在使用之前必须先进行初始化。可以让它指向一个已定义的对象&#xff0c;也可以用new运算符动态建立堆对象。 定义对象指针的格式为&#xff1a; 类名 *对象指针 &对象; //或者 类名 *对象指针 new 类名(参数); 用对象指针访问对象数据成员的格式为&#xff1a…

ubuntu16.04不能在主机和虚拟机之间拷贝文本

问题 ubuntu16.04不能在主机和虚拟机之间拷贝文本。 原因 vmware tools没安装好。 解决办法 让虚拟机加载C:\Program Files (x86)\VMware\VMware Workstation\linux.iso光盘文件&#xff0c;设置如下&#xff1a; 拷贝虚拟机光盘中的VMwareTools-10.3.22-15902021.tar.gz文…

点旋转 与 坐标系旋转

之前想明白过&#xff0c;隔了一段时间没看&#xff0c;现在又忘记了。重新复习一下。 这篇博客写的很明白 推公式的话从坐标旋转开始推&#xff0c;容易理解&#xff0c;又容易推导。 1、坐标系中点的旋转的旋转矩阵 xrcos(αβ) r(cosαcosβ-sinαsinβ) xcosβ-ysinβ…

虹科Pico汽车示波器 | 免拆诊断案例 | 2019款别克GL8豪华商务车前照灯水平调节故障

一、故障现象 一辆2019款别克GL8豪华商务车&#xff0c;搭载LTG发动机&#xff0c;累计行驶里程约为10.7万km。车主反映&#xff0c;车辆行驶过程中组合仪表提示前照灯水平调节故障。 二、故障诊断 接车后试车&#xff0c;起动发动机&#xff0c;组合仪表上提示“前照灯水平…

线上剧本杀小程序开发,剧本杀行业的发展趋势

剧本杀一时火爆全网&#xff0c;剧本杀门店也是迅速占领了大街小巷&#xff0c;成为年轻人热衷的游戏娱乐方式。 不过&#xff0c;线下剧本杀因为价格高、剧本质量不过关等问题&#xff0c;迎来了“寒冬期”&#xff0c;线下剧本杀门店的发展逐渐“降温”。 随着互联网的发展…

跨平台内容策略:Kompas.ai让你的内容在各大平台上发光发热

在数字化营销的今天&#xff0c;品牌需要在多个社交媒体平台上建立强大的在线存在。每个平台都有其独特的用户群体和内容消费习惯&#xff0c;这就要求品牌制定精准的跨平台内容策略&#xff0c;以确保在不同的社交环境中都能发光发热。本文将深入探讨不同社交媒体平台的特点及…

一次性邮箱API发送邮件的步骤?如何使用?

一次性邮箱API发送邮件的注意事项&#xff1f;怎么确保安全发信&#xff1f; 使用一次性邮箱API发送邮件&#xff0c;不仅能保证邮件发送的高效性&#xff0c;还能确保用户邮箱信息的安全性。下面&#xff0c;AokSend将详细介绍使用一次性邮箱API发送邮件的具体步骤。 一次性…

ngAlain下使用nz-select与文件上传框出现灵异bug

bug描述 初始化页面&#xff0c;文件上传框无法出现&#xff1a; 但点击一次选择框以后&#xff0c;就会出现&#xff1a; 真的很神奇。。。 下面逐步排查看看是什么原因。 设想一&#xff1a; 选择框与文件框不可同时存在&#xff0c;删掉选择框看看&#xff1a; 还…

【OpenCV】 基础入门(一)初识 Mat 类 | 通过 Mat 类显示图像

&#x1f680; 个人简介&#xff1a;CSDN「博客新星」TOP 10 &#xff0c; C/C 领域新星创作者&#x1f49f; 作 者&#xff1a;锡兰_CC ❣️&#x1f4dd; 专 栏&#xff1a;【OpenCV • c】计算机视觉&#x1f308; 若有帮助&#xff0c;还请关注➕点赞➕收藏&#xff…

Windows11 使用WSL安装虚拟机

Windows11 使用WSL安装Unbuntu 安装Unbuntu 使用管理员命令打开powershell&#xff0c;执行如下命令&#xff0c;默认安装Unbuntu最新版本 wsl --install使用如下命令&#xff0c;获取在线的所有版本 wsl --list --online指定版本安装 wsl --install <Name>默认安装…

操作系统:动静态库

目录 1.动静态库 1.1.如何制作一个库 1.2.静态库的使用和管理 1.3.安装和使用库 1.4.动态库 1.4.1.动态库的实现 1.4.2.动态库与静态库的区别 1.4.3.共享动态库给系统的方法 2.动态链接 2.1.操作系统层面的动态链接 1.动静态库 静态库&#xff08;.a&#xff09;&…