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

preferred-install 不是开关,而是匹配策略
它不会强制所有包走 dist 或 source,只是告诉 Composer “优先尝试什么”。最终行为由三者叠加决定:命令行参数、composer.lock 固化记录、包自身是否提供 dist 或 source 字段。
常见错误现象:preferred-install 设为 "source" 后,某些包仍走 zip 下载。这是因为该包在 composer.lock 里已记录为 dist 安装方式,而 lock 文件的优先级高于配置;或者该包本身只声明了 dist,没提供 source 字段,Composer 只能 fallback 到 dist。
- 验证配置是否生效,必须删掉
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 从上到下匹配,第一个命中即止。
-
"dev-*"这类通配符也有效,适合只对开发版包启用源码安装 - 不要写成
"myorg/*": "source", "myorg/utils": "dist",后者永远不会生效,因为myorg/utils已被前面的myorg/*捕获 - 如果包名含斜杠但不是命名空间前缀(比如
acme/some-tool),通配符需完整匹配,"acme/*"才管用
--prefer-dist 和 --prefer-source 的真实影响
这两个命令行参数是硬开关,会跳过 preferred-install 配置,直接决定安装路径。它们的区别不在“新旧”,而在语义和用途:
-
--prefer-dist下载的是 tag 对应的归档包(zip/tar),不含 .git 目录、无历史提交,适合部署和 CI -
--prefer-source会git clone默认分支(通常是main),可git checkout、git pull,但首次安装慢、磁盘占用大 - dist 包无法切换到未打 tag 的 commit;source 方式可以,但前提是远程分支真有那条提交
- 若包只提供
source(如老旧私有库),加--prefer-dist会失败并报错Could not fetch...
为什么全局设置 preferred-install 是个坑
执行 composer config -g preferred-install source 看似方便,实则让所有新项目默认克隆代码——包括 monolog/monolog 这种纯 dist 包。一旦它没显式声明 dist 字段,Composer 就 fallback 到 source,CI 构建可能卡在 Cloning into 'vendor/monolog/monolog'。
真正该做的只有两步:
- 删掉全局配置:
composer config -g --unset preferred-install - 在项目
composer.json的config段里加精确策略,比如"preferred-install": {"*": "dist"}
团队协作时,这个配置必须进版本库,否则 CI 和本地行为不一致。容易被忽略的一点:Docker 构建镜像时若用缓存层,composer install 可能复用旧 lock 文件,导致配置看似生效实则没起作用——构建脚本里加 --no-cache 或显式 rm composer.lock 才可靠。


















