banner
banner
banner
NEWS LETTER

VUE3-渲染机制、渲染函数、JSX

Scroll down

虚拟DOM

将目标所需的UI通过数据结构“虚拟”地表示出来,保存在内存中,然后将真实的DOM与之保持同步

1
2
3
4
5
6
7
8
9
const vnode ={
type: 'div', // 创建一个<div>元素,并且是虚拟DOM树的根节点
props: {
id: 'rootdiv' // div的 id 为rootdiv
},
children: [
/*更多的元素*/
]
}
  • 挂载:

    • 一个运行时渲染器将会遍历整个虚拟DOM树,并据此构建真实的DOM树
  • 更新:

    • 存在两份及以上的虚拟树,渲染器会有比较地遍历它们,找区别并应用到真实DOM`上

渲染管线

  • 编译:

    • Vue模板被编译成渲染函数,即用来返回虚拟DOM树的函数
  • 挂载:

    • 运行时渲染器调用渲染函数,遍历返回的虚拟DOM树,并基于它创建实际的DOM节点。会作为响应式副作用执行,会追踪所用到的所有响应式依赖
  • 更新:

    • 依赖变化,副作用重新运行,创建一个更新后的虚拟DOM树。运行时渲染器遍历新树,与旧树对比,更新到真实DOM上

模板 vs 渲染函数

  • 渲染函数一般只会在需要处理高度动态渲染逻辑的可重用组件中使用
  • 模板对大多数应用场景都是够用且高效

带编译时信息的虚拟 DOM

  • Vue 中,框架同时控制着编译器和运行时。
  • 编译器可以静态分析模板并在生成的代码中留下标记,使得运行时尽可能地走捷径。
  • 保留了边界情况时用户想要使用底层渲染函数的能力。

静态提升

  • 模板中的元素不带任何绑定,完全静态,vue编译器会自动提升这部分元素到这个模板的渲染函数外,对比时完全跳过去

更新类型标记

  • 对于单个有动态绑定的元素,vue在vnode调用中直接编码每个元素所需的更新类型,且一个元素可以有多个更新类型的标记

树结构打平

  • 打平成一个数组,且仅包含所有动态的后代节点。重渲染时,只需遍历打平的树,减少虚拟DOM更新时需遍历的节点数量

对SSR激活的影响 – 提升

渲染函数

  • 基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { h } from 'vue'
// h() 函数用于创建vnodes,能生成 HTML (超文本标记语言) 的 JavaScript
const vnode = h(
'div', // type
{ id: 'foo', class: 'bar' }, // props
[
/* children */
]
)

const vnode = h('div', { id: 'foo' }, [])

vnode.type // 'div'
vnode.props // { id: 'foo' }
vnode.children // []
vnode.key // null
  • 声明渲染函数 — 使用render

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import { h } from 'vue'

    export default {
    data() {
    return {
    msg: 'hello'
    }
    },
    // render() 函数可以访问同一个 this 组件实例
    render() {
    return h('div', this.msg)
    }
    }
  • Vnodes 必须唯一

  • 渲染多个重复的元素或者组件 — 工厂函数

1
2
3
4
5
6
7
8
9
示例:
function render() {
return h(
'div',
Array.from({ length: 20 }).map(() => {
return h('p', 'hi')
})
)
}

JSX / TSX — 是 JavaScript 的一个类似 XML 的扩展

  • const vnode = <div id={dynamicId}>hello, {userName}</div>
  • Vue 的 JSX 编译方式与 React 中 JSX 的编译方式区别

    • 可以使用 HTML attributes 比如 class 和 for 作为 props
    • 不需要使用 className 或 htmlFor。
    • 传递子元素给组件 (比如 slots) 的方式不同。
  • 当使用 TSX 语法时,确保在 tsconfig.json 中配置了 “jsx”: “preserve”

渲染案例

  • v-if

    • 渲染函数:h('div', [this.ok ? h('div', 'yes') : h('span', 'no')])
    • JSX: <div>{this.ok ? <div>yes</div> : <span>no</span>}</div>

v-for

  • 渲染函数:

    1
    2
    3
    4
    5
    6
    h(
    'ul',
    this.items.map(({ id, text }) => {
    return h('li', { key: id }, text)
    })
    )
  • JSX:

    1
    2
    3
    4
    5
    <ul>
    {this.items.map(({ id, text }) => {
    return <li key={id}>{text}</li>
    })}
    </ul>
  • v-on

    • 渲染函数:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      h(
      'button',
      {
      onClick(event) {
      /* ... */
      }
      },
      'click me'
      )
    • JSX:
      1
      2
      3
      4
      5
      6
      7
      <button
      onClick={(event) => {
      /* ... */
      }}
      >
      click me
      </button>
  • 事件修饰符 – 使用驼峰写法将他们拼接在事件名后面

    • .passive —> onClickPassive
    • 对于事件和按键修饰符,可以使用 withModifiers 函数:
    • 渲染函数:
      1
      2
      3
      4
      5
      import { withModifiers } from 'vue'

      h('div', {
      onClick: withModifiers(() => {}, ['self'])
      })
    • JSX:<div onClick={withModifiers(() => {}, ['self'])} />
  • 组件 – 不再需要注册组件,直接导入

    • 渲染函数: return h('div', [h(Foo), h(Bar)])
    • JSX: <Foo />
  • 渲染插槽 – 通过 this.$slots 来访问插槽

    • 渲染函数: h('div', this.$slots.default())
    • JSX: <div>{this.$slots.default()}</div>
  • 传递插槽

    • 传递一个插槽函数或者是一个包含插槽函数的对象而非是数组
    • 插槽函数的返回值同一个正常的渲染函数的返回值一样——并且在子组件中被访问时总是会被转化为一个 vnodes 数组。
    • 渲染函数:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      // 单个默认插槽
      h(MyComponent, () => 'hello')

      // 具名插槽
      // 注意 `null` 是必需的
      // 以避免 slot 对象被当成 prop 处理
      h(MyComponent, null, {
      default: () => 'default slot',
      foo: () => h('div', 'foo'),
      bar: () => [h('span', 'one'), h('span', 'two')]
      })
    • JSX:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 默认插槽
      <MyComponent>{() => 'hello'}</MyComponent>

      // 具名插槽
      <MyComponent>{{
      default: () => 'default slot',
      foo: () => <div>foo</div>,
      bar: () => [<span>one</span>, <span>two</span>]
      }}</MyComponent>
    • 插槽以函数的形式传递使得它们可以被子组件懒调用。
  • 内置组件

    • 在渲染函数中必须导入才能使用
  • v-model

    • 扩展为 modelValue 和 onUpdate:modelValue, 模板编译过程中必须自己提供props
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      export default {
      props: ['modelValue'],
      emits: ['update:modelValue'],
      render() {
      return h(SomeComponent, {
      modelValue: this.modelValue,
      'onUpdate:modelValue': (value) => this.$emit('update:modelValue', value)
      })
      }
      }
  • 自定义指令

1
2
3
4
5
6
7
8
9
10
11
12
import { h, withDirectives } from 'vue'

// 自定义指令
const pin = {
mounted() { /* ... */ },
updated() { /* ... */ }
}

// <div v-pin:top.animate="200"></div>
const vnode = withDirectives(h('div'), [
[pin, 200, 'top', { animate: true }]
])
  • 当一个指令是以名称注册并且不能被直接导入时,可以使用 resolveDirective 函数

  • 函数式组件:使用 withDirectives 将自定义指令应用于 vnode

1
2
3
4
5
// 因为函数式组件里没有 this 引用,Vue 会把 props 当作第一个参数传入
function MyComponent(props, context) {
context:{attrs,emit,slots}
}
MyComponent.inheritAttrs = false // 没有明确指定 props 的函数式组件,需禁透传
其他文章
cover
VUE3-应用规模化
  • 24/11/01
  • 14:39
  • VUE
cover
VUE3-响应式原理
  • 24/11/01
  • 14:39
  • VUE
目录导航 置顶
  1. 1. 虚拟DOM
    1. 1.1. 挂载:
    2. 1.2. 更新:
  2. 2. 渲染管线
    1. 2.1. 编译:
    2. 2.2. 挂载:
    3. 2.3. 更新:
  3. 3. 模板 vs 渲染函数
  4. 4. 带编译时信息的虚拟 DOM
    1. 4.1. 静态提升
    2. 4.2. 更新类型标记
    3. 4.3. 树结构打平
    4. 4.4. 对SSR激活的影响 – 提升
  5. 5. 渲染函数
    1. 5.1. 基本用法
    2. 5.2. 声明渲染函数 — 使用render
    3. 5.3. Vnodes 必须唯一
    4. 5.4. 渲染多个重复的元素或者组件 — 工厂函数
  6. 6. JSX / TSX — 是 JavaScript 的一个类似 XML 的扩展
    1. 6.1. Vue 的 JSX 编译方式与 React 中 JSX 的编译方式区别
    2. 6.2. 当使用 TSX 语法时,确保在 tsconfig.json 中配置了 “jsx”: “preserve”
  7. 7. 渲染案例
    1. 7.1. v-if
    2. 7.2. v-for
    3. 7.3. v-on
    4. 7.4. 事件修饰符 – 使用驼峰写法将他们拼接在事件名后面
    5. 7.5. 组件 – 不再需要注册组件,直接导入
    6. 7.6. 渲染插槽 – 通过 this.$slots 来访问插槽
    7. 7.7. 传递插槽
    8. 7.8. 内置组件
    9. 7.9. v-model
    10. 7.10. 自定义指令
    11. 7.11. 函数式组件:使用 withDirectives 将自定义指令应用于 vnode
请输入关键词进行搜索