美文网首页
[webpack 学习系列] bundle splitting,

[webpack 学习系列] bundle splitting,

作者: 小黄人get徐先生 | 来源:发表于2019-12-30 16:23 被阅读0次

官方链接:code splitting

SplitChunksPlugin 配置参数:https://webpack.js.org/plugins/split-chunks-plugin/

两篇非常棒的讲代码划分的文章(可以在阅读完下面基础知识后学习):


何为代码分割?

首先假设我们的index.js代码内容如下:

import _ from 'lodash'; // 假设 lodash 模块有 1mb 大

// 假设下面的逻辑有 1mb 大
console.log(_.join(['hello', 'webpack']));

webpack.config.js 配置内容为:

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

module.exports = {
    mode: 'development',
    entry: './src/index.js',
    devtool: "source-map",
    devServer: {
        contentBase: './dist',
        open: true,
        hot: true,
        hotOnly: true
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            }
        ]
    }
};

执行 yarn build 后(实际执行的是 webpack),打包出来的文件存放在 dist 目录下,文件名为 bundle.js。大致内容如下:

/******/ (function(modules) { // webpackBootstrap
...省略
/***/ "./node_modules/lodash/lodash.js":
/*!***************************************!*\
 !*** ./node_modules/lodash/lodash.js ***!
 \***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
...省略
/***/ "./src/index.js":
/*!**********************!*\
 !*** ./src/index.js ***!
 \**********************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
...省略

我们发现打包后的内容除了 webpack 启动代码之外,就是我们的 lodash 模块和我们在 index.js 里面的逻辑代码。

如我们上面的假设,假设 lodash 模块有 1mb 大,我们在 index.js 文件实现的逻辑有 1mb 大。那么如果客户端在请求该页面的时候,就会请求一个 2mb 大的 js 代码,size 较大的 js 文件无疑会影响客户端浏览器的加载效率;另外如果 index.js 文件发生变化,客户端就必须重新请求这个 2mb 的文件(浏览器缓存功能失效)。

所以这里我们就需要做 bundle splitting,将 lodash 和我们在 index.js 文件里的逻辑划分成 2 个打包文件。这样客户端发送请求的时候,就会请求两个 1mb 的文件。另外就是即使我们的 index.js 文件内容发生变化,因为客户端缓存的原因,lodash 打包好的模块使用缓存即可,只需要重新请求 index.js 打包好的模块,这样我们便提高了客户端加载的效率。

代码分割是 webpack 最引人注目的特性之一。该特性允许您将代码分割成不同的包,这些包可以按需加载,也可以并行加载。它可以用于实现更小的包,并控制资源负载优先级,如果使用正确,将对负载时间产生重大影响。

代码分割的三种方法

  1. Entry points:使用 entry 配置手动划分代码
    就拿我们上面的代码例子举例:
    添加 lodash.js ,内容如下:
import _ from 'lodash';

window._ = _;

index.js 内容如下:

console.log(_.join(['hello', 'webpack']));

webpack 配置如下:

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

module.exports = {
    mode: 'development',
    entry: { // 入口
        app: './src/index.js',
        lodash: './src/lodash.js'
    },
    devtool: "source-map",
    devServer: {
        contentBase: './dist',
        open: true,
        hot: true,
        hotOnly: true
    },
    output: { // 出口
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            }
        ]
    }
};

重新执行 yarn build 打包(即 webpack)后,打包内容如下:


这样我们就通过 entry 配置的方式实现了代码分割。(当然这只是个例子,实际思路确实这样)

lodash 的全局化也可以使用 shimming 功能实现,如下图:

  1. 使用 SplitChunksPlugin 插件去重和分割块(chunks)
    有时候,某个公用模块被多个 js 文件导入使用,index.jsanother.index.js 内容如下:
iimport * as _ from 'lodash';

console.log(_.join(['hello', 'webpack']));

webpack 配置如下:

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

module.exports = {
    mode: 'development',
    entry: {
        app: './src/index.js',
        app1: './src/another-index.js'
    },
    devtool: "source-map",
    devServer: {
        contentBase: './dist',
        open: true,
        hot: true,
        hotOnly: true
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    resolve: {
        extensions: ['.js']
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            }
        ]
    }
};

这样我们打包出来的文件如下:


但是我们发现 app.bundle.jsapp1.bundle.js 文件里面都打包了 lodash 这个模块,这就存在了导入重复的问题。

这时候,就需要使用 SqlitChunksPlugin 来帮助我们解决这个问题了。

SplitChunksPlugin 允许我们将公共依赖项提取到现有的条目块(chunk)或全新的块(chunk)中。

修改 webpack.config.js 文件如下:

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

module.exports = {
    mode: 'development',
    entry: {
        app: './src/index.js',
        app1: './src/another-index.js'
    },
    devtool: "source-map",
    devServer: {
        contentBase: './dist',
        open: true,
        hot: true,
        hotOnly: true
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    resolve: {
        extensions: ['.js']
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            }
        ]
    },
    // 新添加的内容
    optimization: {
        splitChunks: {
            chunks: 'all'
        }
    }
};

重新执行 yarn build(即 webpack)后,我们发现打包出来的目录结构变成了下面形式:


lodash 被提取到一个公共的块(chunk)中,名为 vendors-app-app1.bundle.js。这样我们就是实现了去重。
  1. 动态导入:通过模块内的内联函数调用拆分代码。
    修改 index.jsanother-index.js 文件如下:
import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) =>{
    console.log(_.join(['hello', 'webpack']));
});

修改 webpack.config.js 文件内容如下:

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

module.exports = {
    mode: 'development',
    entry: {
        app: './src/index.js',
        app1: './src/another-index.js'
    },
    devtool: "source-map",
    devServer: {
        contentBase: './dist',
        open: true,
        hot: true,
        hotOnly: true
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    resolve: {
        extensions: ['.js']
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            }
        ]
    }
};

执行 yarn build (即 webpack)打包,打包后的结果如下:


没有任何的配置,动态导入的形式自动帮我们实现了代码划分。

如果想修改打包出来公共模块的名字,我们可以这样做:
webpack.config.js 下添加 chunkFileName 选项

    output: {
        filename: '[name].bundle.js',
        chunkFilename: "[name].bundle.js",
        path: path.resolve(__dirname, 'dist')
    },

这里的 name 就是我们动态导入时候设置的注释,如下的 webpackChunkName

import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) =>{
    console.log(_.join(['hello', 'webpack']));
});

相关文章

网友评论

      本文标题:[webpack 学习系列] bundle splitting,

      本文链接:https://www.haomeiwen.com/subject/lhcmoctx.html