何时/使用 Vue3 render 函数的教程详解

什么是 DOM?

如果我们把这个 HTML 加载到浏览器中,浏览器创建这些节点,用来显示网页。所以这个HTML映射到一系列DOM节点,然后我们可以使用JavaScript进行操作。例如:

let item = document.getElementByTagName('h1')[0]
item.textContent = "New Heading"

VDOM

网页可以有很多DOM节点,这意味着DOM树可以有数千个节点。这就是为什么我们有像Vue这样的框架,帮我们干这些重活儿,并进行大量的JavaScript调用。

然而,搜索和更新数千个DOM节点很明显会变慢。这就是Vue和其他类似框架有一种叫做虚拟DOM的东西。虚拟DOM是表示DOM的一种方式。例如,这个HTML也可以通过一个虚拟节点来表示,看起来像这样。如您所见,它只是一个JavaScript对象。

<div>Hello</div>
{
 tag: 'div',
 children: [
  {
   text: 'Hello'
  }
 ]
}

Vue知道如何使用此虚拟节点并挂载到DOM上,它会更新我们在浏览器中看到的内容。实际上还有一个步骤其中,Vue基于我们的模板创建一个渲染函数,返回一个虚拟DOM节点。

渲染函数可以是这样的:

render(h) {
 return h('div', 'hello')
}

当组件更改时,Render函数将重新运行,它将创建另一个虚拟节点。然后发送旧的 VNode 和新的 VNode 到Vue中进行比较并以最高效的方式在我们的网页上更新。

我们可以将虚拟DOM和实际DOM的关系类比为蓝图和实际建筑的关系。假设我更改了29楼的一些数据。我改变了家具的布局还加了一些橱柜。我有两种方法可以改变。首先,我可以拆除29楼的一切从头开始重建。或者我可以创造新的蓝图,比较新旧蓝图并进行更新以尽可能减少工作量。这就是虚拟DOM的工作原理。Vue 3让这些更新更快并且更高效。

核心模块

Vue 的三个核心模块:

  • Reactivity Module 响应式模块
  • Compiler Module 编译器模块
  • Renderer Module 渲染模块

响应式模块允许我们创建 JavaScript 响应对象并可以观察其变化。当使用这些对象的代码运行时,它们会被跟踪,因此,它们可以在响应对象发生变化后运行。

编译器模块获取 HTML 模板并将它们编译成渲染函数。这可能在运行时在浏览器中发生,但在构建 Vue 项目时更常见。这样浏览器就可以只接收渲染函数。

渲染模块的代码包含在网页上渲染组件的三个不同阶段:

  • 渲染阶段
  • 挂载阶段
  • 补丁阶段

在渲染阶段,将调用 render 函数,它返回一个虚拟 DOM 节点。
在挂载阶段,使用虚拟DOM节点并调用 DOM API 来创建网页。
在补丁阶段,渲染器将旧的虚拟节点和新的虚拟节点进行比较并只更新网页变化的部分。

现在让我们来看一个例子,一个简单组件的执行。它有一个模板,以及在模板内部使用的响应对象。首先,模板编译器将 HTML 转换为一个渲染函数。然后初始化响应对象,使用响应式模块。接下来,在渲染模块中,我们进入渲染阶段。这将调用 render 函数,它引用了响应对象。我们现在监听这个响应对象的变化,render 函数返回一个虚拟 DOM 节点。接下来,在挂载阶段,调用 mount 函数使用虚拟 DOM 节点创建 web 页面。最后,如果我们的响应对象发生任何变化,正在被监视,渲染器再次调用render函数,创建一个新的虚拟DOM节点。新的和旧的虚拟DOM节点,发送到补丁函数中,然后根据需要更新我们的网页。

渲染器机制

拥有虚拟DOM层有一些好处,最重要的是它让组件的渲染逻辑完全从真实DOM中解耦,并让它更直接地重用框架的运行时在其他环境中。例如,Vue允许第三方开发人员创建自定义渲染解决方案目标,不仅仅是浏览器也包括IOS和Android等原生环境,也可以使用API创建自定义渲染器直接渲染到WebGL而不是DOM节点。在Vue 2中我们实际上已经有了这种能力但是,我们在Vue 2中提供的API没有正式记录并且需要分叉源代码。所以这给维护带来了很大的负担,对开发这些定制解决方案的开发人员在Vue 3中,我们让自定义渲染器API成为一等公民。因此开发人员可以直接拉取Vue运行时核心作为依赖项,然后利用自定义渲染器API构建自己的自定义渲染器。事实上,我们已经有了早期用户报告他们已经成功地构建了一个使用Vue 3 API关于虚拟DOM的WebGL渲染器。

另一个重要方面,它提供了能力以编程方式构造、检查、克隆以及操作所需的DOM结构,在实际返回渲染引擎之前你可以利用JavaScript的全部能力做到这些。这个能力很重要,因为总会有某些情况在UI编程中使用模板语法会有一些限制,你只需要一种有充分灵活性的合适的编程语言来表达潜在的逻辑。现在,这种情况实际上是相当罕见的在日常UI开发中。但当你在创作一个库的时候,这种情况更常见或编写UI组件套件,你打算上传供第三方开发者使用。让我们想象一下一个,像复杂类型的顶部框这样的组件或者一个与一堆文本相关联的输入框,这些类型的组件通常包含很少的标记,但它们将包含很多交互逻辑在这些情况下,模板语法有时候会限制你更容易地表达潜在的逻辑,或者有时候你会发现自己在模板中加入了很多逻辑,但你还是有很多逻辑在JavaScript 中而 render 函数允许你把这些逻辑组合在一个地方你通常不需要想太多关于这些情况下的标记。

所以我理解是模板会完成你要做的事在99%的情况下你只需要写出HTML就好了,但偶尔可能想做些更可控的事情在,你需要编写一个渲染函数。Vue 2中的渲染函数如下所示,

render(h) {
 return h (
  'div', {
  attrs: {
   id: foo
  },
  on: {
   click: this.onClick
  },
  'hello'
 })
}

所以这是组件定义中的一个选项,相对于提供一个 template 选项,在 Vue 2 中你可以为组件提供一个渲染函数,你会得到 h 参数,直接作为渲染函数的参数。你可以用它来创造我们称之为虚拟DOM节点,简称 vnode。

vnode 接受三个参数:

  • 第一个参数是类型,所以我们在这里创建一个 div。
  • 第二个参数是一个对象包含 vnode 上的所有数据或属性,API有点冗长从某种意义上说,你必须指明传递给节点的绑定类型。例如,如果要绑定属性你必须把它嵌套在attrs对象下如果要绑定事件侦听器你得把它列在 on 下面。
  • 第三个参数是这个 vnode 的子节点。所以直接传递一个字符串是一个方便的 API,表明此节点只包含文本子节点,但它也可以是包含更多子节点的数组。所以你可以在这里有一个数组并且嵌套了更多的嵌套 h 调用。

在Vue 3中我们改变了API,目标是简化它。

import { h } from 'vue'

render () {
 return h(
  'div',
  {
   id: 'foo',
   onClick: this.onClick
  },
  'hello'
 })
}

第一个显著的变化是我们现在有了一个扁平的 props 结构。当你调用 h 时,第二个参数现在总是一个扁平的对象。你可以直接给它传递一个属性,这里我们只是给它一个 ID。按惯例监听器以 on 开头,所以任何带 on 的都会自动绑定为一个监听器所以你不必考虑太多嵌套的问题。

在大多数情况下,你也不需要思考是应将其作为 attribute 绑定还是DOM属性绑定,因为 Vue 将智能地找出为你做这件事的最好方法。我们检查这个 key 是否作为属性存在在原生 DOM 中。如果存在,我们会将其设置为 property,如果它不存在,我们将它设置为一个attribute。

render API 的另一项改动是 h helper 现在是直接从 Vue 本身全局导入的。一些用户在 Vue 2 中因为 h 在这里传递而在这里面 h 又很特别,因为它绑定到当前组件实例。当你想拆分一个大的渲染函数时,你必须把这个 h 函数一路传递给这些分割函数。所以,这有点困难,但有了全局引入的 h 你导入一次就可以分割你的渲染函数,在同一个文件里分割多少个都行。

渲染函数不再有 h 参数了,在内部它确实接收参数,但这只是编译器使用的用来生成代码。当用户直接使用时,他们不需要这个参数。所以,如果你用 TypeScript 使用定义的组件 API 你也会得到 this 的完整类型推断。

Q&A

1.我知道原始的那种虚拟 Dom 的实现得到了启发来自其他项目对吗?

是的有一个库叫snabbdomVue 2基本上就是从这个库中分离出来的。

2.好的然后是Vue 3,你在这里的编码方式只是改进了Vue 2的模式吗?

好吧,Vue 3是一个彻底的重写,几乎从头开始一切都是定制的显然,有现有的算法看起来像没有变化,因为这些是我们看到社区在做广泛研究的领域所以这是建立在所有这些以前的实现的基础上的但代码本身现在是从头开始。

3.都是用TypeScript写的,对吧?

是的,都是 TypeScript 写的。

何时/如何使用 render 函数

看看渲染函数在 Vue 中是什么样子。在 Vue 2 中,一个传统的 Vue 组件,有一个 template 选项,但是为了重用渲染函数我们可以用一个名为 render 的函数来代替它,我们会通过参数得到这个称为 h(hyperscript)。但在这里,我们只是示范一下我们如何在 Vue 3 中使用它。我们会从 vue 导入 h,我们可以用它来返回 h。

import { h } from 'vue'

const App = {
 render () {
  return h('div')
 }
}

// 等效模板中的普通 div

1.所以它返回 div 的 JavaScript 对象表示?

完全正确。

2.那么,你的虚拟dom就像…编译器?是编译器接收它吗?

是渲染器,渲染器接收它。

3.然后它实际上进行 dom 调用将其带入浏览器?

完全正确。

所以我们可以给这个虚拟节点一些 props,

import { h } from 'vue'

const App = {
 render () {
  return h(
   'div',
   {
    id: 'hello'
   },
   [
    h('span','world')
   ]
  )
 }
}

// <div id="hello"><span>world</span></div>

现在,我们知道如何生成静态结构。但是当人们第一次使用 render 函数会问 “我该怎么写,比如说,v-if 或者 v-for”?我们没有像 v-if 或者类似的东西。相反,您可以直接使用 JavaScript。

import { h } from 'vue'

const App = {
 render () {
  return this.ok
   ? h('div',{ id: 'hello' },[h('span','world')]
   : h('p', 'other branch')
  )
 }
}

如果 ok 的值为 true,它将呈现 div,反之,它将呈现 p。同样,如果你想做 v-else-if 你需要嵌套这个三元表达式:

import { h } from 'vue'

const App = {
 render () {
  return this.ok
   ? h('div',{ id: 'hello' },[h('span','world')]
   : this.otherCondition
    ? h('p', 'other branch')
    : h('span')
  )
 }
}

我想你可能会喜欢创建一个变量,将不同的节点添加到该变量。所以当你不得不将这整个东西嵌套在一个表达式调用中这会很有用,但你不必这么做。

import { h } from 'vue'

let nodeToReturn
if(this.ok) {
 nodeToReturn = ...
} else if () {

}

const App = {
 render () {
  return this.ok
   ? h('div',{ id: 'hello' },[h('span','world')]
   : this.otherCondition
    ? h('p', 'other branch')
    : h('span')
  )
 }
}

这就是 JavaScript 灵活的地方,这看起来更像普通的 JavaScript。当你的代码变得更加复杂时您可以使用普通的 JavaScript 重构技巧使它们更容易理解。

我们讨论了 v-if, 接下来看看 v-for。 类似的,你也可以给它们加上 key,这是渲染函数中的渲染列表。

import { h } from 'vue'

const App = {
 render () {
  return this.list.map(item => {
   return h('div', {key: item.id}, item.text)
  }))
 }
}

在渲染函数中,您可能要处理插槽。当你写一个重标记组件(markup heavy component),或者我更喜欢称之为特性组件(feature component),它与你的应用程序的外观布局结构有关,将实际的 HTML 显示给用户。对于那些类型的组件,我更喜欢始终使用模板。只有在我必须使用渲染函数的时候,比如我在写一些功能型的组件,有时会期望获取一些插槽内容,将其打包或者以某种方式操纵他们。在 Vue 3 里默认插槽将暴露在这个 this.$slot.default。如果对于组件什么都没有提供,这将是 undefined,所以你得先检查一下它的存在。如果它存在,它将永远是一个数组。有了作用域槽,我们可以将 props 传递给作用域槽,所以把数据传递到作用域槽只是通过传递一个参数到这个函数调用中。因为这是一个数组你可以将它直接放在 children 位置。

import { h } from 'vue'

const App = {
 render () {
  const slot = this.$slot.default
   ? this.$slot.default()
   : []

  return h('div', slot)
 }
}

你可以在 render 函数中用插槽做一件很强大的事,比如以某种方式操纵插槽,因为它只是一个 JavaScript 对象数组,你可以用 map 遍历它。

import { h } from 'vue'

const App = {
 render () {
  const slot = this.$slot.default
   ? this.$slot.default()
   : []

  slot.map(vnode => {
   return h('div', [vnode])
  })
 }
}

这里有一个例子,截住并更改插槽数据。假设我们有一个堆栈组件(tack component),在一些用户界面库(UI libraries)中很常见。你可以传递很多属性给它,得到嵌套的堆栈渲染结果,有点像 HTML 中 ulol 的默认样式。

<Stack size="4">
 <div>hello</div>
 <Stack size="4">
  <div>hello</div>
  <div>hello</div>
 </Stack>
</Stack>

渲染成这样:

<div class="stack">
 <div class="mt-4">
   <div>hello</div>
 </div>
 <div class="mt-4">
   <div class="stack">
   <div class="mt-4">
    <div>hello</div>
   </div>
   </div>
 </div>
</div>

这里有一个普通的基于模板的语法,在同一个插槽内它们都是默认插槽,你能做的只有渲染这个部分,在模板很难实现。但是你可以用渲染函数来实现,程序化的遍历插槽内的每个项目然后把它们变成别的东西。

import { h } from 'vue'

const Stack = {
 render () {
  const slot = this.$slots.default
   ? this.$slots.default()
   : []

  return h(
   'div',
   {class: 'stack'},
   slot.map(child => {
    return h(
      'div',
      {class: `mt-${this.$props.size}`},
      [child]
     )
   })
  )
 }
}

我们用 slot.map 生成新的 vnode 列表,原来的子插槽被包装在里面。有了这个,我们把它放到一个 stack.html 文件里。

stack.html

<script src="https://unpkg.com/vue@next"></script>
<style>
 .mt-4 {
  margin: 10px
 }
</style>

<div id="app"></div>

<script>
 const { h, createApp } = Vue

 const Stack = {
  render() {
   const slot = this.$slots.default
    ? this.$slots.default()
    : []

   return h(
    'div',
    { class: 'stack' },
    slot.map(child => {
     return h('div', { class: `mt-${this.$attrs.size}` }, [child])
     // this.$props.size ?
    })
   )
  },
 }

 const App = {
  components: {
   Stack
  },
  template: `
  <Stack size="4">
   <div>hello</div>
   <Stack size="4">
    <div>hello</div>
    <div>hello</div>
   </Stack>
  </Stack>
  `
 }

 createApp(App).mount('#app')
</script>

当你创作这些底层的公用设施组件,有时真的会遇到麻烦,这时渲染函数更有效。但话说回来,也需要了解每种方法的利弊,这些是为了让你更好地理解在什么情况下应该使用模板或使用渲染函数。基本上是当你用一个模板时遇到限制时,比如你就像我们刚才看到的那样,可能改为使用渲染函数会更有效。当你意识到想表达的逻辑用 JavaScript 更容易而不是使用模板语法时就使用它。从我的经验来看,这种情况在您创作可重用的功能组件,要跨多个应用程序共享或者在组织内部共享时更常见。在日常开发中你主要是在编写特性组件,模板通常是有效的方式,模板的好处是更简单,当你有很多标记的时候会通过编译器优化,它的另一个好处是它更容易让设计师接管组件并用CSS设计样式。因此,Vue 提供了这两个选项,当情况出现的时候以便您可以选择合适的方式。

到此这篇关于何时/使用 Vue3 render 函数的教程详解的文章就介绍到这了,更多相关Vue3 render 函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 了解VUE的render函数的使用

    Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML.然而在一些场景中,你真的需要 JavaScript 的完全编程的能力,这就是 render 函数,它比 template 更接近编译器. 在 HTML 层, 我们决定这样定义组件接口:通过传入不同的level 1-6 生成h1-h6标签,和使用slot生成内容 <div id="div1"> <child :level="1">Hello world!</chil

  • 浅谈vue的iview列表table render函数设置DOM属性值的方法

    如下所示: { title: '负责人社保照片', key: 'leaderIdNumber', render: (h, params) => { return h('img',{domProps:{ src:params.row.leaderIdNumber }}) } }, 找了好多,终于找到了原因,如果想要让列表返回的是一个img标签,并且设置img的src,这里不能用props,而是要用domProps就ok了. 以上这篇浅谈vue的iview列表table render函数设置DOM属

  • Vue2.x中的Render函数详解

    Render函数是Vue2.x版本新增的一个函数:使用虚拟dom来渲染节点提升性能,因为它是基于JavaScript计算.通过使用createElement(h)来创建dom节点.createElement是render的核心方法.其Vue编译的时候会把template里面的节点解析成虚拟dom: 什么是虚拟dom? 虚拟dom不同于真正的dom,它是一个JavaScript对象.当状态发生变化的时候虚拟dom会进行一个diff判断/运算:然后判断哪些dom是需要被替换的而不是全部重绘,所以性能

  • 详解vue渲染函数render的使用

    1.什么是render函数? vue通过 template 来创建你的 HTML.但是,在特殊情况下,这种写死的模式无法满足需求,必须需要js的编程能力.此时,需要用render来创建HTML. 比如如下我想要实现如下html: <div id="container"> <h1> <a href="#" rel="external nofollow" rel="external nofollow"

  • 如何理解Vue的render函数的具体用法

    本文介绍了如何理解Vue的render函数的具体用法,分享给大家,具体如下: 第一个参数(必须) - {String | Object | Function} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>render</title> <script src="https://cdn.b

  • Vue中render函数的使用方法

    render函数 vue通过 template 来创建你的 HTML.但是,在特殊情况下,这种写死的模式无法满足需求,必须需要js的编程能力.此时,需要用render来创建HTML. 什么情况下适合使用render函数 在一次封装一套通用按钮组件的工作中,按钮有四个样式(default success error ).首先,你可能会想到如下实现 <div v-if="type === 'success'">success</div> <div v-else

  • 何时/使用 Vue3 render 函数的教程详解

    什么是 DOM? 如果我们把这个 HTML 加载到浏览器中,浏览器创建这些节点,用来显示网页.所以这个HTML映射到一系列DOM节点,然后我们可以使用JavaScript进行操作.例如: let item = document.getElementByTagName('h1')[0] item.textContent = "New Heading" VDOM 网页可以有很多DOM节点,这意味着DOM树可以有数千个节点.这就是为什么我们有像Vue这样的框架,帮我们干这些重活儿,并进行大量

  • vue中render函数的使用详解

    render函数 vue通过 template 来创建你的 HTML.但是,在特殊情况下,这种写死的模式无法满足需求,必须需要js的编程能力.此时,需要用render来创建HTML. render方法的实质就是生成template模板: 通过调用一个方法来生成,而这个方法是通过render方法的参数传递给他的: 通过这三个参数,可以生成一个完整的模板 官网实例 //未使用render函数 Vue.component('anchored-heading', { template: '#anchor

  • VUE render函数使用和详解

    目录 前言 render的作用 render函数讲解 render和template的区别 render举例 总结 前言 在平时编程时,大部分是通过template来创建html.但是在一些特殊的情况下,使用template方式时,就无法很好的满足需求,在这个时候就需要 通过JavaScript 的编程能力来进行操作.此时,就到了render函数展示拳脚去时候了. render的作用 官网示例入口 在官网的这里示例中,使用组件,将相同的内容通过solt放进h1-h6的标签中,在使用传统方式时,代

  • Vue3+TS+Vant3+Pinia(H5端)配置教程详解

    该模板将帮助您开始使用Vue 3.Vite3.0中的TypeScript以及Pinia.Vant3进行开发.该模板使用Vue3,请查看文档了解更多教程. 推荐的IDE设置 VS Code + Volar 键入支持.TS中的vue导入 因为TypeScript无法处理的类型信息.vue导入,默认情况下,它们填充为通用vue组件类型.在大多数情况下,如果您不真正关心模板之外的组件道具类型,那么这很好.然而,如果你想得到实际的道具类型.vue导入,您可以通过以下步骤启用Volar的接管模式: 1.运行

  • C++ 基础教程之虚函数实例代码详解

    虚函数的定义 虚函数:就是在基类的成员函数前加关键字virtual(即被virtual关键字修饰的成员函数),并在一个或多个派生类中被重新定义的成员函数:虚函数:就是在编译的时候不确定要调用哪个函数,而是动态决定将要调用哪个函数.它的作用就是为了能让这个函数在它的子类里面可以被重载,这样的话,编译器就可以使用后期绑定来达到多态了,也就是用基类的指针来调用子类的这个函数:虚函数的作用:在于用专业术语来解释就是实现多态性,多态性是将接口与实现进行分离,通过指向派生类的基类指针或引用,访问派生类中同名

  • Python中非常实用的Math模块函数教程详解

    目录 math模块常数 1. 圆周率 2. Tau (τ) 3. 欧拉数 4. 无限 5. 不是数字 算术函数 1. factorial() 2. ceil() 3. floor() 4. trunc() 5. isclose() 幂函数 1. exp() 2. 对数函数 其他重要的math模块功能 由于该math模块与 Python 版本一起打包,因此您不必单独安装它,直接导入: import math math模块常数 Pythonmath模块提供了多种预定义常量.访问这些常量提供了几个优点

  • 小程序开发之云函数的使用教程详解

    目录 1.云函数 1.1 云函数API和云函数创建 2.云函数案例 1.云函数 云函数是部署在云端的函数,他和小程序本地的函数存在很大的区别,云函数应用涉及云端云函数定义和本地引用云端云函数的API接口两个问题. 1.1 云函数API和云函数创建 1.1.1 小程序云函数API接口 小程序云函数API接口是指小程序调用云端函数的接口,(和wx.request()类似).小程序提供了wx.cloud.callFunction()接口作为云函数API接口,它的属性如表所示. 属性 类型 默认值 必填

  • python模块shutil函数应用示例详解教程

    目录 本文大纲 知识串讲 1)模块导入 2)复制文件 3)复制文件夹 4)移动文件或文件夹 5)删除文件夹(慎用) 6)创建和解压压缩包 本文大纲 os模块是Python标准库中一个重要的模块,里面提供了对目录和文件的一般常用操作.而Python另外一个标准库--shutil库,它作为os模块的补充,提供了复制.移动.删除.压缩.解压等操作,这些 os 模块中一般是没有提供的.但是需要注意的是:shutil 模块对压缩包的处理是调用 ZipFile 和 TarFile这两个模块来进行的. 知识串

  • 非常实用的MySQL函数全面总结详解示例分析教程

    目录 1.MySQL中关于函数的说明 2.单行函数分类 3.字符函数 4.数学函数 5.日期时间函数 6.其它常用系统函数 7.流程控制函数 8.聚合函数 1)聚合函数的功能和分类: 2)聚合函数的简单使用 3)五个聚合函数中传入的参数,所支持的数据类型有哪些? 4)聚合函数和group by的使用"最重要": 1.MySQL中关于函数的说明 "概念":类似java.python中的方法,将一组逻辑语句封装在方法体中,对外暴露方法名: "好处":

  • django中的ajax组件教程详解

    Ajax(Asynchronous Javascript And XML)翻译成英文就是"异步Javascript和XML".即用Javascript语言与服务器进行异步交互,传输的数据为XML,(现在使用更多的是json数据). 向服务器发送请求的途径 1.浏览器地址栏 http://www.baidu.com 默认是get请求 2.form表单发送请求: GET请求 POST请求 3.a标签 href属性 默认是get请求 4.ajax() Ajax的特点 异步交互:客户端发送一个

随机推荐