姿勢很重要,末尾有福利
vue-antd-ui開源了一段時間後,收到了一些反饋,尤其是Form元件上線後,很多使用者對JSX的使用感到迷惑和不習慣,為此專門介紹下Vue JSX的使用姿勢及注意事項。
Form元件的自動收集校驗功能需要在JSX下使用,當然如果不需要自動收集校驗,你依然可以使用template
Vue 推薦在絕大多數情況下使用template來建立你的HTML。然而在一些場景中,你真的需要 JavaScript 的完全程式設計的能力,就需要使用render函式,它比 template 更接近編譯器。但是複雜的render函式書寫異常痛苦,好在官方提供了一個Babel 外掛,可以將更接近於模板語法的JSX轉譯成JavaScript。
使用過React的同學對JSX肯定不陌生,但是Vue的JSX寫法和React的還是有一些區別。
React中父子之間傳遞的所有資料都是屬性,即所有資料均掛載在props
下(style, className, children, value, onChange等等)。
Vue則不然,僅僅屬性就有三種:元件屬性props
,普通html屬性attrs
,Dom屬性domProps
。
接下來我們通過一個示例來詳細解釋他們的區別:
本文程式碼可在codesandbox檢視執行
const ButtonCounter = {
name: "button-counter",
props: ["count"],
methods: {
onClick() {
this.$emit("change", this.count + 1);
}
},
render() {
return (
<button onClick={this.onClick}>You clicked me {this.count} times.</button>
);
}
};
export default {
name: "button-counter-container",
data() {
return {
count: 0
};
},
methods: {
onChange(val) {
this.count = val;
}
},
render() {
const { count, onChange } = this;
return (
<div>
<ButtonCounter
style={{ marginTop: "10px" }}
count={count}
type="button"
onChange={onChange}
/>
<ButtonCounter
style={{ marginTop: "10px" }}
count={count}
type="button"
domPropsInnerHTML={`hello ${this.count}.`}
onChange={onChange}
/>
</div>
);
}
};
複製程式碼
元件屬性props
:指元件宣告的屬性,即上述示例中宣告的props: ['count']
。
普通html屬性attrs
: __指元件未宣告的屬性,即上述示例中的type="button"
,該屬性預設會直接掛載到元件根節點的上,如果不需要掛載到根節點,可宣告 inheritAttrs: false
。
Dom屬性domProps
:指的Dom屬性,如上述示例中的innerHTML
,它會覆蓋元件內部的children
, 這類屬性我們一般很少使用到。
同樣事件屬性也分了兩種:on
nativeOn
那麼問題來了,元件是如何區分各類屬性的呢?
答:正則則則...... ?,babel-plugin-transform-vue-jsx外掛會通過正則匹配的方式在編譯階段將書寫在元件上屬性進行“分類”。 onXXX的均被認為是事件,nativeOnXXX是原生事件,domPropsXXX是Dom屬性。
class,staticClass,style,key,ref,refInFor,slot,scopedSlots
這些被認為是頂級屬性,至於我們屬性宣告的props,以及html屬性attrs,不需要加字首,外掛會將其統一分類到attrs屬性下,然後在執行階段根據是否在props宣告來決定屬性歸屬(即屬於props
還是attrs
)。
在編譯階段通過正則來區分,畢竟不是很嚴謹,那麼根據以上分類規則會有哪些問題呢?
第一、屬性分類是編譯階段進行的分類,那麼對於動態屬性如何劃分分類?
在React中所有屬性都是頂級屬性,直接使用{...props}
就可以了,但是在Vue中,你需要明確該屬性所屬的分類,如一個動態屬性value和事件change,你可以使用如下方式(延展屬性)傳遞:
const dynamicProps = {
props: {},
on: {},
}
if(haValue) dynamicProps.props.value = value
if(hasChange) dynamicProps.on.change = onChange
<Dynamic {...dynamicProps} />
複製程式碼
當然你可以混合著使用:
<Dynamic {...dynamicProps} style="color: red"/>
複製程式碼
先別高興太早,如果你沒有深入使用過Vue JSX,不建議你使用混合方式,因為Vue會對其進行屬性合併,至於合併的規則官方也並沒有詳細的文件,文件中有一處示例,我在這再舉一個例子:
const dynamicProps2 = { on: { change: onChange2 } };
<Dynamic
{...{ on: { change: onChange1 } }}
{...dynamicProps2}
onChange={onChange3}
/>
複製程式碼
上例中的onChange1、onChange2、onChange3都會觸發,而你想要的可能僅僅是onChange3。其它屬性的合併規則我就不一一列舉了,總之,我不建議你使用混合方式,除非你及你的團隊其他小夥伴對其規則瞭解的足夠透徹。
注:理想情況你不應該需要動態屬性,在業務開發中也比較少的使用動態屬性,但如果你嘗試開發一些通用性比較強的元件,就很難逃過動態屬性的使用。
第二、如果宣告的屬性就是onXXX怎麼處理?
首先我不建議你這麼做,但如果真的需要,你必須明確該屬性的分類,如下所示:
<div>
<Dynamic value="獲取到value,然而並不能獲取到onXXX" onXXX="?" />
<Dynamic {...{ props: { onXXX: "獲取到onXXX,但不建議使用" } }} />
</div>
複製程式碼
第三、函式式元件的props如何處理?
關於函式式元件的相關概念可檢視官方文件,文件中有如下一段內容:
注意:在 2.3.0 之前的版本中,如果一個函式式元件想要接受 props,則
props
選項是必須的。在 2.3.0 或以上的版本中,你可以省略props
選項,所有元件上的屬性都會被自動解析為 props。
props
和attrs
之間存在著很微妙的關係,在普通元件中,只要明確宣告的屬性會被劃分到props
分類中,剩下的均在attrs
中。而對於函式式元件,只要省略了props
選項,傳參時不管是否明確分類,最終context.props
獲取到的都是全部屬性,如果你需要獲取明確的分類情況,可以在context.data
下檢視。
總之,在函式式元件中,推薦省略props
選項。
第四、指令是否還可用?
很不幸的告訴你,大多數指令並不能在JSX中使用,對於原生指令,只有v-show
是支援的。
大部分指令在JSX中可以使用表示式來替代,如:條件運算子(?:)替代v-if
、array.map
替代v-for
對於自定義的指令,可以使用如下方式使用:
const directives = [
{ name: 'my-dir', value: 123, modifiers: { abc: true } }
]
return <div {...{ directives }}/>
複製程式碼
更多Vue JSX的知識,可以檢視官方文件。
總結:
說了那麼多,其實只要記住一點,儘量使用明確分類的方式傳遞屬性,而不是要babel外掛幫你分類及合併屬性。
最後彙報下vue-antd-ui的進度,目前元件數量48個,相較react版,還有List、TreeSelect、Metion、Carousel沒有開發,再過去的一段時間裡,我們更多的去完善元件單元測試,測試覆蓋率83%,單測數量610個,接下來測試依然是我們的主要工作。
質量永遠要比數量重要的多。
歡迎star,歡迎pr。
福利環節:
本人蔘與翻譯的React書籍(React Quickly,中文名:快速上手React程式設計)即將上市,現有樣書4本,為了答謝支援vue-antd-ui的使用者,使用者可到該issue下按照規範(需截圖)回覆後私信我地址,郵費自理,先到先得,數量有限,敬請諒解。
相對深入淺出系列,該書更加適合初學者入門,如果你是Vue開發者,想要快速的學習React及周邊技術(Redux、GraphQL、Jest等),我想這本書應該是不二之選。