React-router 4 按需加载的实现方式及原理详解
React-router4
介绍了在router4以后,如何去实现按需加载Component,在router4以前,我们是使用getComponent的的方式来实现按需加载的,router4中,getComponent方法已经被移除,下面就介绍一下react-router4是入围和来实现按需加载的。
1.router3的按需加载方式
route3中实现按需加载只需要按照下面代码的方式实现就可以了。 constabout=(location,cb)=>{ require.ensure([],require=>{ cb(null,require('../Component/about').default) },'about') } //配置route
2.router4按需加载方式(threesteps)
onestep:
创建Bundle.js文件,这个文件其实是个通过bundle-loader包装后的组件来使用,下面会具体讲这个东西。
importReactfrom'react'; importPropTypesfrom'prop-types'; classBundleextendsReact.Component{ state={ //shortfor"module"butthat'sakeywordinjs,so"mod" mod:null } componentWillMount(){ //加载初始状态 this.load(this.props); } componentWillReceiveProps(nextProps){ if(nextProps.load!==this.props.load){ this.load(nextProps); } } load(props){ //重置状态 this.setState({ mod:null }); //传入组件的组件 props.load((mod)=>{ this.setState({ //handlebothesimportsandcjs mod:mod.default?mod.default:mod }); }); } render(){ //ifstatemodenotundefined,Thecontainerwillrenderchildren returnthis.state.mod?this.props.children(this.state.mod):null; } } Bundle.propTypes={ load:PropTypes.func, children:PropTypes.func }; exportdefaultBundle;
secondstep:
importaContainerfrom'bundle-loader?lazy!./containers/A' constA=(props)=>(//这里只是给this.props.child传一个方法,最后在Bundle的render里面调用 {(Container)=> } )
thirdstep:
render(){ return() }Welcome!
3.router4按需加载方方式解析
(1).首先解释一下按需加载,通俗的将就是我当前的location在Home,那么我只应该加载Home的东西,而不应该去加载About等等其他的。
(2).Bundle.js这个文件的作用
先看这段代码:
module.exports=function(cb){ __webpack_require__.e/*require.ensure*/(2).then((function(require){ cb(__webpack_require__(305)); }).bind(null,__webpack_require__)).catch(__webpack_require__.oe); };
这里是我们通过importloadDashboardfrom'bundle-loader?lazy!./containers/A'这种方式引入的container控件。我们使用了bundle-loader将A的源码转化成了上面的代码,具体实现大家可以看bundle-loader源码,代码很少。
上面说到Bundle.js其实就使用来处理这个文件的,这个文件需要一个callback的参数,在Bundle的load方法中,我们会设置这个callback,当路由要调到AContainer这里的时候,就回去加载AContainer,然后调用这个callback,这个callback会调用setState方法,将我们之前传入的load设置给mod,然后渲染出来。
4.webpack进行bundle-loader统一配置
这里匹配的是src/routers/下面的containers文件夹下面所有的js文件,包括二级目录。
{ //匹配routers下面所有文件 //([^/]+)\/?([^/]*)匹配xxx/xxx或者xxx test:/containers\/([^/]+)\/?([^/]*)\.jsx?$/, include:path.resolve(__dirname,'src/routers/'), //loader:'bundle-loader?lazy' loaders:['bundle-loader?lazy','babel-loader'] }
5.部分源码
1.bundle-loader的源码
varloaderUtils=require("loader-utils"); module.exports=function(){}; module.exports.pitch=function(remainingRequest){ this.cacheable&&this.cacheable(); varquery=loaderUtils.getOptions(this)||{}; if(query.name){ varoptions={ context:query.context||this.options.context, regExp:query.regExp }; varchunkName=loaderUtils.interpolateName(this,query.name,options); varchunkNameParam=","+JSON.stringify(chunkName); }else{ varchunkNameParam=''; } varresult; if(query.lazy){ result=[ "module.exports=function(cb){\n", "require.ensure([],function(require){\n", "cb(require(",loaderUtils.stringifyRequest(this,"!!"+remainingRequest),"));\n", "}"+chunkNameParam+");\n", "}"]; }else{ result=[ "varcbs=[],\n", "data;\n", "module.exports=function(cb){\n", "if(cbs)cbs.push(cb);\n", "elsecb(data);\n", "}\n", "require.ensure([],function(require){\n", "data=require(",loaderUtils.stringifyRequest(this,"!!"+remainingRequest),");\n", "varcallbacks=cbs;\n", "cbs=null;\n", "for(vari=0,l=callbacks.length;i2.A的源码
importReactfrom'react'; importPropTypesfrom'prop-types'; import*asreactReduxfrom'react-redux'; importBaseContainerfrom'../../../containers/ReactBaseContainer'; classAextendsBaseContainer{ constructor(props){ super(props); this.renderCustom=functionrenderCustom(){ return(HelloworldInA); }; } render(){ //返回父级view returnsuper.render(); } } A.propTypes={ dispatch:PropTypes.func, }; functionmapStateToProps(state){ return{state}; } exportdefaultreactRedux.connect(mapStateToProps)(A);3.route.js的源码
importReactfrom'react'; import{BrowserRouter,Switch,Link}from'react-router-dom'; import{Route}from'react-router'; importPostContainerfrom'../containers/PostsContainer'; //设置trunk文件的名字thebasenameoftheresource importaContainerfrom'./containers/A'; importbContainerfrom'./containers/B'; importcContainerfrom'./containers/C'; importBundlefrom'../utils/Bundle'; constA=()=>({Component=> } ) constapp=()=> {/*path="/about"*/} {/*"/about/"可以,但"/about/1"就不可以了exact配置之后,需要路径绝对匹配,多个斜杠没有关系,这里直接在浏览器里面设置还有问题*/} {/*path="/about/"*/} {/*"/about/1"可以,但"/about"就不可以了用了strict,path要大于等于的关系,少一个斜杠都不行*/} {/*exact和strick都用了就必须一模一样,连斜杠都一样*/}; exportdefaultfunction(){ //用来判断本地浏览器是否支持刷新 constsupportsHistory='pushState'inwindow.history; return(Linktoabout {/* */} {app()}); }以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。