vite-plugin-prefetch-api
A prefetch plugin for an API, which inserts the APIs that need to be requested in advance into the head tag and mounts the request information on the window object. Suitable for CSR projects.
Installation
pnpm i vite-plugin-prefetch-api
or
yarn add vite-plugin-prefetch-api
or
npm i vite-plugin-prefetch-api
Example
Configuration
// vite.config.js
import { defineConfig } from "vite";
import PrefetchPlugin from "vite-plugin-prefetch-api";
export default defineConfig({
plugins: [
PrefetchPlugin({
list: [
{
url: '/api/task',
method: 'post',
adapter: function(data) {
return { body: { username: data.cookie.username } }
}
},
{
url: '/api/msg',
method: 'get',
adapter: function(data) {
return { query: { uid: data.cookie.uid } }
},
trigger: function(data) {
return data.hash === 'inbox'
}
}
]
})
],
});
Get Prefetch Data
import { getCache } from "vite-plugin-prefetch-api/util";
export async function request(url: string, options: any) {
const cacheFetch = getCache({ url, ...options });
if (cacheFetch) {
return cacheFetch;
}
return fetch(url, options).then((res) => res.json());
}
Html before:
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>example</title>
<script type="module" crossorigin src="/assets/index-127f2da4.js"></script>
<link rel="stylesheet" href="/assets/index-c322ae43.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
Html after:
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>example</title>
<script type="module" crossorigin src="/assets/index-b747b36c.js"></script>
<link rel="stylesheet" href="/assets/index-8c9b6ecb.css">
<script>!function(){try{const n="__PREFETCH__",c={cookie:document.cookie.split("; ").reduce(function(e,t){var[t,o]=t.split("=");return e[t]=decodeURIComponent(o),e},{}),query:location.search.slice(1).split("&").reduce(function(e,t){t=t.split("=");return e[decodeURIComponent(t[0])]=decodeURIComponent(t[1]||""),e},{}),hash:location.hash.slice(2),path:location.pathname};function r(e,t,o){t=function r(c){return Object.keys(c).sort().reduce(function(t,e){var o=c[e];if(void 0!==o){var n=e+":"+("object"==typeof o?r(o):o);for(let e=0;e<n.length;e++)t=(t<<5)-t+n.charCodeAt(e)}return t},0).toString(36)}(t);window[n]=window[n]||{},window[n][t]={count:o||1,value:e}}function o(e){var t=function(e,t){var{url:o,method:n}=e;try{var r=e["adapter"];const{body:i,header:a,query:u={}}=r(t)||{};var c=Object.keys(u).map(e=>e+"="+u[e]).join("&");return{url:c?o+"?"+c:o,method:n,body:i,header:a}}catch(e){return{url:o,method:n}}}(e,c);const{url:o,...n}=t;r(function(e,t){const{header:o,method:n="get"}=t,r=new Headers;return r.append("Content-Type","application/json"),o&&Object.keys(o).forEach(function(e){r.append(e,o[e])}),t={body:t.body?JSON.stringify(t.body):void 0,headers:r,method:n.toLocaleUpperCase()},fetch(e,t).then(e=>e.ok?e.json():Promise.reject(e.statusText))}(o,n),t,e.count||1)}[{url:"/api/task",method:"post",adapter:function(e){return{body:{username:e.cookie.username}}},count:1,trigger:function(){return 1}},{url:"/api/msg",method:"get",adapter:function(e){return{query:{uid:e.cookie.uid}}},trigger:function(e){return"inbox"===e.hash},count:1}].forEach(({trigger:e,...t})=>{e(c)&&o(t)})}catch(e){console.error("[prefetch-api error]",String(e))}}();</script>
</head>
<body>
<div id="app"></div>
</body>
</html>
Prefetch API
Prefetch Plugin Config
interface PluginConfig {
api?: "xhr" | "fetch"; // use "xhr" or "fetch", default "fetch"
minify?: boolean; // minify code, default true
list: RequestConfig[]; // prefetch api list
}
interface RequestConfig {
url: string;
method: "get" | "post" | "put" | "delete";
count?: number; // The number of times response can be used, default 1
adapter?: ParamsAdaptFunc; // Here you can set the request parameters according to the current environment information, default () => ({})
trigger?: TriggerFunc | boolean; // default true, If the trigger is equal to false, it is filtered out before the html is inserted
}
type ParamsAdaptFunc = (data: SourceData) => ParamsInfo;
type TriggerFunc = (data: SourceData) => boolean;
type SourceData = {
cookie: Record<string, unknown>;
query: Record<string, unknown>;
hash: string;
path: string;
};
type ParamsInfo = {
header?: Record<string, unknown>;
query?: Record<string, unknown>;
body?: unknown;
};
Prefetch GetCache API
Prefetch-plugin generates the hash value according to the RequestParams and stores the response to the window.
Therefore, when you want get response, keep the Settings of RequestParams and Prefetch-plugin consistent.
type RequestParams = {
url: string;
method: "get" | "post" | "put" | "delete";
header?: Record<string, unknown>;
body?: unknown;
}
type Response<T> = {
data: T;
status: number;
statusText: string;
}
type ResponseReject = {
status: number;
statusText: string;
}
function getCache<T>(params: RequestParams): Promise<Response<T>> | null;
Why prefetch?
The CSR project is executed in the normal order of network load execution and JS execution: