手把手教你配置 Webpack,並優化

在日常的開發過程當中,若是沒有手動地配置或者優化打包後尺寸,那麼用戶打開網站時,首屏加載會很慢,幾秒後纔出現內容,大大增長了用戶的等待時間。css

爲了解決這個問題,咱們須要從打包這個環節進行優化。常見的優化打包工具webpack,咱們從流行的ReactVue庫着手,嘗試着優化它們。html


首先,使用create-react-app腳手架建立一個React應用。react

若是沒有,首先須要從全局中安裝腳手架,命令以下:webpack

npm install create-react-app -g
複製代碼

接着新建一個文件夾,並在終端編寫命令爲:web

create-react-app webpack-optimiation-react
複製代碼

模板建立完畢以後,它目錄結構以下:npm

.
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── README.md
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   ├── serviceWorker.js
│   └── setupTests.js
├── tree.txt
└── yarn.lock
複製代碼

模板文件建好後,咱們在不一樣的庫中添加相同的代碼,而後使用router使他們可以正常運轉。json

// src/Home.js
import React from 'react';
export default () => <h1>Home</>; 複製代碼
// src/About.js
import React from 'react';
export default () => <h1>About</>; 複製代碼
// src/Concat.js
import React from 'react';
export default () => <h1>Concat</h1>;
複製代碼

而後使用npm包管理器,添加react-router-dom後端

npm install react-router-dom -D
複製代碼

以上的代碼寫好後,在src/index.js中添加:跨域

import React, { lazy, Suspense } from 'react';
import { Switch, BrowserRouter as Router, Link, Route } from 'react-router-dom';

const Home = lazy(() => import('./Home'));
const Concat = lazy(() => import('./Concat'));
const About = lazy(() => import('./About'));

const NavBar = () => (
  <div> <Link to='/'>Home</Link> <Link to='/about'>About</Link> <Link to='/concat'>Concat</Link> </div>
);

function App() {
  return (
    <Router className='App'>
      <>
        <NavBar />
        <Suspense fallback={<div>loading...</div>}>
          <Switch>
            <Route path='/' exact component={Home} />
            <Route path='/about' component={About} />
            <Route path='/concat' component={Concat} />
          </Switch>
        </Suspense>
      </>
    </Router>
  );
}

export default App;
複製代碼

編寫完成以後,使用npm run start,瀏覽器會自動打開,並顯示:瀏覽器

react

OK,運行正常。

咱們打包試試,在終端輸入命令:

npm run build
複製代碼

這時就會出現一個build文件夾,這就是打包後的結果,能夠給後端部署了。

build以後的文件大小竟然有600k之多,爲了查看具體那些包體積大,就須要配置webpack

analysis

可是create-react-app並不暴露webpack配置文件,須要輸入命令才能看到webapck配置文件:

npm run eject
複製代碼

它的結構目錄以下:

.
├── config
│   ├── env.js
│   ├── jest
│   │   ├── cssTransform.js
│   │   └── fileTransform.js
│   ├── modules.js
│   ├── paths.js
│   ├── pnpTs.js
│   ├── webpack.config.js
│   └── webpackDevServer.config.js
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── README.md
├── scripts
│   ├── build.js
│   ├── start.js
│   └── test.js
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   ├── serviceWorker.js
│   └── setupTests.js
├── tree.txt
└── yarn.lock
複製代碼

爲了解決上面打包600kb的問題,首先須要分析,那些地方打包後的尺寸過大。咱們須要webpack-bundle-analyzer這個插件才能可視化地看到那些包尺寸較大。

安裝方法:

npm install webpack-bundle-analyzer -D
複製代碼

而後在config/webpack.config.js寫入代碼:

首先導入這個包,而後再添加插件:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

plugins:[
    ...
     isEnvProduction && new  BundleAnalyzerPlugin(),
     // isEnvProduction這個變量是指,在是否在生產環境
    ...
]
複製代碼

可視化分析出來的結果以下:

webpack_analysis

從上圖咱們能夠發現,打包後最大的包是react-dom

那麼咱們就着手優化它吧,從兩個方面考慮:

  1. 減小服務器端的壓力方法

  2. 減小尺寸的方法

減小服務器壓力的方法有:

  • 使用AggressiveSplittingPlugin插件

  • 使用prefetch&preload方法

  • 使用gzip方法

減小尺寸的方法有:

  • 使用ModuleConcatenationPlugin插件

  • 使用uglifyjs插件

  • 使用exterals選項


AggressiveSplittingPlugin

那麼如今試着用AggressiveSplittingPlugin優化打包後的代碼吧。

直接在config/webpack.config.jsplugins中添加代碼:

plugins: [
  new webpack.optimize.AggressiveSplittingPlugin({
    minSize: 3000,
    maxSize: 5000,
    chunkOverhead: 0,
    entryChunkMultiplicator: 1
  })
];
複製代碼

build文件夾中出現了很是多的小文件。這個插件是的超過必定體積會分割文件。有利於減小服務器的請求壓力。AggressiveSplittingPlugin能夠將bundle拆分紅更小的chunk,直到各個chunk的大小達到option設置的 maxSize。它經過目錄結構將模塊組織在一塊兒。

它記錄了在webpack Records裏的分離點,並嘗試按照它開始的方式還原分離。這確保了在更改應用程序後,舊的分離點(和 chunk)是可再使用的,由於它們可能早已在客戶端的緩存中。所以強烈推薦使用Records


preload&prefetch

第二項是預加載的能力,和預請求能力。咱們先來看看使用preload會怎麼樣?

個人html-webpack-plugin版本是4.0.0-beta.11,爲了正確安裝這個 preload 插件,必需要將它的版本設置爲3.0.0-beta.3,否則它會報如下的錯誤:

Plugin could not be registered at 'html-webpack-plugin-before-html-processing'. Hook was not found.
複製代碼

使用npm管理器安裝這個版本:

npm install preload-webpack-plugin@3.0.0-beta.3
複製代碼

接着在webpack.config.js中添加:

const PreloadWebpackPlugin = require('preload-webpack-plugin');
// ...
plugins: [
  new HtmlWebpackPlugin(
    Object.assign(
      {},
      {
        inject: true,
        template: paths.appHtml
      },
      isEnvProduction
        ? {
            minify: {
              removeComments: true,
              collapseWhitespace: true,
              removeRedundantAttributes: true,
              useShortDoctype: true,
              removeEmptyAttributes: true,
              removeStyleLinkTypeAttributes: true,
              keepClosingSlash: true,
              minifyJS: true,
              minifyCSS: true,
              minifyURLs: true
            }
          }
        : undefined
    )
  ),
  // ...
  new PreloadWebpackPlugin()
];
// ...
複製代碼

打開調試臺,若是的你節點出現了這樣的樣子,那麼說明你成功了。

preload

後面有一個ref=preload就是預加載。

preload有能作什麼?

  • 能夠預先加載文件或者資源。
  • 不會阻塞頁面加載。

舉個例子,若是一個網頁中使用了許多的字體文件,那麼用戶在瀏覽的時候就會等待字體加載,出現白屏的現象。

若是使用了preload的話,那麼瀏覽器不會每次請求一個頁面,到指定頁面中從新加載,而是預先加載字體文件,到指定頁面中不用再次加載。


如今,咱們嘗試使用prefetch,是什麼結果。

config/webpack.config.js修改:

// ...
plugins: [
  // ...
  new PreloadWebpackPlugin({
    rel: 'prefetch'
  })
  // ...
];
// ...
複製代碼

這時,preload就變成了prefetch,以下圖:

prefetch

若是你看到了ref=prefetch的話,那麼表明預處理成功。

它們的共同點是?

  1. 異步加載資源,不會阻塞網頁渲染
  2. 下載並不執行文件
  3. 可以提早請求文件
  4. 沒有同域名限制

prefetch 和 preload 的區別是什麼?

  1. preload會首先優先加載,而且會佔用HTTP併發數,也就是剛進入頁面就會請求。而prefetch會瀏覽器出空閒期時,再請求文件。
  2. preload能夠跨域請求,prefetch不會。

Gzip

gzip可以爲咱們減小存儲空間,以及減小傳輸的時間。

咱們須要安裝一下插件:

npm install -D compression-webpack-plugin
複製代碼

config/webpack.config.js中添加:

//...
plugins: [
  //...
  new CompressionWebpackPlugin({
    filename: '[path].gz[query]',
    algorithm: 'gzip',
    test: /\.(js|css)/,
    threshold: 1024,
    minRatio: 0.8
  })
  //...
];
//...
複製代碼

build完成以後,打開瀏覽器。若是你看到這樣的header的話,那麼表明開啓gzip成功。

gzip


如今,咱們開始減小尺寸的優化。

使用ModuleConcatenationPlugin能夠提升代碼的執行速度和預編譯:

config/webpack.config.js中添加:

// ...
plugins: [
  // ...
  new webpack.optimize.ModuleConcatenationPlugin()
];
// ...
複製代碼

build後,尺寸比之前集減小接近10kb,並且網頁的打開速度也提升很多。

相比以前打開網頁的速度,提升了50ms之多。再加上懶加載,並不須要提早加載全部頁面,因此首屏渲染速度提升了許多。


Uglify

接着,咱們上一個大殺器,UglifyJS插件,它最大程度壓縮和醜化代碼。

安裝它的命令行:

npm install -D uglifyjs-webpack-plugin
複製代碼

導入到config/webpack.config.js中,並使用:

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

 // ...
optimization: {
  minimizer:{
    new UglifyJsPlugin(),
  }
}
// ...
複製代碼

能夠看見,壓縮後的代碼有多180kb

uglify

Externals

externals這個webpack選項是在打包過程當中剔除掉。這樣就有效地減小了依賴,使用第三方CDN減小HTTP壓力和請求的壓力。

使用externals只須要在webpack.config.js中添加一下的代碼:

// ...
externals:{
      'react': 'React',
      'react-dom': 'ReactDOM',
      'react-router-dom': 'ReactRouterDOM'
}
// ...
複製代碼

接着在index.html中添加CDN

<script src="https://cdn.bootcss.com/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdn.bootcss.com/react-router-dom/5.1.2/react-router-dom.min.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
複製代碼
相關文章
相關標籤/搜索