🚀 Focus on improving the first screen loading speed of applications and providing the best user experience, inspiration comes from the preloading of NextJS. (https://web.dev/route-prefetching-in-nextjs/)
MIT License
🚀 Focus on improving the first screen loading speed of applications and providing the best user experience, inspiration comes from the preloading of NextJS.
As we can see from the gif, the display time of the loading component is greatly reduced by route-resource-preload
relative to react.lazy
.
Split modules loads as needed
, improving the first screen loading experience of your App.Minimize dynamic component loading time
and providing the best user experience.automatic the preloading of resources
( JS / Component / Module-Federation / UMD / Svg / Png , Etc) and providing the best user experience.manually to preload
.non-component
.typescript
.React <Suspense>
.automatic preloading
and manual preloading
of components to avoid poor component interaction experience due to component rendering delays.non-component
when to preload in more detail
Module-Federation
More types
of resource (image/font/svg/css/js/...)non-component
.typescript
.Component | Normal Lazy Load(ms) | Preload (ms) |
---|---|---|
Simple Component (one-resource) | 150 | 1 |
Complex Component (six-resource) | 350 | 10 |
It can be seen from the table that preloading significantly improves the loading speed of components, especially for complex components, the improvement of loading speed is more obvious. This shows that in complex business scenarios,
preloading can significantly improve component loading speed and user experience
.
npm install @route-resource-preload/webpack-plugin @route-resource-preload/react
Dynamic
import { dynamic } from '@route-resource-preload/react'
const Image = dynamic({
loader: () => import('Components'),
loading: (props) => <>loading...</>,
})
const handleClick = () => {
// execute preloading
Image.preload()
}
export default function Main(props){
return <div onClick={handleClick}>
<Image {...props} />
</div>
}
plugin
in your build config.const RouteResourcePreloadPlugin = require('@route-resource-preload/webpack-plugin')
webpack: {
plugins: {
add: [
new RouteResourcePreloadPlugin({
// [the-preloading-flag]: ['path']
// project's components(modules)
modulePreloadMap: {
"flagA": ["../components/A"]
},
// module-federation's components(modules)
mfPreloadMap: {
"flagMF": ["ling_core/Components"]
},
// static assets (just like js/css/png/jpg/font, etc.)
assetPreloadMap: {
"flagA": ['https://domain.com/xxx.png']
}
})
]
},
}
Preloader
and run
import { Preloader } from '@route-resource-preload/react'
const preloader = new Preloader()
// execute preloading
preloader.run('flagA')
plugin
in your build config.const RouteResourcePreloadPlugin = require('@route-resource-preload/webpack-plugin')
webpack: {
plugins: {
add: [
new RouteResourcePreloadPlugin({
// [the-preloading-flag]: ['path']
// project's components(modules)
modulePreloadMap: {
"flagA": ["../components/A"]
},
// module-federation's components(modules)
mfPreloadMap: {
"flagMF": ["ling_core/Components"]
},
// static assets (just like js/css/png/jpg/font, etc.)
assetPreloadMap: {
"flagA": ['https://domain.com/xxx.png']
}
})
]
},
}
Dynamic
import component and render PreloadLink
import { dynamic, PreloadLink } from '@route-resource-preload/react'
// project's component
const ComponentA = dynamic({
loader: ()=>import('../components/A'),
loading: () => <>loading...</>
})
// module-federation's component
const Image = dynamic({
loader: ()=>import('your_lib/Components'),
loading: () => <>loading...</>,
submodule: 'Image' // may be you didn't export default, just like " export { Image, ...Others } " in js.
})
export default function Main(props){
return <>
<PreloadLink flag="flagA" onClick={()=>{
navigate('/A') // navigate comes from react-router-dom, you can custom your code.
}}
>
Preload Component A
</PreloadLink>
<PreloadLink flag="flagMF">
{/* Link comes from react-router-dom, you can custom your code. */}
<Link to="flagMF" >Preload MF</Link>
</PreloadLink>
</>
}
const Modal = dynamic({
loader: () => import('xxx/Modal'),
// loading: () => <>loading...</>,
// suspense: true,
// submodule: 'submodule',
// visible: true,
})
Param | Description | Type | Default Value | necessary |
---|---|---|---|---|
loader | dynamic import module | () => Promise<Record<string, T extends ComponentType>> | - | ✅ |
loading | A spinner for displaying loading state | ComponentType | - | ❌ |
submodule | maybe you didn't export default, you need it | string | - | ❌ |
visible | whether to render immediately after the components in the view are preloaded | boolean | true | ❌ |
suspense | use react <Suspense> for displaying loading state |
boolean | - | ❌ |
dynamic
will return a HOC withonEnd
prop, which will call back after the component is dynamically rendered to adapt to complex and changeable business scenarios, such as custom loading package elements/or computing component rendering time-consuming, etc.
function CommonLoading (props: { moduleName: string }) {
const { moduleName } = props
const [loading, setLoading] = useState(true)
const Com = useMemo(()=>dynamic({ loader: () => import(`${moduleName}`)}),[moduleName])
// custom loading
return <Spin spinning={loading}>
<Com onEnd={()=>{ setLoading(false)}} />
</Spin>
}
<CommonLoading moduleName={moduleName} />
flag
const preload = new Preloader(options)
preload.run('flag') // plugin flag
Param | Description | Type | Default Value | necessary |
---|---|---|---|---|
publicPath | yout server publicPath | string | - | ❌ |
Preloader's
publicPath
is the same as RouteResourcePreloadPlugin'spublicPath
flag
<PreloadLink flag="flagA" >
Preload Component
</PreloadLink>
Props | Description | Type | Default Value | necessary |
---|---|---|---|---|
flag | the preloading flag | string | - | ✅ |
children | children ReactNode | ReactNode | - | ✅ |
action | trigger preload action | string (init / inview / hover) | hover | ❌ |
onClick | PreloadLink click event | () => void | - | ❌ |
className | PreloadLink classname | string | - | ❌ |
publicPath | yout server publicPath | string | - | ❌ |
PreloadLink's
publicPath
is the same as RouteResourcePreloadPlugin'spublicPath
RouteResourcePreloadPlugin's
publicPath
is the same as PreloadLink'spublicPath
new RouteResourcePreloadPlugin({
// [the-preloading-flag]: ['path']
// project's components(modules)
modulePreloadMap: {
"flagA": ["../components/A"]
},
// module-federation's components(modules)
mfPreloadMap: {
"flagMF": ["xxx/Components"]
},
// static assets (just like js/css/png/jpg/font, etc.)
assetPreloadMap: {
"flagA": ['https://domain.com/xxx.png']
}
})
Params | Description | Type | Default Value | necessary |
---|---|---|---|---|
modulePreloadMap | project's components(modules) | modulePreloadMap Object | - | ❌ |
mfPreloadMap | module-federation's components(modules) | mfPreloadMap Object | - | ❌ |
assetPreloadMap | static assets | assetPreloadMap Object | - | ❌ |
publicPath | your server publicPath | string | - | ❌ |
value | Description |
---|---|
init | Trigger preload after PreloadLink rendering |
inview | Trigger preload after PreloadLink in the view |
hover | Trigger preload after your mouse hover in the PreloadLink |
{
"flagA": ["../components/A"],
// [the-preloading-flag]: ['your project's components path']
}
{
"flagMF": ["ling_core/Components"]
// [the-preloading-flag]: ['your module-federation's components path']
}
{
"flagA": ['https://domain.com/xxx.png']
// [the-preloading-flag]: ['your static assets link'] (image/font/svg/css/js/...)
}