Clean Architecture 中 Vue 组件该怎么放?

兴敏酱~ 阅读 35

我最近在用 Vue 3 + TypeScript 搭一个 Clean Architecture 的项目,但卡在了组件的目录结构上。按照 Clean Architecture 的分层,UI 层应该只负责展示,不能包含业务逻辑。但我现在把 Vue 组件直接放在了 presentation 目录下,结果发现组件里还是得调用 useCase,感觉有点混乱。

比如下面这个登录组件,它直接依赖了 LoginUseCase,这样是不是违反了分层原则?有没有更干净的组织方式?

<script setup lang="ts">
import { LoginUseCase } from '@/core/usecases/LoginUseCase'
import { ref } from 'vue'

const email = ref('')
const password = ref('')

const handleLogin = async () => {
  await new LoginUseCase().execute(email.value, password.value)
}
</script>

<template>
  <form @submit.prevent="handleLogin">
    <input v-model="email" type="email" />
    <input v-model="password" type="password" />
    <button type="submit">登录</button>
  </form>
</template>
我来解答 赞 11 收藏
二维码
手机扫码查看
2 条解答
小怡轩
小怡轩 Lv1
把 useCase 的调用封装成 composable,组件只依赖 composable,不直接碰 useCase。

建一个 useLogin.ts

import { LoginUseCase } from '@/core/usecases/LoginUseCase'
import { ref } from 'vue'

export function useLogin() {
const email = ref('')
const password = ref('')
const loading = ref(false)

const login = async () => {
loading.value = true
try {
await new LoginUseCase().execute(email.value, password.value)
} finally {
loading.value = false
}
}

return { email, password, login, loading }
}


组件改成这样:

<script setup lang="ts">
import { useLogin } from '@/presentation/composables/useLogin'

const { email, password, login } = useLogin()
</script>

<template>
<form @submit.prevent="login">
<input v-model="email" type="email" />
<input v-model="password" type="password" />
<button type="submit">登录</button>
</form>
</template>


组件现在是纯展示的,业务逻辑全在 composable 里,依赖方向就干净了。目录结构大概是 presentation/components/ 放组件,presentation/composables/ 放业务粘合层。
点赞 1
2026-03-10 21:04
Air-恩贝
把 useCase 从组件里抽出来,通过依赖注入的方式传入。组件只负责 UI 展示,业务逻辑交给 Presenter 或 ViewModel。

// presentation/presenters/LoginPresenter.ts
export class LoginPresenter {
constructor(private loginUseCase: LoginUseCase) {}

async login(email: string, password: string) {
return await this.loginUseCase.execute(email, password)
}
}

// presentation/views/LoginView.vue


更讲究的做法是把 presenter 也作为依赖注入进去,组件完全不知道 useCase 的存在。
点赞
2026-03-10 18:09