react实现按需加载-代码分割(上)
# react实现按需加载-代码分割(上)
# 前言
我们直接使用webpack
配置的react
环境,在编译打包之后,页面只生成了一个文件bundle.js
,这样在首屏加载时就会很慢,因为加载一个页面的时候也同时加载了其他页面的js
,首次进入页面时,只加载自己的JS,这样首屏加载就会快很多。
# 一、按需加载
1.首屏加载需要使用bundle-loader
这个插件,所以先安装这个插件
npm install bundle-loader --save-dev
2.新建Bundle.js
import React, {Component} from 'react'
class Bundle extends Component {
constructor(props){
super(props);
this.state = {
// short for "module" but that's a keyword in js, 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({
// handle both es imports and cjs
mod: mod.default ? mod.default : mod
})
})
}
render() {
return this.props.children(this.state.mod)
}
}
export default Bundle;
此文件是官方提供,参考文档
3.改造路由器
// 原路由文件
import React from 'react';
import {Route,Switch,Redirect} from 'react-router-dom';
import Login from './container/login/login'
import Register from "./container/register/register";
import Bossinfo from "./container/bossinfo/bossinfo";
import Geniusinfo from "./container/geniusinfo/geniusinfo";
import AuthRoute from "./components/authroute/authroute";
import Dashboard from "./components/dashboard/dashboard";
import Chat from './components/chat/chat';
class App extends React.Component{
constructor(props){
super(props);
this.state = {
hasError:false
}
}
componentDidCatch(err,info){
console.log(err,info);
this.setState({
hasError:true
})
}
render(){
return(
this.state.hasError ? <Redirect to='/msg'></Redirect>
:
<div>
<AuthRoute/>
<Switch>
<Route path='/login' component={Login}></Route>
<Route path='/register' component={Register}></Route>
<Route path='/bossinfo' component={Bossinfo}></Route>
<Route path='/geniusinfo' component={Geniusinfo}></Route>
<Route path='/chat/:user' component={Chat}></Route>
<Route component={Dashboard}></Route>
</Switch>
</div>
)
}
}
export default App;
改造:
import React from 'react';
import {Route,Switch,Redirect} from 'react-router-dom';
import Bundle from './Bundle';
import Login from 'bundle-loader?lazy&name=login!./container/login/login'
import Register from "bundle-loader?lazy&name=register!./container/register/register";
import Bossinfo from "bundle-loader?lazy&name=bossinfo!./container/bossinfo/bossinfo";
import Geniusinfo from "bundle-loader?lazy&name=geniusinfo!./container/geniusinfo/geniusinfo";
import AuthRoute from "bundle-loader?lazy&name=authroute!./components/authroute/authroute";
import Dashboard from "bundle-loader?lazy&name=dashboard!./components/dashboard/dashboard";
import Chat from 'bundle-loader?lazy&name=chat!./components/chat/chat';
const Loading = function () {
return <div>Loading...</div>
};
// 调用bundle高阶组件,包裹原始组件
const createComponent = (component) => (props) => (
<Bundle load={component}>
{
(Component) => Component ? <Component {...props}/> : <Loading/>
}
</Bundle>
);
class App extends React.Component{
constructor(props){
super(props);
this.state = {
hasError:false
}
}
componentDidCatch(err,info){
console.log(err,info);
this.setState({
hasError:true
})
}
render(){
return(
this.state.hasError ? <Redirect to='/msg'></Redirect>
:
<div>
<AuthRoute/>
<Switch>
<Route path='/login' component={createComponent(Login)}></Route>
<Route path='/register' component={createComponent(Register)}></Route>
<Route path='/bossinfo' component={createComponent(Bossinfo)}></Route>
<Route path='/geniusinfo' component={createComponent(Geniusinfo)}></Route>
<Route path='/chat/:user' component={createComponent(Chat)}></Route>
<Route component={createComponent(Dashboard)}></Route>
</Switch>
</div>
)
}
}
export default App;
现在你可以npm start
,打开浏览器,看是不是进入新的页面,都会加载自己的JS的~
但是发现,名字都是0.bundle.js
这样子的,分不清楚是哪个页面的js
。
修改下webpack.dev.config.js
,加个chunkFilename
。chunkFilename
是除了entry
定义的入口js
之外的js
~
output: {
path: path.join(__dirname, './dist'),
filename: 'bundle.js',
chunkFilename: '[name].js'
}
现在运行发现名字变成home.js
,这样的了。
那么问题来了home
是在哪里设置的?webpack
怎么知道他叫home
?
其实在这里我们定义了,router.js
里面
import Home from 'bundle-loader?lazy&name=home!pages/Home/Home';
这里有个name=home
。
# 二、缓存
想象一下这个场景~
我们网站上线了,用户第一次访问首页,下载了home.js
,第二次访问又下载了home.js
~
这肯定不行呀,所以我们一般都会做一个缓存,用户下载一次home.js
后,第二次就不下载了。
有一天,我们更新了home.js
,但是用户不知道呀,用户还是使用本地旧的home.js
。出问题了~
怎么解决?每次代码更新后,打包生成的名字不一样。比如第一次叫home.a.js
,第二次叫home.b.js
。
我们照着文档来
webpack.dev.config.js
output: {
path: path.join(__dirname, './dist'),
filename: '[name].[hash].js',
chunkFilename: '[name].[chunkhash].js'
}
每次打包都用增加hash
~
现在我们试试,是不是修改了文件,打包后相应的文件名字就变啦?
但是你可能发现了,网页打开报错了~因为你dist/index.html
里面引用js
名字还是bundle.js
老名字啊,改成新的名字就可以啦。
这样每次都去改名字是不现实的。