使用 React Native 開發 APP 的一大好處就是可以與 React Web 共用程式,除了 UI, Routing 等無法共用以外,商業邏輯、hooks、utils、api 層處理,通通可以共用,要達到此目的我們可以使用 Monorepo,如果不知道 Monorepo 是什麼的可以參考 Fireship 的影片,概念上就是將所有的專案都放在同一個 repo 下達到方便共用的目的
Monorepos - How the Pros Scale Huge Software Projects // Turborepo vs Nx
1. 建立專案
這我們要做的文件夾結構,各個專案放在 packages 的資料夾內,其中的 common 資料夾是放 app 與 web 共用的程式
├── package.json |
1.1 建立文件夾與各專案
cd 到 packages 目錄後,使用 CLI 工具建立各專案
- APP 的部分使用 React Native CLI,這裡我們設定使用 TypeScript 的模板
npx react-native init app --template react-native-template-typescript |
- Web 的部分使用 Create-React-App
npx create-react-app web --template typescript |
2. 設定 Yarn Workspace
先移除各專案中的 node_modules
rm -rf packages/*/node_modules/ |
這理我們使用 Yarn Workspace 來建立 Monorepo,當然也可以使用其它的工具像是 Nx, Turborepo, Lerna 等,有機會之後試玩看看在寫文章與大家分享差別在哪
設定專案根目錄的 package.json,加上 private 為 true 和workspaces 屬性
{ |
根目錄的 package.json
其中比較特別的是 nohoist 屬性,因為 yarn workspaces 會將所有的 dependencies 都安裝在專案根目錄中的 node_modules 裡,來避免底下的專案安裝同樣的包,來達到共享依賴包的機制,yarn 在 packages 的專案裡的 node_modules 會創建 symlinks (軟鏈結),來讓專案可以找到需要的依賴包
但是 react-native 所使用的 metro bundler 沒有支援 symlinks 的功能,所以 react-native 專案需要的套件必須在 app 目錄裡的 node_moudles 裡,metro 在打包時才找得到
3. 設定共用專案
在 packages 目錄新增一個 common 資料夾,然後使用 npm init -y
來新增一個 npm 模組,更改 package.json 來調整套件名稱,方便之後在 app 或 web 的專案裡辨識
如果要用 TypeScript 的話,記得將 package.json 裡的 main 屬性改為 index.ts,這會設定這個專案的主要檔案為 index.ts
{ |
common 的 package.json
然後試著新增一個 index.ts,export 一個 function 方便我們測試
export function testFunction () { |
index.ts
4. 使用共用專案
在 app 的 pacakge.json 中新增共用依賴包
"dependencies": { |
然後跑一次 yarn 指令來安裝依賴包
到這裡我們還不能使用 common 包裡的程式,我們需要設定 metro 來讀取根目錄的 node_modules,因為我們的 common 包會被放在那
const path = require('path'); |
我們主要設定了
- resolver 裡的 extraNodeModules 為根目錄的 node_modules
- 設定 watchFolders 監看整個專案
5. 測試看看吧
我們分別在 app 跟 web 的 App.tsx 元件裡使用剛剛從 common 裡 export 的 function
測試 App
import { testFunction } from '@todo/common' |
測試 Web
import React from 'react'; |
web 的 App.tsx