重構(gòu)、Flexbox 布局與熱重載實戰(zhàn))
桌面應(yīng)用前端UI組件【免費下載鏈接】proton-nativeA React environment for cross platform desktop apps項目地址https://gitcode.com/gh_mirrors/pr/proton-native點擊查看免費下載Proton Native 是一個用 React 語法編寫跨平臺桌面應(yīng)用的框架其 V2 版本是一次從底層渲染引擎到開發(fā)者體驗的全面重構(gòu)。本文以倉庫內(nèi) docs/v2_changes.md 為核心結(jié)合 src 與 examples 中的真實源碼系統(tǒng)講解 V2 在組件體系、雙后端架構(gòu)、Flexbox 布局、Qt 樣式系統(tǒng)、安裝分發(fā)與熱重載方面的設(shè)計與實現(xiàn)幫助讀者理解這套無需 Electron 的 React 桌面開發(fā)方案的來龍去脈并掌握從零初始化到日常開發(fā)調(diào)試的完整工作流。一、V2 誕生的背景V1 時代的技術(shù)債Proton Native 最初誕生于作者使用 React Native 開發(fā)移動應(yīng)用、又希望把同樣的心智模型遷移到桌面端的訴求。V1 版本選擇基于libui-node構(gòu)建——這是當(dāng)時 Node.js 生態(tài)中為數(shù)不多的 GUI 綁定庫底層由跨平臺原生組件庫libui驅(qū)動。正如 docs/v2_changes.md 所描述的V1 雖然能用、也是 React但兩年下來暴露出四大核心缺陷而 V2 的所有改動幾乎都源自對這四個問題的回應(yīng)組件匱乏Lack of Componentsissue 追蹤器上充斥著請求更多組件的訴求布局困難Difficult Layoutlibui自帶的布局系統(tǒng)與開發(fā)者習(xí)慣的 Web/Flexbox 心智模型差異巨大缺乏樣式Lack of Styling界面只能使用千篇一律的主題外觀復(fù)雜應(yīng)用幾乎無從下手安裝困難Difficult Installationlibui-node的安裝問題尤其在 Windows 上占了 issue 總數(shù)的近四分之一。下面逐一展開 V2 是如何解決這些問題的以及對應(yīng)在源碼中的實現(xiàn)依據(jù)。二、組件體系重構(gòu)放棄 libui自研 node-qt-napi2.1 為什么是 QtV1 組件匱乏的根源在于libui仍處于 alpha 階段而libui-node同步上游更新又非常緩慢——依賴一個不可控的第三方綁定庫導(dǎo)致 Proton Native 無法自主擴充組件。V2 在跨平臺且成熟的 GUI 庫中選擇了Qt理由是作者對 Qt 更熟悉、Qt 天然支持 CSS 式樣式表、API 簡單。由于當(dāng)時沒有維護良好的 Node.js Qt 綁定項目決定自研一套薄封裝綁定node-qt-napi見 package.json 中的依賴聲明。這套綁定刻意不做成通用庫只為 Proton Native 用到的功能做包裝同時額外添加了諸如圖片平鋪image tiling這類自用函數(shù)。直接面向 Qt 編程讓開發(fā)速度大幅提升也讓組件可以完全對標(biāo) React Native——同樣的組件、同樣的 props、同樣的觀感成為 V2 的硬性承諾。從 src/backends/qt.ts 可以看到這套薄封裝的形態(tài)每個組件類都直接映射到對應(yīng)的 Qt 控件例如AppElement→QApplicationsrc/backends/qt.tsWindowElement→QMainWindowsrc/backends/qt.tsViewElement→QWidgetsrc/backends/qt.tsButtonElement→QPushButtonsrc/backends/qt.tsTextInputElement→QLineEdit/QPlainTextEdit多行輸入時切換見 src/backends/qt.tsPickerElement→QComboBoxsrc/backends/qt.ts2.2 原生組件之爭與雙后端這里需要澄清一個常見的認(rèn)知誤區(qū)Qt 繪制出來的控件并非操作系統(tǒng)原生的控件。Qt 會自行繪制所有組件而非調(diào)用 OS 提供的原生控件。Proton Native 本身的目標(biāo)是模仿 React Native——RN 的繪制也大量依賴自繪而非原生控件——所以自繪對大多數(shù)場景并無影響。但桌面端確實有一批用戶看重原生外觀。為此 V2 引入了雙后端架構(gòu)Qt 與 wxWidgets。從 src/backends/index.ts 可見其設(shè)計極其簡潔——一個全局變量記錄當(dāng)前后端setBackend()切換getBackend()動態(tài)require對應(yīng)模塊export let BACKEND: qt | wx qt; export function setBackend(backend: qt | wx) { BACKEND backend; } export function getBackend() { return require(./${BACKEND}); }需要明確當(dāng)前狀態(tài)Qt 是且始終是主后端wxWidgets 后端目前組件很少、可定制性有限處于實驗階段完整說明見 docs/wx_backend.md且其永遠無法獲得與 Qt 同等的樣式便利性。切換 wx 后端的方式如下npm i -S node-wx-napi # 安裝 wxWidgets 后端然后在代碼頂部import { setBackend } from proton-native; setBackend(wx); // 默認(rèn)是 qtwx 后端目前支持的組件與 props 相當(dāng)有限且只支持布局/尺寸類樣式視覺類樣式如fontWeight不支持ComponentPropsAppWindowstyle (backgroundColor), onResizeViewstyle (backgroundColor)Buttonstyle (backgroundColor), onPress, title2.3 許可證問題LGPL 與動態(tài)鏈接選用 Qt 必然涉及許可證考量。原文檔給出如下事實作者自述非律師意見Qt 采用LGPL它是 GPL 的一個變體GPL 要求任何修改都必須公開源碼LGPL 相對寬松——只要 Qt 的二進制可以被替換即動態(tài)鏈接你的代碼就可以閉源Proton Native 本身以MIT許可發(fā)布并且始終與 Qt 動態(tài)鏈接因此基于 Proton Native 開發(fā)的應(yīng)用可以選擇閉源分發(fā)。三、布局革命用 yoga-layout 把 Flexbox 帶進桌面V1 布局困難的根源同樣在libui的布局系統(tǒng)上——作者曾提案支持手動定位/縮放以接入自定義布局但當(dāng)時并未落地。Qt 雖然自帶布局系統(tǒng)但也允許手動放置控件作為回退方案。V2 借此實現(xiàn)了yoga-layoutFacebook 開源的 Flexbox 引擎讓用戶可以用熟悉的 Flexbox 體系排布組件。在倉庫中布局相關(guān)代碼集中在 src/utils/yogaHelper.ts。它定義了一個mixedYogaValueTransformers表把 React Native 風(fēng)格的樣式屬性逐一映射到 yoga 節(jié)點的 setter 與枚舉值盒模型margin/padding及四個方向的變體分別映射為setMargin/setPadding與EDGE_TOP/EDGE_RIGHT/EDGE_BOTTOM/EDGE_LEFT定位top/right/bottom/left→setPosition 對應(yīng) EDGEposition: relative | absolute→setPositionTypePOSITION_TYPE_RELATIVE/POSITION_TYPE_ABSOLUTE對齊alignItems、alignSelf、alignContent、justifyContent分別映射到ALIGN_*與JUSTIFY_*系列常量其中justifyContent額外支持space-evenly彈性flexDirectioncolumn/row、flexWrapwrap/nowrap/wrap-reverse、overflowvisible/hidden/scroll、displayflex/none其他屬性如flexGrow則通過getYogaNodeSetFunctionName將駝峰屬性名轉(zhuǎn)換為setFlexGrow這類標(biāo)準(zhǔn) settersrc/utils/yogaHelper.ts。proton-native使用的 yoga 版本來自yoga-layout-prebuilt預(yù)編譯包package.json這也是安裝順滑的關(guān)鍵一環(huán)詳見下文第五節(jié)。四、樣式系統(tǒng)Qt 樣式表 與 React Native 對齊的 style 對象V1 沒有樣式能力V2 借助 Qt 的CSS 樣式表QSS機制實現(xiàn)了幾乎完整的 React Nativestyle對象支持。核心實現(xiàn)位于 src/utils/convertStyleSheet.ts其工作流程是維護一個excluded列表display、top/right/bottom/left、margin*、padding*、position、overflow、alignItems、justifyContent、flex*、width/height等布局類屬性——這些屬性不走樣式表而是交給 yoga 布局引擎處理對fontSize這類需要像素單位的值做px追加convertToPx數(shù)組把駝峰屬性名轉(zhuǎn)換為連字符小寫fontSize→font-size最終拼裝成 Qt 可識別的樣式表字符串通過 src/backends/qt.ts 中BaseElement.setStyleSheet調(diào)用element.setStyleSheet(...)下發(fā)。const convertToPx [fontSize]; const convertStyleSheet (style: React.CSSProperties) Object.entries(style).reduce((styleString, [propName, propValue]) { if (excluded.includes(propName)) return styleString; if (convertToPx.includes(propName) typeof propValue number) { propValue ${propValue}px; } propName propName.replace(/([A-Z])/g, matches -${matches[0].toLowerCase()}); return ${styleString}${propName}:${propValue};; }, );這段代碼意味著布局/尺寸類屬性由 yoga 負(fù)責(zé)視覺類屬性顏色、字體、背景等由 Qt 樣式表負(fù)責(zé)兩條管線在渲染時合并從而讓style用法與 React Native 保持高度一致。五、安裝體驗的徹底改觀預(yù)編譯二進制 CLI5.1 預(yù)編譯二進制策略安裝難是 V1 最大的用戶流失點。V2 引入了兩個 C 依賴yoga-layout以yoga-layout-prebuilt預(yù)編譯包形式引入node-qt-napi項目為 NAPINode-API版本 2、3、4 預(yù)編譯二進制覆蓋 Linux、Mac、Windows 上的現(xiàn)代 Node.js 版本。兩者都提供預(yù)編譯二進制安裝即用當(dāng)然也保留了自己編譯源碼的途徑。Linux 下運行前的系統(tǒng)級依賴為qtbase5-dev見 docs/quickstart.md。5.2 用 proton-native-cli 初始化項目V2 全新提供了管理工具proton-native-cli一條命令即可完成腳手架搭建# 安裝 CLI全局 npm install -g proton-native-cli # 創(chuàng)建項目 proton-native init my-app # 進入項目目錄 cd my-app # 普通方式運行 npm run start # 或者帶熱重載運行 npm run dev也可以使用npx免全局安裝docs/quickstart.mdnpx proton-native-cli init my-app cd my-app npm run start # 或 npm run dev以倉庫中的 examples/Calculator/package.json 為參照一個 V2 項目的典型腳本結(jié)構(gòu)為startbabel-node index.js直接執(zhí)行入口devwebpack --modedevelopment啟動帶熱重載的開發(fā)構(gòu)建webpackRun運行 webpack 打包產(chǎn)物dist/index.out.jsbuild用 babel 編譯到bin/。5.3 Mac 用戶須知Node 版本限制由于 Node.js 所依賴的 libuv 存在一個已知 buglibuv#2593相關(guān) issue node#31328Proton Native 在 Mac 上無法運行于 Node 版本 12.13.1 和 13.0.1 的環(huán)境。在該問題修復(fù)前建議使用低于上述版本的 Node可用nvm方便地安裝切換。該提示同時出現(xiàn)在 docs/v2_changes.md 與 docs/quickstart.md 中。六、熱重載npm run dev背后的機制V2 的一個彩蛋是內(nèi)置于每個 starter 應(yīng)用的熱重載hot reloading。它作為可選腳本存在——運行npm run dev而非npm run start即可開啟。配合webpack與react-proxy改動源碼后界面會即時刷新且不丟失狀態(tài)。其核心實現(xiàn)在 src/misc/hot.ts模塊級緩存一個appProxyReactProxyComponent注釋特別說明導(dǎo)出它是為了防止被 GC 回收首次調(diào)用時用react-proxy的createProxy創(chuàng)建代理后續(xù)模塊熱替換時調(diào)用appProxy.update(Component)更新最終通過React.createElement(appProxy.get(), null)返回代理后的組件export function hot(Component: React.ComponentType) { if (appProxy) { appProxy.update(Component); } else { appProxy createProxy((Component as any).type); } return React.createElement(appProxy.get(), null); }這正是改動立即生效、狀態(tài)得以保留的原理react-proxy會保留組件實例并只替換其渲染邏輯。此外 V2 還大幅強化了react-devtools支持依賴見 package.json配合 src/devtools.ts 讓調(diào)試體驗更接近 Web 開發(fā)。七、Changelog 全景V2 的完整改進清單以下是 V2 的完整變更清單繼承自 docs/v2_changes.mdFlexbox布局與排布大幅簡化與 React Native 完全一致底層使用 yoga-layout。Styling通過 Qt 樣式表支持樣式應(yīng)用外觀可完全自定義。Qt 取代 libuilibui 迭代緩慢、過于年輕且缺少所需功能未來將逐步轉(zhuǎn)向 wxWidgets 原生組件但需要時間。組合優(yōu)先于繼承代碼重構(gòu)為更清晰、更易擴展的結(jié)構(gòu)。與 React Native 同款組件組件、props、觀感三對齊復(fù)制粘貼 RN 代碼外觀一致同時不為兼容性犧牲能力例如仍支持創(chuàng)建多窗口。熱重載大幅提升開發(fā)效率。Devtools 改進react-devtools支持更穩(wěn)健調(diào)試體驗更好。proton-native-cli全新的項目管理工具為后續(xù)功能擴展預(yù)留空間。TypeScript全部代碼遷移到 TypeScript 以減少 bug見 tsconfig.json 與build: tsc -b構(gòu)建腳本作者也坦言實現(xiàn)仍需更完善但當(dāng)前可用。八、實戰(zhàn)對照從 V1 到 V2 的代碼演變8.1 V1 時代的寫法libui 風(fēng)格V1 的組件體系是libui的布局術(shù)語典型的 CatApi 示例長這樣原文摘錄class Main extends Component { render() { return ( App Window titleCatApi (Patent Pending) size{{ h: 500, w: 500 }} menuBar{false} margined Box padded Form stretchy{false} padded TextInput stretchy{false} labelID onChange{id this.props.setId(id)} / Picker stretchy{false} labelSize selected{sizeConsts.length - 1} onSelect{index this.props.setSize(sizeConsts[index])} {sizeConsts.map((s, i) Picker.Item key{i}{s}/Picker.Item)} /Picker Picker stretchy{false} labelType selected{0} onSelect{index this.props.setType(typeConsts[index])} {typeConsts.map((s, i) Picker.Item key{i}{s}/Picker.Item)} /Picker /Form Button onClick{() { this.props.search(); }} stretchy{false}Submit/Button TextInput stretchy{true} readOnly{true}{this.props.url}/TextInput /Box /Window /App ); } }可以看到 V1 依賴Box、Form、stretchy、margined、padded這類 libui 特有的布局概念與 React Native 差異明顯。8.2 V2 的寫法React Native 風(fēng)格V2 中同樣的界面改用 React Native 的組件與style對象書寫。以 examples/Calculator 為參照該示例仿照 iOS 計算器核心結(jié)構(gòu)如下class Calculator extends Component { // ... render() { return ( App Window style{{ width: 450, height: 900, backgroundColor: black }} View style{{ width: 100%, height: 30%, justifyContent: flex-end, alignItems: flex-end, }} Text style{{ color: white, fontSize: 80, textAlign: right, marginRight: 35, marginBottom: 15, fontWeight: 200, }} {this.state.primary.toString().length 7 ? this.state.primary.toExponential(4) : this.state.primary} /Text /View {this.getButtons().map((buttonGroup, index1) ( View key{index1.toString()} style{{ flex: 1, flexDirection: row, justifyContent: space-evenly, }} {buttonGroup.map((button, index2) ( CircleButton key{index1.toString() index2.toString()} {...buttonStyle[button.type]} onPress{button.onPress} width{button.width} start{button.start} {button.text} /CircleButton ))} /View ))} /Window /App ); } }對比可見size變成了style{{ width, height }}Box/Form/stretchy被View Flexbox 屬性取代交互回調(diào)也從onChange/onSelect對齊到 RN 風(fēng)格的onPress等。這正是 V2 復(fù)制粘貼 React Native 代碼外觀保持一致 承諾的直接體現(xiàn)。完整的 CatApi 示例位于 examples/CatApi其目錄結(jié)構(gòu)actions、reducers、components也表明 V2 與 Redux 等既有 React 生態(tài)庫可以無縫協(xié)作。九、橫向?qū)Ρ萈roton Native vs. 同類項目V2 發(fā)布時桌面 React 領(lǐng)域已出現(xiàn)多個同類項目。原文檔給出的對比要點如下react-nodegui目標(biāo)同樣是把 Qt 帶到 React但 API 更貼近 Qt 而非 React Native基于qodeNode 的一個 fork文檔相對完善。react-native-desktopReact Native 的一個 fork把 desktop 作為新目標(biāo)平臺RN 代碼開箱即用但也因此不支持窗口、菜單等桌面特有能力文檔相對較少。Proton Native 的差異化定位是React Native 風(fēng)格 API 桌面能力多窗口、菜單等 無需 Electron 的輕量運行并強調(diào)為使用者提供易用的開發(fā)體驗、充足的文檔、熟悉的技術(shù)棧并最終走向穩(wěn)定。十、路線圖與社區(qū)原文檔公開的后續(xù)計劃包括更多組件持續(xù)擴充組件庫讓用戶能更輕松地構(gòu)建應(yīng)用完善 wxWidgets 后端由于缺少 CSS 樣式支持且作者對其不熟悉這需要大量工作但對用戶的選擇自由很重要更多 props當(dāng)前各組件僅支持最基礎(chǔ)的 props目標(biāo)是逐步對齊 React Native 的完整 props 集。需要說明的是倉庫 README 亦聲明作者因時間有限已暫停維護并已由社區(qū) fork 繼續(xù)推進本倉庫作為該項目的鏡像其 LICENSE 為 MITCONTRIBUTING.md 歡迎社區(qū) PR。關(guān)于各組件 props 的完整參考可查閱 docs/components.md、docs/components/Window.md 與 docs/components/View.md打包發(fā)布桌面應(yīng)用的流程見 docs/packaging.md調(diào)試與外部功能接入分別見 docs/debugging.md 與 docs/external_functionality.md。結(jié)語Proton Native V2 的這次重構(gòu)本質(zhì)上是一次用成熟桌面 GUI 基礎(chǔ)設(shè)施替換實驗性依賴的務(wù)實升級Qt 提供穩(wěn)定控件與 CSS 樣式y(tǒng)oga-layout 帶來 Flexboxnode-qt-napi預(yù)編譯二進制與proton-native-cli解決安裝門檻熱重載與 devtools 補齊開發(fā)體驗。它在組件 API 上全面對齊 React Native同時保留了多窗口等桌面獨有能力——這套RN 語法 桌面能力 無 Electron的組合構(gòu)成了它區(qū)別于其他同類方案的核心價值。贊分享桌面應(yīng)用前端UI組件【免費下載鏈接】proton-nativeA React environment for cross platform desktop apps項目地址https://gitcode.com/gh_mirrors/pr/proton-native點擊查看免費下載相關(guān)推薦如何將 UniFi Protect 攝像頭完美融入 HomeKit完整集成指南如何將 UniFi Protect 攝像頭完美融入 HomeKit完整集成指南 你是否擁有 UniFi Protect 攝像頭系統(tǒng)卻羨慕 HomeKit 用對比分析HRNet-W18與其他主流圖像分類模型的優(yōu)劣對比對比分析HRNet W18與其他主流圖像分類模型的優(yōu)劣對比 在計算機視覺領(lǐng)域選擇合適的圖像分類模型對項目成功至關(guān)重要。HRNet W18作為一款輕量級高性能如何理解BiomedNLP-BiomedBERT預(yù)訓(xùn)練數(shù)據(jù)來源PubMed摘要與全文文本的完整處理流程如何理解BiomedNLP BiomedBERT預(yù)訓(xùn)練數(shù)據(jù)來源PubMed摘要與全文文本的完整處理流程 BiomedNLP BiomedBERT base u創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考