模块是带独立路由和生命周期的子应用,需继承yii\base\Module并注册到主应用配置中,通过ID映射实现三段式路由访问,支持事件通信与依赖注入,严禁硬编码路径或跨模块直接实例化。

模块是什么:不是插件,是带独立路由和生命周期的子应用
Yii 2.0 的模块(Module)本质是一个可复用、可挂载的“子应用”——它有自己的控制器、视图、配置、语言包,甚至能定义自己的 bootstrap 行为。但它不等于 Composer 包或独立服务,必须注册到主应用才能生效。
常见误解是把它当“功能开关”用:比如只放几个辅助函数就建个模块。这会导致路由混乱、配置冗余,反而增加维护成本。
- 真正适合拆成模块的场景:用户中心(
UserModule)、后台管理(AdminModule)、API 接口集合(ApiV1Module) - 模块名必须是合法 PHP 类名(驼峰,不含下划线),且全局唯一;重复注册会静默覆盖前一个
- 模块类必须继承
yii\base\Module,且至少实现init()方法(哪怕空着)
怎么注册模块:在 config/web.php 里配 modules 数组
模块不会自动加载,必须显式声明。核心是把模块 ID(路由前缀)映射到类路径,Yii 才知道该实例化谁、挂到哪。
错误现象:访问 /admin/dashboard 报 404,但控制器文件存在——大概率是没在 modules 里注册 admin 这个 ID,或者 ID 和 URL 前缀不一致。
-
modules是应用配置项,写在config/web.php的'components' => [...]同级位置 - ID 名(如
'admin')就是 URL 中的第一段路径,'admin/dashboard/index'的admin就来自这里 - 类名必须用完整命名空间,比如
'app\modules\admin\AdminModule',不能只写AdminModule - 如果模块有自定义配置(如启用缓存、切换布局),直接在数组里加键值对:
'admin' => ['class' => 'app\modules\admin\AdminModule', 'layout' => 'main-admin']
路由怎么访问:ID + 控制器 + 动作,三段式不可省略
模块内控制器的路由格式固定为 模块ID/控制器ID/动作ID,比如 admin/user/create。漏掉模块 ID 或大小写不匹配(Admin ≠ admin)都会 404。
容易踩的坑是控制器类名和 ID 混淆:控制器文件叫 UserController.php,对应 ID 是 user,不是 User 或 UserController;模块 ID 是注册时用的键名,不是类名。
- 控制器必须放在模块目录下的
controllers/子目录,且类名以Controller结尾(如UserController) - 默认动作是
index,所以/admin/user实际访问的是admin/user/index - 如果想用短路由(如
/admin直达首页),得在模块的init()里设置:$this->defaultRoute = 'dashboard/index'; - 模块内生成 URL 要用
\yii\helpers\Url::to(['site/index']),而不是硬写/admin/site/index—— 否则换模块 ID 就全崩
模块间怎么通信:靠事件、依赖注入或共享服务,别直接 new 类
模块之间默认隔离,不能直接 new \app\modules\user\models\User() 调用对方模型——这不是解耦,是埋雷。一旦模块路径改了、命名空间变了,所有调用点都得手动修。
更稳妥的方式是让主应用统一提供接口,或者通过 Yii 的事件机制松耦合通信。强行跨模块调用,后期迁移或抽离模块时会卡死。
- 需要共享逻辑?提取成独立组件(
common/components/UserHelper.php),在config/main.php里注册为components - 需要响应另一个模块的动作?比如用户注册后发通知,在
UserModule里触发Event::trigger('user.registered', $event),NotifyModule监听即可 - 模块内要用 DB?别自己 new
Connection,用\Yii::$app->db—— 它是主应用配置好的,模块自动继承 - 模块类的
init()方法里别做耗时操作(如读大文件、查库),会影响所有请求启动速度
模块的边界感比想象中重要:它不是代码文件夹,而是运行时的上下文切片。ID 命名、路由前缀、类路径、配置作用域,四者稍有错位,调试时就会陷入“文件明明在,就是找不到”的循环。











