從學習曲線角度來說,結合我我的體會,React 學習路線是比 Vue 陡峭的,這個和 JSX、Template 有關嗎?固然有。在 React 中使用 JSX,衆所周知, JSX 須要通過 Transform 才能在瀏覽器中運行。立刻就有小夥伴反駁了,Vue 有官方的 Vue-cli, React 使用 create-react-app 初始化項目就行了呀,並不須要比 Vue 多學習其餘工具呀。css
咱們從另外一個角度來看這個問題,使用 JSX 還須要熟練 ESM,這是繞不開的,由於一個 React 組件就是一個 js 或 JSX,這可能會給新手帶來疑惑,組件是如何連接的?先彆着急反駁,接着往下看。html
僅僅掌握「三劍客」的新手就能夠輕鬆上手 Vue,這在 Vue 官方文檔中有所體現。vue
巧妙之處就在與上圖中的 「易用」,僅僅學會了 HTML、CSS、JavaScript 就能上手。Vue 的「漸進式」不只體如今跟隨項目的複雜度上的,還有很大程度上適應使用者的水平。這不是空穴來風,當年鄙人就僅學習了這「三劍客」,就用 Vue-cli 初始化並完成了小項目。現在我思考在當時 Vue 是如何幫我避開 ESM 的。答案在於 vue 的「三段式結構」。react
<script> export defalut { // ... } </script>
個人注意點徹底在於導出的這個對象之中以及上方的 Template 和下方的 Style,只知道在這個對象裏面寫一些 Data、Methods 等等。甚至徹底沒有在乎 export defalut
這個關鍵字。git
不只如此,Vue 的 Template + Options API 基本就規範了處理一些邏輯時的大致形式,這在經過搜索引擎檢索一些遇到的問題時,一般可以輕易看懂檢索到的代碼,甚至能夠「依葫蘆畫瓢」,這對新手無疑是一個優點。相比 JSX, 寫法上,Template 雖不可以從「優雅」上勝出,但 JSX 更容易帶入我的編碼風格,這應該能使你們有所認同,這也就加大了新手的理解難度。github
回到問題,那麼 Vue 是如何引入組件的呢?做爲新手,我只會「依葫蘆畫瓢」,經過一條 import 語句導入進來,而後在 components 裏面將組件註冊,這一切都被我天然得誤認爲是 Vue 的能力。typescript
這不表明使用 Vue 不須要學習 ESM,沒錯,這是究極必要的,熟悉 ESM 能夠更好得組織項目代碼。上面我只是在思考當年 Vue 是怎麼幫我避開 ESM 的,或者說我做爲新手時是如何毫無察覺地避開它的,Template 「功不可沒」。express
題外話,Vue 的 Template 能夠放到 Script 標籤下方,這可能更符合 React 用戶的直覺,看尤大在推上分享一些 Demo 時有時會這麼寫。若是您樂意,也能夠這麼作,但在多人協做的項目中還要考慮小夥伴的感覺。瀏覽器
<script> export defalut { // ... } </script> <template> </template>
所謂「蘿蔔白菜,各有所愛」。在 React 中只能使用 JSX,Vue 從 2.0 開始乾脆把 JSX 也支持完事了。有人說,在 Vue 中使用 JSX 爲何不直接使用 react? 這個話題就大了,這不只和項目的技術選型有關,且 Vue 和 React 設計上就有很大差異。在 Vue 中不管使用 Template 仍是 JSX 都會被編譯成 h 函數(在 Vue 2 中稱爲 render 函數)。因此,在 Vue 中使用 JSX,只是換了一種表現形式而已。app
咱們來看一下 Vue 將 Template 渲染到瀏覽器的過程。
<div>Hello</div>
這個HTML也能夠經過一個虛擬節點 VNode 來表示,也就是用 JavaScript 對象的形式來表示。
{ tag: 'div', children: [ { text: 'Hello' } ] }
Vue 知道如何將此虛擬節點並掛載到 DOM 上,它會更新咱們在瀏覽器中看到的內容,可是 VNode 從哪裏來的呢?實際還有一個步驟,Vue 基於咱們的 Template 建立一個渲染函數,返回一個虛擬 DOM 節點。
渲染函數能夠是這樣的:
render(h) { return h('div', 'hello') }
當組件更改時,Render 函數將從新運行,它將建立另外一個虛擬節點。而後發送舊的 VNode 和新的 VNode 到 Vue中進行比較並以最高效的方式在咱們的網頁上更新。例以下圖所示,僅僅更新了文本內容。
題外話,實際 Vue 有三個核心模塊:
響應式模塊容許建立 JavaScript 響應對象並能夠觀察其變化。當使用對象的代碼運行時,它們會被跟蹤,所以,它們能夠在響應對象發生變化後運行。
編譯器模塊獲取 HTML 模板並將它們編譯成渲染函數。這可能在運行時在瀏覽器中發生,但在構建 Vue 項目時更常見。這樣瀏覽器就能夠只接收渲染函數。
渲染模塊在網頁上渲染組件經歷三個不一樣階段:在渲染階段,將調用 render 函數,它返回一個虛擬 DOM 節點;在掛載階段,使用虛擬 DOM 節點並調用 DOM API 來建立網頁;在補丁階段,渲染器將舊的虛擬節點和新的虛擬節點進行比較並只更新變化的部分。
上面是 Vue 將 Template 渲染到瀏覽器的過程,和 Template 相似,在 Vue 中使用的 JSX 也會轉化成 h 函數,眼見爲實。
因此,瞭解這個過程,您可能不是那麼排斥在 Vue 中使用 JSX 了,或者或許對它在 Vue 中的表現不是那麼陌生了。
從另外一個方面來說,Vue 中的 JSX 和 React 中的 JSX 在寫法上也有差異。在 Vue JSX 中您能夠直接使用熟悉的指令(directives),例如 Vue 內置的 v-show
、 v-model
甚至 v-models
和自定義指令,但例如 slots 在 Vue JSX 中或許沒有在 React 中那麼方便。
在 Vue sfc(single file component)中同時編輯 script、template、style,這提供了「聯合」的能力,即在一個文件中使 Style 和 Template 共享 Script,幫助開發者更容易實現業務。例如:rfcs-style-variables,該提案雖然尚未最終肯定下來,但 Vue 展現了這種能力。基礎示例:
<template> <div class="text">hello</div> </template> <script> export default { data:()=> ({ color: 'red', font: { size: '2em' } }) } </script> <style> .text { color: v-bind(color); /* expressions (wrap in quotes) */ font-size: v-bind('font.size'); } </style>
經過 style-variables 寫有一個有趣的示例,此處爲部分代碼。
<script> import { defineComponent, ref } from 'vue' export default defineComponent({ setup() { const inputBorderColor = ref('') const onEngineChange = engine => { for (const { name, color } of enginesData) { if (name === engine) { inputBorderColor.value = color return } } } return { inputBorderColor, } }, }) </script> <style lang="scss"> input { &:focus { border-color: v-bind(inputBorderColor); } } </style>
若是在 Vue 中使用 JSX, 就沒法利用這個優點了。題外話,在 React 中相似的 css-in-js 方案是 styled-component。
Vue 幾乎全部面向業務構建的生態內容給出的示例代碼都是基於 Template 的。咱們一般在項目中安裝 UI 組件庫依賴,組件庫中給出的示例代碼也是基於 Template 的。當前 Vue 組件庫文檔示例代碼正在由 Options API 向 Composition API 過渡,但幾乎沒有可能從 Template 向 JSX 過渡,或者向用戶提供 JSX 的組件示例代碼。這很是重要,若是您的項目依賴組件庫,查看文檔後還須要將 Template 轉到 JSX,這無疑增長開發成本。
在 script 中,vue3 相比 vue2 已經出現質的飛躍。從 Template 來看,Vue 還有待完善,但這個可行的。JSX 一開始也沒有類型支持,徹底是 TS 給加了一套針對 JSX 的推導機制。
模版和類型推導,表面上看,隔了一層模版語法 + 編譯,彷佛確實存在 「斷層」,但其實裏面沒您想的差那麼遠。Vue 的模版是編譯成 virtual dom 渲染函數的,生成的 js 跟 React 的渲染函數同樣能夠類型推導,而模版跟生成的 js 之間是完整的邏輯映射,因此這裏其實主要是須要作一些工具鏈上的銜接,把對生成的 js 分析出來的 intellisense 反饋到 IDE 裏的模版上。技術上是徹底可行的。
vscode 插件 Volar 已經支持 Template 表達式的類型檢查。Vetur 雖然不支持還未定稿的 RFC,也已經支持基於現有 API 的模板類型檢查和組件 props 類型檢查了。另外,在 vue3 中不管是否使用 TS,經過 defineComponent 定義組件都能得到更好的提示。
能夠預見的是,Vue 從升級 3.0 後,對於 TypeScript 的支持將會愈來愈好。
在大多數場景下(尤爲是業務場景)使用 Template 多是更好的選擇, vue3 基於 Template 分析作了大量的優化,而使用 JSX 須要手動作一些優化。
JSX 因爲更具靈活性,一般一些組件庫的不二之選,例如:Ant Design Vue、Vant、Element Plus(部分使用)。看到這麼個例子: 根據 props 上的 reverse 屬性,決定是否要調換兩塊內容的順序。使用 JSX 輕易就能實現,且可讀性也很高。
const renderContent = () => { const Content = [ <div class="foo">Foo DOM...</div>, <div class="bar">Bar DOM...</div>, ]; if (props.reverse) Content.reverse(); return <div>{Content}</div>; }
若是經過模板來實現,在不抽象子組件的狀況下,foo 和 bar 的模板結構須要重複寫兩遍,才能知足這個需求:
<template> <div> <template v-if="reverse"> <div class="bar">Bar DOM...</div> <div class="foo">Foo DOM...</div> </template> <template v-else> <div class="foo">Foo DOM...</div> <div class="bar">Bar DOM...</div> </template> </div> </template>
所以,在動態性強的場景下,JSX 會有必定優點。Composition API + JSX 是某些場景下追求極致的選擇,相應地須要付出更多開發成本。
參考資料:
[1] https://www.zhihu.com/question/436260027/answer/1647182157
[2] https://www.zhihu.com/question/310485097/answer/591869966
[3] http://www.javashuo.com/article/p-kdmdgmyd-kq.html