在react开发中,我们经常需要优化应用性能。其中一个重要手段就是代码分割和懒加载。React提供的React.lazy就是专门用于这个目的的工具。它能帮助我们按需加载组件,减少初始包体积,提升用户体验。
React.lazy是React官方提供的api,用于实现组件的懒加载。它让我们能够动态导入组件,只有在组件真正需要显示时才加载对应的代码。
先看一个基本的使用示例:
import React, { Suspense } from 'react';
// 使用React.lazy动态导入组件
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>加载中...</div>}>
<LazyComponent />
</Suspense>
);
}这里有几个关键点:
要理解React.lazy的原理,最好的方法就是自己实现一个。下面我们来创建一个myLazy函数:
function myLazy(loadComponent) {
// 保存加载状态和结果
let component = null;
let error = null;
let loaded = false;
let loadingPromise = null;
// 开始加载组件
loadingPromise = loadComponent()
.then((module) => {
component = module.default; // 获取默认导出的组件
loaded = true; // 标记为已加载
})
.catch((err) => {
error = err; // 保存错误信息
});
// 返回一个特殊的React组件
function MyLazyComponent(props) {
if (error) {
throw error; // 抛出错误,让错误边界捕获
}
if (loaded) {
// 如果已加载,正常渲染组件
return React.createElement(component, props);
}
// 如果还在加载中,抛出Promise
// 这会被Suspense捕获
throw loadingPromise;
}
return MyLazyComponent;
}使用我们自定义的myLazy:
import React, { Suspense } from 'react';
// 使用自定义的myLazy
const LazyComponent = myLazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>组件加载中,请稍候...</div>}>
<LazyComponent />
</Suspense>
);
}动态import()的作用
动态import()是ES6模块的语法,它返回一个Promise。当模块加载完成后,Promise会resolve,我们可以获得模块的导出内容。
// 动态import返回Promise
import('./MyComponent.js')
.then(module => {
// module.default就是默认导出的组件
const Component = module.default;
});Suspense的工作机制
Suspense是React的另一个重要特性。它的作用是:
监听子组件树中抛出的Promise
在Promise未完成时显示fallback内容
当Promise完成后重新渲染子组件
这里的关键在于"抛出Promise"的概念。在常规JavaScript中,我们通常不会throw一个Promise,但React内部对此有特殊处理。
React内部的特殊处理
当组件在渲染过程中抛出Promise时,React会:
暂停当前组件的渲染
向上查找最近的Suspense组件
显示Suspense的fallback内容
等待Promise完成
重新尝试渲染被暂停的组件
这个过程类似于异常处理,但是专门为异步操作设计的。
错误处理
懒加载可能失败,比如网络问题。我们需要错误边界来捕获这些错误:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <div>组件加载失败</div>;
}
return this.props.children;
}
}
// 使用错误边界包裹Suspense
<ErrorBoundary>
<Suspense fallback={<div>加载中...</div>}>
<LazyComponent />
</Suspense>
</ErrorBoundary>命名导出的处理
如果组件不是默认导出,需要稍作调整:
// 对于命名导出
const LazyComponent = myLazy(() =>
import('./LazyComponent').then(module => ({
default: module.NamedComponent
}))
);预加载优化
在某些场景下,我们可以提前开始加载组件:
// 提前开始加载
const componentPromise = import('./LazyComponent');
const LazyComponent = myLazy(() => componentPromise);
// 在需要的时候再渲染
function HomePage() {
const [showComponent, setShowComponent] = useState(false);
const handleClick = () => {
// 点击按钮时显示组件
setShowComponent(true);
};
return (
<div>
<button onClick={handleClick}>显示组件</button>
{showComponent && (
<Suspense fallback={<div>加载中...</div>}>
<LazyComponent />
</Suspense>
)}
</div>
);
}路由级别的代码分割
在大型应用中,我们通常按路由进行代码分割:
import React, { Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = React.lazy(() => import('./routes/Home'));
const About = React.lazy(() => import('./routes/About'));
const Contact = React.lazy(() => import('./routes/Contact'));
function App() {
return (
<Router>
<div className="app">
<Suspense fallback={<div>页面加载中...</div>}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
</Suspense>
</div>
</Router>
);
}加载状态优化
我们可以创建更友好的加载状态:
function ProgressiveFallback() {
return (
<div className="skeleton-loader">
<div className="skeleton-header"></div>
<div className="skeleton-content"></div>
<div className="skeleton-content"></div>
</div>
);
}
// 使用渐进式加载状态
<Suspense fallback={<ProgressiveFallback />}>
<LazyComponent />
</Suspense>通过自己实现React.lazy,我们可以更深入地理解其工作原理:
核心机制:基于动态import()和Suspense的Promise捕获机制
关键步骤:在组件未加载完成时抛出Promise,由Suspense处理加载状态
性能价值:实现真正的按需加载,减少初始包大小
这种实现方式展示了React设计的巧妙之处。它利用现有的JavaScript特性(Promise、动态导入)和React自身的渲染机制,构建了一个强大而灵活的懒加载方案。
在实际项目中,合理使用懒加载可以显著提升应用性能。特别是在大型单页应用中,将不同路由和功能模块进行代码分割,能够有效减少首屏加载时间,改善用户体验。
理解这些底层原理不仅有助于我们更好地使用React,也为我们解决其他复杂问题提供了思路。当我们明白工具背后的工作原理时,就能更自信地使用它们,并在遇到问题时快速找到解决方案。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
javascript中alert是Bom中的成员函数,alert对话框是模态的,具有阻塞性质的,不点击是不会执行后续代码的。js的阻塞是指在调用结果返回之前,当前线程会被挂起, 只有在得到结果之后才会继续执行。
如何优化async代码?更好的编写async函数:使用return Promise.reject()在async函数中抛出异常,让相互之间没有依赖关系的异步函数同时执行,不要在循环的回调中/for、while循环中使用await,用map来代替它
Javascript语言的执行环境是单线程,异步模式非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。
js异步加载又被称为非阻塞加载,浏览器在下载JS的同时,还会进行后续页面处理。那么如何实现js异步加载呢?下面整理了多种实现方案供大家参考。异步加载js方案:Script Dom Element、onload时的异步加载、$(document).ready()、async属性、defer属性、es6模块type=module属性
回调函数方式:将异步方法如readFile封装到一个自定义函数中,通过将异步方法得到的结果传给自定义方法的回调函数参数。事件驱动方式:使用node events模块,利用其EventEmitter对象
JavaScript引擎是基于单线程 (Single-threaded) 事件循环的概念构建的,同一时刻只允许一个代码块在执行,所以需要跟踪即将运行的代码,那些代码被放在一个任务队列 (job queue) 中
传统的异步解决方案采用回调函数和事件监听的方式,而这里主要记录两种异步编程的新方案:ES6的新语法Promise;ES2017引入的async函数;Generator函数(略)
JS本身是一门单线程的语言,所以在执行一些需要等待的任务(eg.等待服务器响应,等待用户输入等)时就会阻塞其他代码。如果在浏览器中JS线程阻塞了,浏览器可能会失去响应,从而造成不好的用户体验。
请实现如下的函数,可以批量请求数据,所有的URL地址在urls参数中,同时可以通过max参数 控制请求的并发度。当所有的请求结束后,需要执行callback回调。发请求的函数可以直接使用fetch。
将setState()认为是一次请求而不是一次立即执行更新组件的命令。为了更为可观的性能,React可能会推迟它,稍后会一次性更新这些组件。React不会保证在setState之后,能够立刻拿到改变的结果。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!