代碼分割
-
打包
大多數 React 應用都會使用 Webpack,Rollup 或 Browserify 這類的構建工具來打包文件。 打包是一個將文件引入併合併到一個單獨文件的過程,最終形成一個 “bundle”。 接着在頁面上引入該 bundle,整個應用即可一次性加載。 -
代碼分割
隨着應用增長,代碼包也將隨之增長。尤其是在整合了體積巨大的第三方庫的情況下。你需要關注你代碼包中所包含的代碼,以避免因體積過大而導致加載時間過長。
需要代碼分割
代碼分割是由諸如 Webpack,Rollup 和 Browserify(factor-bundle)這類打包器支持的一項技術,能夠創建多個包並在運行時動態加載。
對你的應用進行代碼分割能夠幫助你“懶加載”當前用戶所需要的內容,能夠顯著地提高你的應用性能。儘管並沒有減少應用整體的代碼體積,但你可以避免加載用戶永遠不需要的代碼,並在初始加載的時候減少所需加載的代碼量。
import()
使用之前:
import { add } from './math';
console.log(add(16, 26));
使用之後:
import("./math").then(math => {
console.log(math.add(16, 26));
});
當 Webpack 解析到該語法時,會自動進行代碼分割。如果你使用 Create React App,該功能已開箱即用,你可以立刻使用該特性。Next.js 也已支持該特性而無需進行配置。
React.lazy
React.lazy 和 Suspense 技術還不支持服務端渲染。如果你想要在使用服務端渲染的應用中使用,我們推薦 Loadable Components 這個庫。它有一個很棒的服務端渲染打包指南。
React.lazy
函數能讓你像渲染常規組件一樣處理動態引入(的組件)。
使用之前:
import OtherComponent from './OtherComponent';
使用之後:
const OtherComponent = React.lazy(() => import('./OtherComponent'));
此代碼將會在組件首次渲染時,自動導入包含 OtherComponent 組件的包。
React.lazy 接受一個函數,這個函數需要動態調用 import()。它必須返回一個 Promise,該 Promise 需要 resolve 一個 defalut export 的 React 組件。
然後應在 Suspense 組件中渲染 lazy 組件,如此使得我們可以使用在等待加載 lazy 組件時做優雅降級(如 loading 指示器等)。
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
fallback 屬性接受任何在組件加載過程中你想展示的 React 元素。你可以將 Suspense 組件置於懶加載組件之上的任何位置。你甚至可以用一個 Suspense 組件包裹多個懶加載組件。
-
異常捕獲邊界(
Error boundaries
)
如果模塊加載失敗(如網絡問題),它會觸發一個錯誤。你可以通過異常捕獲邊界(Error boundaries)技術來處理這些情況,以顯示良好的用戶體驗並管理恢復事宜。 -
基於路由的代碼分割
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)
Context
錯誤邊界
錯誤邊界是一種 React 組件,這種組件可以捕獲並打印發生在其子組件樹任何位置的 JavaScript 錯誤,並且,它會渲染出備用 UI,而不是渲染那些崩潰了的子組件樹。錯誤邊界在渲染期間、生命週期方法和整個組件樹的構造函數中捕獲錯誤。
注意
錯誤邊界無法捕獲以下場景中產生的錯誤:
1.事件處理(瞭解更多)
2.異步代碼(例如 setTimeout 或 requestAnimationFrame 回調函數)
3.服務端渲染
4.它自身拋出來的錯誤(並非它的子組件)
如果一個 class 組件中定義了 static getDerivedStateFromError() 或 componentDidCatch() 這兩個生命週期方法中的任意一個(或兩個)時,那麼它就變成一個錯誤邊界。當拋出錯誤後,請使用 static getDerivedStateFromError() 渲染備用 UI ,使用 componentDidCatch() 打印錯誤信息。
== 自 React 16 起,任何未被錯誤邊界捕獲的錯誤將會導致整個 React 組件樹被卸載。==
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能夠顯示降級後的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你同樣可以將錯誤日誌上報給服務器
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定義降級後的 UI 並渲染
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
使用:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>