做者 DBCdouble
本文将基于上一篇文章《Webpack4+Babel7优化70%速度》所搭建的环境去作动态路由加载,同时完成 React16 和 React-Router4 的升级工做,使整个项目的技术栈以及性能体验尽量达到最佳状态。javascript
咱们知道,Webpack主要从两个方面进行优化,一个是提高构建速度,另外一个则是减少文件体积,而在上一篇文章《Webpack4+Babel7优化70%速度》中,咱们已经完成了提高构建速度的部分,这一章咱们将经过实现动态加载路由的方式来将最终生成的打包文件拆分成多个子文件来减少bundle.js的体积,这样就能极大的减少首屏加载过慢的痛点,至此以后,也就不再用担忧随着应用愈来愈大,bundle.js的体积愈来愈大致使首屏加载的速度愈来愈慢的问题了。
css
这里一个个模块安装升级是为了更准确地来把控更新以后有可能引发的报错html
npm install react@16.8.4 --save复制代码
这里安装目前react的最新版本v16.8.4,安装完成以后开启项目,发现页面报错,如图:java
出现上面的报错的缘由是React v15.5及以上版本已经将PropTypes模块剔除,而后执行 react
npm uninstall react-router && npm install react-router-dom --save复制代码
react-router-dom
依赖react-router
,因此咱们使用npm
安装依赖的时候,只须要安装相应环境下的库便可,不用再显式安装react-router
webpack
一、入口文件git
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';import App from './app';
ReactDOM.render(
<BrowserRouter> <App /> </BrowserRouter>,
document.getElementById('app')
);复制代码
BrowserRouter是一个高阶组件,内置history的api来保持 UI 和 URL 的同步github
二、路由配置web
// routes.js
import Home from './home';
import About from './about';
import Help from './help';
export default [{
path: '/',
exact: true,
component: Home
}, {
path: '/about',
component: About
}, {
path: '/help',
component: Help
}];复制代码
// app.js
import React from 'react';
import { Switch, Route } from 'react-router';
import routes from './routes';
class App extends React.Component {
render() {
return (
<Switch> {routes.map((route, i) => <Route key={i} exact={!!route.exact} path={route.path} component={route.component} />)} </Switch> ); } } export default App;复制代码
Switch用于渲染与路径匹配的第一个子 <Route>
或 <Redirect>
。npm
异步动态加载路由从狭义上理解就是页面上没有出现的页面不加载对应的js和css,只加载当前页面展现出来页面的js和css,经过动态导入(dynamic imports)文件的方式实现代码拆分
//async_load.js
import React, { Component } from 'react'
export default (loadComponent, placeholder = '拼命加载中...') => {
return class AsyncComponent extends Component {
unmount = false
constructor () {
super()
this.state = {
Child: null
}
}
componentWillUnmount () {
this.unmount = true
}
async componentDidMount () {
const { default: Child } = await loadComponent()
if (this.unmount) return
this.setState({
Child
})
}
render () {
const { Child } = this.state
return (
Child ? <Child {...this.props} /> : placeholder ) } } }复制代码
// routes.js
import React from 'react';
import Load from './async_load';
export default [{
path: '/',
exact: true,
component(props) {
// 这里的 component 函数也是一个高阶组件
return <Load {...props} load={() => import('./home')} />;
}
}, {
path: '/about',
component(props) {
return <Load {...props} load={() => import('./about')} />;
}
}, {
path: '/help',
component(props) {
return <Load {...props} load={() => import('./help')} />;
}
}];复制代码
当涉及到动态代码拆分时,webpack 提供了两个相似的技术。对于动态导入,第一种,也是优先选择的方式是,使用符合 ECMAScript 提案 的 import()
语法。第二种,则是使用 webpack 特定的 require.ensure
。
完成以上配置以后开启打包,出现一下错误
由于import语法还处于提案阶段,因此须要经过安装Babel的插件@babel/plugin-syntax-dynamic-import才能使用,安装完成以后须要在配置babel-loader的options:
{
plugins: ['@babel/plugin-syntax-dynamic-import']
}复制代码
完成以上的步骤以后,想必已经没问题了吧,因而启动项目,又报错了:
在这个报错上我停留了过久,因而把问题抛给了一个朋友(大佬),在webpack在github上的Issue找到了答案,原来是webpack的4.29.x版本有bug
因而我回退了版本webpack@4.28.2,最终启动成功,能够看到bundle.js被拆分红多个子js文件
在浏览器打开开发者工具点击network查看请求的js文件,能够看到每进入一个新的页面,会动态加载一个新的js
进入首屏路由页面
进入另一个路由页面
不得不说升级老项目过程很痛苦,坑也不少。可是把坑一个个填完,最终完美升级也是一件颇有意思,颇有成就感的事。但愿这篇文章能对你有所帮助。
文章有任何不清楚或者不许确的地方,麻烦在评论区指出