這是我在2023第十五屆 iThome 鐵人賽發表的系列文章。https://ithelp.ithome.com.tw/users/20136637/ironman/6408
下方為一個組件中的 import 區塊,現在看起來還算是簡潔的因為 import 的東西不多,但當 import 的內容越來越多時就會顯得雜亂,該如何簡化呢?
import React, { useState } from "react"
import { Text } from "react-native"
import { Input } from "../components/atoms/Input"
import { TextArea } from "../components/atoms/TextArea"
// ...
Index file
從上面的例子可以看出來,Input 跟 TextArea 組件都是從 ../components/atoms/
裡面引入的,如果能將它們合併在一起就能簡化一部份:
import { Input, TextArea } from "../components/atoms"
可以使用 index file 來管理資料夾底下的組件 exports
新建一個 index.ts
到 components/atoms 中,並將 atoms 中的所有組件 exports:
// index.ts
export * from './Input'
export * from './ResizeImage'
export * from './TextArea'
// Input.tsx
export const Input = () => {
// ...
}
// ResizeImage.tsx
export const ResizeImage = () => {
// ...
}
// TextArea.tsx
export const TextArea = () => {
// ...
}
這樣寫的意思是,將 Input.tsx, ResizeImage.tsx, TextArea.tsx 中所有 export 的內容全部放在 index.ts 中一起 exports
// Usage
import { Input, TextArea } from "../components/atoms"
如果組件是 export default 要這麼寫:
// index.ts
export { default as CustomInput } from './Input'
export { default as ResizeImage } from './ResizeImage'
export { default as CustomTextArea } from './TextArea'
// Input.tsx
const Input = () => {
// ...
}
export default Input
// ResizeImage.tsx
const ResizeImage = () => {
// ...
}
export default ResizeImage
// TextArea.tsx
const TextArea = () => {
// ...
}
export default TextArea
Path alias
雖然使用 index file 後已經簡潔了不少,但是還可以再更簡潔一點。
如果今天專案結構比較複雜,很可能 import from '../../../components'
這麼深,所以我們還需要把 ../../../
這段相對路徑給簡化。
import { Input, TextArea } from "../../../components/atoms"
這是目前的專案結構:
設置 path alias 的方式是需要先安裝 babel-plugin-module-resolver 模組解析器:
npm install --save-dev babel-plugin-module-resolver
// or
yarn add --dev babel-plugin-module-resolver
接著在 babel.config.js
中新增一個 plugin module-resolver
,寫法如下:
// babel.config.js
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
// ...
[
'module-resolver',
{
root: '.',
alias: {
'@components': './src/components',
'@hooks': './src/hooks',
'@provider': './src/provider',
'@screens': './src/screens'
},
extensions: ['.ios.js', '.android.js', '.js', '.jsx', '.ts', '.tsx', '.json']
}
]
]
}
- root: 設置為模組解析的根目錄,當你在程式碼中引入模組時,Babel 將從 root 資料夾開始尋找模組,而不是從預設的根目錄或其他路徑。
- alias: 可以使用短而易記的名稱來引入長或複雜的路徑。
- extensions: 在模組解析過程中要尋找的文件擴展名。
進階設置可以參考:https://github.com/tleunen/babel-plugin-module-resolver/blob/master/DOCS.md
設置完成後,就可以將所有相對路徑改為別名了:
import { ImagePage } from '@screens'
import { ModalProvider } from '@provider'
import { useModal } from "@hooks"
或者也可以只將 src 設為 @
:
// babel.config.js
alias: {
'@': './src',
}
import { ImagePage } from '@/screens'
import { ModalProvider } from '@/provider'
import { useModal } from "@/hooks"
這樣一來也能提升開發效率,因為 import 時就不需要再去想需要向上移動多少層才能找到。
如果只是單純想基於 root 引入的話,可以使用 babel-plugin-root-import
IDE 錯誤提示
此時,如果你直接使用剛剛定義好的路徑別名,應該會有紅色波浪線提示你找不到模組:
這是因為使用 TS 開發的時候,在 babel.config.js
中定義還不夠,還要在 tsconfig.json
中也定義:
// tsconfig.json
{
"extends": "@tsconfig/react-native/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"],
"@hooks/*": ["src/hooks/*"],
"@provider/*": ["src/provider/*"],
"@screens/*": ["src/screens/*"]
}
}
}
現在 import 時 IDE 也會自動將路徑轉換為別名:
在 ./index.js 引入 src/App.tsx
如果要將 src 設為基本路徑,可以這樣改:
// tsconfig.json
{
"extends": "@tsconfig/react-native/tsconfig.json",
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@components/*": ["components/*"],
"@hooks/*": ["hooks/*"],
"@provider/*": ["provider/*"],
"@screens/*": ["screens/*"]
}
}
}
IDE 提示的路徑也會將 src
省略:
在 ./index.js 引入 src/App.tsx
現在 import 區塊已經變得十分簡潔,如果要再更簡潔的話,還可以使用 eslint,這邊就不繼續展開了。