mthoutai

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

最近要面试,来复习总结一下vue渲染相关的知识。

h 函数

h 函数会返回一个 vnode(虚拟节点)

h 函数底层是基于 createVnode 创建虚拟节点的

  • 优势:跳过了模版的编译
  • 缺点:写起来不方便(可以用 jsx 去代替)

不过对于写一些弹窗之类的,需要挂载到 #app 根节点外面的,用 h 函数会比较方便。

template 编译过程:parser -> ast -> transform -> render

const vnode = h('div', '111')
console.log(vnode)
// {
//     type: "div",
//     props: null,
//     children: "111",
// }

渲染

  1. 普通组件
<template>
  <vnode />
</template>
  <script setup>
  import { h } from 'vue'
  const vnode = h('div', '111')
</script>
  1. 动态组件

<component> 可以接收一个 vnode,渲染到页面

<template>
  <component :is="vnode" />
</template>
  <script setup lang="ts">
  import { h, createVNode } from 'vue'
  const vnode = h('div', '111')

h 函数的写法(参数)

// 写法1
const vnode = h('div', '333')
// 写法2 第一个参数可以传递一个组件
import A from './A.vue'
const vnode = h(A)
// 写法3 第二个参数可以传递多个子节点
const vnode = h('div', [h('span', 'hello'), h('span', 'world')])
// 写法4 第二个参数可以传递props
const vnode = h('div', { style: { color: 'red' } }, '333')
// 写法5 插槽
import { h, createVNode, useSlots } from 'vue'
const slots = useSlots()
const vnode = h('div', slots.default()) // slots.default() 返回一个vnode数组

补充

import A from './A.vue'
console.log(A)

当我们去打印组件的时候,会有一个 render 属性,是一个函数,这个函数会返回一个 vnode,也就是 A.render() 调用后返回的就是 vnode。

关于渲染响应式数据

vnode 会正常渲染,但是响应式数据变了,不会触发 rerender,但是 watch 可以监听到数据变化

因为当前的环境是 setup 的环境,没有被 effect 包裹,所以响应式数据不会响应式

// ⚠️注意:响应式数据不会响应式,因为响应式数据需要effect包裹
let num = ref(0)
const vnode = h('div', num.value)
setTimeout(() => {
num.value++
}, 1000)
watchEffect(() => {
console.log(num.value)
})

而下面的这种写法,就会触发 rerender(采用函数 () => h('div', num.value)

因为当组件在渲染的时候,会帮我们调用 vnode 函数,调用函数的环境就是一个effect

变成函数可以理解为是一个组件(函数式组件),就不是一个 vnode 了

let num = ref(0)
const vnode = () => h('div', num.value)
setTimeout(() => {
num.value++
}, 1000)

函数式组件

上面的例子就是一个函数式组件,可以接收props,类型为 FunctionalComponent,返回一个 vnode


<script setup lang="ts">
import { h, type FunctionalComponent } from 'vue'
const Vnode: FunctionalComponent<{ count: number }> = (props, ctx) => {
  // ctx.attrs => props => { count: 1 }
  // ctx.slots => { default: () => [h('div', 'hello')] } // vctx.slots.default():node数组
  // ctx.emit
  return h('div', [props.count, ctx.slots.default()])
}
</script>

JSX

写过 react 的对 JSX 应该不陌生,这里也不过多赘述

在.vue 文件或.jsx/.tsx 文件中的基础 JSX 用法

import { defineComponent } from 'vue'
export default defineComponent({
setup() {
const greeting = 'Hello, Vue with JSX!'
return () => (
<div>
  <h1>{greeting}</h1>
    <p>This is rendered using JSX.</p>
      </div>
        )
        }
        })

defineComponent 是 vue 提供的一个辅助函数,为 ts 提供更好的类型支持。

对于 <script setup> 语法,默认已经是类型推导的,因此通常不需要显式使用 defineComponent

函数式组件写法也可以:

// A.tsx
import type { FunctionalComponent } from "vue"
export const Vnode: FunctionalComponent<{ count: number }> = (props, ctx) => {
  return <div>
    <div>{props.count}</div>
      </div>
        }

h 函数的关系

JSX 是一种创建 VNode 的语法糖,最终转化为 h() 调用。

这意味着你可以选择是直接使用 h() 还是使用更直观的 JSX 语法。两者本质上是一样的——都是为了创建 VNode。

render 函数

模板编译后的 render 函数

在 vue 中我们平时使用最多的写法就是template模版,模版的本质就是 render函数的语法糖。

render 函数作为组件的渲染入口,返回的就是虚拟节点(vnode)

  • vue3:

在 vue3 中可以用setup 函数返回一个 render 函数

export default {
setup() {
return () => h('h1', 'hello world')
// return () => <h1>hello world</h1>
}
}
  • vue2:
// 示例:render函数返回vnode
export default {
render(h) {
return h('div', 'hello world')
}
}

Vue 包里的 render 函数

import { render } from 'vue'

这个 render 和 模板编译后的 render 函数作用是不一样的。

  • 作用:将虚拟节点渲染到真实 DOM 容器中
  • 参数(vnode, container) - 虚拟节点和容器元素
  • 调用时机:手动控制何时渲染/更新/卸载
  • 用途:手动挂载组件、动态渲染等
import { h, render } from 'vue'
const vnode = h('div', { class: 'container' }, 'Hello World')
const container = document.getElementById('app')
// 渲染到页面
render(vnode, container)
// 更新渲染(传入新的 vnode)
const newVnode = h('div', { class: 'container updated' }, 'Updated Content')
render(newVnode, container)
// 卸载组件(传入 null)
render(null, container)

实际应用场景

  1. 手动挂载应用(类似 createApp)
import { h, render } from 'vue'
import App from './App.vue'
const appVNode = h(App)
const container = document.getElementById('app')
render(appVNode, container)

使用 createApp(推荐) createApp(App).mount('#app')

  1. 动态渲染组件
import { h, render } from 'vue'
function showMessage(message: string) {
const vnode = h(
'div',
{
class: 'message',
onClick: () => {
render(null, container) // 点击关闭
}
},
message
)
const container = document.createElement('div')
document.body.appendChild(container)
render(vnode, container)
}

通常开发中使用 createApp().mount() 更方便,底层就是基于 render 实现的。

posted on 2025-10-31 11:07  mthoutai  阅读(7)  评论(0)    收藏  举报