與 React 的差異
Preact 並非打算重新實作 React。兩者之間存在差異。許多差異微不足道,或者可以使用 preact/compat 完全移除,preact/compat 是 Preact 上的一層薄層,嘗試與 React 達到 100% 相容性。
Preact 不嘗試包含 React 的每個功能,原因在於保持小巧和專注 - 否則,更合理的做法是直接將最佳化提交到 React 專案,而 React 專案本身已經是一個非常複雜且架構良好的程式碼庫。
主要差異
Preact 和 React 之間的主要差異在於 Preact 沒有實作合成事件系統,這是為了考量大小和效能。Preact 使用瀏覽器的標準 addEventListener
來註冊事件處理常式,這表示事件命名和行為在 Preact 中與在純 JavaScript/DOM 中相同。請參閱 MDN 的事件參考,以取得 DOM 事件處理常式的完整清單。
標準瀏覽器事件的運作方式與 React 中的事件非常類似,只有幾個小差異。在 Preact 中
- 事件不會透過
<Portal>
元件冒泡 - 應使用標準的
onInput
取代 React 的onChange
,以作為表單輸入(僅當未使用的preact/compat
) - 應使用標準的
onDblClick
取代 React 的onDoubleClick
(僅當未使用的preact/compat
) - 通常應將
onSearch
用於<input type="search">
,因為清除「x」按鈕不會在 IE11 中觸發onInput
另一個顯著的差異是 Preact 更緊密地遵循 DOM 規格。自訂元素與其他元素一樣受到支援,並且自訂事件以大小寫敏感的名稱受到支援(就像在 DOM 中一樣)。
版本相容性
對於 preact 和 preact/compat,版本相容性是根據 React 的目前和前一主要版本來衡量的。當 React 團隊宣布新功能時,如果符合 專案目標,可能會將其新增到 Preact 的核心。這是一個相當民主的過程,透過公開討論和決策、使用問題和提交請求不斷演進。
因此,網站和文件在討論相容性或進行比較時,會反映 React
15.x
到17.x
。
偵錯訊息和錯誤
我們的彈性架構讓附加元件能以任何方式增強 Preact 體驗。其中一個附加元件是 preact/debug
,它會加入 有用的警告和錯誤,並附加 Preact 開發人員工具 瀏覽器擴充功能(如果已安裝)。這些功能會在開發 Preact 應用程式時提供指引,讓檢查發生的事情變得容易許多。你可以透過加入相關匯入陳述來啟用這些功能
import "preact/debug"; // <-- Add this line at the top of your main entry file
這與 React 不同,React 需要一個 bundler 在建置時透過檢查 NODE_ENV != "production"
來移除偵錯訊息。
Preact 獨有的功能
Preact 實際上加入了一些由 (P)React 社群工作激發的便利功能
ES 模組原生支援
Preact 從一開始就以 ES 模組為構想而建置,並且是第一批支援 ES 模組的架構之一。你可以在瀏覽器中直接透過 import
關鍵字載入 Preact,而不需要先透過 bundler 傳遞。
Component.render()
中的引數
為了方便,我們將 this.props
和 this.state
傳遞給類別元件中的 render()
方法。看看這個使用一個 prop 和一個狀態屬性的元件。
// Works in both Preact and React
class Foo extends Component {
state = { age: 1 };
render() {
return <div>Name: {this.props.name}, Age: {this.state.age}</div>;
}
}
在 Preact 中,也可以這樣寫
// Only works in Preact
class Foo extends Component {
state = { age: 1 };
render({ name }, { age }) {
return <div>Name: {name}, Age: {age}</div>;
}
}
兩個程式片段都會產生完全相同的效果,提供 render 引數是為了方便。
原始 HTML 屬性/屬性名稱
Preact 旨在緊密符合所有主要瀏覽器支援的 DOM 規範。在將 props
套用到元素時,Preact 會 偵測 應將每個 prop 設定為屬性或 HTML 屬性。這使得在自訂元素上設定複雜屬性成為可能,但也表示你可以在 JSX 中使用 class
等屬性名稱
// This:
<div class="foo" />
// ...is the same as:
<div className="foo" />
大多數 Preact 開發人員偏好使用 class
,因為寫起來較短,但兩種寫法都受支援。
JSX 中的 SVG
在屬性和特性的名稱方面,SVG 頗為有趣。SVG 物件上有些屬性(及其特性)使用駝峰式大小寫(例如 clipPath 元素上的 clipPathUnits),有些屬性使用連字號大小寫(例如 許多 SVG 元素上的 clip-path),而其他屬性(通常是從 DOM 繼承而來的,例如 oninput
)則全部使用小寫。
Preact 會按原樣套用 SVG 屬性。這表示你可以將未修改的 SVG 片段直接複製貼上到你的程式碼中,然後就能直接使用。這讓 Preact 與設計人員用來產生圖示或 SVG 插圖的工具有更好的互通性。
// React
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<circle fill="none" strokeWidth="2" strokeLinejoin="round" cx="24" cy="24" r="20" />
</svg>
// Preact (note stroke-width and stroke-linejoin)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<circle fill="none" stroke-width="2" stroke-linejoin="round" cx="24" cy="24" r="20" />
</svg>
如果你來自 React,你可能習慣於以駝峰式大小寫指定所有屬性。你可以繼續使用一律駝峰式大小寫的 SVG 屬性名稱,方法是將 preact/compat 加入你的專案,它會複製 React API 並將這些屬性標準化。
使用 onInput
取代 onChange
主要基於歷史原因,React 的 onChange
事件實際上與瀏覽器提供的 onInput
事件語意相同,而且到處都受支援。input
事件最適合用於大多數情況,也就是當你想要在表單控制項被修改時做出反應。在 Preact 核心,onChange
是標準的 DOM 變更事件,當使用者提交元素的值時會觸發。
// React
<input onChange={e => console.log(e.currentTarget.value)} />
// Preact
<input onInput={e => console.log(e.currentTarget.value)} />
如果您使用 preact/compat,大部分 onChange
事件在內部會轉換成 onInput
,以模擬 React 的行為。這是我們用來確保與 React 生態系統相容性最大的技巧之一。
JSX 建構函式
JSX 是 JavaScript 的語法擴充,會轉換成巢狀函式呼叫。使用這些巢狀呼叫來建構樹狀結構的構想早於 JSX,之前在 JavaScript 中由 hyperscript 專案推廣。這種方法的價值遠遠超出 React 生態系統的範圍,因此 Preact 推廣原始的通用社群標準。若要更深入探討 JSX 及其與 Hyperscript 的關係,請閱讀這篇文章,了解 JSX 的運作方式。
來源:(JSX)
<a href="/">
<span>Home</span>
</a>
輸出
// Preact:
h(
'a',
{ href:'/' },
h('span', null, 'Home')
);
// React:
React.createElement(
'a',
{ href:'/' },
React.createElement('span', null, 'Home')
);
最後,如果您查看 Preact 應用程式的產生輸出程式碼,很明顯較短且沒有名稱空間的「JSX 實用程式」既容易閱讀,又更適合縮小等最佳化。在大部分 Preact 應用程式中,您會遇到 h()
,不過您使用哪個名稱並不重要,因為也提供了 createElement
別名匯出。
不需要 contextTypes
舊版的 Context
API 要求元件使用 React 的 contextTypes
或 childContextTypes
宣告特定屬性,才能接收這些值。Preact 沒有這個需求:預設所有元件都會收到 getChildContext()
產生的所有 context
屬性。
preact/compat
獨有的功能
preact/compat
是我們的相容性層,可將 React 程式碼轉譯為 Preact。對於現有的 React 使用者來說,這是一個簡單的方法,可以在不變更任何程式碼的情況下試用 Preact,只要在您的套件管理員設定中設定幾個別名即可。
Children API
Children
API 是一組特殊的方法,用於處理 props.children
的值。對於 Preact,這通常是不必要的,我們建議改用內建的陣列方法。在 Preact 中,props.children
可能是 Virtual DOM 節點、空值(例如 null
)或 Virtual DOM 節點的陣列。前兩種情況是最簡單且最常見的,因為可以直接使用或傳回 children
// React:
function App(props) {
return <Modal content={Children.only(props.children)} />
}
// Preact: use props.children directly:
function App(props) {
return <Modal content={props.children} />
}
對於需要反覆運算傳遞給組件的子項目的特殊情況,Preact 提供了一個 toChildArray()
方法,它接受任何 props.children
值,並傳回一個扁平化且正規化的 Virtual DOM 節點陣列。
// React
function App(props) {
const cols = Children.count(props.children);
return <div data-columns={cols}>{props.children}</div>
}
// Preact
function App(props) {
const cols = toChildArray(props.children).length;
return <div data-columns={cols}>{props.children}</div>
}
preact/compat
提供了一個相容於 React 的 Children
API,以實現與現有組件函式庫的無縫整合。
特殊化組件
preact/compat 附帶了每個應用程式都不一定需要的特殊化組件。這些組件包括
- PureComponent:僅在
props
或state
變更時更新 - memo:精神上類似於
PureComponent
,但允許使用自訂比較函式 - forwardRef:提供
ref
給指定的子組件。 - Portals:繼續將目前的樹狀結構呈現到不同的 DOM 容器中
- Suspense:實驗性允許在樹狀結構尚未準備好的情況下顯示備用內容
- lazy: 實驗性 延遲載入非同步程式碼,並標記樹狀結構為準備就緒/尚未準備就緒。