使用 React Loadable 进行组件和路由代码分割

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费信用额度!

为了让 Web 应用程序满足不同类型用户的需求,应用程序可能需要比一个用户类型更多的代码,以便应用程序可以处理和适应不同的场景和用例,从而带来新的功能。当这种情况发生时,随着代码库的增长,应用程序的性能下降是可以预料的。

代码分割是一种技术,它允许应用程序仅在需要时加载所需的代码,而不是加载更多代码。例如,当用户导航到主页时,可能不需要加载为后端仪表板提供支持的代码。使用代码分割,我们可以确保仅加载主页的代码,并将无关代码排除在外,以获得更优化的加载体验。

可以使用 React Loadable 在 React 应用程序中实现代码分割。它提供了一个 高阶组件,可以配置为在特定时间动态导入特定组件。

组件分割

在某些情况下,我们可能希望根据用户事件有条件地渲染组件,例如,当用户登录帐户时。处理此问题的常用方法是利用状态 - 组件根据应用程序的登录状态进行渲染。我们称之为组件分割

让我们看看代码示例。

查看 Pen
React-Loadable
由 Kingsley Silas Chijioke (@kinsomicrote)
CodePen 上。

作为一个基本示例,假设我们想要有条件地渲染一个包含<h2> 标题“Hello”的组件。如下所示

const Hello = () => {
	return (
		<React.Fragment>
			<h2>Hello</h2>
		</React.Fragment>
	)
}

我们在 App 组件中可以有一个openHello 状态,初始值为false。然后,我们可以使用一个按钮来切换状态,显示或隐藏组件。我们将把它放在handleHello 方法中,如下所示

class App extends React.Component {
	state = {
		openHello: false
	}

	handleHello = () => {
		this.setState({ openHello: !this.state.openHello })
	}
	render() {
		return (
			<div className="App">
				<button onClick={this.handleHello}>
					Toggle Component
				</button>

				{
					this.state.openHello ?
						<Hello />
					: null
				}
			</div>
		);
	}
}

快速浏览 DevTools 并注意网络选项卡

现在,让我们重构以使用 LoadableHello。我们不会直接导入组件,而是使用 Loadable 进行导入。我们将首先安装 react-loadable 包

## yarn, npm or however you roll
yarn add react-loadable

现在它已添加到我们的项目中,我们需要将其导入应用程序

import Loadable from 'react-loadable';

我们将使用 Loadable 创建一个“加载”组件,它将如下所示

const LoadableHello = Loadable({
	loader: () => import('./Hello'),
	loading() {
		return <div>Loading...</div>
	}
})

我们将一个函数作为值传递给loader,该函数返回我们之前创建的Hello 组件,并使用import() 动态导入它。我们想要在导入组件之前渲染的回退 UI 由loading() 返回。在这个例子中,我们返回一个div 元素,但如果需要,我们也可以在其中放入一个组件。

现在,我们不再直接在App 组件中输入Hello 组件,而是将LoadableHello 放在那里,以便条件语句如下所示

{
	this.state.openHello ?
		<LoadableHello />
	: null
}

看看 - 现在我们的Hello 组件只有在通过按钮切换状态时才会加载到 DOM 中

这就是组件分割:能够异步加载一个组件来加载另一个组件!

基于路由的分割

好的,我们已经了解了如何使用 Loadable 通过其他组件加载组件。另一种方法是使用基于路由的分割。这里的区别在于组件根据当前路由加载。

因此,假设用户位于应用程序的主页,并点击了一个路由为/hello 的 Hello 视图。属于该路由的组件将是唯一加载的组件。这是一种在许多应用程序中处理分割的相当普遍的方式,并且通常效果很好,尤其是在不太复杂的应用程序中。

这是一个应用程序中定义路由的基本示例。在这种情况下,我们有两个路由:(1) 主页 (/) 和 (2) Hello (/hello)。

class App extends Component {
	render() {
		return (
			<div className="App">
				<BrowserRouter>
					<div>
						<Link to="/">Home</Link>
						<Link to="/hello">Hello</Link>
						<Switch>
							<Route exact path="/" component={Home} />
							<Route path="/hello" component={Hello} />
						</Switch>
					</div>
				</BrowserRouter>
			</div>
		);
	}
}

就目前而言,当用户切换路径时,所有组件都将渲染,即使我们希望根据该路径渲染一个Hello 组件。当然,如果我们只讨论几个组件,这并不是什么大问题,但随着组件的增加和应用程序规模的增长,这种情况肯定会变得更加严重。

使用 Loadable,我们可以通过为每个组件创建可加载的组件来仅导入我们想要的组件

const LoadableHello = Loadable({
	loader: () => import('./Hello'),
	loading() {
		return <div>Loading...</div>
	}
})
const LoadableHome = Loadable({
	loader: () => import('./Home'),
	loading() {
		return <div>Loading...</div>
	}
})
class App extends Component {
	render() {
		return (
			<div className="App">
				<BrowserRouter>
					<div>
						<Link to="/">Home</Link>
						<Link to="/hello">Hello</Link>
						<Switch>
							<Route exact path="/" component={LoadableHome} />
							<Route path="/hello" component={LoadableHello} />
						</Switch>
					</div>
				</BrowserRouter>
			</div>
		);
	}
}

现在,我们在正确的时间提供正确的代码。感谢 Loadable!

错误和延迟怎么办?

如果导入的组件加载速度很快,则不需要闪烁“加载”组件。值得庆幸的是,Loadable 具有延迟显示加载组件的功能。这有助于防止它过早地显示,在那里它会显得愚蠢,而是在预期看到它加载后的一段显著时间过去后显示它。

为此,我们的示例 Loadable 组件将如下所示;

const LoadableHello = Loadable({
	loader: () => import('./Hello'),
	loading: Loader,
	delay: 300
})

在这里,我们将Hello 组件作为值传递给loading,它通过loader 导入。默认情况下,delay 设置为 200ms,但我们将我们的设置稍晚一些,为 300ms。

现在让我们在Loader 组件中添加一个条件,告诉它仅在我们设置的 300ms 延迟过去后才显示加载程序

const Loader = (props) => {
	if (props.pastDelay) {
		return <h2>Loading...</h2>
	} else {
		return null
	}
}

因此,Loader 组件仅在Hello 组件在 300ms 后没有显示时才会显示。

react-loader 还提供了一个error 属性,我们可以用它来返回遇到的错误。而且,因为它是一个属性,我们可以让它输出我们想要的任何内容。

const Loader = (props) => {
	if (props.error) {
		return <div>Oh no, something went wrong!</div>;
	} else if (props.delay) {
		return <h2>Loading...</h2>
	} else {
		return null;
	}
}

请注意,我们实际上将延迟和错误处理结合在一起!如果一开始就出现错误,我们将显示一些消息。如果没有错误,但 300ms 已过去,那么我们将显示一个加载程序。否则,请加载Hello 组件!

总结

我们现在能够更加自由灵活地加载和显示代码,难道不棒吗?代码分割 - 无论是通过组件还是通过路由 - 都是 React 的设计理念之一。React 允许我们编写包含隔离代码的模块化组件,我们可以随时随地提供这些组件,并允许它们与 DOM 和其他组件交互。非常酷!

希望这能让你对代码分割这个概念有一个很好的了解。当您开始动手实践并开始使用它时,值得查看更深入的文章,以更深入地了解这个概念。