Webpack按需加載秒開應用

做者 DBCdouble

1、前言

本文將基於上一篇文章《Webpack4+Babel7優化70%速度》所搭建的環境去作動態路由加載,同時完成 React16 和 React-Router4 的升級工做,使整個項目的技術棧以及性能體驗儘量達到最佳狀態。javascript

2、背景

咱們知道,Webpack主要從兩個方面進行優化,一個是提高構建速度,另外一個則是減少文件體積,而在上一篇文章《Webpack4+Babel7優化70%速度》中,咱們已經完成了提高構建速度的部分,這一章咱們將經過實現動態加載路由的方式來將最終生成的打包文件拆分成多個子文件來減少bundle.js的體積,這樣就能極大的減少首屏加載過慢的痛點,至此以後,也就不再用擔憂隨着應用愈來愈大,bundle.js的體積愈來愈大致使首屏加載的速度愈來愈慢的問題了。
css

3、升級模塊

這裏一個個模塊安裝升級是爲了更準確地來把控更新以後有可能引發的報錯html

一、React16

npm install react@16.8.4 --save複製代碼

這裏安裝目前react的最新版本v16.8.4,安裝完成以後開啓項目,發現頁面報錯,如圖:java



出現上面的報錯的緣由是React v15.5及以上版本已經將PropTypes模塊剔除,而後執行 react

npm install prop-types --save 
將代碼中
 import { PropTypes } from 'react' 
代碼修改成
import PropTypes from 'prop-types'
注意:除了入口文件下的代碼須要替換PropTypes,你項目中使用到的第三方庫內部也有可能使用到了 
import { PropTypes } from 'react'
,遇到這種狀況,須要將該第三方庫升級到最新版本,包括react-router以前的老版本就是依賴於react庫中的PropTypes做數據類型判斷,因此接下來升級react-router

二、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
webpack

  • 將代碼中的
     import { Link } from 'react-router' 
    修改成
     import { Link } from 'react-router-dom'
  • 由於react-router4.x版本較以前版本改動較大,基本須要重寫項目中的路由層

4、路由配置

一、入口文件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

5、異步動態加載路由和Code Splitting

異步動態加載路由從狹義上理解就是頁面上沒有出現的頁面不加載對應的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

進入首屏路由頁面


進入另一個路由頁面



6、總結

不得不說升級老項目過程很痛苦,坑也不少。可是把坑一個個填完,最終完美升級也是一件頗有意思,頗有成就感的事。但願這篇文章能對你有所幫助。


文章有任何不清楚或者不許確的地方,麻煩在評論區指出

相關文章
相關標籤/搜索