对 Redux 应用程序进行代码分割
介绍
现代网站通常将所有 JavaScript 合并到一个大型包中。以这种方式提供 JavaScript 时,加载性能会受到影响。大量 JavaScript 还会占用主线程,从而延迟交互性。对于内存和处理能力较弱的设备来说,情况尤其如此。
大型包的替代方案是代码分割,即将 JavaScript 分割成较小的块。这样可以发送提供价值所需的最少代码,从而缩短页面加载时间。其余部分可以按需加载。
在本指南中,我们将在React、Redux和React-Redux应用程序的背景下讨论代码分割。
本指南假设您对 React、Redux 和 React-Redux 有一定程度的熟悉。
什么是代码分割?
代码分割是推迟导入部分 JavaScript 直到稍后通过用户交互(例如按钮单击、滚动、键入等)需要它的行为。
代码分割有助于减少使我们的应用程序尽快加载所需的 JavaScript 数量,从而最大限度地提高用户参与度并改善页面加载时间。
代码分割是Webpack、Rollup和Browserify(通过factor-bundle)等捆绑程序支持的功能,它可以创建可在运行时动态加载的多个捆绑包。
React 组件中的代码分割
在 React 组件中引入代码分割的首选方法是通过动态 import()。import()函数形式将模块名称作为参数并返回一个Promise,该 Promise 始终解析为模块的命名空间对象。
import('./Module').then(Module => Module.method())
// SomeComponent.js
const SomeComponent = () => <p>This is a test component</p>
export default SomeComponent
// App.js
import React, { Component } from 'react'
class App extends Component {
handleClick = () => {
import('./SomeComponent')
.then(({ SomeComponent }) => {
// Use SomeComponent
})
.catch(err => {
// Handle failure
});
};
render() {
return (
<div>
<button onClick={this.handleClick}>Click Me</button>
</div>
);
}
}
export default App;
上面的例子将SomeComponent.js作为单独的块包含在内,仅在单击“Click Me”按钮后才会加载。
React.lazy方法可以轻松地使用动态导入在组件级别对 React 应用程序进行代码拆分。
React.lazy接受一个必须调用动态import()的函数。该函数必须返回一个Promise,该 Promise 解析为一个默认导出的模块,该模块包含一个 React 组件。
const SomeComponent = React.lazy(() => import('./SomeComponent'));
然后应该在Suspense组件内部呈现惰性组件,这允许我们在等待惰性组件加载时显示一些后备内容(例如加载指示器)。
const SomeComponent = React.lazy(() => import('./SomeComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<SomeComponent />
</Suspense>
</div>
);
}
Suspense fallback props 接受在等待组件加载时正在渲染的任何 React 元素。我们可以将Suspense组件放置在惰性组件上方的任何位置。我们甚至可以用单个Suspense组件包装多个惰性组件。
const SomeComponent = React.lazy(() => import('./SomeComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<SomeComponent />
<AnotherComponent />
</Suspense>
</div>
);
}
React.lazy目前仅支持默认导出。
基于路由的代码分割
这是一个如何使用React Router和React.lazy等库在我们的应用程序中设置基于路由的代码拆分的示例。
// App.js
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</Switch>
</Suspense>
</Router>
);
使用 Redux 进行代码分割
从前面的部分,我们已经能够演示如何动态加载我们的 React 组件,但我们仍然需要在加载时将正确的数据放入我们的模块中。
Redux 作为一个状态管理库,允许我们在使用createStore函数创建 store 时提供 Reducer 函数,但不允许我们按需注册 Reducer 函数。那么我们该如何实现呢?
事实证明,Redux 存储 API 公开了一个replaceReducer函数,该函数用新的根 Reducer 函数替换当前活动的根 Reducer 函数。
// store.js
import { combineReducers, createStore } from "redux"
const initialState = {}
const store = createStore(createReducer(), initialState)
const newRootReducer = combineReducers({
existingSlice: existingSliceReducer,
newSlice: newSliceReducer
})
store.replaceReducer(newRootReducer)
我们可以更进一步,除了 replaceReducer 之外,还可以创建一个可重用的injectionReducer函数,该函数保留对所有现有切片减速器的引用,并将其附加到存储实例。
// reducers.js
import { combineReducers } from 'redux'
const createReducer = (asyncReducers) => {
return combineReducers({
...asyncReducers
})
}
export default createReducer
// store.js
import { createStore } from "redux"
import createReducer from "./reducers"
const store = createStore(createReducer())
export default function configureStore() {
// Add a dictionary to keep track of the registered async reducers
store.asyncReducers = {}
// Create an inject reducer function
// This function adds the async reducer, and creates a new combined reducer
store.injectReducer = (key, asyncReducer) => {
store.asyncReducers[key] = asyncReducer
store.replaceReducer(createReducer(store.asyncReducers))
}
// Return the modified store
return store
}
export function getStore() {
return store
}
注意:injectReducer不是 redux store API 的一部分。
用法如下:
// App.js
import React from "react";
import { getStore } from '../store'
const store = getStore()
const Section = React.lazy(() => import('../containers/Section')
.then(async module => {
const todos = await import('../reducers/todos')
.then(todosModule => todosModule.default)
store.injectReducer('todos', todos)
return module
}))
const App = () => (
<React.Suspense fallback={<div>loading...</div>}>
<MainSection />
</React.Suspense>
);
export default App;
一些值得一试的 Redux 库:
下面的链接指向一个使用React.lazy、React.Suspense和Redux的 todo 应用程序的工作示例。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~