深入解讀ahooks
本文適合對(duì)打造工具函數(shù)庫(kù)感興趣的小伙伴閱讀。
歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~
作者:廣東靚仔
一、前言
本文基于開(kāi)源項(xiàng)目:
https://github.com/alibaba/hooks
https://ahooks.js.org/guide/
https://github.com/lerna/lerna

??
? ? 廣東靚仔在看ahooks項(xiàng)目代碼的時(shí)候發(fā)現(xiàn)了一些有意思的東西,這里分享下。
二、ahooks是什么
易學(xué)易用 支持 SSR 對(duì)輸入輸出函數(shù)做了特殊處理,且避免閉包問(wèn)題 包含大量提煉自業(yè)務(wù)的高級(jí) Hooks
包含豐富的基礎(chǔ) Hooks
使用 TypeScript 構(gòu)建,提供完整的類型定義文件
$?npm?install?--save?ahooks
#?or
$?yarn?add?ahooks
使用:
import?{?useRequest?}?from?'ahooks';三、項(xiàng)目目錄

看到這個(gè)目錄,有種親切的感覺(jué),經(jīng)過(guò)粗略的查看,跟dumi腳手架有點(diǎn)相似,經(jīng)過(guò)求證,確定是使用dumi搭建的。
目錄講解:
+--?config?????????????????//?項(xiàng)目配置
+--?docs???????????????????//?組件庫(kù)文檔目錄??
|??+--?guide???????????????//?組件庫(kù)文檔其他路由
|??+--?index.en-US.md??????//?組件庫(kù)文檔首頁(yè)(英文)
|??+--?index.zh-CN.md??????//?組件庫(kù)文檔首頁(yè)(中文)
+--?packages???????????????//?lerna包
|??+--?hooks???????????????//?子包hooks
|??+--?use-url-state???????//?子包use-url-state
+--?webpack.common.js??????//?webpack配置文件
+--?gulpfile.js????????????//?自動(dòng)化構(gòu)建工具配置
+--?lerna.json?????????????//?把各個(gè)小功能拆分成獨(dú)立的npm庫(kù)
看到這里我們先來(lái)溫習(xí)下dumi相關(guān)知識(shí)。
四、dumi
dumi是為組件開(kāi)發(fā)場(chǎng)景而生的文檔工具。
dumi經(jīng)常與father搭配使用,father 負(fù)責(zé)構(gòu)建,而 dumi 負(fù)責(zé)組件開(kāi)發(fā)及組件文檔生成。
安裝使用:
$?npx?@umijs/create-dumi-lib????????#?初始化一個(gè)文檔模式的組件庫(kù)開(kāi)發(fā)腳手架
#?or
$?yarn?create?@umijs/dumi-lib
$?npx?@umijs/create-dumi-lib?--site?#?初始化一個(gè)站點(diǎn)模式的組件庫(kù)開(kāi)發(fā)腳手架
#?or
$?yarn?create?@umijs/dumi-lib?--site
效果如下:

篇幅有限,這里稍微提下:
en-US?是默認(rèn)語(yǔ)言,如果需要中文創(chuàng)建一個(gè)帶?zh-CN?locale 后綴的同名 Markdown 文件即可。更多的dumi相關(guān)知識(shí)可以前往官方文檔查看:
https://d.umijs.org/guide
五、hooks
回到ahooks中,從項(xiàng)目目錄中可以看到hooks文件是整個(gè)工具庫(kù)的核心
+--?hooks?
|??+--?src???????????????//?子包hooks的源碼目錄
|??+--?gulpfile.js???????//?自動(dòng)化構(gòu)建配置
|??+--?package.json??????
|??+--?tsconfig.json?????//?ts配置
|??+--?webpack.config.js?//?webpack配置
|??+--?yarn.lock?????????//?鎖定版本
在查看源代碼的過(guò)程中,廣東靚仔看到了lodash的影子,推薦一下lodash。
下面我們來(lái)看看這個(gè)hooks具體內(nèi)容。
tsconfig.json
{
??"extends":?"../../tsconfig.json",
??"compilerOptions":?{
????"rootDir":?"src"
??}
}
利用extends屬性從根目錄的tsconfig.json配置文件里繼承配置。
compilerOptions指定src文件目錄(用于輸出),用于控制輸出目錄結(jié)構(gòu)
webpack.config.js
const?merge?=?require('webpack-merge');
const?common?=?require('../../webpack.common.js');
const?path?=?require('path');
module.exports?=?merge(common,?{
??entry:?'./es/index.js', // 入口模塊的文件相對(duì)路徑
??output:?{ // 輸出到的目錄
????filename:?'ahooks.js',
????library:?'ahooks',
????path:?path.resolve(__dirname,?'./dist'),
??},
});
?webpack.common.js?公共配置文件? -- 抽離出公共的部分? 通過(guò)merge進(jìn)行合并
代碼裁剪下,方便理解:
module.exports?=?merge(common,?config);webpack.common.js
里面沒(méi)有什么特殊的,打包的時(shí)候,ahooks不想把react打到bundle中,所以使用了如下配置:
?externals:?[
????{
??????react:?'React',
????},
??],gulpfile.js
const?commonConfig?=?require('../../gulpfile');
exports.default?=?commonConfig.default;
ahooks使用了gulp自動(dòng)化構(gòu)建工具增強(qiáng)工作流程
gulpfile配置
const?gulp?=?require('gulp');
const?babel?=?require('gulp-babel');
const?ts?=?require('gulp-typescript');
const?del?=?require('del');
gulp.task('clean',?async?function?()?{
?...
});
gulp.task('cjs',?function?()?{
???...
});
gulp.task('es',?function?()?{
???...
});
gulp.task('declaration',?function?()?{
??...
});
gulp.task('copyReadme',?async?function?()?{
???...
});
exports.default?=?gulp.series('clean',?'cjs',?'es',?'declaration',?'copyReadme');
可以看到使用了gulp的series()將clean、cjs、es、declaration、copyReadme組合成更大的操作,然后依次執(zhí)行
clean里面調(diào)用了del,類似于rimraf刪除當(dāng)前工作目錄
cjs使用gulp-typescript編譯 TypeScript 文件流
declaration生成相應(yīng)的 .d.ts 文件
copyReadme創(chuàng)建一個(gè)用于將?hooks文件的元數(shù)據(jù)對(duì)象寫入到文件系統(tǒng)的流
lerna.json
ahooks使用了lerna集中了子包在同一個(gè)站點(diǎn)中。
Lerna 是一個(gè)工具,可以優(yōu)化使用 git 和 npm 管理多包存儲(chǔ)庫(kù)的工作流程。
廣東靚仔把package.json里面關(guān)于lerna剪切了下來(lái),如下:
"scripts":?{
????"bootstrap":?"lerna?bootstrap",
????"clean":?"lerna?clean?--yes",
????"build":?"lerna?run?build",
????"pub":?"yarn?run?build?&&?lerna?publish",
????"pub:beta":?"yarn?run?build?&&?lerna?publish?--dist-tag?beta"
??},
Lerna 中的兩個(gè)主要命令是lerna bootstrap和lerna publish。
bootstrap將 repo 中的依賴項(xiàng)鏈接在一起。publish將幫助發(fā)布任何更新的包。
ahooks的lerna配置如下:
{
??"version":?"3.1.9",
??"packages":?["packages/*"],
??"npmClient":?"yarn",
??"command":?{
????"version":?{
??????"allowBranch":?"master",
??????"includeMergedTags":?true
????},
????"publish":?{
??????"message":?"chore(release):?publish",
??????"registry":?"https://registry.npmjs.org/"
????}
??}
}
version:存儲(chǔ)庫(kù)的當(dāng)前版本。packages: 用作包位置的 glob 數(shù)組。
npmClient:用于指定特定客戶端以運(yùn)行命令的選項(xiàng)(也可以在每個(gè)命令的基礎(chǔ)上指定)。更改為"yarn"使用 yarn 運(yùn)行所有命令。默認(rèn)為“npm”。command.version.allowBranch: lerna version當(dāng)從除master. 僅限制lerna version在主分支被認(rèn)為是最佳實(shí)踐
command.version.includeMergedTags:?檢測(cè)到更改的包時(shí)包括來(lái)自合并分支的標(biāo)簽
command.publish.message:執(zhí)行版本更新以進(jìn)行發(fā)布時(shí)的自定義提交消息
command.publish.registry:使用它來(lái)設(shè)置要發(fā)布到的自定義注冊(cè)表 url 而不是 npmjs.org,如果需要,您必須已經(jīng)過(guò)身份驗(yàn)證。
六、工具函數(shù)
export?{
??useRequest,
??useControllableValue,
??useDynamicList,
??useVirtualList,
??useResponsive,
??useEventEmitter,
??useLocalStorageState,
??useSessionStorageState,
??useSize,
??configResponsive,
??useUpdateEffect,
??useUpdateLayoutEffect,
??useBoolean,
??useToggle,
??useDocumentVisibility,
??useSelections,
??useThrottle,
??useThrottleFn,
??useThrottleEffect,
??useDebounce,
??useDebounceFn,
??useDebounceEffect,
??usePrevious,
??useMouse,
??useScroll,
??useClickAway,
??useFullscreen,
??useInViewport,
??useKeyPress,
??useEventListener,
??useHover,
??useUnmount,
??useSet,
??useMemoizedFn,
??useMap,
??useCreation,
??useDrag,
??useDrop,
??useMount,
??useCounter,
??useUpdate,
??useTextSelection,
??useEventTarget,
??useHistoryTravel,
??useCookieState,
??useSetState,
??useInterval,
??useWhyDidYouUpdate,
??useTitle,
??useNetwork,
??useTimeout,
??useReactive,
??useFavicon,
??useCountDown,
??useWebSocket,
??useLockFn,
??useUnmountedRef,
??useExternal,
??useSafeState,
??useLatest,
??useIsomorphicLayoutEffect,
??useDeepCompareEffect,
??useAsyncEffect,
??useLongPress,
??useRafState,
??useTrackedEffect,
??usePagination,
??useAntdTable,
??useFusionTable,
??useInfiniteScroll,
??useGetState,
??clearCache,
??useFocusWithin,
};
ahook寫了以上工具函數(shù),平時(shí)開(kāi)發(fā)過(guò)程中自己用到的其實(shí)并沒(méi)有這么多,我們可以封裝自己的hooks。
七、總結(jié)
在看源碼前,我們先去官方文檔復(fù)習(xí)下框架設(shè)計(jì)理念、源碼分層設(shè)計(jì) 閱讀下框架官方開(kāi)發(fā)人員寫的相關(guān)文章 借助框架的調(diào)用棧來(lái)進(jìn)行源碼的閱讀,通過(guò)這個(gè)執(zhí)行流程,我們就完整的對(duì)源碼進(jìn)行了一個(gè)初步的了解 接下來(lái)再對(duì)源碼執(zhí)行過(guò)程中涉及的所有函數(shù)邏輯梳理一遍
關(guān)注我,一起攜手進(jìn)階
歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~
