在以往的性能优化清单中,减小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