Composer install 反复下载包是因为 GitHub Actions 每次运行都在全新虚拟机上,vendor/ 和全局缓存均不存在;应缓存 ~/.composer/cache 目录,key 需包含操作系统、PHP 版本及 composer.lock 的 hash 值以确保命中率和兼容性。

为什么 composer install 会反复下载包?
GitHub Actions 每次运行都在全新虚拟机上,vendor/ 目录和 Composer 的全局缓存(~/.composer/cache)都不存在。不加缓存时,composer install 会重新解析依赖、下载所有 .zip 或 .tar.gz 包,耗时常达 2–5 分钟。
用 actions/cache 配合 composer cache-dir 最可靠
官方推荐方式是缓存 Composer 自身的全局缓存目录,而非直接缓存 vendor/ —— 因为 vendor/ 受 composer.json、composer.lock、PHP 版本、平台配置等多因素影响,缓存命中率低且易出错。
- 先用
composer config --global cache-dir查出实际缓存路径(通常为~/.composer/cache) - 在 workflow 中用
actions/cache@v4缓存该路径,key 基于composer.lock的 hash 和 PHP 版本 - 确保
composer install使用--no-interaction --prefer-dist --optimize-autoloader
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: ~/.composer/cache
key: ${{ runner.os }}-php-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
- name: Install dependencies
run: composer install --no-interaction --prefer-dist --optimize-autoloader缓存 key 不带 PHP 版本会出问题
Composer 缓存的 dist 包解压后内容可能受 PHP 版本影响(例如某些扩展启用状态不同导致安装逻辑分支变化),若 key 忽略 matrix.php-version 或 env.PHP_VERSION,跨版本构建可能复用错误缓存,引发 Class not found 或 Undefined function 错误。
- key 中必须包含 PHP 版本标识,即使只跑一个版本也建议显式写入
-
hashFiles('**/composer.lock')要写全路径通配,不能只写composer.lock(否则子模块变更不触发更新) - 如果项目有多个
composer.json(如 monorepo),需确认是否要分别缓存或统一用根 lock 文件
vendor 缓存仅适合简单单项目场景
极少数情况下(如固定 PHP 版本 + 无 platform config + 无脚本钩子),可尝试缓存 vendor/,但风险明显:
- key 必须同时包含
composer.lockhash、PHP 版本、composer --version输出(因不同 Composer 版本解析 lock 方式可能微调) - 需在
composer install前加rm -rf vendor,否则部分文件残留会导致 autoload 错乱 - 一旦 CI 环境中
COMPOSER_HOME或COMPOSER_CACHE_DIR配置异常,vendor缓存就彻底失效
真正省时间的是让 Composer 复用已下载解压好的包,不是跳过 autoloader 生成——这点容易被忽略。










