vue2.x中h函數(shù)(createElement)與vue3中的h函數(shù)詳解
1. vue2.x的 h 函數(shù)(createElement)
- 使用方法及介紹:(參考官網(wǎng)提取)
- h函數(shù)第一個是標簽名字 或者是組件名字,第二個參數(shù)是配置項,第三個參數(shù)是 innerText ,不會幫你轉(zhuǎn)換節(jié)點,如果需要轉(zhuǎn)換成節(jié)點(v-html)請去第二個參數(shù)中的 domProps 配置 innerHTML
- 當?shù)诙€參數(shù)是字符串的時候則會直接當 innerText 渲染(相當于配置項參數(shù)為空對象)【h(‘span’, ‘姓名’)】
- 第三個參數(shù)如果為字符串的時候可以理解為默認值?。╥nnerText的默認值)如果同時設(shè)置了這兩個則配置中的權(quán)重更高
- 第三個參數(shù)也是該元素的子集合、插槽設(shè)置的地方。
- 溫馨提示:vue2.x的h函數(shù)跟vue3.x的有點不一樣,第二個參數(shù)配置項格式變了,第三個參數(shù)為函數(shù)返回,具體情況看本文第二要點
- 以下為常見的常規(guī)配置
import SelectEdit from './SelectEdit' export default { data() { return { name: '' } }, render(h) { // 如果使用原生的則 // return h('div', { // 這個是掛載組件 return h(SelectEdit, { // 此處是給 SelectEdit 組件傳值的(props傳值) props: { value: 1, type: 'on' }, // class可以數(shù)組的形式也可以對象的形式 // class: ['speci-class'], class: { 'speci-class': true }, // 樣式有-的注意小駝峰 或者使用 string 模式 style: { color: 'red', fontSize: '14px', // 或者這樣 'font-size': '14px' }, // 普通的 HTML attribute attrs: { placeholder: '這是給原生html賦值placeholder屬性' }, // DOM property domProps: { innerHTML: 'DOM property', // 這個參數(shù)等同于h函數(shù)的第三個參數(shù) innerText: 'xxxxxxx' }, // 這里是掛載方法的但不再支持如 `v-on:keyup.enter` 這樣的修飾器 on: { // 方法名可以自定義(組件內(nèi) $emit('xxxchange', {name: 'zs'})) 'xxxchange': val => { this.name = val.name; }, 'click': val => { this.name = val.name; }, }, // 僅用于組件,用于監(jiān)聽原生事件,而不是組件內(nèi)部使用 // `vm.$emit` 觸發(fā)的事件。 nativeOn: { click: this.nativeClickHandler }, // 自定義指令。注意,你無法對 `binding` 中的 `oldValue` directives: [ { name: 'my-custom-directive', value: '2', expression: '1 + 1', arg: 'foo', modifiers: { bar: true } } ], // 作用域插槽的格式為 scopedSlots: { default: props => createElement('span', props.text) }, // 如果組件是其它組件的子組件,需為插槽指定名稱 slot: 'name-of-slot', // 其它特殊頂層 property key: 'myKey', ref: 'myRef', // 如果你在渲染函數(shù)中給多個元素都應(yīng)用了相同的 ref 名, // 那么 `$refs.myRef` 會變成一個數(shù)組。 refInFor: true }, '這里是顯示文本') } }
舉例子了:如果你想要實現(xiàn)以下效果(v-model)
<div class="row zs active info" name="zs"> <span style="background-color: red;font-size: 16px;" @click="handleName">姓名:</span> <i >{{ name }}</i> <input v-model="name" /> </div>
目測沒問題
data() { return { name: '' } }, render(h) { return h('div', { class: ['row zs', 'active', 'info'], attrs: { name: 'zs' } }, [ h('span', { style: { backgroundColor: red, 'font-size': '16px' }, on: { click: handleName } }, '姓名:'), h('i', '張三'), h('input', { domProps: { value: this.name }, on: { input: function (event) { this.$emit('input', event.target.value) } } }) ] ) }
2. vue3 h函數(shù)配置項
- 與2.x相比,第一個參數(shù)格式?jīng)]有更變,第二個參數(shù)格式更變了,第三個參數(shù)變?yōu)榻ㄗh使用函數(shù)返回了
- 第三個參數(shù)如果為字符串的時候可以理解為默認值!(innerText的默認值)如果同時設(shè)置了這兩個則配置中的權(quán)重更高
- 第三個參數(shù)也是該元素的子集合、插槽設(shè)置的地方。
- 第三個參數(shù)不使用函數(shù)會有一個vue警告(所以說直接函數(shù)吧)
- 具體使用如下:
import { h } from 'vue'; import { ElButton } from 'element-plus' h( ElButton, { type: 'primary', innerText: '修改11', onClick: () => { console.log(11); } }, () => '修改' )
2.1 v-model實現(xiàn)(以下開始為官網(wǎng)實現(xiàn))
props: ['modelValue'], emits: ['update:modelValue'], render() { return h(SomeComponent, { modelValue: this.modelValue, 'onUpdate:modelValue': value => this.$emit('update:modelValue', value) }) }
自己搞忘記后重新弄的踩坑記錄:
- 雙向綁定實現(xiàn):2.x中是value,但是到了3.x中不是value了而是modelValue
- onUpdate:modelValue 相當于就是 v-model,只不過這個變成了函數(shù),在這個函數(shù)里面需要你自己給綁定元素賦值。不賦值則會出現(xiàn)雙向綁定失效的問題!
2.2 v-on
給定有效的事件名稱,例如(onClick, onChange)或自定義的名稱
render() { return h('div', { onClick: $event => console.log('clicked', $event.target) }) }
2.3 事件修飾符
對于 .passive 、.capture 和 .once 事件修飾符,可以使用駝峰寫法將他們拼接在事件名后面:
render() { return h('input', { onClickCapture: this.doThisInCapturingMode, onKeyupOnce: this.doThisOnce, onMouseoverOnceCapture: this.doThisOnceInCapturingMode }) }
對于所有其它的修飾符,私有前綴都不是必須的,因為你可以在事件處理函數(shù)中使用事件方法:
這里是一個使用所有修飾符的例子:
render() { return h('input', { onKeyUp: event => { // 如果觸發(fā)事件的元素不是事件綁定的元素 // 則返回 if (event.target !== event.currentTarget) return // 如果向上鍵不是回車鍵,則終止 // 沒有同時按下按鍵 (13) 和 shift 鍵 if (!event.shiftKey || event.keyCode !== 13) return // 停止事件傳播 event.stopPropagation() // 阻止該元素默認的 keyup 事件 event.preventDefault() // ... } }) }
2.4 插槽
你可以通過 this.$slots 訪問靜態(tài)插槽的內(nèi)容,每個插槽都是一個 VNode 數(shù)組:
render() { // `<div><slot></slot></div>` return h('div', {}, this.$slots.default()) }
props: ['message'], render() { // `<div><slot :text="message"></slot></div>` return h('div', {}, this.$slots.default({ text: this.message })) }
渲染函數(shù)將插槽傳遞給子組件
render() { // `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>` return h('div', [ h( resolveComponent('child'), {}, // 將 `slots` 以 { name: props => VNode | Array<VNode> } 的形式傳遞給子對象。 { default: (props) => h('span', props.text) } ) ]) }
插槽以函數(shù)的形式傳遞,允許子組件控制每個插槽內(nèi)容的創(chuàng)建。任何響應(yīng)式數(shù)據(jù)都應(yīng)該在插槽函數(shù)內(nèi)訪問,以確保它被注冊為子組件的依賴關(guān)系,而不是父組件。相反,對 resolveComponent 的調(diào)用應(yīng)該在插槽函數(shù)之外進行,否則它們會相對于錯誤的組件進行解析。
// `<MyButton><MyIcon :name="icon" />{{ text }}</MyButton>` render() { // 應(yīng)該是在插槽函數(shù)外面調(diào)用 resolveComponent。 const Button = resolveComponent('MyButton') const Icon = resolveComponent('MyIcon') return h( Button, null, { // 使用箭頭函數(shù)保存 `this` 的值 default: (props) => { // 響應(yīng)式 property 應(yīng)該在插槽函數(shù)內(nèi)部讀取, // 這樣它們就會成為 children 渲染的依賴。 return [ h(Icon, { name: this.icon }), this.text ] } } ) }
如果一個組件從它的父組件中接收到插槽,它們可以直接傳遞給子組件。
render() { return h(Panel, null, this.$slots) }
也可以根據(jù)情況單獨傳遞或包裹住。
render() { return h( Panel, null, { // 如果我們想傳遞一個槽函數(shù),我們可以通過 header: this.$slots.header, // 如果我們需要以某種方式對插槽進行操作, // 那么我們需要用一個新的函數(shù)來包裹它 default: (props) => { const children = this.$slots.default ? this.$slots.default(props) : [] return children.concat(h('div', 'Extra child')) } } ) }
2.5 component 和 is
在底層實現(xiàn)里,模板使用 resolveDynamicComponent 來實現(xiàn) is attribute。如果我們在 render 函數(shù)中需要 is 提供的所有靈活性,我們可以使用同樣的函數(shù):
const { h, resolveDynamicComponent } = Vue // ... // `<component :is="name"></component>` render() { const Component = resolveDynamicComponent(this.name) return h(Component) }
就像 is, resolveDynamicComponent 支持傳遞一個組件名稱、一個 HTML 元素名稱或一個組件選項對象。
通常這種程度的靈活性是不需要的。通常 resolveDynamicComponent 可以被換做一個更直接的替代方案。
例如,如果我們只需要支持組件名稱,那么可以使用 resolveComponent 來代替。
如果 VNode 始終是一個 HTML 元素,那么我們可以直接把它的名字傳遞給 h:
// `<component :is="bold ? 'strong' : 'em'"></component>` render() { return h(this.bold ? 'strong' : 'em') }
同樣,如果傳遞給 is 的值是一個組件選項對象,那么不需要解析什么,可以直接作為 h 的第一個參數(shù)傳遞。
與 < template > 標簽一樣,< component > 標簽僅在模板中作為語法占位符需要,當遷移到 render 函數(shù)時,應(yīng)被丟棄。
2.6 自定義指令
可以使用 withDirectives 將自定義指令應(yīng)用于 VNode:
const { h, resolveDirective, withDirectives } = Vue // ... // <div v-pin:top.animate="200"></div> render () { const pin = resolveDirective('pin') return withDirectives(h('div'), [ [pin, 200, 'top', { animate: true }] ]) }
resolveDirective 是模板內(nèi)部用來解析指令名稱的同一個函數(shù)。只有當你還沒有直接訪問指令的定義對象時,才需要這樣做
2.7 內(nèi)置組件
諸如 < keep-alive >、< transition >、< transition-group > 和 < teleport > 等內(nèi)置組件默認并沒有被全局注冊。這使得打包工具可以 tree-shake,因此這些組件只會在被用到的時候被引入構(gòu)建。不過這也意味著我們無法通過 resolveComponent 或 resolveDynamicComponent 訪問它們。
在模板中這些組件會被特殊處理,即在它們被用到的時候自動導(dǎo)入。當我們編寫自己的 render 函數(shù)時,需要自行導(dǎo)入它們:
const { h, KeepAlive, Teleport, Transition, TransitionGroup } = Vue // ... render () { return h(Transition, { mode: 'out-in' }, /* ... */) }
2.8 渲染函數(shù)的返回值
在我們目前看過的所有示例中,render 函數(shù)返回的是單個根 VNode。但其實也有別的選項。
返回一個字符串時會創(chuàng)建一個文本 VNode,而不被包裹任何元素:
render() { return 'Hello world!' }
我們也可以返回一個子元素數(shù)組,而不把它們包裹在一個根結(jié)點里。這會創(chuàng)建一個片段 (fragment):
// 相當于模板 `Hello<br>world!` render() { return [ 'Hello', h('br'), 'world!' ] }
可能是因為數(shù)據(jù)依然在加載中的關(guān)系,組件不需要渲染,這時它可以返回 null。這樣我們在 DOM 中會渲染一個注釋節(jié)點
2.9 JSX
如果你寫了很多渲染函數(shù),可能會覺得下面這樣的代碼寫起來很痛苦:
h( 'anchored-heading', { level: 1 }, { default: () => [h('span', 'Hello'), ' world!'] } )
特別是對應(yīng)的模板如此簡單的情況下:
<anchored-heading :level="1"> <span>Hello</span> world! </anchored-heading>
這就是為什么會有一個 Babel 插件,用于在 Vue 中使用 JSX 語法,它可以讓我們回到更接近于模板的語法上。
import AnchoredHeading from './AnchoredHeading.vue' const app = createApp({ render() { return ( <AnchoredHeading level={1}> <span>Hello</span> world! </AnchoredHeading> ) } }) app.mount('#demo')
有關(guān) JSX 如何映射到 JavaScript 的更多信息,請參閱使用文檔 。
參考鏈接
總結(jié)
到此這篇關(guān)于vue2.x中h函數(shù)(createElement)與vue3中的h函數(shù)詳解的文章就介紹到這了,更多相關(guān)vue2.x與vue3中的h函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
antd+vue實現(xiàn)動態(tài)驗證循環(huán)屬性表單的思路
今天通過本文給大家分享antd+vue實現(xiàn)動態(tài)驗證循環(huán)屬性表單的思路,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-09-09Vuejs入門教程之Vue生命周期,數(shù)據(jù),手動掛載,指令,過濾器
本篇文章主要介紹了Vuejs入門教程之Vue生命周期,數(shù)據(jù),手動掛載,指令,過濾器的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04Vue全局注冊中的kebab-case和PascalCase用法
這篇文章主要介紹了Vue全局注冊中的kebab-case和PascalCase用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03