和异步加载script的目的同样(异步加载script的方法),按需加载/代码切割也能够解决首屏加载的速度。javascript
若是是大文件,使用按需加载就十分合适。好比一个近1M的全国城市省市县的json文件,在我首屏加载的时候并不须要引入,而是当用户点击选项的时候才加载。若是不点击,则不会加载。就能够缩短首屏http请求的数量以及时间。css
若是是小文件,能够没必要太在乎按需加载。过多的http请求会致使性能问题。html
这里介绍React-router+Webpack
实现按需加载的功能,效果以下:
java
注意!我这里使用的是最新版本的React-router-dom^4.3.1.若是是4.0如下的react-route可直接看四react
4.0相比之前实现方式要复杂。须要引入bundle-loader模块。而且本身建立bundle模型实现。webpack
import React from 'react'; class Bundle extends React.Component { constructor(arg){ super(arg) this.state = { mod: null, } } componentWillMount() { this.load(this.props); } componentWillReceiveProps(nextProps) { if (nextProps.load !== this.props.load) { this.load(nextProps); } } // load 方法,用于更新 mod 状态 load(props) { // 初始化 this.setState({ mod: null }); /* 调用传入的 load 方法,并传入一个回调函数 这个回调函数接收 在 load 方法内部异步获取到的组件,并将其更新为 mod */ props.load(mod => { this.setState({ mod: mod.default ? mod.default : mod }); }); } render() { /* 将存在状态中的 mod 组件做为参数传递给当前包装组件的'子' */ return this.state.mod ? this.props.children(this.state.mod) : null; } } export default Bundle ;
// 懒加载方法 import React from 'react'; import Bundle from './Bundle'; console.log(Bundle); // 默认加载组件,能够直接返回 null const Loading = () => <div>Loading...</div>; /* 包装方法,第一次调用后会返回一个组件(函数式组件) 因为要将其做为路由下的组件,因此须要将 props 传入 */ const lazyLoad = loadComponent => props => ( <Bundle load={loadComponent}> {Comp => (Comp ? <Comp {...props} /> : <Loading />)} </Bundle> ); console.log(lazyLoad); export default lazyLoad; //实际上lazyLoad就是一个函数,组件调用便可
上面两个文件的关系:
lazyLoad.js从名字上看,叫懒加载.其实是一个中间件的做用。最后lazyLoad会暴露一个函数出来供组件调用。lazyLoad导出的内容:web
function lazyLoad(loadComponent) { return function(props) { return ( <Bundle load={loadComponent}> {Comp => (Comp ? <Comp {...props} /> : <Loading />)} </Bundle> ) } }
显而易见,loadComponent就是要加载的组件,在路由中调用,例如:异步调用page1组件json
<Route path="/page1" component={lazyLoad(Page1)}/>
Bundle.js做为按需加载的核心,在lazyLoad中间件就已经引入,并传入一个自定义的方法load,值为组件内容。以及动态的子内容children:react-router
{Comp => (Comp ? <Comp {...props} /> : <Loading />)}
最终返回组件信息,并附带相应的props.若是不存在相关组件,则Loading
app
import React from 'react'; import { NavLink,Route,Switch,BrowserRouter as Router } from 'react-router-dom' import './style/style.css' import 'bundle-loader' // bundle模型用来异步加载组件 import Bundle from '../routes/Bundle.js'; import lazyLoad from '../routes/lazyLoad'; import Page1 from 'bundle-loader?lazy&name=page1!../components/page1/index'; import Page2 from 'bundle-loader?lazy&name=page2!../components/page2/index'; import Page3 from 'bundle-loader?lazy&name=page3!../components/page3/index'; class AppPage extends React.Component{ constructor(arg){ super(arg) this.state={} } render(){ return( <Router basename="/" > <div className="appWried"> <div className="appBtn"> <NavLink to="/page1" className="button" activeClassName="active"> PAGE1 </NavLink> <NavLink to="/page2" className="button" activeClassName="active"> PAGE2 </NavLink> <NavLink to="/page3" className="button" activeClassName="active"> PAGE3 </NavLink> </div> <Route path="/" render={props => ( <Switch> <Route path="/page1" component={lazyLoad(Page1)}/> <Route path="/page2" component={lazyLoad(Page2)}/> <Route path="/page3" component={lazyLoad(Page3)}/> </Switch> )} /> </div> </Router> ) } } export default AppPage;
其中bundle-loader表示loader:'bunle-loader'(需加载bundle-loader模块)
lazy表示lazy:true;懒加载
name:表示异步生成的文件名字
<Switch> <Route path="/page1" component={lazyLoad(Page1)}/> <Route path="/page2" component={lazyLoad(Page2)}/> <Route path="/page3" component={lazyLoad(Page3)}/> </Switch>
//webpack.config.js ... module.exports = { ... output:{ path:path.join(__dirname + '/dist'), //打包地方 filename:'bundle.js', //打包名字 publicPath: '/', //自动生成html引入js的路径 //按需加载 chunkFilename:'[name]_[chunkhash:8].js' }, ... } ...
这里要注意一下publicPath这个参数.
若是未设置publicPath参数,则默认打包生成的html引入bundle的时候为:
<script type="text/javascript" src="bundle.js"></script>
若是设置publicPath为publicPath: '/dist',则打包后html文件引入js格式为:
<script type="text/javascript" src="/dist/bundle.js"></script>