vem*_*und 8 javascript reactjs
我正在尝试使用新的React Lazy和Suspense来创建一个后备加载组件.这很好用,但后备只显示几毫秒.有没有办法添加额外的延迟或最短时间,所以我可以在渲染下一个组件之前显示此组件的动画?
现在懒惰导入
const Home = lazy(() => import("./home"));
const Products = lazy(() => import("./home/products"));
Run Code Online (Sandbox Code Playgroud)
等待组件:
function WaitingComponent(Component) {
return props => (
<Suspense fallback={<Loading />}>
<Component {...props} />
</Suspense>
);
}
Run Code Online (Sandbox Code Playgroud)
我可以这样做吗?
const Home = lazy(() => {
setTimeout(import("./home"), 300);
});
Run Code Online (Sandbox Code Playgroud)
Est*_*ask 19
lazy函数应该{ default: ... }返回由import()具有默认导出的模块返回的对象的promise .setTimeout没有回复承诺,也不能像那样使用.任意承诺可以:
const Home = lazy(() => {
return new Promise(resolve => {
setTimeout(() => resolve(import("./home")), 300);
});
});
Run Code Online (Sandbox Code Playgroud)
如果目标是提供最小延迟,这不是一个好的选择,因为这会导致额外的延迟.
最小延迟是:
const Home = lazy(() => {
return Promise.all([
import("./home"),
new Promise(resolve => setTimeout(resolve, 300))
])
.then(([moduleExports]) => moduleExports);
});
Run Code Online (Sandbox Code Playgroud)
小智 13
正如 loopmode 所提到的,组件回退应该有一个超时。
import React, { useState, useEffect } from 'react'
const DelayedFallback = () => {
const [show, setShow] = useState(false)
useEffect(() => {
let timeout = setTimeout(() => setShow(true), 300)
return () => {
clearTimeout(timeout)
}
}, [])
return (
<>
{show && <h3>Loading ...</h3>}
</>
)
}
export default DelayedFallback
Run Code Online (Sandbox Code Playgroud)
然后只需导入该组件并将其用作后备。
<Suspense fallback={<DelayedFallback />}>
<LazyComponent />
</Suspense>
Run Code Online (Sandbox Code Playgroud)
Suspense使用和 的后备组件动画lazy@Akrom Sprinter 在加载时间过快的情况下有一个很好的解决方案,因为它隐藏了后备微调器并避免了整体延迟。这是 OP 请求的更复杂动画的扩展:
\n\nconst App = () => {\r\n const [isEnabled, setEnabled] = React.useState(false);\r\n return (\r\n <div>\r\n <button onClick={() => setEnabled(b => !b)}>Toggle Component</button>\r\n <React.Suspense fallback={<Fallback />}>\r\n {isEnabled && <Home />}\r\n </React.Suspense>\r\n </div>\r\n );\r\n};\r\n\r\nconst Fallback = () => {\r\n const containerRef = React.useRef();\r\n return (\r\n <p ref={containerRef} className="fallback-fadein">\r\n <i className="fa fa-spinner spin" style={{ fontSize: "64px" }} />\r\n </p>\r\n );\r\n};\r\n\r\n/*\r\n Technical helpers\r\n*/\r\n\r\nconst Home = React.lazy(() => fakeDelay(2000)(import_("./routes/Home")));\r\n\r\n// import_ is just a stub for the stack snippet; use dynamic import in real code.\r\nfunction import_(path) {\r\n return Promise.resolve({ default: () => <p>Hello Home!</p> });\r\n}\r\n\r\n// add some async delay for illustration purposes\r\nfunction fakeDelay(ms) {\r\n return promise =>\r\n promise.then(\r\n data =>\r\n new Promise(resolve => {\r\n setTimeout(() => resolve(data), ms);\r\n })\r\n );\r\n}\r\n\r\nReactDOM.render(<App />, document.getElementById("root"));Run Code Online (Sandbox Code Playgroud)\r\n/* Delay showing spinner first, then gradually let it fade in. */\r\n.fallback-fadein {\r\n visibility: hidden;\r\n animation: fadein 1.5s;\r\n animation-fill-mode: forwards;\r\n animation-delay: 0.5s; /* no spinner flickering for fast load times */\r\n}\r\n\r\n@keyframes fadein {\r\n from {\r\n visibility: visible;\r\n opacity: 0;\r\n }\r\n to {\r\n visibility: visible;\r\n opacity: 1;\r\n }\r\n}\r\n\r\n.spin {\r\n animation: spin 2s infinite linear;\r\n}\r\n\r\n@keyframes spin {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n 100% {\r\n transform: rotate(359deg);\r\n }\r\n}Run Code Online (Sandbox Code Playgroud)\r\n<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>\r\n<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>\r\n<link\r\n rel="stylesheet"\r\n href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"\r\n/>\r\n<div id="root"></div>Run Code Online (Sandbox Code Playgroud)\r\n您只需向组件添加一些@keyframes动画Fallback,并通过setTimeout状态标志或纯 CSS(animation-fill-mode并-delay在此处使用)延迟其显示。
这是可能的,但需要一个包装器。在卸载组件之前,我们没有直接的 API 来等待Suspense淡出动画。Fallback
让我们创建一个自定义useSuspenseAnimationHook,将承诺延迟足够React.lazy长的时间,以便我们的结束动画完全可见:
// inside useSuspenseAnimation\nconst DeferredHomeComp = React.lazy(() => Promise.all([\n import("./routes/Home"), \n deferred.promise // resolve this promise, when Fallback animation is complete\n ]).then(([imp]) => imp)\n)\nRun Code Online (Sandbox Code Playgroud)\n\n// inside useSuspenseAnimation\nconst DeferredHomeComp = React.lazy(() => Promise.all([\n import("./routes/Home"), \n deferred.promise // resolve this promise, when Fallback animation is complete\n ]).then(([imp]) => imp)\n)\nRun Code Online (Sandbox Code Playgroud)\r\nconst App = () => {\r\n const { DeferredComponent, ...fallbackProps } = useSuspenseAnimation(\r\n "./routes/Home"\r\n );\r\n const [isEnabled, setEnabled] = React.useState(false);\r\n return (\r\n <div>\r\n <button onClick={() => setEnabled(b => !b)}>Toggle Component</button>\r\n <React.Suspense fallback={<Fallback {...fallbackProps} />}>\r\n {isEnabled && <DeferredComponent />}\r\n </React.Suspense>\r\n </div>\r\n );\r\n};\r\n\r\nconst Fallback = ({ hasImportFinished, enableComponent }) => {\r\n const ref = React.useRef();\r\n React.useEffect(() => {\r\n const current = ref.current;\r\n current.addEventListener("animationend", handleAnimationEnd);\r\n return () => {\r\n current.removeEventListener("animationend", handleAnimationEnd);\r\n };\r\n\r\n function handleAnimationEnd(ev) {\r\n if (ev.animationName === "fadeout") {\r\n enableComponent();\r\n }\r\n }\r\n }, [enableComponent]);\r\n\r\n const classes = hasImportFinished ? "fallback-fadeout" : "fallback-fadein";\r\n\r\n return (\r\n <p ref={ref} className={classes}>\r\n <i className="fa fa-spinner spin" style={{ fontSize: "64px" }} />\r\n </p>\r\n );\r\n};\r\n\r\n/* \r\nPossible State transitions: LAZY -> IMPORT_FINISHED -> ENABLED\r\n- LAZY: React suspense hasn\'t been triggered yet.\r\n- IMPORT_FINISHED: dynamic import has completed, now we can trigger animations.\r\n- ENABLED: Deferred component will now be displayed \r\n*/\r\nfunction useSuspenseAnimation(path) {\r\n const [state, setState] = React.useState(init);\r\n\r\n const enableComponent = React.useCallback(() => {\r\n if (state.status === "IMPORT_FINISHED") {\r\n setState(prev => ({ ...prev, status: "ENABLED" }));\r\n state.deferred.resolve();\r\n }\r\n }, [state]);\r\n\r\n return {\r\n hasImportFinished: state.status === "IMPORT_FINISHED",\r\n DeferredComponent: state.DeferredComponent,\r\n enableComponent\r\n };\r\n\r\n function init() {\r\n const deferred = deferPromise();\r\n // component object reference is kept stable, since it\'s stored in state.\r\n const DeferredComponent = React.lazy(() =>\r\n Promise.all([\r\n // again some fake delay for illustration\r\n fakeDelay(2000)(import_(path)).then(imp => {\r\n // triggers re-render, so containing component can react\r\n setState(prev => ({ ...prev, status: "IMPORT_FINISHED" }));\r\n return imp;\r\n }),\r\n deferred.promise\r\n ]).then(([imp]) => imp)\r\n );\r\n\r\n return {\r\n status: "LAZY",\r\n DeferredComponent,\r\n deferred\r\n };\r\n }\r\n}\r\n\r\n/*\r\ntechnical helpers\r\n*/\r\n\r\n// import_ is just a stub for the stack snippet; use dynamic import in real code.\r\nfunction import_(path) {\r\n return Promise.resolve({ default: () => <p>Hello Home!</p> });\r\n}\r\n\r\n// add some async delay for illustration purposes\r\nfunction fakeDelay(ms) {\r\n return promise =>\r\n promise.then(\r\n data =>\r\n new Promise(resolve => {\r\n setTimeout(() => resolve(data), ms);\r\n })\r\n );\r\n}\r\n\r\nfunction deferPromise() {\r\n let resolve;\r\n const promise = new Promise(_resolve => {\r\n resolve = _resolve;\r\n });\r\n return { resolve, promise };\r\n}\r\n\r\nReactDOM.render(<App />, document.getElementById("root"));Run Code Online (Sandbox Code Playgroud)\r\n/* Delay showing spinner first, then gradually let it fade in. */\r\n.fallback-fadein {\r\n visibility: hidden;\r\n animation: fadein 1.5s;\r\n animation-fill-mode: forwards;\r\n animation-delay: 0.5s; /* no spinner flickering for fast load times */\r\n}\r\n\r\n@keyframes fadein {\r\n from {\r\n visibility: visible;\r\n opacity: 0;\r\n }\r\n to {\r\n visibility: visible;\r\n opacity: 1;\r\n }\r\n}\r\n\r\n.fallback-fadeout {\r\n animation: fadeout 1s;\r\n animation-fill-mode: forwards;\r\n}\r\n\r\n@keyframes fadeout {\r\n from {\r\n opacity: 1;\r\n }\r\n to {\r\n opacity: 0;\r\n }\r\n}\r\n\r\n.spin {\r\n animation: spin 2s infinite linear;\r\n}\r\n\r\n@keyframes spin {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n 100% {\r\n transform: rotate(359deg);\r\n }\r\n}Run Code Online (Sandbox Code Playgroud)\r\n1.) useSuspenseAnimationHook 返回三个值:
hasImportFinished( boolean) \xe2\x86\x92 如果true,则Fallback可以开始其淡出动画enableComponent(回调)\xe2\x86\x92 调用它来卸载Fallback在动画完成时调用它来 unmount 。DeferredComponent\xe2\x86\x92 动态加载的扩展惰性组件import2.) 听animationendDOM 事件,以便我们知道动画何时结束。
| 归档时间: |
|
| 查看次数: |
3117 次 |
| 最近记录: |