项目结构优化核心是清晰表达意图:composer.json须置于根目录,vendor/不得污染源码,autoload需与路径/命名空间严格对齐,前端资源应与PHP依赖物理隔离。

项目结构不是“越深越好”,而是要让 composer.json 能清晰表达意图、vendor/ 不污染源码、自动加载不绕弯、部署时能精准剔除无关内容。
composer.json 必须放在项目根目录,且不能嵌套
Composer 只认当前工作目录下的 composer.json,不会向上查找父目录,也不支持子目录里放多个 composer.json 来“分模块管理”。一旦你把 composer.json 放进 src/ 或 app/,运行 composer install 就会报错或生成错误的 vendor/ 路径。
- 所有命令(
composer install、composer update、composer dump-autoload)都必须在composer.json所在目录执行 - 如果项目是库(library),根目录下除了
composer.json还应有src/、tests/、README.md,但绝不能有vendor/(库本身不安装依赖,由使用者安装) - 如果项目是应用(application),根目录下可有
public/(Web 入口)、config/、bin/,但vendor/必须与composer.json同级,且不能提交到 Git
autoload 配置要匹配实际目录结构,别硬套 PSR-4 模板
PSR-4 映射不是“写对了就行”,而是要和文件系统路径、命名空间声明、类文件位置三者完全对齐。常见错误是改了目录名但忘了同步改 composer.json 里的 "psr-4" 值,或者类文件里写的 namespace 和映射前缀不一致。
- 例如:目录是
src/Http/Controllers/,类文件里是namespace AppHttpControllers;,那composer.json就得写:"autoload": { "psr-4": { "App\": "src/" } } -
src/下不能混放不同命名空间的类,否则自动加载器会找不到类或误加载 - 开发时用
composer dump-autoload -o生成优化后的映射,但上线前务必确认-o不会漏掉动态注册的类(比如某些测试工具依赖的反射类)
vendor/ 目录必须被 .gitignore 排除,但 composer.lock 不能丢
vendor/ 是构建产物,不是源码 —— 它的内容由 composer.lock 精确决定,重复提交只会制造冲突、增大仓库体积、误导新成员以为“改了 vendor 就算改了依赖”。
-
.gitignore中必须包含:/vendor/ !composer.lock
- CI/CD 流水线中必须用
composer install --no-dev --optimize-autoloader,而不是composer update;后者会重写composer.lock,破坏环境一致性 - 如果项目是库,
composer.lock可以不提交(因为库不直接运行),但如果是应用(如 Laravel、Symfony 项目),composer.lock是强制提交项,否则不同机器上composer install结果可能不一致
前端资源不要塞进 vendor/,用 scripts 钩子桥接 npm/yarn
Composer 管 PHP,npm/yarn 管 JS/CSS,强行用 fxp/composer-asset-plugin 或自定义 installer 把前端包下到 vendor/ 已被主流社区弃用 —— 它导致版本混乱、构建不可控、安全扫描误报。
- 前端资源统一放在
resources/(源码)和public/build/(构建输出),与vendor/物理隔离 - 在
composer.json的"scripts"中定义钩子:"post-install-cmd": [ "@php -r "file_exists('package.json') && system('npm ci && npm run build');"" ], "post-update-cmd": [ "@php -r "file_exists('package.json') && system('npm ci && npm run build');"" ] - 注意:用
npm ci而非npm install,它严格按package-lock.json安装,避免node_modules/出现差异
真正难的不是结构本身,而是每次加新包、换框架、升级 PHP 版本时,能否一眼看出 autoload 是否断了、dev 依赖有没有漏掉 --no-dev、前端构建是否被钩子跳过 —— 这些细节藏在日常操作里,不在文档首页。











