在Vue中使用JSX的正確姿勢(有福利)

zeka發表於2018-06-14

姿勢很重要,末尾有福利

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。

propsattrs之間存在著很微妙的關係,在普通元件中,只要明確宣告的屬性會被劃分到props分類中,剩下的均在attrs中。而對於函式式元件,只要省略了props選項,傳參時不管是否明確分類,最終context.props獲取到的都是全部屬性,如果你需要獲取明確的分類情況,可以在context.data下檢視。 總之,在函式式元件中,推薦省略props選項。

第四、指令是否還可用?

很不幸的告訴你,大多數指令並不能在JSX中使用,對於原生指令,只有v-show是支援的。 大部分指令在JSX中可以使用表示式來替代,如:條件運算子(?:)替代v-ifarray.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等),我想這本書應該是不二之選。

相關文章