React 按需加載 - 代碼分隔
代碼分隔
我們現在大多數React
項目都是以Webpack
或者 Browserify
等將一堆的jsx文件組織一起,並且由一個類似index.js
的入口文件串聯起來的單頁面web
頁面。
例如:
// math.js
export function add(a, b) {
return a + b;
}
App:
// app.js
import { add } from ‘./math.js‘;
console.log(add(16, 26)); // 42
打完包後:
function add(a, b) { return a + b; } console.log(add(16, 26)); // 42
從這個例子可以看出,打完包後將所有的js都壓縮到一個文件裏了。隨著項目越來越大,打包的文件也會越來越大,如果再引入一些第三方的js
庫,那就更龐大了。
接下來介紹一下如何將React
代碼分隔。(以下內容是16.6.0版本才支持的)
Code Splitting
會幫助你的應用實現lazy load
.
這麽做,即使沒有減少整個項目的代碼量,也會避免在項目初始加載時,加載沒必須的js
,從而使用項目性能有所提升。
import()
最簡單直接的方式就是引入動態 import
實現代碼分隔。
使用 動態 import
之前:
import { add } from ‘./math‘; console.log(add(16, 26));
使用動態 import
後:
import("./math").then(math => {
console.log(math.add(16, 26));
});
註意,動態import
並不是標準的EcmaScript,所以需要配置babel-plugin-syntax-dynamic-import
React.lazy
React.lazy
可以以一個React標準組件的方法渲然一個動態引入的組件。
以前的做法:
import OtherComponent from ‘./OtherComponent‘; function MyComponent() { return ( <div> <OtherComponent /> </div> ); }
使用 React.lazy
:
const OtherComponent = React.lazy(() => import(‘./OtherComponent‘));
function MyComponent() {
return (
<div>
<OtherComponent />
</div>
);
}
這樣在組件MyComponent
渲然的時候才加載OtherComponent
。
React.lazy
必須使用動態 import()
引入組件,必須返回一個 Promise Component
。
React.lazy
目前不支持服務端渲然
Suspense
假如在 React.lazy
時,import
失敗或者異常時,我們需要給於提示,或者一個默認的組件,我們就需要使用 Suspense
.
例如:
const OtherComponent = React.lazy(() => import(‘./OtherComponent‘));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
fallback
也是一個組件,但不能通過 動態 import
引入的組件.
Suspense
只能包裹一個單結點,如果有多個 動態 import
的組件需要放在一個 Suspense
中時,可以使用類似 React.Fragmet
包裹一下,也可以使用其他的 React
組件包裹。例如:
const OtherComponent = React.lazy(() => import(‘./OtherComponent‘));
const AnotherComponent = React.lazy(() => import(‘./AnotherComponent‘));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</div>
);
}
基於Router的代碼分隔
基於 Router
的代碼分隔,也是我們通常所說的按需加載。是我們推薦的方式。
例如:
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>
);
Named Exports
React.lazy
目前只支持 default
導出,不支持命名導出。例如,只支持:
export default () => {
return(<div>I am a Lazy component</div>);
}
如果要支持命令導出,需要重新再 export
,例如:
// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from ‘react‘;
const MyComponent = lazy(() => import("./MyComponent.js"));
推薦閱讀 《React 手稿》
React 按需加載 - 代碼分隔