composer install 重复下载包是因默认隔离策略,需启用 cache-vcs 和配置缓存头;验证看终端是否显示“using cached”;ci 中应缓存 ~/.composer/cache 而非 vendor/。

composer install 时为什么还在下载已装过的包?
因为默认情况下,Composer 每个项目都维护独立的 vendor/ 目录,不共享下载缓存——哪怕同一个 package、同一版 1.2.3,在不同项目里执行 composer install 仍会重新拉取 ZIP 或克隆 Git。这不是 bug,是默认隔离策略。
真正起作用的是 Composer 的全局「artifact cache」和「repository cache」,但它们默认不启用或未配置到位。
- 本地
~/.composer/cache/确实存在,但只缓存 ZIP 包和元数据,且仅当源支持(如 Packagist)且响应头含ETag/Last-Modified才能复用 - Git 包(
vcs类型)默认每次 clone,除非显式启用cache-vcs - 如果你用的是私有仓库或自建 Satis,没配好 HTTP 缓存头,缓存就形同虚设
怎么开启并验证全局缓存生效?
运行 composer config -g cache-dir ~/.composer/cache 是冗余的(新版默认已设),关键在启用两个开关:
- 打开 VCS 缓存:
composer config -g cache-vcs true—— 这会让 Composer 对 Git 仓库做 shallow clone 并复用.composer/cache/vcs/下的裸仓库 - 确认元数据缓存可用:
composer config -g cache-files-ttl 15552000(6个月),避免频繁刷新 packagist.org 的packages.json - 执行
composer clear-cache后再install,观察终端输出:若出现Downloading <code>vendor/package(1.2.3) 变成Using cached <code>vendor/package(1.2.3),说明命中
私有包或 Git 仓库下缓存为啥不工作?
因为 Composer 默认把每个 Git URL 当作独立源,https://git.example.com/repo.git 和 git@git.example.com:repo.git 被视为两个仓库,缓存不互通;同时,若 Git 服务器禁用了 info/refs?service=git-upload-pack 的缓存头,Composer 就无法判断远程是否有更新,只能强制 fetch。
- 统一使用 HTTPS 地址,并确保 Nginx/Apache 返回
Cache-Control: public, max-age=3600 - 在
composer.json中显式指定dist信息,绕过 Git 操作:{ "type": "package", "package": { "name": "myorg/mylib", "version": "dev-main", "dist": { "url": "https://releases.myorg.com/mylib-1.2.3.zip", "type": "zip" } } } - 避免混用 SSH 和 HTTPS 协议地址,否则
cache-vcs彻底失效
CI 环境下如何复用缓存避免重复拉取?
CI(如 GitHub Actions、GitLab CI)默认无持久缓存,composer install 每次都是“全新”环境。不能只靠 cache-vcs,得把整个 ~/.composer/cache/ 目录挂载为缓存键。
- GitHub Actions 示例:用
actions/cache缓存~/.composer/cache路径,key 基于composer.lockhash - GitLab CI:在
cache:下配置key: $CI_COMMIT_REF_SLUG+paths: [ "~/.composer/cache" ] - 注意:不要缓存
vendor/,它依赖 PHP 版本和扩展,跨环境不安全;只缓存 Composer 自身的下载产物
最易被忽略的一点:cache-vcs 在容器或 rootless 环境中可能因权限问题静默失败,建议在 CI 启动脚本里加一句 ls -la ~/.composer/cache/vcs/ 确认目录可写且有内容。










