一文解析Vue h函數(shù)到底是個啥
h 到底是個啥?
對于了解或?qū)W習(xí)Vue高階組件(HOC)的同學(xué)來說,h() 函數(shù)無疑是一個經(jīng)常遇到的概念。

那么,這個h() 函數(shù)究竟如何使用呢,又在什么場景下適合使用呢?
一、h 是什么
看到這個函數(shù)你可能會有些許困惑,為什么叫h呢?代表著什么呢?
官方定義:
返回一個“虛擬節(jié)點(diǎn)” ,通常縮寫為 VNode: 一個普通對象,其中包含向 Vue 描述它應(yīng)該在頁面上呈現(xiàn)哪種節(jié)點(diǎn)的信息,包括對任何子節(jié)點(diǎn)的描述。用于手動編寫render
h其實(shí)代表的是 hyperscript 。它是 HTML 的一部分,表示的是超文本標(biāo)記語言,當(dāng)我們正在處理一個腳本的時候,在虛擬 DOM 節(jié)點(diǎn)中去使用它進(jìn)行替換已成為一種慣例。這個定義同時也被運(yùn)用到其他的框架文檔中
Hyperscript 它本身表示的是 “生成描述 HTML 結(jié)構(gòu)的腳本”
二、語法
h() 函數(shù)的基本語法如下:
h(tag, props, children)
- tag:可以是字符串或組件,表示要創(chuàng)建的 HTML 標(biāo)簽或 Vue 組件。
- props:是一個對象,包含要傳遞給標(biāo)簽或組件的屬性,如類名、樣式、事件監(jiān)聽器等。
- children:可以是字符串、數(shù)組或函數(shù),表示子節(jié)點(diǎn)。子節(jié)點(diǎn)可以是文本、其他 VNode 或一個返回 VNode 的函數(shù)。
以下是一個簡單的代碼示例,展示了如何使用 h() 函數(shù)創(chuàng)建一個包含標(biāo)題和段落的組件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>h()</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0-rc.4/vue.global.js"></script>
</head>
<body>
<div id="app">
</div>
<script>
const App = {
render() {
return Vue.h('h1', {}, '二川兄弟')
}
}
// console 結(jié)果請?jiān)诳刂婆_查看
console.log(Vue.h('h1', {}, '二川兄弟'))
Vue.createApp(App).mount('#app')
</script>
</body>
</html>效果:

打?。?/p>

在這個示例中,我們創(chuàng)建了一個 <h1> 容器,內(nèi)部插入文字 “二川兄弟”。所有這些都是通過 h() 函數(shù)來實(shí)現(xiàn)的,而不是使用模板語法。
三、源碼解析
要深入理解 h() 函數(shù),我們需要查看 Vue.js 的源碼。在 Vue.js 的實(shí)現(xiàn)中,h() 函數(shù)是一個封裝了 VNode 創(chuàng)建邏輯的工具函數(shù)。它接收標(biāo)簽名、屬性和子節(jié)點(diǎn)作為參數(shù),并返回一個包含這些信息的 VNode 對象。
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
if (arguments.length === 2) {
if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
// single vnode without props
if (isVNode(propsOrChildren)) {
return createVNode(type, null, [propsOrChildren])
}
// props without children
return createVNode(type, propsOrChildren)
} else {
// omit props
return createVNode(type, null, propsOrChildren)
}
} else {
if (isVNode(children)) {
children = [children]
}
return createVNode(type, propsOrChildren, children)
}
}VNode 是 Vue.js 對真實(shí) DOM 的一種輕量級表示。它包含了節(jié)點(diǎn)的類型、屬性、子節(jié)點(diǎn)等信息,但不包含具體的 DOM 元素。Vue.js 會在渲染時將 VNode 轉(zhuǎn)換為真實(shí)的 DOM 元素。
在 h() 函數(shù)的源碼中,你會看到大量的邊界情況處理和類型檢查。例如,如果子節(jié)點(diǎn)是一個字符串,它會被轉(zhuǎn)換為一個文本節(jié)點(diǎn);如果子節(jié)點(diǎn)是一個數(shù)組,它會遍歷數(shù)組并遞歸地創(chuàng)建子節(jié)點(diǎn)的 VNode;如果子節(jié)點(diǎn)是一個函數(shù),它會調(diào)用該函數(shù)并傳遞當(dāng)前上下文來創(chuàng)建子節(jié)點(diǎn)的 VNode。
_createVNode 又做了啥?
function _createVNode(
type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
props: (Data & VNodeProps) | null = null,
children: unknown = null,
// 更新標(biāo)志
patchFlag: number = 0,
// 自定義屬性
dynamicProps: string[] | null = null,
// 是否是動態(tài)節(jié)點(diǎn),(v-if v-for)
isBlockNode = false
): VNode {
// type必傳參數(shù)
if (!type || type === NULL_DYNAMIC_COMPONENT) {
if (__DEV__ && !type) {
warn(`Invalid vnode type when creating vnode: ${type}.`)
}
type = Comment
}
// Class 類型的type標(biāo)準(zhǔn)化
// class component normalization.
if (isFunction(type) && '__vccOpts' in type) {
type = type.__vccOpts
}
// class & style normalization.
if (props) {
// props 如果是響應(yīng)式,clone 一個副本
if (isProxy(props) || InternalObjectKey in props) {
props = extend({}, props)
}
let { class: klass, style } = props
// 標(biāo)準(zhǔn)化class, 支持 string , array, object 三種形式
if (klass && !isString(klass)) {
props.class = normalizeClass(klass)
}
// 標(biāo)準(zhǔn)化style, 支持 array ,object 兩種形式
if (isObject(style)) {
// reactive state objects need to be cloned since they are likely to be
// mutated
if (isProxy(style) && !isArray(style)) {
style = extend({}, style)
}
props.style = normalizeStyle(style)
}
}
// encode the vnode type information into a bitmap
const shapeFlag = isString(type)
? ShapeFlags.ELEMENT
: __FEATURE_SUSPENSE__ && isSuspense(type)
? ShapeFlags.SUSPENSE
: isTeleport(type)
? ShapeFlags.TELEPORT
: isObject(type)
? ShapeFlags.STATEFUL_COMPONENT
: isFunction(type)
? ShapeFlags.FUNCTIONAL_COMPONENT
: 0
if (__DEV__ && shapeFlag & ShapeFlags.STATEFUL_COMPONENT && isProxy(type)) {
type = toRaw(type)
warn(
`Vue received a Component which was made a reactive object. This can ` +
`lead to unnecessary performance overhead, and should be avoided by ` +
`marking the component with \`markRaw\` or using \`shallowRef\` ` +
`instead of \`ref\`.`,
`\nComponent that was made reactive: `,
type
)
}
// 構(gòu)造 VNode 模型
const vnode: VNode = {
__v_isVNode: true,
__v_skip: true,
type,
props,
key: props && normalizeKey(props),
ref: props && normalizeRef(props),
scopeId: currentScopeId,
children: null,
component: null,
suspense: null,
dirs: null,
transition: null,
el: null,
anchor: null,
target: null,
targetAnchor: null,
staticCount: 0,
shapeFlag,
patchFlag,
dynamicProps,
dynamicChildren: null,
appContext: null
}
normalizeChildren(vnode, children)
// presence of a patch flag indicates this node needs patching on updates.
// component nodes also should always be patched, because even if the
// component doesn't need to update, it needs to persist the instance on to
// the next vnode so that it can be properly unmounted later.
// patchFlag 標(biāo)志存在表示節(jié)點(diǎn)需要更新,組件節(jié)點(diǎn)一直存在 patchFlag,因?yàn)榧词共恍枰?,它需要將?shí)例持久化到下一個 vnode,以便以后可以正確卸載它
if (
shouldTrack > 0 &&
!isBlockNode &&
currentBlock &&
// the EVENTS flag is only for hydration and if it is the only flag, the
// vnode should not be considered dynamic due to handler caching.
patchFlag !== PatchFlags.HYDRATE_EVENTS &&
(patchFlag > 0 ||
shapeFlag & ShapeFlags.SUSPENSE ||
shapeFlag & ShapeFlags.TELEPORT ||
shapeFlag & ShapeFlags.STATEFUL_COMPONENT ||
shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT)
) {
// 壓入 VNode 棧
currentBlock.push(vnode)
}
return vnode
}四、使用場景
h() 函數(shù)在 Vue.js 中有多種使用場景,以下是一些常見的場景:
渲染函數(shù):
- 當(dāng)需要完全控制組件的渲染邏輯時,可以使用渲染函數(shù),并在其中使用
h()函數(shù)來創(chuàng)建 VNode。這種方式提供了比模板語法更高的靈活性和控制力。
以下是一個使用渲染函數(shù)的示例,展示了如何根據(jù)條件動態(tài)渲染不同的內(nèi)容:
import { h } from 'vue';
export default {
props: ['isLoggedIn'],
render() {
return h('div',
{},
this.isLoggedIn
? h('p', {}, 'Welcome back!')
: h('p', {}, 'Please log in.')
);
}
};高階組件(HOC):
- 高階組件是一種模式,它接收一個組件作為參數(shù),并返回一個新的組件。在創(chuàng)建新組件的過程中,
h()函數(shù)用于定制或擴(kuò)展原始組件的渲染邏輯。
以下是一個高階組件的示例,展示了如何為組件添加額外的類名:
function withClassName(WrappedComponent, className) {
return {
props: WrappedComponent.props,
render() {
return h(WrappedComponent, { ...this.$props, class: className });
}
};
}
// 使用高階組件
const MyComponentWithClass = withClassName(MyComponent, 'my-custom-class');動態(tài)組件:
- 在需要根據(jù)條件動態(tài)渲染不同組件時,
h()函數(shù)可以方便地根據(jù)條件創(chuàng)建不同的 VNode。
以下是一個動態(tài)組件的示例,展示了如何根據(jù)條件渲染不同的組件:
import { h, defineComponent } from 'vue';
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
export default defineComponent({
props: ['condition'],
render() {
return this.condition
? h(ComponentA, { ...this.$props })
: h(ComponentB, { ...this.$props });
}
});手動創(chuàng)建復(fù)雜結(jié)構(gòu):
- 在某些情況下,可能需要手動創(chuàng)建復(fù)雜的組件結(jié)構(gòu),而不是使用模板語法。這時,
h()函數(shù)就顯得非常有用。
以下是一個手動創(chuàng)建復(fù)雜結(jié)構(gòu)的示例,展示了如何創(chuàng)建一個帶有嵌套子組件的表格:
import { h } from 'vue';
import TableRow from './TableRow.vue';
export default {
props: ['data'],
render() {
return h('table',
{},
this.data.map(row =>
h(TableRow, { key: row.id, row: row })
)
);
}
};五、其他比較
與模板語法相比,使用 h() 函數(shù)提供了更高的靈活性和控制力。模板語法更適合于簡單的組件和靜態(tài)內(nèi)容,而 h() 函數(shù)則更適合于復(fù)雜的組件和動態(tài)內(nèi)容。
此外,與 JSX 相比,h() 函數(shù)更加簡潔和直接。JSX 是一種語法糖,它允許在 JavaScript 中編寫類似 HTML 的代碼。然而,JSX 需要額外的編譯步驟,并且可能會增加代碼的復(fù)雜性。而 h() 函數(shù)則是 Vue.js 內(nèi)置的工具函數(shù),無需額外的編譯步驟,并且更加符合 Vue.js 的設(shè)計(jì)哲學(xué)。
六、最佳實(shí)踐
在使用 h() 函數(shù)時,以下是一些最佳實(shí)踐:
保持簡潔:
- 盡量避免在渲染函數(shù)中編寫過多的邏輯。可以將復(fù)雜的邏輯拆分成多個函數(shù)或組件,以提高代碼的可讀性和可維護(hù)性。
使用輔助函數(shù):
- 可以編寫一些輔助函數(shù)來簡化
h()函數(shù)的使用。例如,可以編寫一個函數(shù)來創(chuàng)建帶有特定樣式的節(jié)點(diǎn),或者一個函數(shù)來創(chuàng)建帶有事件監(jiān)聽器的節(jié)點(diǎn)。
避免過度使用:
- 在大多數(shù)情況下,模板語法已經(jīng)足夠滿足需求。只有在需要更高的靈活性和控制力時,才應(yīng)該考慮使用
h()函數(shù)。
與模板語法結(jié)合使用:
- 可以將
h()函數(shù)與模板語法結(jié)合使用。例如,可以在模板中使用<script setup>語法來定義渲染函數(shù),并在其中使用h()函數(shù)來創(chuàng)建復(fù)雜的節(jié)點(diǎn)結(jié)構(gòu)。
七、總結(jié)
h() 函數(shù)是 Vue.js 框架中一個強(qiáng)大且靈活的工具,用于在渲染函數(shù)中創(chuàng)建虛擬 DOM 節(jié)點(diǎn)。通過深入理解 h() 函數(shù)的語法、源碼和使用場景,開發(fā)者可以更好地掌握 Vue.js 的渲染機(jī)制,并在實(shí)際開發(fā)中靈活運(yùn)用這一工具來創(chuàng)建高效、可維護(hù)的組件。無論是編寫渲染函數(shù)、實(shí)現(xiàn)高階組件,還是處理動態(tài)組件和復(fù)雜結(jié)構(gòu),h() 函數(shù)都是不可或缺的一部分。
到此這篇關(guān)于一文解析Vue h函數(shù)到底是個啥的文章就介紹到這了,更多相關(guān)Vue h函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3如何將html元素變成canvas(海報(bào)生成),進(jìn)行圖片保存/截圖
這篇文章主要介紹了vue3實(shí)現(xiàn)將html元素變成canvas(海報(bào)生成),進(jìn)行圖片保存/截圖,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05
vue頁面渲染數(shù)組中數(shù)據(jù)文案后添加逗號最后不加
這篇文章主要為大家介紹了vue頁面渲染數(shù)組中數(shù)據(jù)文案后添加逗號最后不加逗號示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
vue通過v-html指令渲染的富文本無法修改樣式的解決方案
這篇文章主要介紹了vue通過v-html指令渲染的富文本無法修改樣式的解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
vue中的事件觸發(fā)(emit)及監(jiān)聽(on)問題
這篇文章主要介紹了vue中的事件觸發(fā)(emit)及監(jiān)聽(on)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
vue使用SVG實(shí)現(xiàn)圓形進(jìn)度條音樂播放
這篇文章主要為大家詳細(xì)介紹了vue使用SVG實(shí)現(xiàn)圓形進(jìn)度條音樂播放,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04

