vendor 目录庞大主因是 composer 默认安装开发冗余文件;需用 archive.excludes 插件源头过滤 tests/docs 等,配合 --no-dev、--prefer-dist、--optimize-autoloader 与 --classmap-authoritative 联合生效,并在 docker 中分步构建确保缓存复用。

vendor 目录庞大,根本不是因为你 require 了太多业务包,而是 Composer 默认把开发期才需要的东西全塞进去了——phpunit、symfony/var-dumper、.git 目录、tests/、docs/,甚至 vendor/bin/ 下的几十个二进制文件,它们在线上环境一个都用不上,却占掉 40%~70% 的体积。
composer install --no-dev 为什么没真正变小?
它只跳过 require-dev 里的包,但对已安装包内部的冗余内容完全不管:比如 monolog/monolog 的 tests/ 目录、guzzlehttp/guzzle 里带的桩测试和异步扩展、laravel/framework 的 resources/ 和 examples/ —— 这些都在 dist zip 包里,--no-dev 不会删它们。
- 运行
du -sh vendor/* | sort -hr | head -10,确认大包目录下是否还存在tests/、.git/、docs/ - 检查
composer show monolog/monolog输出中的dist字段是否存在;若为null或缺失,说明该包走的是 source 模式,--prefer-dist也无效 - 私有包若未在
composer.json中正确定义dist,或未上传 zip 归档,--prefer-dist会静默 fallback 到 source,不报错但体积照涨
如何让 tests/ docs/ .git 在解压时就消失?
靠 find vendor -name tests -exec rm -rf {} + 是危险操作:有些包(如旧版 monolog/monolog)把 Handler 示例放在 tests/ 下,删了会导致 Class not found。真正在源头过滤,得用 archive.excludes 插件。
- 先执行
composer require --dev composer-archive-excludes - 在
composer.json的config节点下加:"archive": { "excludes": ["/tests/", "/docs/", "/examples/", "/.github/", "/CHANGELOG.md"] } - 之后每次
composer install或update,这些路径会在 unzip 阶段被跳过,不会落地到vendor/ - 注意:该配置仅对 packagist 上提供 dist 归档的包生效,对
path类型或 source 模式仓库无效
为什么 --optimize-autoloader 和 --classmap-authoritative 必须一起用?
--optimize-autoloader 只生成 autoload_classmap.php,但它默认仍保留 fallback 扫描逻辑;如果某个类没进 classmap,autoloader 会退回去遍历整个 src/ 目录——此时哪怕你删了 tests/,只要 autoload 配置里还声明了它,PHP 就可能因 scandir() 失败而报错,尤其在容器或 NFS 环境下。
- 必须加
--classmap-authoritative,强制 autoloader 只查 classmap,彻底关闭文件系统扫描 - 这个参数不减体积,但能让前面所有瘦身操作真正生效:删掉的
tests/不再引发警告,autoload_static.php解析更快,OPcache 命中率更高 - CI/CD 中漏掉它,线上可能偶发
Class not found,且很难复现——因为错误只在某些 autoloader fallback 触发路径下出现
Docker 构建中 vendor 为何反复膨胀?
根本原因是 COPY 顺序和缓存策略错乱:把 composer.json 和 composer.lock 放在 COPY . . 后面,导致每次改一行代码,前面所有依赖层全失效;更糟的是,没加 --no-dev 和 --no-scripts,容器里还跑起了前端构建脚本。
- Dockerfile 中必须分三步:
COPY composer.json composer.lock ./→RUN composer install --no-dev --prefer-dist --no-scripts --optimize-autoloader --classmap-authoritative→COPY . . - 禁用
--no-scripts:防止post-install-cmd生成额外缓存、日志或 build 产物(比如laravel-mix在 vendor 里写node_modules) - 确保
composer.lock已提交进 Git,否则 BuildKit 缓存无法按文件内容命中,每次构建都重装 - 别在基础镜像里预装
vendor/:PHP 版本、扩展、OPcache 配置不一致时,本地生成的autoload_classmap.php在容器里可能直接失效
最常被忽略的点是:精简不是一次性动作,而是部署链路里的每个环节都要对齐——composer.lock 里不能含 dev 包、Docker 构建命令不能漏参数、archive.excludes 配置要覆盖所有大包、--classmap-authoritative 必须开启。少一个,前面所有操作都可能白做。











