webpack學習筆記(六) 緩存

爲何要使用緩存呢?單就功能來說,不用緩存也能達到一樣的效果,使用緩存最大的目的實際上是爲了 提升性能javascript

爲此咱們必需要確保 webpack 編譯生成的文件能被客戶端緩存,而且當文件發生變化後,客戶端能獲得新的文件html

一、哈希命名

下面,咱們先來搭建一個小小的項目,一步一步說明如何在 webpack 中配置使用緩存java

建立一個空文件夾 Demo,做爲項目的根目錄,而後在該目錄中運行如下命令安裝依賴node

> # 建立 package.json 文件
> npm init -y
> # 安裝 webpack
> npm install --save-dev webpack
> npm install --save-dev webpack-cli
> # 安裝 lodash 模塊
> npm install --save lodash

在根目錄下建立 distsrc 文件夾,分別用於存放資源文件和打包以後的輸出文件webpack

並在 src 文件夾下建立 index.js 文件,該文件的做用是添加一個 div 元素做爲 body 的子節點web

import _ from 'lodash';

function component() {
    var element = document.createElement('div');
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
    return element;
}

let elem = component();
document.body.appendChild(elem);

以後,咱們在根目錄下建立一個 webpack.config.js 文件,用於指定 webpack 的一些配置shell

經過 hash,咱們可使得每次打包生成的文件命名都是惟一的npm

const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist')
    }
};

二、使用 HtmlWebpackPlugin 插件

這裏有一個問題,若是每次打包以後生成的文件命名都不同,那麼咱們要怎麼引用文件呢?json

答案是使用 HtmlWebpackPlugin,這個插件會自動生成一個 HTML5 文件,動態添加每次編譯後引用的資源緩存

首先安裝插件

> npm install --save-dev html-webpack-plugin

而後在 webpack.config.js 文件中進行配置

const path = require('path');
// 引入 HtmlWebpackPlugin 插件
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        // 使用 HtmlWebpackPlugin 插件
        new HtmlWebpackPlugin()
    ]
};

三、使用 CleanWebpackPlugin 插件

另一個問題,若是通過屢次修改和打包,因爲以前打包生成的文件也會殘留下來,因此文件會越堆越多

要怎麼解決呢?答案是使用 CleanWebpackPlugin,該插件能夠在從新打包的時候,把沒有用的文件自動清除

仍是先安裝插件

> npm install --save-dev clean-webpack-plugin

而後在 webpack.config.js 文件中進行配置

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入 CleanWebpackPlugin 插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new HtmlWebpackPlugin(),
        // 使用 CleanWebpackPlugin 插件
        new CleanWebpackPlugin()
    ]
};

四、提取模板

有的時候,將第三方庫提取到獨立的 chunk 文件是比較推薦的作法

由於它們不多被修改,因此利用客戶端的長效緩存機制,能夠最大限度的減小客戶端向服務器請求資源的次數

修改 webpack.config.js 文件以下

const path = require('path');
// 引入 webpack 內置插件
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    entry: {
        main: './src/index.js',
        // 新增 vender 入口,指定第三方庫
        vender: [
            'lodash'
        ]
    },
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        // 將指定的模塊分離到單獨的文件
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor'
        }),
        new HtmlWebpackPlugin(),
        new CleanWebpackPlugin()
    ]
};

五、使用 NamedModulesPlugin / HashedModuleIdsPlugin 插件

至此,還有最後一個問題,假如咱們在 src 目錄下添加一個文件 hello.js,文件內容以下

export function SayHello() {
    console.log('Hello World')
}

而後,咱們在 src 目錄下的 index.js 文件中引用新建的文件,修改 index.js 文件以下

import _ from 'lodash';
import { SayHello } from './hello.js';

function component() {
    SayHello()
    var element = document.createElement('div');
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
    return element;
}

let elem = component();
document.body.appendChild(elem);

可是當咱們從新打包的時候,卻發現以前打包 vender 生成的 bundle 文件的 hash 也發生了改變

這是由於 module.id 會基於默認的解析順序進行增量,因此 vender 生成的 bundle 文件的 hash 也會所以變化

對於這種狀況,webpack 提供兩種解決方法

一是使用 NamedModulesPlugin(適合開發環境使用),二是使用 HashedModuleIdsPlugin(適合生產環境使用)

修改 webpack.config.js 文件以下

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    entry: {
        main: './src/index.js',
        vender: [
            'lodash'
        ]
    },
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor'
        }),
        // 使用 HashedModuleIdsPlugin 插件
        new webpack.HashedModuleIdsPlugin(),
        new HtmlWebpackPlugin(),
        new CleanWebpackPlugin()
    ]
};

【 更新:webpack.optimize.CommonsChunkPlugin 已棄用,請使用 config.optimization.splitChunks 】

對於報錯信息,不針對上面的例子,下面是一個比較通用的寫法

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new webpack.HashedModuleIdsPlugin(),
        new HtmlWebpackPlugin(),
        new CleanWebpackPlugin()
    ],
    optimization: {
        splitChunks: {
            chunks: 'all',  // 可選值有三個,initial(入口)、async(異步)和 all
            minSize: 30000, // 最小尺寸
            minChunks: 1,   // 最小引用次數
            maxAsyncRequests: 5,   // 最大請求異步 chunk 的次數
            maxInitialRequests: 3, // 最大請求入口 chunk 的次數
            cacheGroups: {
                vendor: { // 提取第三方庫
                    test: /[\\/]node_modules[\\/]/, // 根據需求修改
                    name: 'vendor',
                },
                common: { // 提取公共文件
                    test: /[\\/]src[\\/]js[\\/]/, // 根據需求修改
                    name: 'common'
                }
            }
        }
    }
};

【 閱讀更多 webpack 系列文章,請看 webpack學習筆記

相關文章
相關標籤/搜索