和異步加載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>