webpack+vite前端构建工具 - 8 代码分割

发布于:2025-06-23 ⋅ 阅读:(17) ⋅ 点赞:(0)

8 代码分割

  • 代码分割:按需求将代码分割为不同的文件,使项目更快打开。
  • 经常使用代码打包工具(如webpack)处理代码分割

8.1 为什么进行代码分割

8.1.1 为何进行单入口处理

  • 例如vue单页面项目等单入口(一个入口文件打包后对应一个出口js文件)情况,意味所有代码在一个文件里(一个app.js里包含所有的引入的其他js文件),这样会导致代码过大

一个有200多个页面的项目,全都在一个js文件里,意味着要展示首页,要将所有的js文件下载下来。
文件过多,首页加载速度变慢

  • 需要把不是马上用到的代码拆分出来,用于加快首屏速度

例如d.js不是首页要用到的js,可能是进行某个操作要用到的js,那么就将d.js从app.js拆分出来,单独打包。这样就会打出2个js文件,一个app.js,一个d.js. —— 异步引入

8.1.2 为何进行多入口处理

  • 单入口和多入口要处理的问题是不同的

    • 单入口处理的问题是所有的文件都在一个文件里,为避免体积过大,因此要拆为异步引入。
    • 但多入口本身已经拆分为了不同的入口不同的页面,每个入口对应一个结果,不会存在体积过大的问题。
  • 多入口的问题

    • 问题描述:重复加载同一段逻辑代码
    • 问题举例
      • app.js和app2.js同时依赖b.js,打包后的两个js分别打包了b.js文件。
      • b.js分别在app.js和app2.js都存在。
      • 浏览器运行app.js时 ,将b.js代码下载一遍,运行app2.js时,又将b.js下载一遍。
    • 解决思路:相同的模块独立打包。
      • 浏览器存在缓存,对于加载过的文件会缓存下来。
      • 因此加载app.js时加载b.js,并缓存,再加载app2.js时直接使用缓存的b.js而无需再次加载

8.2 何时代码分割

8.2.1 何时处理单入口情况

  • 异步引入的前提分析

    • 不是说所有首页不会马上用到的js都可以拆为异步引入
    • 拆成异步引入 → 减少打包的app.js的体积 → 加快首屏速度
    • 拆成异步引入 → 但增加http请求的数量 → 增加服务器压力
    • 与base64本质相似
      • base64可以减少http请求,但是不能滥用
      • 大的图片转为base64 → 图片体积加到app.js → 增加打包的app.js → 减慢首屏加载速度
        • 原因:app.js下载完成后才能加载页面 → app.js体积过大 → 下载速度会变慢
  • 异步引入的结论

    • 除非文件非常大&首页不会马上使用 → 推荐异步引入

8.3 如何进行代码分割

8.3.1 如何处理单入口情况

  • 异步引入的方法
    • import
      • 较多使用
    • require.ensure
1 情景构建&异步引入

例如app.js需要在3s后使用a.js。

// app.js
setTimeout(() => {
    import('./a.js').then(res => {
        // res是a.js export的内容
        console.log("🚀 ~ import ~ res:", res);
        console.log("🚀 ~ import ~ res.default:", res.default);
    });
}, 3000);
// a.js
let b = 3;
console.log(b);
export default b + 1;
2 打包

打包结果:除了app1.xxxx.xxxx.js,还有一个js文件。
在这里插入图片描述

3 实际效果

浏览器打开index.html,打开控制台。
可以看到,初始只有3个文件。

在这里插入图片描述
3s后请求第4个文件。
在这里插入图片描述
控制台打印结果
在这里插入图片描述

4 指定文件命名

添加魔法注释magic comment——/*webpackChunkName:"a"*/

// app.js
simport "./test.css";

setTimeout(() => {
    import(/*webpackChunkName:"a"*/'./a.js').then(res => {
        // res是a.js export的内容
        console.log("🚀 ~ import ~ res:", res);
        console.log("🚀 ~ import ~ res.default:", res.default);
    });
}, 3000);

打包结果
在这里插入图片描述

5 require.ensure的写法

以上是import的写法,本节介绍require.ensure的写法。

  • 第1个参数是第2个参数回调要用到的依赖
  • 文件命名在于第3个参数
// app.js
import "./test.css";

setTimeout(() => {
    // import(/*webpackChunkName:"a"*/'./a.js').then(res => {
    //     // res是a.js export的内容
    //     console.log("🚀 ~ import ~ res:", res);
    //     console.log("🚀 ~ import ~ res.default:", res.default);
    // });
    // 第1个参数是第2个参数回调要用到的依赖
    require.ensure(["jquery"], ($) => {
        let b = require("./a.js");
        // 此处的b与import写法的res是一样的
        console.log("🚀 ~ require.ensure ~ b:", b);
        console.log("🚀 ~ require.ensure ~ b.default:", b.default);
    }, "a");// 文件命名在于第3个参数
}, 3000);

8.3.2 如何处理多入口情况

1 情景构建

app.js和app2.js都引入了a.js

// app.js
import "./test.css";
import './a.js'
// app2.js
import './a.js'
console.log("app2.js")

a.js不赘述了,跟之前一样。

直接打包,看无处理的情况是怎样的。

  • app.js和app2.js打包的文件均有a.js的代码。
  • 即a.js的代码要加载两次
    在这里插入图片描述

在这里插入图片描述

2 配置

在optimization配置splitChunks属性。

const htmlwebpack = require('html-webpack-plugin');
module.exports = {
    // entry: "./app.js",//单入口写法,入口指定为app.js
    entry: {  // 多入口写法:入口名称+入口文件
        app1: ["./app.js"],
        app2: "./app2.js"
    },
    // entry: ["./app.js", './app2.js'] // 两个文件同时作为一个入口
    output: {
        path: __dirname + '/dist', // 绝对路径,__dirname是node的全局变量,表示当前目录的绝对路径
        filename: "[name].[hash:4].bundle.js", //将name加到filename里,打包结果文件是app.bundle.js和app2.bundle.js,hash是对文件是否有改变的标志,:4表示截取前4位
    },
    mode: "development", //webpack4以后要指定mode
    // loader
    module: {
        rules: []
    },
    plugins: [
        new htmlwebpack({
            template: './index.html',// 写法1,指定html模板
            // templateContent: function () {  // 写法2,自定义模板,使用频率较低
            //     return '<div>123</div>'
            // }
            filename: "index.html",
            chunks: ["app1"],
            minify: {
                collapseInlineTagWhitespace: false, // 是否一行展示
                removeComments: false, // 是否移除注释
                removeAttributeQuotes: false, // 是否移除属性之间的多余空格
            },
            inject: "body" // 指定js加载位置: body|true(加载到body中), head, false(不加载)
        }),
        new htmlwebpack({
            template: './index2.html',
            filename: "index2.html",
            chunks: ["app2"]
        })
    ],
    optimization: {
        splitChunks: {
            chunks: "all", // 分割规则:all, async(只拆分异步),initial(只拆分同步)
            minChunks: 2, // 一个chunk重复出现几次才拆分为独立的文件
            minSize: 0 // 最小拆分量(拆分量多了会增加http请求数量,造成资源浪费),单位bit
        }
    }
}
3 打包

只有1个a.js被打包。
在这里插入图片描述
app.js和app2.js都不再有a.js的内容。
在这里插入图片描述
在这里插入图片描述

4 其他配置
多入口文件命名

optimization的splitChunks的属性name值为要指定的文件名。

// webpack.config.js 局部
    optimization: {
        splitChunks: {
            chunks: "all", // 分割规则:all, async(只拆分异步),initial(只拆分同步)
            minChunks: 2, // 一个chunk重复出现几次才拆分为独立的文件
            minSize: 0, // 最小拆分量(拆分量多了会增加http请求数量,造成资源浪费),单位bit
            name: "a"
        }
    }

8.4 指定要分割的文件

需要将某个文件单独分割出来,或将第三方库单独打包,runtime代码等单独打包。

这是单入口和多入口通用的功能。

p7 22:00
不看了,多入口的情况用不到,现在碰到的项目还是vue居多,用到再看。


网站公告

今日签到

点亮在社区的每一天
去签到