如何使用Composer在生产模式下跳过安装开发依赖?
- 内容介绍
- 文章标签
- 相关推荐
本文共计780个文字,预计阅读时间需要4分钟。
markdown使用 `composer install --no-dev` 是一种可靠的方式来安装生产环境下的依赖,这不是建议,而是生产部署的强制要求。不添加此参数,依赖如 `phpunit`、`phpstan`、`laravel-debugbar` 等会被安装,这些包在非开发环境中通常是不必要的。不添加这些包不会导致错误,因为它们不会因为缺少而自动安装。
为什么 composer install --no-dev 有时没效果
根本原因不是命令写错了,而是 composer.lock 文件本身已“污染”:它记录了 require-dev 中的包(比如上次用 composer update 生成的)。此时 --no-dev 会被忽略,甚至报错 dev dependencies not found。
- 验证方法:运行
grep -A 5 "require-dev" composer.lock,输出非空就说明 lock 文件含 dev 条目 - 修复方式:在干净环境(无
vendor/、无旧lock)执行composer update --no-dev --lock,再提交新composer.lock - CI/CD 中必须先清理缓存:比如
git clean -xffd vendor/,否则 Docker 构建可能复用带 dev 包的 layer
--no-dev 实际跳过哪些东西
它不只是删掉 require-dev 列表里的包,而是一整套过滤链:
- 跳过
require-dev中所有包及其全部传递依赖(例如phpunit/phpunit带来的sebastian/exporter也不会进vendor/) - 不加载
autoload-dev配置——像"Tests\": "tests/"这种映射不会注册,new TestsFooTest()必然触发Class not found - 跳过
scripts中标记为@dev的钩子(如post-autoload-dump里调用phpstan的命令会静默跳过)
配套参数不能少:性能和安全都要兼顾
--no-dev 解决“装什么”,但不解决“怎么装得快、装得稳”。线上必须组合使用:
-
--optimize-autoloader(或-o):生成扁平化类映射,减少文件系统查找;但注意——若代码用了动态类名拼接(如new $className),且该类不在 autoload map 里,就会报错 -
--classmap-authoritative:告诉 autoloader “所有类都在 classmap 里,别再去磁盘找”,配合--no-dev更稳妥(因为 dev 类已被排除,classmap 更干净) -
--no-scripts和--no-interaction:避免部署卡在交互提示或执行前端构建等无关操作
真正容易被忽略的是 autoload-dev 映射残留问题:即使没装 phpunit,只要 vendor/autoload.php 里还保留着 tests/ 的 PSR-4 映射,class_exists('TestsFooTest') 仍可能返回 true——这会让某些条件判断逻辑意外通过,埋下线上隐患。
本文共计780个文字,预计阅读时间需要4分钟。
markdown使用 `composer install --no-dev` 是一种可靠的方式来安装生产环境下的依赖,这不是建议,而是生产部署的强制要求。不添加此参数,依赖如 `phpunit`、`phpstan`、`laravel-debugbar` 等会被安装,这些包在非开发环境中通常是不必要的。不添加这些包不会导致错误,因为它们不会因为缺少而自动安装。
为什么 composer install --no-dev 有时没效果
根本原因不是命令写错了,而是 composer.lock 文件本身已“污染”:它记录了 require-dev 中的包(比如上次用 composer update 生成的)。此时 --no-dev 会被忽略,甚至报错 dev dependencies not found。
- 验证方法:运行
grep -A 5 "require-dev" composer.lock,输出非空就说明 lock 文件含 dev 条目 - 修复方式:在干净环境(无
vendor/、无旧lock)执行composer update --no-dev --lock,再提交新composer.lock - CI/CD 中必须先清理缓存:比如
git clean -xffd vendor/,否则 Docker 构建可能复用带 dev 包的 layer
--no-dev 实际跳过哪些东西
它不只是删掉 require-dev 列表里的包,而是一整套过滤链:
- 跳过
require-dev中所有包及其全部传递依赖(例如phpunit/phpunit带来的sebastian/exporter也不会进vendor/) - 不加载
autoload-dev配置——像"Tests\": "tests/"这种映射不会注册,new TestsFooTest()必然触发Class not found - 跳过
scripts中标记为@dev的钩子(如post-autoload-dump里调用phpstan的命令会静默跳过)
配套参数不能少:性能和安全都要兼顾
--no-dev 解决“装什么”,但不解决“怎么装得快、装得稳”。线上必须组合使用:
-
--optimize-autoloader(或-o):生成扁平化类映射,减少文件系统查找;但注意——若代码用了动态类名拼接(如new $className),且该类不在 autoload map 里,就会报错 -
--classmap-authoritative:告诉 autoloader “所有类都在 classmap 里,别再去磁盘找”,配合--no-dev更稳妥(因为 dev 类已被排除,classmap 更干净) -
--no-scripts和--no-interaction:避免部署卡在交互提示或执行前端构建等无关操作
真正容易被忽略的是 autoload-dev 映射残留问题:即使没装 phpunit,只要 vendor/autoload.php 里还保留着 tests/ 的 PSR-4 映射,class_exists('TestsFooTest') 仍可能返回 true——这会让某些条件判断逻辑意外通过,埋下线上隐患。

