如果我放棄 React,那一定只有一個原因

React 有一個我忍了很久的痛點。如果有一天,我決定放棄 React 轉向其他的框架,那么一定是這個痛點我終于忍不了了。
這個痛點就是 className。
<div className='relative pt-16'></div>特別是最近我正在寫一本關于 CSS 的專欄時,這個痛點已經把我逼到了幾乎忍受不了的程度。

由于我的網站里,為了確保一致性、真實性、有效性,文章里的案例全部都是直接使用的內嵌案例的真實代碼,但是網站是用 React 來寫的,因此能夠直接運行的代碼也是 React 組件,展示出來的也就是 React 的代碼。
但是這本專欄是專門介紹 CSS 的,如果代碼中展示出來的是使用的 className,反正我不知道其他人是什么感受,我是越看越別扭,總感覺哪哪哪都不對勁。
所以我就想要研究一下,看看能不能直接在 React 組件中直接使用 class,而不是 className,這樣我的代碼演示不就對味兒了嗎?
export default function Page() {
return (
<div class='relative pt-16'>
<div class="fixed inset-0 -z-10">
<div class="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:40px_40px]"></div>
</div>
...
</div>
)
}我們先來了解一下 className 的歷史原因,以及使用 class 之后可能會存在的問題,最后再給大家介紹具體如何操作。
1.歷史原因
className 在 React 社區中爭議了很多年,但是在設計之初,據說是為了在 token 層面,與聲明類的關鍵字 class 區分開來,因此選擇了重命名為 className。
class MyComponent extends Component {
// ...
}不過除此之外,dan 在 github 中有提過另外一個理由。如下所示,在 JavaScript 中,給 DOM 設置屬性有兩種方式,一種是 property,另外一種 attribute。
node.value = 10; // setting a property
node.setAttribute('value', '10'); // setting an attribute而 JSX 由于被約定為是 JS 語法的擴展,因此,React 團隊認為使用 JS 默認的 className 字段給 DOM 設置屬性比較合適。
node.className = '' // 表現為 property,而非 attribute2.爭議
盡管 dan 為此解釋了很多次,但許多 React 開發者并不為此買賬,在社區中強烈要求能夠在 JSX 中使用 class
并且在后續的更新中,提議將 className 修改為 class 的聲音越來越大。
React 團隊為此不得不做出了妥協。
因此,在 React 17/18 的版本中,其實我們就可以直接在 JSX 中使用 class 了。但是他們又沒完全妥協,選擇的方式是,你可以用,代碼也能正常運行,但是 React 會給你報錯 ~ 就很惡心
dan 的解釋是,
如果 React 團隊毫無預警的同時支持 class 與 className,會在 React 生態中造成巨大的分歧。并不是所有的三方庫都會同時兼容這兩個方案,如果你使用的某一個三方庫不兼容 class,那么就會導致樣式的傳遞中斷,出現問題。
React 團隊無法完全左右社區,對于這種沖突無能為力,因此,長期以來,class 仍然以預警的方式存在。
但是,忍了 7 年了,我已經忍不了了,我決定不管要付出什么樣的代價,必須要用 class,哪怕最后放棄使用 react。
那為了使用 class,我們要做什么額外的事兒呢?
3.弊端以及如何解決
在這之前,我們要明確,React 中,是支持直接使用 class 的。因此從功能的角度上而言,我們啥也不需要做,直接用就可以了。
但是當你直接使用之后,你會遇到一堆麻煩事兒需要解決。
首先,如果你的項目中使用了 TypeScript,他會瘋狂報錯,為了解決這個問題,我們要去修改 TypeScript 的類型聲明。
在 @types/react 這個庫中,我們在 HTMLAttributes 這個類型下面新增 class 字段。
interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
...
className?: string | undefined;
class?: string | undefined;
...
}處理好之后就搞定了。不過手動修改有一個比較麻煩的地方,就是每次你重新 install 之后,這段代碼就會被覆蓋,又還原成之前的樣子。那么這個時候你有兩個選擇,
第一,由于修改起來比較簡單,而且 install 頻率也非常低,因此你可以每次都手動修改一下。
第二,你在 install 時執行一個本地腳本,用 node 代碼自動去插入這段邏輯。
接下來的一個問題就是 eslint 等語法檢測規則可能會報錯,我們可以修改 ESLint 配置文件以消除這個錯誤。
"react/no-unknown-property": ["off"]然后就是在開發環境中,console 面板里會報錯,如下所示:

為了解決這個問題,我們需要在項目入口處,重寫 console.error 的報錯規則,具體代碼我也已經研究出來了,分享給大家。
const originalError = console.error;
console.error = function(...args) {
const ignorePatterns = [
'Invalid DOM property `%s`. Did you mean `%s`?',
'class',
'className'
];
let shouldIgnore = true;
ignorePatterns.forEach((pattern, index) => {
if (typeof args[index] !== 'string' || !args[index].includes(pattern)) {
shouldIgnore = false;
}
});
if (!shouldIgnore) {
originalError.apply(console, args);
}
};這段代碼僅會消除 class 的報錯,而不影響其他。
到這里,基本上所有的報錯都解決掉了。
當我在群里和朋友圈分享這個的時候,有朋友問我是不是引入了什么三方工具或者插件,或者會不會影響編譯結果,現在大家知道了,我沒有用任何三方工具,只是修改了一些無關痛癢的報錯提示。
并且,對于代碼執行、編譯不會有任何影響。
最后一個問題,如果你正在開發底層組件庫,并且決定支持 className 的傳入時,這里還會遇到一個新的問題。一個就是你需要同時支持 class 的傳入,并且,當你使用結構時,必須對 class 進行重命名
const { class: cls } = props;這個小的細節是 React 團隊所擔心的,會引起社區分歧的核心原因。但是如果是個人項目,你可以完全把控你的代碼細節,那么這個分歧對于個人來說并不會有太大的影響。
4.總結
這是一個個人偏好的操作,我并不推薦大家像我一樣使用。如果你也跟我一樣討厭 className,并且決定在項目中使用 class,可以參考一下我的做法,我基本上把潛在的隱患都考慮到了,放心使用是沒問題的。


























