英文:Harry Wolff,翻譯:前端大全 / v2li
本文是我上一篇《Vue 在哪些方面比 React 做得更好?》的後續。
和上一篇不同,本文的重點不是講 Vue.js 做得比 React 好的方面,而是聊聊我不喜歡 Vue.js 的地方。
讓我們來研究一下我認爲 React 比 Vue.js 做得更好的地方吧。
引言:我儘量能客觀地來表述,但是您肯定會感受到我的一些主觀立場,畢竟,在過去 5 年中,我在專職用 React,顯然,它是我在 UI 框架中的首選。
模板
Vue.js 最大的一個特點(坦率地說,Vue.js 最大的優勢)就是其編寫 UI 的模板語法。
在使用 React 5 年之後(儘可能接近原生 JavaScript),我並不想費心去學習另一種模板語法。
在我的職業生涯中,我曾學過各種語法,比如:Mustache.js、Handlebars、Lodash、Django 甚至更多。我不想因爲要使用 Vue.js 而必須去學習另一種模板語法。
雖然每種模板語法都有一些相似之處,但它們各自的特點讓我在從一種切換到另一種的過程中非常頭痛。
另一件我不喜歡的關於模板語法的事情是,它會在你編寫的內容和在瀏覽器中運行的內容之間添加一層抽象。
React 中,會通過 JSX 編譯爲函數調用:
// React in
<div title="Hello">Message: {message}</div>;
// React out
React.createElement(div, { title: 'Hello' }, 'Message: ' + message);
而在 Vue.js 中,我不知道模板將會編譯成什麼樣。
React 和 Vue.js 在它們的模板中都僅允許使用 JavaScript 表達式,考慮到 JavaScript 的限制,我可以理解這點。但是 Vue.js 讓我感到困惑的地方是它只能訪問全局中的一部分內容。我知道這種限制肯定事出有因,但是我真的不願意在開發的時候時刻想着 Vue.js 模板不是簡單封裝了一層 JavaScript。
指令
指令是 Vue.js 的殺手級功能。它讓 Vue.js 的模板變得特別強大。
但是。
指令實際上是一種 API,您必須學習它們才能更有效地使用 Vue.js 模板。雖然與 Angular.js(我以前使用的方式)相比,Vue.js 指令少了很多,但這還是會提高你的使用成本。
而 Vue.js 賦予指令的靈活性則進一步加劇了這種情況,它帶來了更多額外的學習成本。
比如指令參數,它有動態參數 然後還有指令修飾符(雖然我在第一篇文章中把它作爲 Vue.js 的優點,但這也帶來了額外的學習成本)!
指令的大多數語法都不可怕,但是我確實發現 v-for 指令 的語法非常反 JavaScript。它比其他任何東西都更接近 Python,這出現在 JavaScript 框架中會很奇怪。
組件
這點有點吹毛求疵了,但它可以佐證我對模板的觀點。
由於 Vue.js 在很大程度上是模板驅動的框架,因此當您使用自定義組件擴展它時,您需要向 Vue.js 模板編譯器告知所使用的組件。
這導致了很多重複的代碼,在我看來似乎完全是多餘的。
// Import your components as you normally would with ES Modules
import ComponentA from './ComponentA';
import ComponentC from './ComponentC';
export default {
components: {
// Register them with the template compiler
ComponentA,
ComponentC,
},
// Then finally use them in your template
template: `
<ComponentA />
`,
};
自定義事件
除了模板之外,自定義事件也是 Vue.js 和 React 的一個很大的區別。
React 中的所有內容都是組件和 props(到了一種很不健康的地步)。當您希望子組件與父組件進行通信時,您可以傳遞一個函數讓子組件調用:
function Parent() {
const onClick = () => alert('hello!');
return <Child onClick={onClick} />;
}
function Child({ onClick }) {
return <button onClick={onClick}>Click me!</button>;
}
而在 Vue.js 中則通過事件來進行父子組件通信:
const ParentComponent = {
components: { ChildComponent },
template: `<ChildComponent @greeting="alert('hello')" />`,
};
const ChildComponent = {
emits: ['greeting'],
template: `<button @click="$emit('greeting')">Click me!</button>`,
};
老實說,我不太確定我對通過事件進行父子組件通信的看法。
由於對發出和監聽什麼事件沒有具體的約定,在 Angular.js 中使用這種方法會十分糟糕。
但是,Vue.js 通過工具解決了這個問題,當父級嘗試監聽某個事件時,組件實際上會發出這個事件。
如果不使用這個工具,我認爲 Vue.js 可能會遇到與 Angular.js 相同的問題,但是 Vue.js 的工具確實很出色。
話雖如此,React 通過 props 傳遞函數的方式也不錯,並且個人認爲它更強大。
事件處理方法
與自定義事件相關的是 Vue.js 如何爲其模板添加事件處理器。這也是我最鄙視的機制之一:引用字符串!
當在 Vue.js 模板中引用一個方法時,需要通過字符串的形式傳遞函數名稱:
<button @click="greet">Greet</button>
嗯…過去我在使用字符串引用的時候一直很糟。
但是像上面一樣,Vue.js 會通過工具捕獲任何愚蠢的錯別字。可惜,這已經是我非常不喜歡的一種方式了,並且不希望再次使用它。
響應式
Vue.js 的大部分魔力來自其響應式。它讓 Vue.js 能夠在數據改變時有效且快速地更新 UI。它使我想起了 MobX,但在 Vue.js 中它是專爲 Vue.js 設計的並且內置其中。
但是,MobX 和 Vue.js 的響應式都有權衡取捨,在組件中使用響應式時,您必須考慮其實現細節。
例如,當創建響應式對象時,您通過reactive 函數包裝對象。但是,當您想使用原始值時,需要將其包裝在 ref 中,其作用與 React 的 useRef 的 hook 非常相似。
基於 Vue.js 的響應式原理(使用 Proxy),所以當要破壞一個 reactive 對象時,則需要將其包裝在 toRefs(reactiveObject)中,以確保您不會丟失反應性綁定。
對於原始參考值,Vue.js 會自動解開模板中的參考值,這點雖然很好,但會造成引用值不一致的問題。
在模板中,您不必解包,但是在組件 JavaScript 中,您需要解包。對我而言,這些上下文切換似乎是不必要的而且乍一看會造成混亂。
對於常見的用例,確實很少會遇到這些邊緣情況,但是我很關注它的擴展性。
這與 React 幾乎都是簡單應用 useState 和 useRef 相反,後者會返回 setter 函數和一致的 ref-object 接口。也許 React 的 API 太簡單了,因此它將大多數操作推給了最終開發者。但這也是我現在最關心的,就是裏邊不要有什麼奇淫巧計。
尾聲
與 Vue.js 相比,我更喜歡 React 的哲學。
我不喜歡 Vue.js 替我做了太多事情。我更喜歡使用原始的函數和方法來完全控制我的 UI(React 就是這樣的)。
React 並非沒有怪異模式,但至少 React 的怪異點我是清楚的。我寫的東西和 React 做的事情之間幾乎沒有間接層。(忽略 react-reconciler 庫的黑暗魔力,祝那些嘗試深層次調試這些堆棧的人好運吧)。
很難說 React 更好,因爲我個人確實更偏愛 React!如果 Vue.js 更合你意,請繼續使用 Vue.js!我唯一想做的就是強調 Vue.js 和 React 之間的區別,以及爲什麼React 仍是我 UI 庫的首選。