為什么推薦使用JSX開發(fā)Vue3
在很長的一段時間中,Vue 官方都以簡單上手作為其推廣的重點。這確實給 Vue 帶來了非常大的用戶量,尤其是最追求需求開發(fā)效率,
往往不那么在意工程代碼質(zhì)量的國內(nèi)中小企業(yè)中,Vue 占據(jù)的份額極速增長。但是作為開發(fā)者自身,我們必須要認清一個重點,簡單易用重來不應該在技術(shù)選型中占據(jù)很大的份額,可維護性才是。
以防萬一有的同學實在不看官方文檔,我先提一嘴,SFC 就是寫 Vue 組件的時候?qū)懙?vue文件,這一個文件就是一個 SFC,全稱 Single File Component,也即單文件組件。
在開始說我個人的觀點之前,我們先來看幾個事實:
一是:Vue3 的定義原生支持 JSX,并且 Vue3 源碼中有jsx.d.ts來便于使用 JSX。 不知道同學們看到這里會想到什么,
我的第一反應是:社區(qū)對于 JSX 的需求聲音是不小的,所以會反向推動 Vue3 官方對于 JSX 的支持。
二是:AntDesign 的 vue3 版本,基本全部都是用 JSX 開發(fā)的,而且 Vue3 現(xiàn)在官方的 babel-jsx 插件就是阿里的人一開始維護的,
雖然我向來不喜歡阿里系的 KPI 推動技術(shù)方式,而且現(xiàn)在的 JSX 語法支持也不是很符合我的期望,但至少在使用 JSX 開發(fā)是更優(yōu)秀的選擇這點上,我還是很認可 AntDesign 團隊的。
OK,說這些呢,主要是先擺出一些事實作為依據(jù),讓有些同學可以不需要拿什么:
- 啊,這都是你空想的,你太自以為是了
- 你再怎么想都沒用,咱們 Vue 就是應該用 SFC 開發(fā)
這些觀點來批斗我,首先我都會從客觀的角度來分析為什么,至少是我是能講出優(yōu)劣勢的理由的。
OK,前言差不多到這里,接下來咱給您分析分析,為什么你應該選擇 JSX 來開發(fā) Vue。
TypeScript 支持
其實第一點就已經(jīng)是殺手了,對于想要使用 TypeScript 來開發(fā) Vue3 應用的同學來說,這簡直就是 SFC 無法克服的世界難題。
一句話概括:TypeScript 原生支持 JSX 語法,而基本無望 TS 官方能支持 SFC 的 template 語法。
TS 毫無疑問在前端社區(qū)的重要性越來越大,但凡未來對于代碼質(zhì)量有一定要求的前端團隊,都應該會選擇使用 TS 來進行開發(fā)。
而且現(xiàn)在基本上在 NPM 上都能看到包你都能找到對應的 TS 定義,現(xiàn)在使用 TS 開發(fā)成本已經(jīng)只剩下你是不是會 TS 語法了,在這種情況下是否支持 TS 則是開發(fā)模式在未來走不走的遠的重要原因。
目前 SFC 只能通過shim讓 TS 可以引入.vue文件,但是對于所有 SFC 的組件的定義都是一樣的:
declare module '*.vue' { import { DefineComponent } from 'vue' const component: DefineComponent<{}, {}, {}, any> export default component }
也就是說你引入的 SFC 組件,TS 是不知道這個組件的 Props 應該接收什么的。所以你無法享受到這些 TS 的優(yōu)勢:
- 開發(fā)時的自動提示
- 編譯時的 TS 校驗,讓你盡早發(fā)現(xiàn)問題
- 編譯組件生成你的組件定義(對于類庫開發(fā)尤其重要)
當然你會說既然 Vue 官方能開發(fā)處 SFC 的語法,自然會支持這些特性。我表示這當然有可能,但是這個難度是非常大的,需要很多方面的支持,甚至可能需要 TS 官方團隊愿意協(xié)助,
但是我想不到 TS 官方有什么理由來支持 SFC,因為這只是 Vue 自己創(chuàng)建的方言,在其他場景下是沒有使用的,TS 是面向全社區(qū)的,我覺得他們不會考慮主動來支持 SFC。
那么有同學要問了,JSX 不也是非原生的 JS 語法么,他怎么就能讓 TS 官方支持了呢,是不是 FB 和微硬之間有什么 PY 交易?
這就涉及第二點了,JSX 和靜態(tài)模板的靈活性區(qū)別。
JSX 其實并不是方言
很多人弄錯了一個問題,就是覺得 SFC 的模板語法和 JSX 是一樣的,都是一種別人發(fā)明的語法,并不是 JS 原生的。這是事實,但又有一些區(qū)別,這個區(qū)別主要是體現(xiàn)在對于 JSX 的認知上。
一句話概括:JSX 并沒有擴展 JS 的語法,他只是縮略了 JS 的寫法!其本質(zhì)就是 JS 的語法糖
就像 es6 給增加的語法糖,比如
const a = 1 const b = 2 const obj = { a, b } // 其實就等價于 const obj = { a: a, b: b }
這種寫法并沒有擴展 JS 的能力,只是簡便了寫法,JSX 也是一樣的。
JSX 其實就是方法調(diào)用,他和 JS 是有一對一對應關(guān)系的,我們來看一個例子:
const element = <div id="root">Hello World</div>
這里的 JSX 語法編譯之后其實就是:
const element = createElement('div', { id: 'root' }, 'Hello World')
而 JSX 就是這些了,沒有什么更多的內(nèi)容,所以說 JSX 只是方便我們寫嵌套的函數(shù)調(diào)用的語法糖,而其本身沒有擴展任何其他的內(nèi)容。
但是 SFC 就不一樣了。
SFC 定義的不僅是語法,更是文件。
SFC 的具體定義是單文件組件,它本身就是把一個文件看作一個單位,所以他的約束性是要大很多的,你必須具有固定的文件結(jié)構(gòu)才能使用 SFC,這做了很多的限制:
- 一個文件只能寫一個組件
- 節(jié)點片段只能寫在 template 里面,非常不靈活
- 變量綁定只能獲取this上面的內(nèi)容,不能使用全局變量(很多時候我們都要把全局變量先掛載到this上)
我們一點點來講
一個文件只能寫一個組件
這個說實話非常非常不方便,很多時候我們寫一個頁面的時候其實經(jīng)常會需要把一些小的節(jié)點片段拆分到小組件里面進行復用(如果你現(xiàn)在沒有這個習慣可能就是因為 SFC 的限制讓你習慣了全部寫在一個文件內(nèi))。
React 生態(tài)中豐富的 css-in-js 方案就是很好的例子,我們可以通過:
const StyledButton = styled('button', { color: 'red', })
如果我們這個頁面需要使用特定樣式的按鈕,通過這種方式在頁面文件里面封裝一下是非常常見的。因為沒必要把這個組件拆分出去,他也不是一個可復用的組件,拆分出去了還要多一次import。
Vue 生態(tài)基本沒有 css-in-js 的成熟方案其實跟這個限制也很有關(guān)系。
再來一個例子,比如我們封裝了一個 Input 組件,我們希望同時導出 Password 組件和 Textarea 組件來方便用戶根據(jù)實際需求使用,而這兩個組件本身內(nèi)部就是用的 Input 組件,只是定制了一些 props:
const Input = { ... } export default Input export const Textarea = (props) => <Input multiline={true} {...props} /> export const Password = (props) => <Input type="password" {...props} />
在 JSX 中可以非常簡單地實現(xiàn),但是如果通過 SFC,你可能就要強行拆成三個文件,另外為了方便,你可能還要增加一個index.js來導出這三個組件,你能想象這多了多少工作量么。
節(jié)點片段只能寫在 template 里面,非常不靈活
我不知道有多少同學看過 Vue 的 template 編譯出來之后的代碼,以我的經(jīng)驗來說看過的可能不會超過 50%(樂觀估計),建議同學們?nèi)绻€不了解的,可以去嘗試看一下。
為什么要看這個呢?因為你看了之后你會發(fā)現(xiàn),你在 template 里面寫的類似 HTMl 的內(nèi)容,其實跟 HTML 根本沒啥關(guān)系,他們也會被編譯成類似 JSX 編譯出來的結(jié)果。
{ render(h) { return h('div', {on: {}, props: {}}, h('span')) } }
類似這樣的結(jié)果,而這里面h函數(shù)調(diào)用的結(jié)果就是一個 VNode,是 Vue 中的節(jié)點的基礎(chǔ)單元。那么既然這些單元就是一個對象,其實理所當然的,他們是可以作為參數(shù)傳遞的。
也就是說,理論上他們是可以通過props把節(jié)點當作參數(shù)傳遞給其他組件的。
這個做法在 React 中非常常見,叫做renderProps,并且其非常靈活:
const Comp = () => <Layout header={<MyHeader />} footer={<MyFooter />} />
但是因為 SFC 模板的限制,我們很難在 SFC 里面的 props 上寫節(jié)點:
<template> <Layout :header="<MyHeader/>"></Layout> </template>
這樣寫是不行的,因為 SFC 定義了:header綁定接受的只能是 js 表達式,而<MyHeader/>顯然不是。
因為通過 props 傳遞不行,所以 Vue 才發(fā)明了 slot 插槽的概念
雖然我們一直再說 Vue 簡單,但是事實上ScopedSlots一度成為新手理解 Vue 的噩夢,很多同學都被這個繞來繞去的作用域整的死去活來。
我們看一個ScopedSlots的例子:
<template> <Comp> <template v-slot:scope="ctx"> <div>{{ctx.name}}</div> </template> </Comp> </template>
這里ctx是Comp里面的屬性,通過這種方式傳遞出來,讓我們在當前組件可以調(diào)用父組件里面的屬性。這簡直就是理解的噩夢,但是如果用 JSX 實現(xiàn)類似功能就非常簡單:
<Comp scope={name => <div>{name}</div>} />
我們只是給一個叫做scope的 props 傳遞來一個函數(shù),這個函數(shù)接受一個name屬性,在Comp里面會調(diào)用這個函數(shù)并傳入name。
簡單來說我們傳入的就是一個構(gòu)建節(jié)點片段的函數(shù),就是這么簡單。
這就是因為 SFC 的模板的限制,導致靈活性不足,Vue 需要去創(chuàng)造概念,創(chuàng)造關(guān)鍵字來抹平這些能力的不足,而創(chuàng)造的概念自然就引入了學習成本。
所以其實我一直不認可 Vue 比 React 好學的說法的,如果你真的認真研究所有用法,并且總是嘗試用最合理的方式實現(xiàn)功能,那么 Vue 絕對不會比 React 簡單。
變量綁定只能獲取this上面的內(nèi)容,不能使用全局變量
這個體現(xiàn)在兩個方面,一個是我們定義在全局的一些固定數(shù)據(jù)如果要在組件內(nèi)使用的話,就要通過this掛載到組件上。
比如我們緩存了一份城市數(shù)據(jù),這種數(shù)據(jù)基本上是不會改的,所以也沒必要掛載到組件上讓其能夠響應式。但是在 SFC 里面這是做不到的,
因為模板的執(zhí)行上下文是在編譯時綁定。你在模板里面訪問的變量,都會在編譯時自動綁定到this上,因為模板需要編譯,其本身也是字符串不具有作用域的概念。
而這在 JSX 中則不復存在:
const citys = [] const Comp = () => { return citys.map(c => <div>{c}</div>) }
另外一個方面則是在組件使用上,在 SFC 中,組件必須事先注冊,因為我們在模板里面寫的只能是字符串而不能是具體某個組件變量。
那么模板中的組件和真實的組件對象只能通過字符串匹配來實現(xiàn)綁定。這帶來了以下問題:
- 多了注冊組件這個步驟,增加代碼量
- 通過字符串名注冊自然就會出現(xiàn)可能的沖突問題
- 模板解析組件支持不同的樣式,比如<MyComp>和<my-comp>,容易導致風格不一的問題
在 JSX 中則沒有這些問題,因為 JSX 里面直接使用組件引用作為參數(shù):
const Comp = {...} const App = () => <Comp />
需要通過directive來擴展能力
其實上面能看出來,除了 SFC 本身的問題之外,Vue 使用字符串模板也會帶來很多的靈活性問題。
最直接的證據(jù),就是 Vue 使用了directive來擴展功能(當然這不是 Vue 發(fā)明的,老早的模板引擎就有類似問題)。
為什么說directive是不得已的選擇呢?因為靜態(tài)模板缺失邏輯處理的能力。我們拿列表循環(huán)舉例,在 JS 中我們可以非常方便地通過map函數(shù)來創(chuàng)建列表:
const list = arr.map(name => <span key={name}>{name}</span>)
而因為 JSX 本身就是函數(shù)調(diào)用,所以上面的代碼和 JSX 結(jié)合起來也非常自然:
const App = () => ( <div> <Header /> {arr.map(name => ( <span key={name}>{name}</span> ))} </div> )
上面的例子對應到 JS 如下:
const App = () => createElement('div', {}, [ <Header />, arr.map(name => createElement('span', { key: name }, name)), ])
這仍然是因為 JSX 只是 JS 的語法糖的原因,所有能在 JS 中實現(xiàn)的在 JSX 里面都能實現(xiàn)。
而 SFC 的模板是基于字符串編譯的,其本身就是一段字符串,我們不能直接在模板里面寫map來循環(huán)節(jié)點,(當然我們可以在可以接收表達式的地方寫,比如v-on里面)。
那么我們不能循環(huán)節(jié)點,有需要這樣的功能來渲染列表,怎么辦呢?就是發(fā)明一個標志來告訴編譯器這里需要循環(huán),在 Vue 中的體現(xiàn)就是v-for指令。
同學們可能要問了,既然 Vue 能實現(xiàn)v-for,為什么不直接實現(xiàn)表達式循環(huán)列表呢?他當然也可以實現(xiàn),但是他肯定不會這么選,因為成本太高了。
他要這么做就相當于他要實現(xiàn)一個 JS 引擎,而其實里面很多內(nèi)容又是不必須的,一個v-for其實就能夠適用大部分情況了。
但有了v-for就需要v-if,那么后面還會需要其他各種能力,這就是一種方言的產(chǎn)生和發(fā)展的過程。
當然指令也不僅僅是 JS 表達式的代替品,其本身也是增加了一些其他能力的,比如它能夠讓我們更方便地訪問 DOM 節(jié)點,
但是嘛,我們用框架的理由不就是為了能夠盡可能的屏蔽 DOM 操作嘛~
總結(jié)
以上就是我對應該選擇使用 JSX 還是 SFC 進行開發(fā)的分析,其實歸根到底 SFC 的問題在于其沒有擁抱 JS,
他的語法是自己發(fā)明的,他需要有一個 JS 實現(xiàn)的 compiler 來讓其最終能在 JS 環(huán)境中運行,這本質(zhì)上就是一種發(fā)明,
我們不能否認發(fā)明確實有優(yōu)點,但我們也不能只看有點不看問題,沒能擁抱 JS 自然就很難完全復用 JS 社區(qū)的優(yōu)勢
而 JS 社區(qū)一直在蓬勃發(fā)展,好用的工具一直在涌現(xiàn),而 SFC 想要使用 JS 社區(qū)的這些工具還要自己再實現(xiàn)一份,我們可以細數(shù)以下 SFC 做了哪些兼容
- vue-loader 之于 webpack
- eslint-plugin-vue 之于 eslint
- rollup-plugin-vue 之于 rollup
- vue-jest 之于 jest
- Vetur 用來做代碼提醒
基本上常用的工具我們都需要等待 Vue 社區(qū)或者官方開發(fā)了插件之后才能運行。而 JSX 因為有 babel 和 typescript 的官方支持,
基本上所有新的 JS 生態(tài)工具原生都是支持的。
在這 Vue3 開始預備發(fā)力的階段,我們還是希望 Vue 社區(qū)能夠使用更優(yōu)秀更規(guī)范的方式來進行開發(fā),
其實如果我們直接使用 JSX 開發(fā) Vue3,我們會發(fā)現(xiàn)很多時候我們都不需要用到emit、attrs這些概念,
甚至如果 Vue3 的 JSX 插件支持,我們甚至能夠拋棄slots。
但是因為 Vue3 一定要考慮兼容 Vue2,導致本身潛力很好的 Vue3 總是顯得縮手縮腳,這不得不說是一種遺憾。
以上就是為什么推薦使用JSX開發(fā)Vue3的詳細內(nèi)容,更多關(guān)于用JSX開發(fā)Vue3的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue+echart?展示后端獲取的數(shù)據(jù)實現(xiàn)
本文主要介紹了Vue+echart?展示后端獲取的數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-01-01關(guān)于Vue不能監(jiān)聽(watch)數(shù)組變化的解決方法
本文主要介紹了Vue不能監(jiān)聽(watch)數(shù)組變化的解決方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09Vue中addEventListener()?監(jiān)聽事件案例講解
這篇文章主要介紹了Vue中addEventListener()?監(jiān)聽事件案例講解,包括語法講解和事件冒泡或事件捕獲的相關(guān)知識,本文結(jié)合示例代碼給大家講解的非常詳細,需要的朋友可以參考下2022-12-12vue-router+vuex addRoutes實現(xiàn)路由動態(tài)加載及菜單動態(tài)加載
本篇文章主要介紹了vue-router+vuex addRoutes實現(xiàn)路由動態(tài)加載及菜單動態(tài)加載,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09