前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
开发者导航
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
开发者导航
  • React专栏

    • React组合式开发实战

      • 前端开发的四个时代
      • 企业管理系统的前世今生
      • 可视化页面搭建工具
      • 实战篇 01:开发前准备
      • 实战篇 02:项目脚手架
      • 实战篇 03:页面布局方案
      • 实战篇 04:权限管理机制
      • 实战篇 05:菜单匹配逻辑
      • 实战篇 06:消息通知设计
      • 实战篇 07:多语言支持
      • 继往开来:可视化页面搭建工具
    • React Hooks与Immutable实战

    • React SSR服务端渲染与同构实践

    • IM聊天系统前端开发实践

    • 微前端开发实战

    • React进阶实践

  • Vue专栏

  • 移动端专栏

  • Node专栏

  • 前端工程化专栏

  • 算法专栏

  • Typescript专栏

  • 其他专栏

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

在生产环境下,为了提高页面加载性能,构建工具一般将项目的代码打包(bundle)到一起,这样上线之后只需要请求少量的 JS 文件,大大减少 HTTP 请求。当然,Vite 也不例外,默认情况下 Vite 利用底层打包引擎 Rollup 来完成项目的模块打包。

某种意义上来说,对线上环境进行项目打包是一个必须的操作。但随着前端工程的日渐复杂,单份的打包产物体积越来越庞大,会出现一系列应用加载性能问题,而代码分割可以很好地解决它们。

在本小节中,我们将围绕代码分割展开学习。首先我们将一起分析Code Splitting解决了单产物打包模式下的哪些问题,然后用具体的项目示例体验一下 Vite 默认自带的 Code Splitting 效果。从中,你将了解到 Vite 的默认分包策略,以及底层所使用的 Rollup 拆包 API——munaulChunks。

当然,在实际的项目场景中,只用 Vite 默认的策略是不够的,我们会更深入一步,学习 Rollup 底层拆包的各种高级姿势,实现自定义拆包,同时我也会带大家通过实际案例复现 Rollup 自定义拆包经常遇到的坑——循环引用问题,一起分析问题出现的原因,也分享一些自己的解决思路和方案,让大家对 Vite 及 Rollup 的代码分割有更加深入的掌握。

需要注意的是,文中会多次提到bundle、chunk、vendor这些构建领域的专业概念,这里给大家提前解释一下:

  • bundle 指的是整体的打包产物,包含 JS 和各种静态资源。
  • chunk指的是打包后的 JS 文件,是 bundle 的子集。
  • vendor是指第三方包的打包产物,是一种特殊的 chunk。

# Code Splitting 解决的问题

在传统的单 chunk 打包模式下,当项目代码越来越庞大,最后会导致浏览器下载一个巨大的文件,从页面加载性能的角度来说,主要会导致两个问题:

    1. 无法做到按需加载,即使是当前页面不需要的代码也会进行加载。
    1. 线上缓存复用率极低,改动一行代码即可导致整个 bundle 产物缓存失效。

首先说第一个问题,一般而言,一个前端页面中的 JS 代码可以分为两个部分: Initital Chunk和Async Chunk,前者指页面首屏所需要的 JS 代码,而后者当前页面并不一定需要,一个典型的例子就是 路由组件,与当前路由无关的组件并不用加载。而项目被打包成单 bundle 之后,无论是Initial Chunk还是Async Chunk,都会打包进同一个产物,也就是说,浏览器加载产物代码的时候,会将两者一起加载,导致许多冗余的加载过程,从而影响页面性能。而通过Code Splitting我们可以将按需加载的代码拆分出单独的 chunk,这样应用在首屏加载时只需要加载Initial Chunk 即可,避免了冗余的加载过程,使页面性能得到提升。

其次,线上的缓存命中率是一个重要的性能衡量标准。对于线上站点而言,服务端一般在响应资源时加上一些 HTTP 响应头,最常见的响应头之一就是cache-control,它可以指定浏览器的强缓存,比如设置为下面这样:

cache-control: max-age=31536000
@前端进阶之旅: 代码已经复制到剪贴板

表示资源过期时间为一年,在过期之前,访问相同的资源 url,浏览器直接利用本地的缓存,并不用给服务端发请求,这就大大降低了页面加载的网络开销。不过,在单 chunk 打包模式下面,一旦有一行代码变动,整个 chunk 的 url 地址都会变化,比如下图所示的场景:

image.png

由于构建工具一般会根据产物的内容生成哈希值,一旦内容变化就会导致整个 chunk 产物的强缓存失效,所以单 chunk 打包模式下的缓存命中率极低,基本为零。

而进行Code Splitting之后,代码的改动只会影响部分的 chunk 哈希改动,如下图所示:

image.png

入口文件引用了A、B、C、D四个组件,当我们修改 A 的代码后,变动的 Chunk 就只有 A 以及依赖 A 的 Chunk 中,A 对应的 chunk 会变动,这很好理解,后者也会变动是因为相应的引入语句会变化,如这里的入口文件会发生如下内容变动:

import CompA from './A.d3e2f17a.js'
// 更新 import 语句
import CompA from './A.a5d2f82b.js'
@前端进阶之旅: 代码已经复制到剪贴板

也就是说,在改动 A 的代码后,B、C、D的 chunk 产物 url 并没有发生变化,从而可以让浏览器复用本地的强缓存,大大提升线上应用的加载性能。

# Vite 默认拆包策略

刚刚我们说到了为什么要进行拆包,实际上 Vite 中已经内置了一份拆包的策略,接下来让我们来看看 Vite 默认的拆包模式是怎样的。

在生产环境下 Vite 完全利用 Rollup 进行构建,因此拆包也是基于 Rollup 来完成的,但 Rollup 本身是一个专注 JS 库打包的工具,对应用构建的能力还尚为欠缺,Vite 正好是补足了 Rollup 应用构建的能力,在拆包能力这一块的扩展就是很好的体现。

我们先通过具体的项目来体验一下 Vite 拆包,示例项目我已经放到了小册的 Gihub 仓库中(

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
开发者导航
  • React专栏

    • React组合式开发实战

      • 前端开发的四个时代
      • 企业管理系统的前世今生
      • 可视化页面搭建工具
      • 实战篇 01:开发前准备
      • 实战篇 02:项目脚手架
      • 实战篇 03:页面布局方案
      • 实战篇 04:权限管理机制
      • 实战篇 05:菜单匹配逻辑
      • 实战篇 06:消息通知设计
      • 实战篇 07:多语言支持
      • 继往开来:可视化页面搭建工具
    • React Hooks与Immutable实战

    • React SSR服务端渲染与同构实践

    • IM聊天系统前端开发实践

    • 微前端开发实战

    • React进阶实践

  • Vue专栏

  • 移动端专栏

  • Node专栏

  • 前端工程化专栏

  • 算法专栏

  • Typescript专栏

  • 其他专栏