<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          擁抱 Vue 3 系列之 JSX 語(yǔ)法

          共 8109字,需瀏覽 17分鐘

           ·

          2020-08-05 02:25

          “別再更新了,學(xué)不動(dòng)了”。這句話(huà)不知道出了多少開(kāi)發(fā)者的辛酸。在過(guò)去的一年中,Vue 團(tuán)隊(duì)一直都在開(kāi)發(fā) Vue.js 的下一個(gè)主要版本,就在 6 月底,尤大更新同步了 Vue 3 及其周邊生態(tài)的狀態(tài):Vue 3: mid 2020 status update (https://github.com/vuejs/rfcs/issues/183)。

          if?(isTrue("I?am?planning?to?use?Vue?3?for?a?new?project"))?{
          ??if?(isTrue("I?need?IE11?support"))?{
          ????await?IE11CompatBuild()?//?July?2020
          ??}
          ??if?(isTrue("RFCs?are?too?dense,?I?need?an?easy-to-read?guide"))?{
          ????await?migrationGuide()?//?July?2020
          ??}
          ??if?(isTrue("I'd?rather?wait?until?it's?really?ready")?{
          ??????await?finalRelease()?//?Targeting?early?August?2020
          ??})
          ??run(`npm?init?vite-app?hello-vue3`)
          ??return
          }

          我們可以看到,如果一切順利的話(huà),預(yù)計(jì)在 8 月份,Vue 3 的正式版本就可以和我們見(jiàn)面了,目前距離發(fā)布正式版還有一定的差距,還要做一些兼容性的工作。同時(shí)還會(huì)提供對(duì) IE11 的支持。

          Vue 3 為了達(dá)到更快、更小、更易于維護(hù)、更貼近原生、對(duì)開(kāi)發(fā)者更友好的目的,在很多方面進(jìn)行了重構(gòu):
          • 全面擁抱 TypeScript

          • 重構(gòu) complier

          • 重構(gòu) Virtual DOM

          • ......

          寫(xiě)在前面

          這是該系列文章的第一篇,后續(xù)會(huì)持續(xù)更新,覆蓋 Vue 3 生態(tài)常用庫(kù)。JSX 是一個(gè)小眾群體使用開(kāi)發(fā)方式,第一篇以 ?JSX 為切入點(diǎn),目標(biāo)是讓大多數(shù)開(kāi)發(fā) Vue 的同學(xué)也對(duì) JSX 有一定的認(rèn)知,在用 Vue 開(kāi)發(fā)復(fù)雜應(yīng)用時(shí),也能有更加靈活的方式。

          比如當(dāng)開(kāi)始寫(xiě)一個(gè)只能通過(guò) level prop 動(dòng)態(tài)生成標(biāo)題 (heading) 的組件時(shí),你可能很快想到這樣實(shí)現(xiàn):

          <script?type="text/x-template"?id="anchored-heading-template">
          ??if
          ="level?===?1">
          ????<slot>slot>
          ??h1>
          ??<h2?v-else-if="level?===?2">
          ????<slot>slot>

          ??h2>
          ??<h3?v-else-if="level?===?3">
          ????<slot>slot>

          ??h3>
          script>

          這里用模板并不是最好的選擇,在每一個(gè)級(jí)別的標(biāo)題中重復(fù)書(shū)寫(xiě)了 ,不夠優(yōu)雅。

          如果嘗試用 JSX 來(lái)寫(xiě),代碼就會(huì)變得簡(jiǎn)單很多。
          const?App?=?{
          ??render()?{
          ????const?tag?=?`h${this.level}`
          ????return?<tag>{this.$slots.default}tag>
          ??}
          }
          看過(guò) Ant Design Vue (https://github.com/vueComponent/ant-design-vue) 源碼 (下面簡(jiǎn)稱(chēng)為 antdv) 的同學(xué)應(yīng)該知道, antdv 的底層是基于 JSX 來(lái)實(shí)現(xiàn)的,也是 Vue 生態(tài)中使用 JSX 的深度用戶(hù)。antd 為了盡快的兼容 Vue 3,和 Vue 官方展開(kāi)合作,于是一起開(kāi)發(fā)了 @ant-design-vue/babel-plugin-jsx (https://github.com/vueComponent/jsx)

          Vue JSX 簡(jiǎn)介

          對(duì)于使用 React 的開(kāi)發(fā)者來(lái)說(shuō),JSX 再熟悉不過(guò)了,但是如果你是一個(gè) Vue 的重度用戶(hù),可能對(duì) JSX 不是特別熟悉,甚至聽(tīng)到有同學(xué)說(shuō)沒(méi)有 template 的 Vue 項(xiàng)目沒(méi)有靈魂。
          先來(lái)看下面一段代碼:
          const?el?=?<div>Vue?3div>;

          這段代碼既不是 HTML 也不是字符串,被稱(chēng)之為 JSX,是 JavaScript 的擴(kuò)展語(yǔ)法。JSX 可能會(huì)使人聯(lián)想到模板語(yǔ)法,但是它具備 Javascript 的完全變成能力。

          看到這里可能會(huì)有疑問(wèn),不少同學(xué)可能會(huì)以為 JSX 是 React 中特有的,其實(shí)不然。大多數(shù)同學(xué)都知道,我們平常在 .vue 文件中開(kāi)發(fā)的代碼,實(shí)際上會(huì)被 vue-loader 處理,但可能少數(shù)同學(xué)去看過(guò)我們手把手寫(xiě)出的代碼,會(huì)變編譯成啥樣。有興趣的同學(xué)可以戳這個(gè)地址來(lái)看下。vue-template-explorer (https://vue-template-explorer.netlify.app/) (因?yàn)楸娝苤脑颍赡茉L問(wèn)略慢)
          <div?id="app">{{?msg?}}div>
          function?render()?{
          ??with(this)?{
          ????return?_c('div',?{
          ??????attrs:?{
          ????????"id":?"app"
          ??????}
          ????},?[_v(_s(msg))])
          ??}
          }

          觀察上述代碼我們發(fā)現(xiàn),到運(yùn)行階段實(shí)際上都是 render 函數(shù)在執(zhí)行。Vue 推薦在絕大多數(shù)情況下使用 template 來(lái)創(chuàng)建你的 HTML。然而在一些場(chǎng)景中,就需要使用 render 函數(shù),它比 template 更加靈活。

          寫(xiě)過(guò) render 函數(shù)的同學(xué)可能深有體會(huì),書(shū)寫(xiě)復(fù)雜的 render 函數(shù)異常痛苦,而且難以維護(hù),非常容易被稱(chēng)之為 “祖?zhèn)鞔a”。好在 2.0 的官方提供了一個(gè) Babel 插件(https://link.zhihu.com/?target=https%3A//github.com/vuejs/babel-plugin-transform-vue-jsx),可以將更接近于模板語(yǔ)法的 JSX 轉(zhuǎn)譯成 JavaScript。

          使用過(guò) React 的同學(xué)對(duì)于如何寫(xiě) JSX 語(yǔ)法一定非常熟悉了,然而,Vue 2 中 的 JSX 寫(xiě)法和 React 還是有一些略微的區(qū)別。React 中所有傳遞的數(shù)據(jù)都掛在頂層。

          const?App?=?<A?className="x"?style={style}?onChange={onChange}?/>
          Vue 2 中,僅僅屬性就有三種:組件屬性 props,普通 html 屬性 attrs,DOM 屬性 domProps。想要更多了解如何在 Vue 2 中寫(xiě) JSX 語(yǔ)法,可以看這篇,在 Vue 中使用 JSX 的正確姿勢(shì) (https://zhuanlan.zhihu.com/p/37920151)。

          Vue 3 中對(duì) JSX 帶來(lái)的改變

          • 屬性傳遞
          Vue 3 中,屬性這塊的傳遞和 React 類(lèi)似,意味這不需要再傳遞 props,attrs 這些屬性。
          //?before
          {
          ??class:?['foo',?'bar'],
          ??style:?{?color:?'red'?},
          ??attrs:?{?id:?'foo'?},
          ??domProps:?{?innerHTML:?''?},
          ??on:?{?click:?foo?},
          ??key:?'foo'
          }

          //?after
          {
          ??class:?['foo',?'bar'],
          ??style:?{?color:?'red'?},
          ??id:?'foo',
          ??innerHTML:?'',
          ??onClick:?foo,
          ??key:?'foo'
          }
          • 指令改版

          Vue 3 把大多數(shù)全局 API 和 內(nèi)部 helper 移到了 ES 模塊中導(dǎo)出(譬如 v-model、transition、teleport),從而使得 Vue 3 在增加了很多新特性之后,基線(xiàn)的體積反而小了。
          v-modelv-show 這些 API 全部通過(guò)模塊導(dǎo)出的方式來(lái)引入

          基線(xiàn)體積:無(wú)法舍棄的代碼體積

          我們來(lái)看一段非常簡(jiǎn)單的代碼 ,在 Vue 2 和 Vue 3 中的編譯結(jié)果有何不同

          //?before
          function?render()?{
          ??with(this)?{
          ????return?_c('input',?{
          ??????directives:?[{
          ????????name:?"model",
          ????????rawName:?"v-model",
          ????????value:?(x),
          ????????expression:?"x"
          ??????}],
          ??????domProps:?{
          ????????"value":?(x)
          ??????},
          ??????on:?{
          ????????"input":?function?($event)?{
          ??????????if?($event.target.composing)?return;
          ??????????x?=?$event.target.value
          ????????}
          ??????}
          ????})
          ??}
          }
          //?after
          import?{?vModelText?as?_vModelText,?createVNode?as?_createVNode,?withDirectives?as?_withDirectives,?openBlock?as?_openBlock,?createBlock?as?_createBlock?}?from?"vue"

          export?function?render(_ctx,?_cache)?{
          ??return?_withDirectives((_openBlock(),?_createBlock("input",?{
          ????"onUpdate:modelValue":?$event?=>?(_ctx.x?=?$event)
          ??},?null,?8?/*?PROPS?*/,?["onUpdate:modelValue"])),?[
          ????[_vModelText,?_ctx.x]
          ??])
          }

          可以看到在 Vue 3 中,對(duì)各個(gè) API 做了更加細(xì)致的拆分,理想狀態(tài)下,用戶(hù)可以在構(gòu)建時(shí)利用搖樹(shù)優(yōu)化 (tree-shaking) 去掉框架中不需要的特性,只保留自己用到的特性。模版編譯器會(huì)生成適合做 tree-shaking 的代碼,不需要使用者去關(guān)心如何去做,這部分的改動(dòng)同樣需要在 JSX 寫(xiě)法中實(shí)現(xiàn)。

          模板編譯器中增加了 PatchFlag,在 JSX 的編譯過(guò)程同樣也做了處理,性能會(huì)有提升,但是考慮到 JSX 的靈活性,做了一些兼容處理,該功能還在測(cè)試階段。

          從 Vue 2 到 Vue 3 的過(guò)渡

          Vue 3 雖然引入了一部分破壞性的更新,但對(duì)于絕大多數(shù) Vue 2 的 API 還是兼容的。那么同樣的,我們也要盡可能讓使用 JSX 的用戶(hù)通過(guò)最小的成本升級(jí)到 Vue 3,這是一個(gè)核心的目標(biāo)。寫(xiě)這篇文章的時(shí)候,antdv 已經(jīng)使用 @ant-design-vue/babel-plugin-jsx (https://github.com/vueComponent/ant-design-vue) 重構(gòu)了大約 70% 的功能,預(yù)計(jì)會(huì)在 Vue 3 正式版之前發(fā)布測(cè)試版,大概率會(huì)是東半球最快兼容 Vue 3 的企業(yè)級(jí)組件庫(kù)。

          Vue 3 JSX 的 API 設(shè)計(jì)

          • 函數(shù)式組件

          const?App?=?()?=>?<div>Vue?3?JSXdiv>
          • 普通組件
          const?App?=?{
          ??render()?{
          ????return?<div>Vue?3.0div>
          ??}
          }
          const?App?=?defineComponent(()?=>?{
          ??const?count?=?ref(0);

          ??const?inc?=?()?=>?{
          ????count.value++;
          ??};

          ??return?()?=>?(
          ????<div?onClick={inc}>
          ??????{count.value}
          ????div>

          ??)
          })
          • Fragment
          const?App?=?()?=>?(
          ??<>
          ????<span>I'mspan>

          ????<span>Fragmentspan>
          ??
          )

          Fragment 參考 React 的寫(xiě)法,盡可能寫(xiě)起來(lái)更加方便

          • Attributes/Props

          const?App?=?()?=>?<input?type="email"?/>
          const?placeholderText?=?'email'
          const?App?=?()?=>?(
          ??<input
          ????type="email"
          ????placeholder={placeholderText}
          ??/>

          )
          • 指令

          建議在 JSX 中使用駝峰 (vModel),但是 v-model 也能用

          v-show

          const?App?=?{
          ??data()?{
          ????return?{?visible:?true?};
          ??},
          ??render()?{
          ????return?<input?vShow={this.visible}?/>;
          ??},
          };

          v-model

          修飾符:使用 (_) 代替 (.) (vModel_trim={this.test})

          export?default?{
          ?data:?()?=>?({
          ???test:?'Hello?World',
          ?}),
          ?render()?{
          ???return?(
          ?????<>
          ???????
          ???????{this.test}
          ?????
          ???)
          ?},
          }

          自定義指令

          const?App?=?{
          ??directives:?{?antRef?},
          ??setup()?{
          ????return?()?=>?(
          ??????<a
          ????????vAntRef={(ref)?=>
          ?{?this.ref?=?ref;?}}
          ??????/>
          ????);
          ??},
          }
          • 插槽

          關(guān)于指令、插槽最終的 API 還在討論中,有想法的可以去留言。Vue 3 JSX Design (https://github.com/vuejs/jsx/issues/141)

          Vue 2 的 JSX 寫(xiě)法如何快速遷移到 Vue 3

          由于 antdv 的底層基本上都是基于 JSX 來(lái)寫(xiě)的,想要快速遷移到 Vue 3,就必須有一個(gè)比較好的插件來(lái)支持,這也是為什么會(huì)有這個(gè)插件的原因。當(dāng)然在實(shí)現(xiàn)過(guò)程中也踩了很多坑。
          目前用法和 Vue 2 的語(yǔ)法大多數(shù)是一致的,為了幫助更快遷移,在插件中做了針對(duì)舊 VNode 格式的兼容層,這里只能兼容一部分寫(xiě)法,以及部分語(yǔ)法的兼容會(huì)增加運(yùn)行時(shí)的性能開(kāi)銷(xiāo),所以我們希望能夠?qū)⑽覀兊慕?jīng)驗(yàn)分享給大家,讓大家少走彎路!
          {
          ??"plugins":?["@ant-design-vue/babel-plugin-jsx",?{?"transformOn":?true,?"compatibleProps":?true?}]
          }
          • transformOn

          針對(duì) Vue 2 中 on: { click: xx } 寫(xiě)法的兼容,在運(yùn)行時(shí)中會(huì)轉(zhuǎn)為 onClick: xxx
          • compatibleProps

          上文提到 Vue 3 對(duì)屬性的傳遞做了變更,propsattrs 這些都不存在了,因此如果設(shè)置了這個(gè)屬性為 true,在運(yùn)行時(shí)也會(huì)被解構(gòu)到第一層的屬性中。
          需要注意的一點(diǎn),目前一旦開(kāi)啟這兩個(gè)屬性,在 createVNode 的第二個(gè)參數(shù),都會(huì)包一個(gè) compatiblePropstransformOn 方法,所以酌情開(kāi)啟這兩個(gè)參數(shù)。對(duì)于使用 Vue 2 的 JSX 同學(xué),如果沒(méi)有使用到比較”不為人知“ 的 API的情況下,都可以快速得遷移。
          那么 antdv 又是如何做遷移的呢?考慮到 antdv 是個(gè)組件庫(kù),都包一層 compatibleProps 勢(shì)必不太優(yōu)雅,因此沒(méi)有選擇開(kāi)啟這個(gè)兩個(gè)開(kāi)關(guān)。這里插一句,目前 antdv 的遷移還在進(jìn)行中,相關(guān)的進(jìn)度都在這個(gè) issue 里面:Vue 3 支持 (https://github.com/vueComponent/ant-design-vue/issues/1913),有興趣的同學(xué)可以關(guān)注下,提一些 PR 過(guò)去。
          對(duì)于 props 的遷移工作比較簡(jiǎn)單,如果你是直接通過(guò)標(biāo)簽的屬性來(lái)傳遞,那么無(wú)須做更改。

          如果是通過(guò)對(duì)象來(lái)傳遞的屬性,只需要把原有分散在 propsonattrs 中的值直接鋪開(kāi)即可。

          ?const?vcUploadProps?=?{
          -??props:?{
          -????...this.$props,
          -???prefixCls,
          -????beforeUpload:?this.reBeforeUpload,
          -??},
          -??on:?{
          -????start:?this.onStart,
          -????error:?this.onError,
          -????progress:?this.onProgress,
          -????success:?this.onSuccess,
          -????reject:?this.onReject,
          -?},
          +??...this.$props,
          +??prefixCls,
          +??beforeUpload:?this.reBeforeUpload,
          +??onStart:?this.onStart,
          +??onError:?this.onError,
          +??onProgress:?this.onProgress,
          +??onSuccess:?this.onSuccess,
          +??onReject:?this.onReject,
          +??ref:?'uploadRef',
          +??attrs:?this.$attrs,
          +??...this.$attrs,
          };

          但是關(guān)于 inheritAttrs 有個(gè)較為底層的變動(dòng),需要開(kāi)發(fā)者根據(jù)實(shí)際情況去修改。什么是inheritAttrs? (https://cn.vuejs.org/v2/api/index.html#inheritAttrs) 在 Vue 2 中,這個(gè)選項(xiàng)不影響 classstyle 綁定,但是在 Vue 3 中會(huì)影響到。因此可能在屬性的傳遞上,需要額外對(duì)這兩個(gè)參數(shù)做處理。

          在事件的處理上,我們建議在 props 中聲明,這樣對(duì)后續(xù)的開(kāi)發(fā)更加易維護(hù),可以很直觀地從 props 看出我這個(gè)組件到底會(huì)傳遞哪些事件。值得一提的是,在 props 中聲明的事件,也可以通過(guò) emit 來(lái)觸發(fā)。例如聲明了 onClick 事件,仍然可以使用 emit('click')

          Vue 3 對(duì) context 的 API 也做了改動(dòng),一般如果不是復(fù)雜的組件,不會(huì)涉及到這個(gè) API。這部分的改動(dòng)可以看原先 Vue Compositon API 的相關(guān)文檔,Dependency Injection (https://composition-api.vuejs.org/api.html#dependency-injection),注意一點(diǎn),在 setup 中取不到 this

          總結(jié)

          如今有超過(guò)百萬(wàn)的開(kāi)發(fā)人員使用 Vue,還有超百萬(wàn)的 React 開(kāi)發(fā)者正在去使用 Vue 的路上。
          雖然說(shuō) Vue 中 JSX 的開(kāi)發(fā)方式是一個(gè)少數(shù)群里,但是 antdv 的使用用戶(hù)也不是少數(shù)。為了讓這部分用戶(hù)可以快速體驗(yàn)到兼容 Vue 3 版本的組件庫(kù),因此在設(shè)計(jì)這個(gè)插件的時(shí)候,第一原則就是要最小的遷移和認(rèn)知成本。
          對(duì)于常年使用 template 的開(kāi)發(fā)者來(lái)說(shuō),JSX 又何嘗不是一片新的天空呢?開(kāi)發(fā)者要與使用者共情,站在使用者的角度出發(fā),設(shè)計(jì)出的工具大概率可能滿(mǎn)足其需求。
          距離 JSX 發(fā)布正式版本,還有一部分路要走。
          最后要感謝 Vue.js 官方團(tuán)隊(duì),尤其是 @sodatea (https://github.com/sodatea) 大佬的信任。
          文中出現(xiàn)的倉(cāng)庫(kù)地址:
          • Ant Design Vue https://github.com/vueComponent/ant-design-vue

          • @ant-design-vue/babel-plugin-jsx https://github.com/vueComponent/jsx


          ?? 看完三件事

          如果你覺(jué)得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:


          1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 關(guān)注我的官網(wǎng)?https://muyiy.cn,讓我們成為長(zhǎng)期關(guān)系

          3. 關(guān)注公眾號(hào)「高級(jí)前端進(jìn)階」,公眾號(hào)后臺(tái)回復(fù)「面試題」 送你高級(jí)前端面試題,回復(fù)「加群」加入面試互助交流群


          》》面試官都在用的題庫(kù),快來(lái)看看《《

          瀏覽 47
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品久久久久久久久AV竹菊 | 精品人妻一区二区蜜桃视频 | 亚洲天堂无码在线 | 国内精品综合视频 | 五月婷婷色色网 |