使用 JSX/TSX 開發(fā) Vue3 組件
源地址:https://zhuanlan.zhihu.com/p/153387704
直接使用 TSX
Vue3 的確可以直接使用 tsx
開發(fā)祥诽,唯一需要處理的就是 children
柑爸,而且處理起來還是比較不爽的杨何,例如你不能這么寫:
<div>
<p>1</p>
<p>1</p>
</div>
你需要:
<div>
{
[
<p>1</p>,
<p>1</p>
]
}
</div>
這還是挺惡心的歉摧,不過也不是沒有辦法呕屎,可以封裝一個(gè)工具函數(shù):
function JSXFactory(tag: any, props: any, ...children: any) {
if (
typeof tag !== 'string' &&
typeof tag !== 'symbol' &&
!tag.__isTeleport &&
!tag.__isKeepAlive
) {
// Component
children = children[0]
}
return createVNode(tag, props, children)
}
然后在 tsconfig.json 中配置 jsxFactory
為我們封裝的這個(gè)函數(shù)就可以了。但是這個(gè)函數(shù)限制了我們?cè)跒榻M件傳遞 slots
時(shí)只能:
const App = {
setup() {
const slots = {
foo: () => <p>foo</p>,
bar: () => <p>bar</p>,
}
return () => <Hello>{ slots }</Hello>
}
}
但是這也沒啥問題嘛暂殖。
有了 JSXFactory
工具函數(shù)之后其實(shí)我們可以很開心的用 tsx
寫了预烙,那為啥還要 jsx
插件呢?這是因?yàn)槭褂昧?jsx
語法后我們丟失了很多模板中提供的便利能力茴晋,例如事件修飾符陪捷、v-model
之類的, 因此 jsx
插件還是有必要的晃跺,但是不是必須的揩局。
下面說說 https://github.com/HcySunYang/vue-next-jsx 的設(shè)計(jì)原則和功能。
既支持 JSX 又支持 TSX
tsx
中不支持 amespaced attribute
掀虎,詳見:https://github.com/microsoft/TypeScript/issues/7411 凌盯,但 babel 中是支持,這就意味著你在 jsx 中可以這么寫:
<p v-on:click={ handler } ></p>
但是 tsx
中則不行烹玉,為了語法統(tǒng)一驰怎,我決定不允許在屬性名中使用 :
,而是使用 -
替代 :
<p v-on-click={ handler } ></p>
統(tǒng)一語法的好處是:降低不同項(xiàng)目差異帶來的額外負(fù)擔(dān)/困擾(有的人使用 : 有的人使用 -)二打。
無論是 jsx 還是 tsx县忌,修飾符都不允許使用 .
,而是使用 _
代替:
<p v-on-click_stop={ handler } ></p>
對(duì)于事件继效,vue-next-jsx
支持全部的模板中可用語法症杏,例如:
<div v-on-click_middle={ handler }></div>
<div v-on-click_stop={ handler }></div>
<div v-on-click_right={ handler }></div>
<div v-on-keyup_esc={ handler }></div>
<div v-on={ obj }></div>
v-model
Vue2 中的 .sync
被 v-model:foo
代替了,例如:
<!-- Vue2 -->
<Comp :foo.sync="val" />
<!-- Vue3 -->
<Comp v-model:foo="val" />
在 jsx 中瑞信,把 :
換成 -
:
<Comp v-model-foo={ refVal.value }/>
也可以帶修飾符厉颤,用 _
分割:
<Comp v-model-foo_a_b={ refVal.value }/>
v-bind
在 j/tsx
中不需要 v-bind
,直接使用 jsxExpressionContainer
和 jsxSpreadAttribute
代替:
<Comp foo={ refVal.value } { ...props } bar="bar" />
slots
我不準(zhǔn)備支持 v-slot
凡简,這是因?yàn)樗鼤?huì)導(dǎo)致類型丟失逼友,例如:
<Comp>
<template v-slot-foo="props">
</template>
</Comp>
這里的 props
沒有類型,它就是一個(gè)字符串秤涩,而且我始終推薦像如下這樣為組件傳遞插槽:
<Comp>{ mySlots }</Comp>
至于插槽 mySlots
我們可以自行構(gòu)建它:
const mySlots = {
default: () => [<p>默認(rèn)插槽</p>]
}
KeepAlive 和 Teleport
這兩個(gè)組件比較特殊帜乞,他們的子節(jié)點(diǎn)不會(huì)作為 slots 存在,而是當(dāng)做正常的 children筐眷,不過你不用擔(dān)心黎烈,vue-next-jsx 幫你處理了。
Optimization mode
優(yōu)化模式,正如 https://zhuanlan.zhihu.com/p/150732926 這篇文章中講述的怨喘,我們可以在 jsx 插件中利用這些信息津畸,盡可能的提升性能。
在 babel.config.json
中打開優(yōu)化模式:
{
"presets": [
"@babel/env"
],
"plugins": [
["@hcysunyang/vue-next-jsx", {
// 開啟優(yōu)化模式
"optimizate": true
}]
]
}
實(shí)際上必怜,你可以查看 vue-next-jsx
的測(cè)試用例生成的 肉拓,并與 Vue3 Compiler
對(duì)比,他們的行為是一致的梳庆,包括及其復(fù)雜的情況暖途。
- vue-next-jsx 測(cè)試用例
snapshots
:https://github.com/HcySunYang/vue-next-jsx/blob/main/tests/snapshots - Vue3 Compiler Explorer:https://vue-next-template-explorer.netlify.app/
指定 source
source
指的是 ImportDeclaration
語句的 source
,例如:
import { createApp } from 'vue'
這里的 source
就是 vue
膏执,但是你可能安裝的不是 vue
而是 @vue/runtime-dom
驻售,這時(shí)你需要指定 source
:
{
"presets": [
"@babel/env"
],
"plugins": [
["@hcysunyang/vue-next-jsx", {
// 指定 source
"source": "@vue/runtime-dom"
}]
]
}
v-html / v-text
在 jsx
中支持這兩個(gè)指令意義不大,全當(dāng)順手更米,它們的使用與在模板中相同:
<p v-html={ refHtml.value }></p>
<p v-text={ refText.value }></p>
發(fā)布于 2020-07-02