Webpack按需加载秒开应用
一、前言
本文將基于上一篇文章《Webpack4+Babel7優化70%速度》所搭建的環境去做動態路由加載,同時完成?React16?和?React-Router4?的升級工作,使整個項目的技術棧以及性能體驗盡可能達到最佳狀態。
二、背景
我們知道,Webpack主要從兩個方面進行優化,一個是提升構建速度,另一個則是減小文件體積,而在上一篇文章《Webpack4+Babel7優化70%速度》中,我們已經完成了提升構建速度的部分,這一章我們將通過實現動態加載路由的方式來將最終生成的打包文件拆分成多個子文件來減小bundle.js的體積,這樣就能極大的減小首屏加載過慢的痛點,至此之后,也就再也不用擔心隨著應用越來越大,bundle.js的體積越來越大導致首屏加載的速度越來越慢的問題了。
三、升級模塊
這里一個個模塊安裝升級是為了更準確地來把控更新之后有可能引起的報錯
1、React16
npm install react@16.8.4 --save復制代碼這里安裝目前react的最新版本v16.8.4,安裝完成之后開啟項目,發現頁面報錯,如圖:
出現上面的報錯的原因是React v15.5及以上版本已經將PropTypes模塊剔除,然后執行?
npm install prop-types --save?將代碼中?import { PropTypes } from 'react'?代碼修改為 import PropTypes from 'prop-types',注意:除了入口文件下的代碼需要替換PropTypes,你項目中使用到的第三方庫內部也有可能使用到了?import { PropTypes } from 'react',遇到這種情況,需要將該第三方庫升級到最新版本,包括react-router之前的老版本就是依賴于react庫中的PropTypes作數據類型判斷,所以接下來升級react-router2、react-router-dom(這里使用react-router-dom,它基于react-router,加入了在瀏覽器運行環境下的一些功能)
npm uninstall react-router && npm install react-router-dom --save復制代碼react-router-dom依賴react-router,所以我們使用npm安裝依賴的時候,只需要安裝相應環境下的庫即可,不用再顯式安裝react-router
- 將代碼中的?import { Link } from 'react-router'?修改為?import { Link } from 'react-router-dom'
- 因為react-router4.x版本較之前版本改動較大,基本需要重寫項目中的路由層
四、路由配置
1、入口文件
// 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 的同步
2、路由配置
// 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>。
五、異步動態加載路由和Code Splitting
異步動態加載路由從狹義上理解就是頁面上沒有出現的頁面不加載對應的js和css,只加載當前頁面展示出來頁面的js和css,通過動態導入(dynamic imports)文件的方式實現代碼拆分
1、封裝一個高階函數來異步加載組件
//async_load.js import React, { Component } from 'react' export default (loadComponent, placeholder = '拼命加載中...') => {return class AsyncComponent extends Component {unmount = falseconstructor () {super()this.state = {Child: null}}componentWillUnmount () {this.unmount = true}async componentDidMount () {const { default: Child } = await loadComponent()if (this.unmount) returnthis.setState({Child})}render () {const { Child } = this.statereturn (Child ? <Child {...this.props} /> : placeholder)}} }復制代碼2、修改路由配置文件
// 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
進入首屏路由頁面
進入另外一個路由頁面
六、總結
不得不說升級老項目過程很痛苦,坑也很多。但是把坑一個個填完,最終完美升級也是一件很有意思,很有成就感的事。希望這篇文章能對你有所幫助。
文章有任何不清楚或者不準確的地方,麻煩在評論區指出
轉載于:https://juejin.im/post/5c7f2b2e6fb9a049c43e6d68
總結
以上是生活随笔為你收集整理的Webpack按需加载秒开应用的全部內容,希望文章能夠幫你解決所遇到的問題。