前言
我将解析ahooks的useRequest源码,将useRequest的功能实现和代码结构解释出来。我主要讲述useRequest的主干,插槽化机制的实现。如果我后续还有动力,我将把剩余的默认的plugin也写下去。如果有问题随时评论,我及时改正。
我的源码学习资料
- ahooks analysis别人对ahook的解析
useRequest的plugin机制
plugin思想
- plugin机制的设计模式是一种符合职责单一的设计模式
- plugin举例:一台台式电脑,主机提供接口让
显示器音响键盘鼠标使用,来实现电脑的可视化和更强大的人机交互。 - 优点就是增强了各种模块的独立性 扩展性 复用性。鼠标与键盘除了调用接口以外需要符合规范,其它可以自由设计,这是独立性与扩展性的体现。鼠标与键盘不仅可以对主机,也可以对那些符合插口规则的机器。这也将更好的在多人合作进行分工。
- 缺点也是有的,当你的键盘与鼠标要联动时,可电脑开的接口没有这个功能,
useRequest的plugin机制解析
插槽提供的生命周期
- 运行的生命周期的地方基本在Fetch中。
- 下面是它插槽中提供了生命周期,可以在运行的某个时间段执行插槽的函数。最后的终点都来onFinally。
这些生命周期都是在Fetch类中的runAsync中运行
- onBefore:是请求前的生命周期,返回值中有stopNow为true可以停止请求,返回data为undefined。retrnNow也是停止请求,但会返回停止后会返回data。其余多出的字段统统存入data中。
- onRequest:也是请求前的生命周期,与onBefore不同的是,生命周期函数可以获得的参数不同,返回值字段的不同。返回有servicePromise的话,会以这个Promise替代掉原本传入请求函数继续处理后续的请求结果
- 这里种有
useRequestImplement提供给插槽的API
//useRequest的类型声明
useRequest(service,options,plugins) => {data,loading,error}
//这里的runAsync是fetch的一个
//runAsyncParams是runAsync所有参数的数组
//serviceReselve servcie的resolve
//serviceError service的error
type fetchOptions = {
manual:false,
...options,
};
function plugin(fetchInstance/*fetch的实例化*/,fetchOptions){
...省略
return {
onBefore(runAsyncParams) => {
stopNow:boolean//如果为true,runAsync返回一个空结果的Promise
returnNow:boolean//如果为true,runAsync返回fetch中的state
...rest
}
//这些所有的参数都会覆盖fetch里的相同state
onRequest(service,runAsyncParams) => {
servicePromise?:Promise //这个如果存在会替代service往下运行
}
onSuccess(serviceReselve,runAsyncParams) => void
onError(serviceError,runAsyncParams) => void
onFinally(runAsyncParams,serviceReselve,serviceError)=> void
onMutate(data)=> void//这个是负责改变内部data的函数
}
}
plugin.onInit = (fetchOptions) => (返回值存入data中,但内部数据后续可能会被覆盖);//实例化前运行
好好记住这些useRequest提供给plugin的接口,在实现uesRequest中才知道才能更好的知道这些代码的意图。
useRequest插槽在启动前都会运行一遍plugin的onInit
这里补充plugin.onInit,plugin.onInit函数运行在plugin函数的运行前面
useRequest代码解析
function useRequest<TData, TParams extends any[]>(
service: Service<TData, TParams>,
options?: Options<TData, TParams>,
plugins?: Plugin<TData, TParams>[],
) {
//useRequestImplement的params声明和useRequest完成一样
return useRequestImplement<TData, TParams>(service, options, [
...(plugins || []),
useDebouncePlugin,
useLoadingDelayPlugin,
usePollingPlugin,
useRefreshOnWindowFocusPlugin,
useThrottlePlugin,
useAutoRunPlugin,
useCachePlugin,
useRetryPlugin,
] as Plugin<TData, TParams>[]);
}
useRequestImplement
function useRequestImplement<TData, TParams extends any[]>(
service: Service<TData, TParams>,
options: Options<TData, TParams> = {},
plugins: Plugin<TData, TParams>[] = [],
) {
//manual决定了是否运行后马上执行请求
const { manual = false, ...rest } = options;
const fetchOptions = {
manual,
...rest,
};
const serviceRef = useLatest(service);
const update = useUpdate();//负责更新如useRef中在更新时,仍旧保留属性不被重置的数据
//useCreation是ahook提供给用户的接口,官网将的肯定比我清楚和清晰
const fetchInstance = useCreation(() => {
const initState = plugins.map((p) => p?.onInit?.(fetchOptions)).filter(Boolean);
return new Fetch<TData, TParams>(
serviceRef,
fetchOptions,
update,
Object.assign({}, ...initState),
);
}, []);
fetchInstance.options = fetchOptions;
// run all plugins hooks
fetchInstance.pluginImpls = plugins.map((p) => p(fetchInstance, fetchOptions));
useMount(() => {
if (!manual) {
// useCachePlugin can set fetchInstance.state.params from cache when init
const params = fetchInstance.state.params || options.defaultParams || [];
// @ts-ignore
fetchInstance.run(...params);
}
});
useUnmount(() => {
fetchInstance.cancel();
});
return {
loading: fetchInstance.state.loading,
data: fetchInstance.state.data,
error: fetchInstance.state.error,
params: fetchInstance.state.params || [],
cancel: useMemoizedFn(fetchInstance.cancel.bind(fetchInstance)),
refresh: useMemoizedFn(fetchInstance.refresh.bind(fetchInstance)),
refreshAsync: useMemoizedFn(fetchInstance.refreshAsync.bind(fetchInstance)),
run: useMemoizedFn(fetchInstance.run.bind(fetchInstance)),
runAsync: useMemoizedFn(fetchInstance.runAsync.bind(fetchInstance)),
mutate: useMemoizedFn(fetchInstance.mutate.bind(fetchInstance)),
} as Result<TData, TParams>;
}
export default useRequestImplement;
function useRequestImplement<TData, TParams extends any[]>(
service: Service<TData, TParams>,
options: Options<TData, TParams> = {},
plugins: Plugin<TData, TParams>[] = [],
)
整个核心是fetch函数与plugin之间的交互,我用文字表达一下运行流程,看上面没问题,下面可以不看。
- 创建fetchOptions,除了options里的manual赋予默认值false以外属相全是引用
- 创建serviceRef 这个函数是最后一次传进来的service函数的引用
- 创建initState 运行所有plugin中的onInit(fetchOptions).filter(Boolean)属性函数
- 创建fetchInstance 引用实例化Fetch,这个fetchInstance永远指向第一次实例化的Fetch
- fetchInstance.options = fetchOptions
- fetchInstance.pluginImpls = (各插槽运行后的返回值)
- 挂载时,判断manual是否为true,来执行fetchInastance.run(fetchInstance.state.params || options.defaultParams || []);
- 卸载时,运行fetchInstance.cancel()