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;i
2.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都用了就必须一模一样,连斜杠都一样*/}
Linktoabout
{/*
*/}
;
exportdefaultfunction(){
//用来判断本地浏览器是否支持刷新
constsupportsHistory='pushState'inwindow.history;
return(
{app()}
);
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。