Skip to content

多页面打包抽离公共代码

本节来学习一下webpack常用的一个功能,抽取公共代码。

公共代码肯定是在有多个页面中,然后有公共的部分,才需要抽取出来。

所以需要配置一个多入口,这两个入口中,有公用的代码。

如果不抽离出来,那么相同的代码会被打包成多份,整体打包体积就变大了。

抽离自定义的公共代码

引出问题

假如有a.js和b.js,还有index.js和other.js,index.js和other.js都需要使用a和b,那么只需要将a和b抽离出去,单独用一个包,那么当index使用a,b时,先生成一个包,然后other再使用a,b时,就直接从已经加载好的包里取,就实现了一个缓存的效果。这就是提取公共代码的好处。

新建a.js

js
// webpack-optimize\src\a.js
console.log('a~~~~~~~~~~~~~');

新建b.js

js
// webpack-optimize\src\b.js
console.log('b~~~~~~~~~~~~~');

新建index.js

js
// webpack-optimize\src\index.js
import './a.js'
import './b.js'
console.log('index.js');

新建other.js

js
// webpack-optimize\src\other.js
import './a.js'
import './b.js'
console.log('other.js');

修改配置文件

js
// webpack-optimize\webpack.config.js

let path = require('path')

// 插件 用来复制html入口模板,到打包后的目录,并把打包后的js文件,以script标签的形式塞到模板文件里
let HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    mode: 'development', // 模式,默认两种模式 production 和 development

    entry: {
        index: './src/index.js',
        other: './src/other.js',
    }, // 多入口

    devServer: { // 开发服务器的配置
        port: 3000, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './dist', // 以哪个文件夹作为服务的根目录 ,如果有就直接使用,没有的话就使用内存中的
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩
    },


    // 打包后的输出路径
    output: {
        filename: '[name].js', // 打包后的文件名
        path: path.resolve(__dirname, 'dist'), // 打包后输出的路径
    },

    // 配置一些loader
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    // 配置一些它的选项
                    options: {
                        // 配置预设,有多个
                        presets: [
                            '@babel/preset-env', // 用来解析es6
                            '@babel/preset-react', // 用来解析react语法,jsx
                        ]
                    }
                }
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
            },
        ]
    },
    // 注册插件
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html', // 模板的位置
        }),
    ]
}

打包,查看结果

image-20220127211709733

果然不出所料,最后两个入口,各自打包了一份a.js和b.js的代码

image-20220127211801064

优化,开始抽离

修改配置文件

js
// webpack-optimize\webpack.config.js

let path = require('path')

// 插件 用来复制html入口模板,到打包后的目录,并把打包后的js文件,以script标签的形式塞到模板文件里
let HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    mode: 'development', // 模式,默认两种模式 production 和 development

    // 抽离多入口引用的公共代码
    optimization: {
        splitChunks: { // 分割代码块
            cacheGroups: { // 缓存组
                common: { // 公共模块
                    chunks: 'initial', // 从入口文件就开始抽离了(暂时没有考虑异步模块)
                    minSize: 0, // 只要代码大于0个字节,就抽离出来
                    minChunks: 2, // 代码被引用多少次,才抽离

                }
                // 上面的解释,刚开始从入口就开始抽离common,只要大于0个字节,被引用2次,就抽离
            }
        }
    },

    entry: {
        index: './src/index.js',
        other: './src/other.js',
    }, // 多入口

    devServer: { // 开发服务器的配置
        port: 3000, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './dist', // 以哪个文件夹作为服务的根目录 ,如果有就直接使用,没有的话就使用内存中的
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩
    },


    // 打包后的输出路径
    output: {
        filename: '[name].js', // 打包后的文件名
        path: path.resolve(__dirname, 'dist'), // 打包后输出的路径
    },

    // 配置一些loader
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    // 配置一些它的选项
                    options: {
                        // 配置预设,有多个
                        presets: [
                            '@babel/preset-env', // 用来解析es6
                            '@babel/preset-react', // 用来解析react语法,jsx
                        ]
                    }
                }
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
            },
        ]
    },
    // 注册插件
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html', // 模板的位置
        }),
    ]
}

打包,查看结果,抽离成功

image-20220127213300868

这样公共的代码就抽离到了一个文件中。

抽离第三方模块

引出问题

有时候,我们会在多个模块中同时引用多次第三方模块,比如jquery

修改index.js

js
// webpack-optimize\src\index.js
import './a.js'
import './b.js'
console.log('index.js');

// 引用第三方模块
import $ from 'jquery'
console.log($);

修改other.js

js
// webpack-optimize\src\other.js
import './a.js'
import './b.js'
console.log('other.js');

// 引用第三方模块
import $ from 'jquery'
console.log($);

这时候,我们想把第三方模块单独的抽离成一个文件,但是如果还使用上面的方法,会将jquery的代码和我们自定义的模块代码抽离到同一个文件中。显然这不是我们想要的。

我们可以看一下,打包

image-20220127213944408

一下300多k,显然jquery也打包到这个文件里了。我们想把公用的第三方模块单独拎出来怎么办?

修改,开始抽离

修改配置文件

js
// webpack-optimize\webpack.config.js

let path = require('path')

// 插件 用来复制html入口模板,到打包后的目录,并把打包后的js文件,以script标签的形式塞到模板文件里
let HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    mode: 'development', // 模式,默认两种模式 production 和 development

    // 抽离多入口引用的公共代码
    optimization: {
        splitChunks: { // 分割代码块
            cacheGroups: { // 缓存组
                common: { // 公共模块
                    chunks: 'initial', // 从入口文件就开始抽离了(暂时没有考虑异步模块)
                    minSize: 0, // 只要代码大于0个字节,就抽离出来
                    minChunks: 2, // 代码被引用多少次,才抽离

                },
                // 上面的解释,刚开始从入口就开始抽离common,只要大于0个字节,被引用2次,就抽离

                vendor: { // 第三方模块
                    test: /node_modules/, // 如果 是从node_modules中引入的模块,就抽离出来
                    chunks: 'initial', // 从入口文件就开始抽离了(暂时没有考虑异步模块)
                    minSize: 0, // 只要代码大于0个字节,就抽离出来
                    minChunks: 2, // 代码被引用多少次,才抽离

                    priority: 1, // 权重高一些,先抽离第三方模块,再去抽离别的
                },
                // 注意:抽离模块的代码 是从上向下执行的
                // 当走 common 的抽离配置时,会直接将jquery也匹配到,抽离出来
                // 所以就导致了,第三方模块的代码 仍旧和自定义模块代码在一个文件中
                // 解决办法是 先抽离第三方模块,再去抽离别的代码
                // 给 vendor 配置 增加一个权重 priority: 1
            }
        }
    },

    entry: {
        index: './src/index.js',
        other: './src/other.js',
    }, // 多入口

    devServer: { // 开发服务器的配置
        port: 3000, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './dist', // 以哪个文件夹作为服务的根目录 ,如果有就直接使用,没有的话就使用内存中的
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩
    },


    // 打包后的输出路径
    output: {
        filename: '[name].js', // 打包后的文件名
        path: path.resolve(__dirname, 'dist'), // 打包后输出的路径
    },

    // 配置一些loader
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    // 配置一些它的选项
                    options: {
                        // 配置预设,有多个
                        presets: [
                            '@babel/preset-env', // 用来解析es6
                            '@babel/preset-react', // 用来解析react语法,jsx
                        ]
                    }
                }
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
            },
        ]
    },
    // 注册插件
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html', // 模板的位置
        }),
    ]
}

测试一下,打包看下结果,已经抽离出来了

image-20220128092227710

这时候的vendor里放的就是jquery,common放的我自定义的模块代码。他们都被引用了两次以上,抽离出来就减小的代码体积。

总结

多页面打包时,可能会有公共代码,打包的时候,相同的代码会被打包两次,很浪费性能,webpack可以将公用的代码抽离出来,变成单独的文件,然后引用的地方就会引这个抽离出来的代码。

  1. 抽离自定义模块

  2. 抽离第三方模块(从node_modules中引入的)

    js
        // 抽离多入口引用的公共代码
        optimization: {
            splitChunks: { // 分割代码块
                cacheGroups: { // 缓存组
                    common: { // 公共模块
                        chunks: 'initial', // 从入口文件就开始抽离了(暂时没有考虑异步模块)
                        minSize: 0, // 只要代码大于0个字节,就抽离出来
                        minChunks: 2, // 代码被引用多少次,才抽离
    
                    },
                    // 上面的解释,刚开始从入口就开始抽离common,只要大于0个字节,被引用2次,就抽离
    
                    vendor: { // 第三方模块
                        test: /node_modules/, // 如果 是从node_modules中引入的模块,就抽离出来
                        chunks: 'initial', // 从入口文件就开始抽离了(暂时没有考虑异步模块)
                        minSize: 0, // 只要代码大于0个字节,就抽离出来
                        minChunks: 2, // 代码被引用多少次,才抽离
    
                        priority: 1, // 权重高一些,先抽离第三方模块,再去抽离别的
                    },
                    // 注意:抽离模块的代码 是从上向下执行的
                    // 当走 common 的抽离配置时,会直接将jquery也匹配到,抽离出来
                    // 所以就导致了,第三方模块的代码 仍旧和自定义模块代码在一个文件中
                    // 解决办法是 先抽离第三方模块,再去抽离别的代码
                    // 给 vendor 配置 增加一个权重 priority: 1
                }
            }
        },

参考

https://www.bilibili.com/video/BV1a4411e7Bz?p=25&spm_id_from=pageDriver