做者 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
進入首屏路由頁面
進入另一個路由頁面
不得不說升級老項目過程很痛苦,坑也不少。可是把坑一個個填完,最終完美升級也是一件頗有意思,頗有成就感的事。但願這篇文章能對你有所幫助。
文章有任何不清楚或者不許確的地方,麻煩在評論區指出