生产环境部署应使用 composer install –no-dev –optimize-autoloader 跳过 dev 依赖并优化自动加载,再结合 find 删除 tests、.md 等非运行时文件,优先采用多阶段构建避免镜像层残留。

composer install 时怎么跳过 dev 依赖减小体积
生产环境部署时,composer install 默认会装 require-dev 里的包(比如 phpunit、mockery),这些在运行时完全用不上,白占几十 MB。不跳过,打包镜像或上传服务器时体积直接多出 30%~50%。
实操建议:
- 上线前统一用
composer install --no-dev --optimize-autoloader,--no-dev是关键,它会让 Composer 忽略require-dev区块 - 如果项目用了
config.platform模拟低版本 PHP 环境,记得保持它和目标环境一致,否则--no-dev可能因平台约束误删运行时必需的包 - CI/CD 脚本里别写成
composer update && composer install --no-dev——update会重写composer.lock,可能引入新 dev 依赖;应固定用install
vendor 目录里哪些文件可以安全删
vendor 不是“全都要”。很多包自带测试、文档、示例、.md 文件甚至整套前端资源,运行时零作用,但占空间。
实操建议:
- 用
composer install --no-dev --optimize-autoloader后,再跑find vendor -name "*.md" -delete、find vendor -name "tests" -type d -prune -exec rm -rf {} +—— 这些后缀和目录名在主流包里高度一致,删掉基本不影响 autoload - 慎删
.git目录:有些包靠它做运行时版本检测(如symfony/flex的 recipe 逻辑),删了可能导致命令报错;不如用composer install --no-scripts避开这类副作用 - 别碰
composer.json和autoload.php—— 即使看起来是“元数据”,实际被classLoader动态读取
为什么 require-dev 里放了 monolog/logger 还是被打进生产包
不是所有 require-dev 包都“纯开发”。有些包在 dev 里声明,但它的子依赖(transitive dependency)被主依赖链拉进来了,结果还是进了 vendor。
实操建议:
- 运行
composer depends monolog/logger(Composer 2.2+)看谁在引用它;如果输出里有your-project-name或某个require下的包,说明它已是运行时依赖,--no-dev压根不生效 - 检查
composer.lock里该包的type字段:如果是library或空值,大概率被 runtime 依赖间接带入;只有type: "dev"才真算纯开发包 - 想彻底隔离,改用
require下的替代方案(比如用psr/log接口 + 轻量实现),而不是在 dev 里塞重型工具
docker 构建时 vendor 体积还是大?试试分层清理
Docker cache 机制会让中间层残留没删干净的文件,表面 RUN rm -rf vendor/tests,实际镜像大小没变 —— 因为上一层还存着。
实操建议:
- 把安装和清理写在同一层:
RUN composer install --no-dev --optimize-autoloader && find vendor -name "tests" -o -name "*.md" | xargs rm -rf - 避免
copy . /app后再RUN composer install—— 这样vendor会和源码混在一层;改用多阶段构建,build 阶段装全量,final 阶段只COPY --from=build /app/vendor /app/vendor - 留意
composer install生成的vendor/composer/autoload_*.php—— 它们体积不大但数量多,gzip 压缩率低,别指望靠 nginx 或 CDN 自动压缩来省空间
最麻烦的其实是嵌套的 require 关系和平台配置冲突,光删文件解决不了;得盯着 composer.lock 里每个包的来源和 type 字段,不然删着删着就 Class not found 了。