漸進式配置webpack4單頁面和多頁面(二)

漸進式配置webpack4單頁面和多頁面

前言

使用包的版本css

webpack ->4.3.0
babel-loader ->8.0.5
npm ->6.4.1
webpack-cli ->3.3.1
複製代碼

每一個章節對應一個demohtml

模塊化拆包1

參考代碼 demo8
node

什麼是模塊化拆包。好比咱們在項目裏面須要引入echarts、xlsx、lodash等比較大的包的時候。若是不作代碼拆包,都會打包到一個js文件裏面。若是每次打包發版都會生成一個新的js打包文件,用戶從新請求頁面的時候會再次請求echarts、xlsx、lodash這些不變的代碼,就會下降用戶體驗。模塊化拆包將這些不變的依賴包打包成一個新的js文件,每次打包發版的時候用戶就不會再次請求echarts、xlsx、lodash這些不變的代碼了。react

代碼更改

webpack-bundle-analyzer 可視化顯示打包的JS引用的包webpack

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

在項目裏面引入echarts、xlsx、lodashnginx

npm i echarts xlsx lodash -S
複製代碼

app.jsgit

import "regenerator-runtime/runtime";
import _ from 'lodash';
import echarts from 'echarts';
import xlsx from 'xlsx';
console.log(echarts)
console.log(xlsx);
document.getElementById('app').innerHTML = _.ceil(2,3,4);

複製代碼

webpack.base.conf.js 配置可視化如今打包文件。github

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

  plugins:[
    new BundleAnalyzerPlugin(),
    new htmlWebpackPlugin({
      filename:'index.html',
      template:'./index.html',
      inject:true,
      minify: {
        // 壓縮 HTML 文件
        removeComments: isPord, // 移除 HTML 中的註釋
        collapseWhitespace: isPord, // 刪除空白符與換行符
        minifyCSS: isPord // 壓縮內聯 css
      },
    })
  ],
複製代碼

運行打包命令web

npm run build

複製代碼

打包提示代碼太大,須要進行拆包。

默認配置

optimization.splitChunks是webpack4新的特性,默認進行代碼拆包。算法

上圖是默認配置。

chunks:

  • all: 無論文件是異步仍是同步引入,均可以使用splitChunks進行代碼拆包。
  • async: 只將異步加載的文件分離,首次通常不引入,到須要異步引入的組件纔會引入。
  • initial:將異步和非異步的文件分離,若是一個文件被異步引入也被非異步引入,那它會被打包兩次(注意和all區別),用於分離頁面首次須要加載的包。

minSize: 文件最小打包體積,單位byte,默認30000。

好比說某個項目下有三個入口文件,a.js和b.js和c.js都是100byte,當咱們將minSize設置爲301,那麼webpack就會將他們打包成一個包,不會將他們拆分紅爲多個包。

automaticNameDelimiter: 鏈接符。

假設咱們生成了一個公用文件名字叫vendor,a.js,和b.js都依賴他,而且咱們設置的鏈接符是"~"那麼,最終生成的就是 vendor~a~b.js

maxInitialRequests 入口點處的最大並行請求數,默認爲3。

若是咱們設置爲1,那麼每一個入口文件就只會打包成爲一個文件

maxAsyncRequests 最大異步請求數量,默認5

若是咱們設置爲1,那麼每一個入口文件就只會打包成爲一個文件

優先級關係。

maxInitialRequest / maxAsyncRequests <maxSize <minSize。

cacheGroups 定製分割包的規則列表

test能夠配置正則和寫入function做爲打包規則。其餘屬性都可繼承splitChunks。

minChunks

依賴最少引入多少次才能進行拆包。

簡單修改配置

複製默認配置,將chunks 改成'all'。

optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  },
複製代碼

運行打包命令

npm run build
複製代碼

多了一個vendors~app.js。這個js裏面儲層的就是須要拆分的代碼。

再次拆包

vendors~app.js是一個1.71mb的文件,太大了,繼續拆包。
添加一個新的拆包規則

cacheGroups: {
        echarts:{ // 新增拆包規則
          name:'echarts', // 規則名字
          chunks:'all', // 同步引入和異步引入均可以使用該規則
          priority:10, 
          // 該規則的優先級,好比 webpack中進行拆包的時候,
          // echarts包會有先匹配priority高的規則,若是知足這個規則,
          // 則將代碼導入到該規則裏面,不會將代碼導入到後面的規則裏面了。
          test:/(echarts)/, // 正則匹配規則
          minChunks:1 // 代碼裏面最少被引入1次就可使用該規則。
        },
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
複製代碼

運行打包命令

npm run build
複製代碼

echarts包就被單獨拆出來了儲層在echarts.js。
其實這個就是Code Splitting的概念之一 官方demo

模塊化拆包2

參考代碼 demo9
這一節只使用splitChunks的默認配置來實現異步加載,理解異步加載的好處。
。 使用react做爲開發框架。

同步引入的拆包

redux -> 16.8.6
react-dom -> 16.8.6
react-router-dom -> 5.0.0
複製代碼

安裝 react

npm i react react-router-dom react-dom -S
複製代碼

解析 jsx代碼還須要@babel/preset-react

npm i @babel/preset-react -D
複製代碼

詳細代碼請看demo9
兩個路由分別對應不一樣的組件。

import About from './src/view/about';
    import Inbox from './src/view/inbox';
    // ....
    
   <App>
        <Route path="/about" component={About} />
        <Route path="/inbox" component={Inbox} />
    </App>
複製代碼

inbox.js引入了echarts而且使用了。

import React from 'react';
import echarts from 'echarts';
class Inbox extends React.Component {
  componentDidMount() {
    var myChart = echarts.init(document.getElementById('main'));
    var option = {
      tooltip: {
        show: true
      },
      legend: {
        data: ['銷量']
      },
      xAxis: [
        {
          type: 'category',
          data: ["襯衫", "羊毛衫2", "雪紡衫222", "褲子111", "高跟鞋", "襪子"]
        }
      ],
      yAxis: [
        {
          type: 'value'
        }
      ],
      series: [
        {
          "name": "銷量",
          "type": "bar",
          "data": [5, 20, 40, 10, 10, 20]
        }
      ]
    };

    // 爲echarts對象加載數據 
    myChart.setOption(option);
  }
  render() {
    return (
      <div>
        <h2>Inbox</h2>
        <div style={{ height: '400px' }} id="main"></div>
      </div>
    )
  }
}
export default Inbox;
複製代碼

splitChunks 使用默認配置

splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
複製代碼

運行打包命令

npm run build
複製代碼

打包的時候已經提示了,打包的代碼太大影響性能。可使用import()或require來限制包的大小。確保延遲加載應用程序的某些部分。

WARNING in webpack performance recommendations: 
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
複製代碼

咱們在首頁的時候,並無/inbox路由,可是/inbox路由裏面的代碼都打包在了app.js這一個js裏面。首屏渲染變的很慢。按照官方提示須要作組件的動態引入,只有路由切換到/inbox的時候才加載對應組件的代碼。

異步引入拆包

異步引入組件須要用到babel的一個插件@babel/plugin-syntax-dynamic-impor

{
	test: /\.(js|jsx)?$/,  // 正則匹配,全部的.js文件都使用這個規則進行編譯
	exclude: /node_modules/, // 排除的文件夾。這個文件夾裏面的的文件不進行轉譯
	loader: "babel-loader", // 轉譯的插件
	options: {  // 轉譯的規則
		presets: [ //轉譯器配置
			[
				"@babel/preset-env", {
					useBuiltIns: "usage",
					corejs: 3
				},
			],
			["@babel/preset-react"]
		],
		plugins: ["@babel/plugin-syntax-dynamic-import"] // 轉譯插件配置
	}
},
複製代碼

新建AsyncComponent.js。

import React, { Component } from "react";

export default function asyncComponent(importComponent) {
  class AsyncComponent extends Component {
    constructor(props) {
      super(props);

      this.state = {
        component: null
      };
    }

    async componentDidMount() {
      const { default: component } = await importComponent();
    // 組件引入完成後渲染組件
      this.setState({
        component: component
      });
    }

    render() {
      const C = this.state.component;

      return C ? <C {...this.props} /> : null;
    }
  }

  return AsyncComponent;
}
複製代碼

更換引入模式

//import Inbox from './src/view/inbox';
const Inbox = asyncComponent(() => import('./src/view/inbox'));
複製代碼

運行打包命令

npm run build
複製代碼

echarts被引入到1.js裏面了。

npm run server
複製代碼

首頁並無加載1.js

路由跳轉後會引入。

react的動態引入組件就OK了。 按照官方文檔,還能夠自定義chunk名稱

const Inbox = asyncComponent(() => import(/* webpackChunkName: "echarts" */ './src/view/inbox'));
複製代碼

其實還能夠異步引入依賴包

async componentDidMount() {
    const {default:echarts}= await import('echarts');
    var myChart = echarts.init(document.getElementById('main'));
}
複製代碼

總結

  • 通常狀況下推薦splitChunks.chunks: 'all',其餘參數能夠不變。'all'的意思是同步引入和異步引入均可以進行拆包。
  • 儘可能小的引入所須要的插件,好比echarts官方提供了按需加載的方法。
  • 在首頁沒有用的某個插件的時候,儘可能使用異步加載的方式進行引入。

JS Tree Shaking

參考代碼 demo10

Tree Shaking

樹抖動是JavaScript上下文中經常使用於消除死代碼的術語。 它依賴於ES2015模塊語法的靜態結構,即導入和導出。 名稱和概念已由ES2015模塊捆綁器彙總推廣。

搖樹的意思。在webpack4.0裏面會自動的把不須要的js在打包的時候剔除。固然須要開發進行配合:按需加載
咱們拿lodash爲例。 爲了清楚咱們搖掉了多少代碼,先將react的包拆出來單獨打包。

splitChunks: {
      chunks: 'all',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        reactVendors: {
          chunks: 'all',
          test: /(react|react-dom|react-dom-router)/,
          priority: 100,
          name: 'react',
        },
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
複製代碼

在about.js裏面使用lodash

import React from 'react';
import _ from 'lodash';
class About extends React.Component {

    render() {
      const s = _.concat([1],[2,3,1,2,3,4,6,78,41]);
      return (
        <h3>{s}</h3>
      )
    }
  }
  export default About;
複製代碼

運行打包命令

npm run build
複製代碼

合併引入的vendors有95kb。咱們只使用了lodash的concat方法,其餘的方法沒使用。 咱們使用lodash的模塊化包 lodash-es

npm i lodash-es -S
複製代碼

改變about.js的代碼

import React from 'react';
//import _ from 'lodash';
import { concat } from 'lodash-es';
class About extends React.Component {

    render() {
      const s = concat([1],[2,3,1,2,3,4,6,78,41]);
      return (
        <h3>{s}</h3>
      )
    }
  }
  export default About;
複製代碼

運行打包命令

npm run build
複製代碼

如今只有28kb。縮小的部分就是loader的按需加載的部分。

Gzip壓縮。

Gzip壓縮的好處。

打開掘金首頁
隨便找一個js資源。

Response Headers 裏面的 content-encoding 表明着資源傳輸格式。說明這段資源使用的是Gzip文件。說明不少公司用的是Gzip進行資源傳輸,由於代碼打包成Gzip傳輸時須要的資源很小。

其實Gzip傳輸是服務器須要配置的。即便咱們上傳代碼沒有進行Gzip壓縮,服務器通過配置後會將資源進行Gzip壓縮而後傳輸。例如nginx的Gzip配置

Gzip配置都是服務器配置的工做,咱們要作什麼呢?其實服務器會尋找資源對應的gzip文件,若是沒有則會進行Gzip壓縮而後傳輸資源。這就多出了壓縮的時間。若是咱們經過webpack生成gzip文件,能夠減小服務器生成gzip文件的時間,也減緩了服務器壓力。

js打包壓縮配置 optimization.minimiz

這個屬性的屬性值能夠是布爾、數組。

當屬性值是布爾時

在mode是prduction時,minimiz默認是true。自動使用terser-webpack-plugin進行壓縮。

當屬性值是數組時

如圖是默認配置。

配置Gzip壓縮

默認的配置不用改,使用壓縮插件compression-webpack-plugin

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

配置 optimization.minimiz

minimizer: [
      new CompressionPlugin({
        filename: '[path].gz[query]', // 生成資源的名字 [path].gz[query] 是默認名字
        algorithm: 'gzip', // 壓縮算法
        test: /\.(js|css|html|svg)$/, // 匹配的資源
        compressionOptions: { level: 8 }, // 壓縮等級 默認9級
        threshold: 10240, // 多大資源使用才壓縮 10kb
        minRatio: 0.8,
        //僅處理壓縮比此比率更好的資源(minRatio =壓縮尺寸/原始尺寸)。
        //示例:您擁有1024b大小的image.png文件,壓縮版本的文件大小爲768b,
        //所以minRatio等於0.75。換句話說,當壓縮大小/原始大小值小於minRatio值時,
        //將處理資源。默認 0.8 。
        deleteOriginalAssets: false, // 是否刪除原始資產。
      }),
      new TerserPlugin({
        parallel: true,
      }),
    ]
複製代碼

運行打包命令

npm run build
複製代碼

.zip 就是打包生成的gzip資源。對比原來的文件壓縮了 38/121≈0.3。壓縮了不少。
相關文章
相關標籤/搜索