React Native 與 Web 共用 Codebase 時的動態載入:import()、require 與平台邊界
在 React Web 與 React Native 共用同一份 Codebase時,有時會遇到這種情況:
- 某些模組只需要在 App 端(RN)載入,Web 不需要
- 反過來:某些模組只在 Web 端需要
- 你不希望「另一端」在 build-time 就因為 resolve 不到平台專屬模組而爆掉
- 更不希望 Web bundle 不小心把 App-only 程式碼打進去,造成體積膨脹
直覺上,可能會在 shared code 裡這樣寫:
if (isAPP) {
// 希望只在 App 載入某個檔案
}
問題來了:這裡到底該用 import、import(),還是 require?
❌ 錯誤用法:在 if 裡使用靜態 import
下面這種寫法在語法層級就不成立:
if (isAPP) {
import NativeModule from 'react-native-some-module' // ❌ 語法層面錯誤
}
因為 import ... from ... 是 ESM 靜態模組語法,必須出現在 top-level。更重要的是:
- 靜態
import會在 build-time 被 bundler(Webpack/Vite/Metro)用來建立 dependency graph - 這件事發生在程式執行之前,與 runtime 的分支判斷無關。
所以只要這份 shared code 會被 Web 端掃到,Web bundler 就可能嘗試 resolve 這個 RN-only 模組,導致 build-time 直接爆掉或引入不必要的依賴。
✅ Runtime 條件載入:import() vs require()
A:動態 import(非同步)—— 推薦首選
if (isAPP) {
const module = await import('react-native-some-module');
module.default.doSomething();
}
B:CommonJS require(同步)—— 有風險
if (isAPP) {
const NativeModule = require('react-native-some-module');
NativeModule.doSomething();
}
import() 對多數 Web bundler 是「明確的動態邊界」,通常會被視為可延遲載入的 boundary(chunk 或等效機制),因此主 bundle 不必包含該依賴。
建議:specifier 盡量用靜態字串(不要 import(someVar)),避免變成難以分析的動態路徑。
require() 在 RN 生態常見,但放在「Web/RN 共用碼」裡對 Web bundler 往往是保守解析:它不敢假設分支永遠不走,因此仍可能 resolve/納入依賴,導致 build-time 失敗或 bundle 膨脹(行為視 bundler 與配置而定)。
只有在你能保證 Web 端根本不會讀到該檔案時,conditional require 才比較可控。
🚀 架構層面的解決方案:檔案後綴法 (Platform Suffix)
這是 React Native 生態系中比較優雅的解法。同一個 module path,不同平台吃不同檔案。
原理
同一個 module path,讓打包工具自己決定吃哪一個檔案。
- Metro Bundler (RN 端):預設優先尋找
.native.js(以及.ios.js,.android.js)。 - Web bundler (Web 端):通常解析
.js或.web.js。
範例
建立兩個檔案:
MyModule.js(Web 實作)MyModule.native.js(RN 實作)
在使用時:
// 不用寫任何 if 判斷,直接 import(不要寫副檔名)
import { doSomething } from './MyModule';
doSomething();
這是在 build-time 就解決分歧:Web bundler 根本不會讀到 .native 檔案,因此 .native 裡可以放心使用 RN-only 的靜態 import;同理,RN 端也會自動吃到 .native 版本。
總結:
| 策略層級 | 方法 | 優點 | 缺點 | 推薦情境 |
| 架構層級 | 檔案後綴x.native.js | 最乾淨。Build-time 隔離。 | 需要建立多個檔案。 | 核心模組、UI Component。 差異較大時使用。 |
| 語法層級 | 動態 import() | 靈活。Web Bundler 會自動切分 Chunk。 | 需處理 async/await 非同步邏輯。 | 偶爾使用的邏輯。 不想為了小功能拆檔案時。 |
| 語法層級 | require | 同步執行,寫法簡單。 | 有風險。Web 可能誤打包導致崩潰。 | 僅限 RN 舊代碼, 或確定 Web 絕對讀不到時。 |
