webpack性能优化(一):分包

如今面试中问到最多的问题就是性能优化了,我们可以站在不同的角度去回答,比如减少http请求、用户体验上的优化等等很多方面。

webpack做性能优化主要从 优化构建后的结果优化构建时的速度 两方面入手,本篇围绕 webpack 的分包来优化构建后的结果。

为什么说分包可以实现更好的代码组织和性能优化呢?

代码组织:分包通常基于模块化开发的原则,将应用程序拆分成独立的模块或组件。每个分包可以包含与特定功能或页面相关的代码。这种模块化组织方式使代码更易于理解、维护和扩展,因为每个模块都有明确定义的职责。

性能优化:分包后主包的体积减小,可以减小每个页面或模块的初始加载时间,因为用户只需下载当前页面或模块所需的代码,而不必加载整个应用程序的代码。这降低了首次加载的时间,提高了用户体验。

配置多入口打包

在一个大型应用中,不同页面或模块可能具有不同的代码需求。通过配置多个入口起点,您可以将每个页面或模块的代码分开打包,使代码更具可维护性和清晰度。

js 复制代码
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path'); 
module.exports = { 
    entry: { 
        main: './src/main.js', // 第一个入口起点 
        app: './src/app.js' // 第二个入口起点 
    }, 
   output: { 
       filename: '[name].bundle.js', // 使用[name]占位符将生成的文件名与入口起点名称对应 
       path: path.resolve(__dirname, 'build') 
    } ,
    plugins: [
        new CleanWebpackPlugin(), // 每次重新打包自动删除之前的build文件夹
        new HtmlWebpackPlugin() // 对index.html进行打包处理
    ],
   };

当进行多入口打包时,可能会遇到一个问题:如果main.jsapp.js都依赖了相同的库,那么构建后的两个包都会包含相同的库。为了解决这个问题,可以考虑对它们共同依赖的库进行共享

js 复制代码
const path = require('path'); 
module.exports = { 
    entry: { 
        main: { import: './src/main.js', dependOn: 'shared' }, // 第一个入口起点 
        app:  { import: './src/app.js', dependOn: 'shared' }, // 第二个入口起点 
        shared: ['dayjs', 'lodash'] // 共享的库
    }, 
   output: { 
       filename: '[name].bundle.js', // 使用[name]占位符将生成的文件名与入口起点名称对应 
       path: path.resolve(__dirname, 'dist') } 
   };

动态导入分包

当代码中存在不确定会被使用的模块时,最佳做法是将其分离为一个独立的 JavaScript 文件。这样可以确保在不需要该模块时,浏览器不会加载或处理该文件的 JavaScript 代码。我们平时使用的路由懒加载的就是这个原理,都是为了优化性能而延迟加载资源。

实现动态导入的方式是使用ES6的import()语法来完成。

js 复制代码
// main.js文件中
const homeBtn = document.createElement('button')
const aboutBtn = document.createElement('button')
homeBtn.textContent = '加载home文件'
aboutBtn.textContent = '加载about文件'

document.body.appendChild(homeBtn)
document.body.appendChild(aboutBtn)

homeBtn.addEventListener('click', () => {
    import('./views/home.js')
})

aboutBtn.addEventListener('click', () => {
    import('./views/about.js')
})

但是我们会发现一个问题,从包名中无法区分是哪个文件构建后的包,我们可以通过webpack.config.js中的output.chunkFilename属性和webpack魔法注释来实现。

js 复制代码
// main.js
homeBtn.addEventListener('click', () => {
    import(/* webpackChunkName: "home" */'./views/home.js') // 让webpack读取的魔法注释,固定写法
})

aboutBtn.addEventListener('click', () => {
    import(/* webpackChunkName: "about" */'./views/about.js')
})
js 复制代码
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { resolve } = require('path');

module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.js',
    path: resolve(__dirname, 'build'),
    chunkFilename: 'chunk_[name]_[id].js',
  },
  plugins: [new CleanWebpackPlugin(), new HtmlWebpackPlugin()],
};

SplitChunks 插件分包

另外一种分包的模式是splitChunk,通过配置SplitChunks,你可以控制哪些模块应该被拆分,以及如何拆分它们。这有助于减小生成的JavaScript文件的大小,提高应用程序的性能,并降低加载时间。该插件webpack已经默认安装和集成,所以我们并不需要单独安装和直接使用该插件,只需要提供SplitChunksPlugin相关的配置信息即可。

splitChunks.chunks中有三个属性,分别是

  • async:只拆分异步导入的模块。
  • initial:只拆分同步导入的模块。
  • all:拆分所有模块,无论是同步还是异步导入的。
js 复制代码
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { resolve } = require('path');

module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.js',
    path: resolve(__dirname, 'build')
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 100, // 生成 chunk 的最小体积(以 bytes 为单位)
      maxSize: 10000, // 将大于maxSize的包,拆分成不小于minSize的包(以 bytes 为单位)
      cacheGroups: { // 用于对拆分的包进行分组
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/, // 通过正则匹配node_modules
          name: 'vender', // 用在filename中的name占位符
          filename: '[name]_[id].js',
        },
      },
    },

  },
  plugins: [new CleanWebpackPlugin(), new HtmlWebpackPlugin()],
};

更多的配置详见SplitChunksPlugin配置

总结

这些分包的方法可以根据项目的需求和情况进行选择和组合,以达到最佳的代码组织和性能优化效果。

相关推荐
LaughingZhu6 小时前
Product Hunt 每日热榜 | 2026-05-21
前端·人工智能·经验分享·chatgpt·html
怕浪猫6 小时前
Electron 开发实战(一):从零入门核心基础与环境搭建
前端·electron·ai编程
小鹏linux7 小时前
Ubuntu 22.04 部署开源免费具有精美现代web页面的Casdoor账号管理系统
linux·前端·ubuntu·开源·堡垒机
前端若水8 小时前
会话管理:创建、切换、删除对话历史
前端·人工智能·python·react.js
Bigger8 小时前
mini-cc:一个轻量级 AI 编程助手的诞生
前端·ai编程·claude
涵涵(互关)8 小时前
Naive-ui树型选择器只显示根节点
前端·ui·vue
BY组态8 小时前
Ricon组态系统最佳实践:从零开始构建物联网监控平台
前端·物联网·iot·web组态·组态
BY组态8 小时前
Ricon组态系统vs传统组态软件:为什么选择新一代Web组态平台
前端·物联网·iot·web组态·组态
SoaringHeart8 小时前
Flutter进阶:OverlayEntry 插入图层管理器 NOverlayZIndexManager
前端·flutter
放下华子我只抽RuiKe58 小时前
React 从入门到生产(四):自定义 Hook
前端·javascript·人工智能·深度学习·react.js·自然语言处理·前端框架