【總結(jié)】2017- 不要再寫滿屏import導(dǎo)入啦!
共 8617字,需瀏覽 18分鐘
·
2024-04-15 18:50
作者:tager 原文:https://juejin.cn/post/7344571285848768524
密密麻麻的import語句不僅僅是一種視覺上的沖擊,更是對代碼組織結(jié)構(gòu)的一種考驗。
我們是如何做到讓import“占領(lǐng)滿屏“的了,又該如何優(yōu)雅地管理這些import語句呢?
本文將從產(chǎn)生大量import語句的原因、可能帶來的問題以及如何優(yōu)化和管理import語句幾個角度來進行探討。
import是如何“占領(lǐng)滿屏“的?
拒絕使用模塊重導(dǎo)(Re-export)
模塊重導(dǎo)是一種通用的技術(shù)。在騰訊、字節(jié)、阿里等各大廠的組件庫中都有大量使用。
如:字節(jié)的arco-design組件庫中的組件:github.com/arco-design…[1]
通過重導(dǎo)在comonents/index.tsx文件暴露所有組件,在使用時一個import就可以使用N個組件了。
// 不使用重導(dǎo)
import Modal from '@arco-design/web-react/es/Modal'
import Checkbox from '@arco-design/web-react/es/Checkbox'
import Message from '@arco-design/web-react/es/Message'
...
// 使用模塊重導(dǎo)
import { Modal, Checkbox, Message} from '@arco-design/web-react'
Re-export一般用于收攏同類型的模塊、一般都是以文件夾為單位,如components、routes、utils、hooks、stories等都通過各自的index.tsx暴露,這樣就能極大程度的簡化導(dǎo)入路徑、提升代碼可讀性、可維護性。
Re-export的幾種形式
1. 直接重導(dǎo)出
直接從另一個模塊重導(dǎo)出特定的成員。
export { foo, bar } from './moduleA';
2. 重命名并重導(dǎo)出(含默認(rèn)導(dǎo)出)
從另一個模塊導(dǎo)入成員,可能會重命名它們,然后再導(dǎo)出。
默認(rèn)導(dǎo)出也可以重命名并重導(dǎo)出
// 通過export導(dǎo)出的
export { foo as newFoo, bar as newBar } from './moduleA';
// 通過export default導(dǎo)出的
export { default as ModuleDDefault } from './moduleD';
3. 重導(dǎo)出整個模塊(不含默認(rèn)導(dǎo)出)
將另一個模塊的所有導(dǎo)出成員作為單個對象重導(dǎo)出。(注意:整個導(dǎo)出不會包含export default)
export * from './moduleA';
4. 收攏、結(jié)合導(dǎo)入與重導(dǎo)出
首先導(dǎo)入模塊中的成員,然后使用它們,最后將其重導(dǎo)出。
import { foo, bar } from './moduleA';
export { foo, bar };
通過這些形式,我們可以靈活地組織和管理代碼模塊。每種形式都有其適用場景,選擇合適的方式可以幫助我們構(gòu)建出更清晰、更高效的代碼結(jié)構(gòu)。
從不使用require.context
require.context 是一個非常有用的功能,它允許我們動態(tài)地導(dǎo)入一組模塊,而不需要顯式地一個接一個地導(dǎo)入。
只需一段代碼讓你只管增加文件、組件,將自動收攏重導(dǎo)。
在項目路由、狀態(tài)管理等固定場景下極其好使(能提效、盡可能避免了增加一個配置要動N個文件的情況)
尤其是在配置路由時、產(chǎn)生大批量的import(多少個頁面就得導(dǎo)入多少個import??)
// 不使用require.context
import A form '@/pages/A'
import B form '@/pages/B'
...
// routes/index.ts文件統(tǒng)一處理
// 創(chuàng)建一個context來導(dǎo)入routes目錄下所有的 .ts 文件
const routesContext = require.context('./routes', false, /.ts$/);
const routes = [];
// 遍歷 context 中的每個模塊
routesContext.keys().forEach(modulePath => {
// 獲取模塊的導(dǎo)出
const route = routesContext(modulePath);
// 獲取組件名稱【如果需要話】,例如:從 "./Header.ts" 提取 "Header"
// const routeName = modulePath.replace(/^./(.*).\w+$/, '$1');
// 將組件存儲在組件對象中
routes.push(route.default || route)
});
export default routes;
在大項目、多路由的情況下,使用 require.context 在處理路由導(dǎo)入上大有可為。
從不使用import動態(tài)導(dǎo)入
動態(tài)import也能實現(xiàn)類似require.context的功能、動態(tài)收攏模塊。關(guān)于import動態(tài)導(dǎo)入的更多內(nèi)容可以看下這篇文章內(nèi)的介紹《如何在Vite5?React?Ts項目中優(yōu)雅的使用Mock數(shù)據(jù)?》[2]
對ProvidePlugin不感興趣
webpack.ProvidePlugin是個好東西,但也不能濫用。
項目中用到的變量/函數(shù)/庫或工具,只要配置后就可以在任何地方使用了。
相信我--看完這個示例,如果你沒用過、那你肯定會迫不及待的想要嘗試了??
const webpack = require('webpack');
module.exports = {
// 其他配置...
plugins: [
new webpack.ProvidePlugin({
React: 'react',
_: 'lodash',
dayjs: 'dayjs',
// 假設(shè)項目中自己定義的utils.js在src目錄下
Utils: path.resolve(__dirname, 'src/utils.js')
})
})
]
// 其他配置...
};
現(xiàn)在你可以在任何地方使用 dayjs、lodash、Utils等,而不需要導(dǎo)入它
小結(jié):
-
webpack.ProvidePlugin是一個強大的工具,它可以幫助我們減少重復(fù)的導(dǎo)入語句,使代碼更加干凈整潔。但是,它不會減少構(gòu)建大小,因為這些庫仍然會被包含在你的最終打包文件中。正確使用這個插件可以提高開發(fā)效率,但需要謹(jǐn)慎使用,以避免隱藏依賴關(guān)系,導(dǎo)致代碼難以理解和維護。 -
對于需要按需加載的模塊或組件,考慮使用動態(tài) import() 語法,這樣可以更有效地控制代碼的加載時機和減小打包體積。 -
謹(jǐn)慎使用 ProvidePlugin,只為那些確實需要在多個地方使用的模塊配置全局變量,以避免不必要的代碼打包。
另外,如果是Vite項目可以使用vite-plugin-inject代替ProvidePlugin的功能
// 配置
import inject from 'vite-plugin-inject'; // 實測暫不可用,有替代方案再更新
...
plugins: [
inject({
// 鍵是你想要提供的全局變量,值是你要提供的模塊
dayjs: 'dayjs', // 例如,這將在全局范圍內(nèi)提供 'dayjs',可以通過 dayjs 訪問
// 你可以繼續(xù)添加其他需要全局提供的模塊
}),
]
...
如果使用了TS,記得配置下類型:
// globals.d.ts文件 處理全局類型
import dayjs from 'dayjs';
declare global {
const dayjs: typeof dayjs;
}
// tsconfig.json文件 也配置一下
{
"compilerOptions": {
// 編譯選項...
},
"include": [ "src/**/*", "globals.d.ts" // 確保 TypeScript 包括這個文件 ]
}
大量使用Typescript導(dǎo)入類型
在TS項目中,滿屏import肯定少不了TS的份。但如果合理配置,必定能急劇減少import的導(dǎo)入
這里介紹下自己在項目中使用最多的方法:TS命名空間。有了它既能讓類型模塊化,更過分的是在使用時可以直接不導(dǎo)入類型??。
同樣,它和ProvidePlugin一樣炸裂,可以直接滅掉import導(dǎo)入。
使用示例:
// accout.ts
declare namespace IAccount {
type IList<T = IItem> = {
count: number
list: T[]
}
interface IUser {
id: number;
name: string;
avatar: string;
}
}
// 任意文件直接使用,無需導(dǎo)入
const [list, setList] = useState<IAccount.IList|undefined>();
const [user, setUser] = useState<IAccount.IUser|undefined>();
注意??eslint可能需要配置下開啟??使用命名空間
《不去充分利用bable特性》
React似乎也意識到不妥:在17版本之前,由于jsx的特性每個組件都需要明文引入import React from 'react',但在這之后由編譯器自行轉(zhuǎn)換,無需引入 React。如果你使用的React17之前的版本也可以通過修改babel達到這個目的,更多細節(jié)可參考React官網(wǎng)[3],有非常詳細的說明。(也提供了自動去除引入的腳本)
其它
1. 設(shè)置webpack、ts別名。
既能縮短導(dǎo)入路徑、也能更有語義化
resolve: {
alias: {
"@src": path.resolve(__dirname, 'src/'),
"@components": path.resolve(__dirname, 'src/components/'),
"@utils": path.resolve(__dirname, 'src/utils/')
}
}
// 使用別名前
import MyComponent from '../../../../components/MyComponent';
// 使用別名后
import MyComponent from '@components/MyComponent';
2. 設(shè)置格式化prettier.printWidth
值設(shè)置的太小可能會導(dǎo)致頻繁換行、給夠難以閱讀。其值在120較為合適吧(看團隊實際的使用情況)。
{
"printWidth": 120,
...
}
3. 按條件動態(tài)全局加載組件
在入口文件引入全局組件,使用require.ensure或import根據(jù)條件動態(tài)加載組件,既能便于維護、減少引用、也能減少性能開銷
// 異步加載全局彈窗,減少性能開銷
Vue.component('IMessage', function (resolve) {
// 指定條件全局加載,無需在具體頁面中引用
if (/^\/pagea|pageb/.test(location.pathname)) {
require.ensure(['./components/message/index.vue'], function() {
resolve(require('./components/message/index.vue'));
});
}
});
4. babel-plugin-import的使用
babel-plugin-import不是直接減少 import 的數(shù)量,而是通過優(yōu)化 import 語句來減少打包體積,提高項目的加載性能。這對于使用了大型第三方庫的項目來說是一個非常有價值的優(yōu)化手段。
以arco-design為例:
// .bablerc配置
{
"plugins": [
["import", {
"libraryName": "@arco-design/web-react",
"libraryDirectory": "es", // 或者 "lib",依賴于具體使用的模塊系統(tǒng)
"style": true // 加載 CSS
}, "@arco-design/web-react"]
]
}
// 這個配置告訴 babel-plugin-import 自動將類似 import { Button } from '@arco-design/web-react'; 的導(dǎo)入語句轉(zhuǎn)換為按需導(dǎo)入的形式,并且加載對應(yīng)的 CSS 文件。
// 業(yè)務(wù)中使用
import { Button } from '@arco-design/web-react';
// 將被bable編譯成
import Button from '@arco-design/web-react/es/button';
import '@arco-design/web-react/es/button/style/css.js'; // 如果 style 配置為 true
總結(jié)
導(dǎo)致import占滿全屏的原因有很多。但不用模塊重導(dǎo)、require.context、import動態(tài)導(dǎo)入、webpack.ProvidePlugin等手段,一定會讓我們寫出滿屏的import????????。
只有想不到的,沒有做不到的。只要你想、相信就一定能如愿以償。
https://github.com/arco-design/arco-design/blob/main/components/index.tsx: https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Farco-design%2Farco-design%2Fblob%2Fmain%2Fcomponents%2Findex.tsx
[2]https://juejin.cn/post/7344571292354838591: https://juejin.cn/post/7344571292354838591
[3]https://zh-hans.legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html: https://link.juejin.cn?target=https%3A%2F%2Fzh-hans.legacy.reactjs.org%2Fblog%2F2020%2F09%2F22%2Fintroducing-the-new-jsx-transform.html
