在以往的性能優化清單中,減小HTTP請求一直是其中的一項。事實上,也作到了。在目前的大多數應用開發中都會用到 webpack 進行應用的構建打包,從而將咱們應用以及應用所需的模塊打包成一個大 bundle。這樣也確實是減小了HTTP的請求。可是這樣作有幾個明顯的劣勢:react
先看一下webpack
進行代碼分離的幾種方法:webpack
entry
進行分離CommonsChunkPlugin
去重和分離chunk
entry: { app: './src/entry.js', vendor: ['react', 'react-dom'] },
對於一些不常變更的庫,好比框架,第三方依賴等等,能夠直接在入口就進行代碼的分離,就比如這裏的react
、react-dom
git
CommonsChunkPlugin
這個插件能夠將公共模塊依賴提取到已有的chunk中,好比下面這樣github
new webpack.optimize.CommonsChunkPlugin({ name: 'common', }),
固然,這個插件在webpack
4.0以上的版本被廢棄掉了,具體的能夠查看SplitChunksPluginweb
import()
來進行動態導入import()
的相關文檔能夠查看import()相關文檔redux
固然,使用import()
須要用到babel-plugin-syntax-dynamic-import
插件api
import()
的使用以下:性能優化
import('lodash').then(_ => { // do something })
但這樣寫的話,每次都很麻煩,這裏有一個高階組件來處理這個事情babel
import React from 'react'; export default function asyncComponent(importComponent) { class AsyncComponent extends React.Component { constructor(props){ super(props); this.state = { Component: null, } } componentDidMount() { importComponent().then(module => { // const { default: component } = res; this.setState({ Component: module.default }); }); } render(){ const { Component } = this.state; return Component ? <Component { ...this.props } /> : null; } } return AsyncComponent; }
有了這個高階組件,使用就方便了:網絡
const Result = asyncComponent(() => import('./container/result/result'))
import
規範不容許控制模塊的名稱或其餘的屬性,由於chunks
是webpack
的概念。若是像上面那樣直接使用的話,輸出的就像下面這樣
好在webpack
能夠註釋接受一些特殊的參數,但還要在配置文件的output
加上chunkFilename
// 第一處修改 const Result = asyncComponent(() => import(/* webpackChunkName: "result" */ './container/result/result')) // 第二處修改 output: { path: '/', publicPath: '/', chunkFilename: '[name].bundle.js', filename: '[name].[hash].js', },
這樣,上面的模塊就會被命名爲result.bundle.js
,而不是[id].bundle.js
。
其實作到這一步,就已經作到了代碼拆分。可是有一個比較好的庫更適合作這個事情,這個庫就是react-loadable
,使用以下:
const Home = Loadable({ loader: () => import(/* webpackChunkName: "Home" */ './container/Home/home'), loading: () => { return <div>loading</div> }, })
有的組件加載實在過快(<200ms),那麼這個咱們能夠給一Loading
組件來設置延遲時間
function Loading(props) { if (props.error) { return <div>Error! <button onClick={ props.retry }>Retry</button></div>; } else if (props.pastDelay) { return <div>Loading...</div>; } else { return null; } }
接着使用react-loadable
const Home = Loadable({ loader: () => import(/* webpackChunkName: "Home" */ './container/Home/home'), loading: Loading, delay: 200, // 200ms內加載出來的話,不顯示加載狀態 })
想了解更多react-loadable
,能夠點擊react-loadable
import React from 'react'; import { Route, Router, BrowserRouter, HashRouter, Switch, Redirect } from 'react-router-dom'; import { ConnectedRouter } from 'react-router-redux'; import createHistory from 'history/createHashHistory'; import App from './container/app'; import Loadable from 'react-loadable'; const Home = Loadable({ loader: () => import(/* webpackChunkName: "Home" */ './container/Home/home'), loading: () => { return <div>loading</div> } }) const Result = Loadable({ loader: () => import(/* webpackChunkName: "result" */ './container/result/result'), loading: () => { return <div>loading...</div> } }) const history = createHistory(); class Routers extends React.Component { render() { return ( <HashRouter> <App> <Switch> <Route exact path="/" render={() => (<Redirect to="/home" />)} /> <Route path="/home" component={Home} /> <Route path="/second" component={Result} /> </Switch> </App> </HashRouter> ) } } export default Routers;
代碼拆分有兩種思路,一種是基於路由拆分,一種是基於組件拆分
左邊這幅圖是基於路由拆分的示意圖,右邊的是基於組件拆分的示意圖,能夠看到基於組件進行拆分的粒度更細一點。
考慮這樣一個場景,你有多個tab,可能有一些tab你從進入應用到離開應用都不會去點擊它,那麼每一個tab頁面下的組件就很適合進行代碼拆分。這徹底根據你的應用場景來決定的。
若是你想對你的一些組件進行拆分,也是一樣的使用react-loadable