preferred-install是优先策略而非强制开关,最终安装方式由命令行参数、composer.lock记录及包自身dist/source字段三者叠加决定;需清空vendor和lock文件并避免--prefer-dist干扰才能验证配置生效。

preferred-install 不是开关,是优先尝试策略
它不会强制所有包走 dist 或 source,而是告诉 Composer “遇到这个包时,优先试试哪种方式”。最终安装方式由三者叠加决定:--prefer-dist 命令行参数、composer.lock 中已记录的安装方式、包自身是否声明了 dist 或 source 字段。
常见错误现象:设了 "preferred-install": "source",但 monolog/monolog 仍下 zip。这是因为该包在 composer.lock 里早已记为 dist 安装,而 lock 文件的优先级高于配置;或者该包根本没提供 source 字段(比如纯 ZIP 发布的私有库),Composer 只能 fallback。
- 验证配置是否生效,必须删掉
vendor/和composer.lock,再执行composer install - 若命令行加了
--prefer-dist,它会直接覆盖preferred-install配置 - 私有 Git 仓库若未启用 archive 功能(即没生成 zip 包),即使设
"dist",也会静默回退到source
怎么让自家包走 source,第三方走 dist
用对象形式配置最稳妥,避免全局污染。例如开发中要频繁调试内部 SDK,但又不想拖慢 CI 构建:
"config": {
"preferred-install": {
"myorg/*": "source",
"*": "dist"
}
}
这个配置的实际效果是:所有 myorg/xxx 开头的包优先 git clone,其余包走 zip。注意匹配顺序——Composer 从上到下匹配,第一个命中即止。
-
"myorg/*": "source"之后再写"myorg/utils": "dist"永远不会生效,因为已被前面通配符捕获 - 包名含斜杠但不是命名空间前缀(如
acme/some-tool),通配符需完整匹配,"acme/*"才管用 -
"dev-*"这类通配符也有效,适合只对开发版包启用源码安装
dist 和 source 在 vendor 里到底差在哪
区别不只在“快不快”,更影响后续操作逻辑和磁盘占用:
-
dist:下载预打包的 zip/tar.gz,速度快、占空间小、解压后无.git目录,不可修改,适合稳定依赖(如 Laravel、Symfony) -
source:执行git clone,带完整 Git 历史,能git checkout调试、打 patch、提 PR,适合你正在协作开发的私有库或 fork 的包 - 本地
path类型仓库天生就是source,preferred-install对它无效 - 开发分支约束(如
"dev-main")通常不发dist,自动退到source
CI 和 Docker 构建中最容易踩的坑
CI 环境里最常出问题的是:本地开发设了 "preferred-install": "source" 并提交了 composer.lock,结果 CI 机器没装 git 或无法访问私有 Git 服务,直接报错 Failed to execute git clone。
- CI 环境一律用
composer install --prefer-dist --no-dev显式指定,不依赖项目配置 - 不要用
composer config -g preferred-install全局设置,它会影响所有项目,导致团队行为分裂 - 若包没声明
dist字段(比如私有 GitLab 仓库没配"dist": {"url": "...", "reference": "..."}),即使设"dist"也会 fallback,且不报错
真正关键的不是“设成什么”,而是确认每个包是否实际支持你想要的方式——运行 composer show vendor/package,看输出里有没有 dist: 或 source: 行。没有对应字段,配置就不起作用。











