Vue.js、Nuxt、Nuxt UI、Supabase 和 Vercel,这个组合简直是天作之合。感觉所有东西都能无缝协作,语法简洁、实时更新、部署简单……完全没有令人头疼的问题!让我们来构建一个简单的待办事项列表应用从开发到部署
这些工具共同创造了一个既轻松又强大的开发环境,非常适合在不被琐事拖累的情况下构建真正的应用程序。
首先,我使用命令行工具创建了一个新的 Nuxt 项目:
# 使用 nuxi 脚手架初始化一个新的 Nuxt 项目
npx nuxi init nuxt-supabase-todo
# 进入项目目录
cd nuxt-supabase-todo
然后,我安装了 UI 和 Supabase 模块:
# 安装 Nuxt UI 和 Nuxt Supabase 模块
npm install @nuxt/ui @nuxtjs/supabase
它们的作用:
@nuxt/ui
:提供美观、可定制的组件(卡片、输入框、按钮),开箱即用。@nuxtjs/supabase
:让你能够与 Supabase 后端通信,而无需编写额外的模板代码。现在我们来处理前端部分。我想要一个极简但功能齐全的应用:可以添加、编辑、标记完成和删除任务。没有什么花哨的功能,但必须工作良好。
这是我构建的主页面 (pages/index.vue
):
<template>
<!-- 整体布局:最小高度为屏幕高度,内容居中显示 -->
<div class="min-h-screen flex items-center justify-center">
<!-- 内容容器:最大宽度为md,水平居中,内边距为4 -->
<div class="max-w-md w-full mx-auto p-4">
<!-- Nuxt UI 卡片组件,用于包裹整个应用界面 -->
<UCard class="p-6 bg-white shadow-md rounded-lg">
<!-- 卡片头部插槽 -->
<template #header>
<h1 class="text-2xl font-bold mb-4 text-center">待办事项列表</h1>
</template>
<!-- 添加新任务的区域 -->
<div class="mb-4">
<!-- Nuxt UI 输入框,双向绑定 newTask -->
<UInput
v-model="newTask"
placeholder="输入新的任务"
class="mr-2"
/>
<!-- Nuxt UI 按钮,点击时调用 addTask 方法 -->
<UButton
label="添加任务"
color="primary"
@click="addTask"
/>
</div>
<!-- 任务列表区域 -->
<div class="space-y-4">
<!-- 循环渲染任务列表 -->
<div
v-for="task in tasks"
:key="task.id"
class="flex items-center p-2 border-b border-gray-200"
>
<!-- Nuxt UI 复选框,绑定任务的完成状态,状态改变时调用 updateTask -->
<UCheckbox
v-model="task.completed"
@change="updateTask(task)"
class="mr-2"
/>
<!-- 如果不在编辑状态,显示任务标题。如果已完成,添加删除线样式 -->
<span
v-if="!isEditing[task.id]"
:class="{ 'line-through': task.completed }"
>{{ task.title }}</span
>
<!-- 如果在编辑状态,显示输入框以供修改 -->
<UInput
v-else
v-model="editedTask"
class="mr-2 flex-1"
/>
<!-- 如果不在编辑状态,显示"编辑"按钮 -->
<UButton
v-if="!isEditing[task.id]"
label="编辑"
size="xs"
color="secondary"
class="ml-2"
@click="startEdit(task)"
/>
<!-- 如果在编辑状态,显示"保存"按钮 -->
<UButton
v-else
label="保存"
size="xs"
color="success"
class="ml-2"
@click="saveEdit(task)"
/>
<!-- 删除按钮 -->
<UButton
label="删除"
size="xs"
color="error"
class="ml-2"
@click="deleteTask(task.id)"
/>
</div>
</div>
</UCard>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
// 获取 Supabase 客户端实例
const supabase = useSupabaseClient();
// 定义响应式状态
const newTask = ref(""); // 新任务的标题
const tasks = ref([]); // 任务列表
const isEditing = ref({}); // 跟踪每个任务是否处于编辑状态
const editedTask = ref(""); // 当前正在编辑的任务标题
let channel; // Supabase 实时订阅频道
// 添加新任务
async function addTask() {
if (newTask.value.trim()) {
const { error } = await supabase.from("tasks").insert({
title: newTask.value,
completed: false,
});
if (error) console.error(error);
else newTask.value = ""; // 成功后清空输入框
}
}
// 更新任务(例如,标记为完成)
async function updateTask(task) {
const { error } = await supabase.from("tasks").update({ completed: task.completed }).eq("id", task.id);
if (error) console.error(error);
}
// 删除任务
async function deleteTask(id) {
const { error } = await supabase.from("tasks").delete().eq("id", id);
if (error) console.error(error);
}
// 开始编辑任务
function startEdit(task) {
isEditing.value[task.id] = true;
editedTask.value = task.title;
}
// 保存编辑后的任务
async function saveEdit(task) {
if (editedTask.value.trim()) {
const { error } = await supabase.from("tasks").update({ title: editedTask.value }).eq("id", task.id);
if (error) console.error(error);
else isEditing.value[task.id] = false; // 成功后退出编辑状态
}
}
// 从 Supabase 获取所有任务
async function fetchTasks() {
const { data, error } = await supabase.from("tasks").select("*");
if (error) console.error(error);
else tasks.value = data;
}
// 组件挂载时执行
onMounted(() => {
// 首次加载时获取任务
fetchTasks();
// 创建一个 Supabase 实时订阅频道
channel = supabase
.channel("tasks-channel") // 频道名称
.on(
"postgres_changes", // 监听数据库变更
{ event: "*", schema: "public", table: "tasks" }, // 监听 tasks 表的所有事件
(payload) => {
// 当数据库发生变化时,重新获取任务列表以更新UI
fetchTasks();
}
)
.subscribe(); // 开始订阅
});
// 组件卸载时执行
onUnmounted(() => {
// 移除频道订阅,防止内存泄漏
if (channel) {
supabase.removeChannel(channel);
}
});
</script>
得益于 Nuxt UI 的 UCard
、UInput
、UButton
和 UCheckbox
等组件,代码结构非常清晰。我还利用了 Vue 的响应式系统,并将其与 Supabase 结合起来,用于获取和更新数据。
我不想为了存储和获取任务而费心去设置服务器或编写大量样板代码。Supabase 让这一切变得简单。它基于 Postgres 构建,但增加了实时更新、用户认证和文件存储等功能,基本上让你拥有一个完整的后端,而没有后端的烦恼。对于像这样一个简单的待办事项应用来说,它绰绰有余。
访问 supabase.com,注册一个账户(免费),然后创建一个新项目。项目启动后,从项目设置的 API 部分获取你的 项目 URL 和 Anon Key。
在 Supabase 仪表盘中,进入 Table Editor 并创建一个名为 tasks
的新表。添加以下列:
id
– Integer (设置为 primary key, auto-increment)title
– Textcompleted
– Boolean点击保存,你就完成了。
行级安全策略 (Row-Level Security, RLS):Supabase 提供 RLS 来控制对表行的访问,通过定义策略(例如,谁可以读取或写入数据)来增加一层安全性。对于这个个人项目,为了简单起见,我禁用了 RLS 以允许公共访问——我创建了一个始终返回
true
的策略,然后完全关闭了 RLS。对于一个演示应用来说,这没问题,但对于生产环境,我肯定会保持 RLS 启用并定义适当的策略来保护用户数据。
更新你的 nuxt.config.ts
文件,包含模块和你的密钥:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ["@nuxt/ui", "@nuxtjs/supabase"],
supabase: {
// 从环境变量中读取 Supabase URL
url: process.env.SUPABASE_URL,
// 从环境变量中读取 Supabase Anon Key
key: process.env.SUPABASE_KEY,
// 禁用默认的重定向行为
redirect: false,
},
});
在你的 Nuxt 项目根目录下创建一个 .env
文件,并添加 Supabase 的项目 URL 和 Anon 公钥:
# .env
# 替换为你的 Supabase 项目 URL
SUPABASE_URL=your-supabase-url
# 替换为你的 Supabase Anon Key
SUPABASE_KEY=your-anon-key
安装模块后,useSupabaseClient()
让你可以在任何组件中完全访问 Supabase。在我的例子中,我用它在页面加载时获取所有任务,以及添加、编辑和删除任务。
所有这些逻辑都已在前面的 index.vue
代码中展示了,而且效果非常好。每当添加或更新任务时,UI 都会即时更新。
注意:Supabase 可以处理 90% 的用例,比如 CRUD 操作、认证、文件存储和实时数据。对于更复杂的应用,它可以通过其边缘函数和 API 覆盖更多场景。
当应用在本地运行正常后,我想把它部署到线上,而 Vercel 让这一切变得异常简单!!我没有动一个配置文件,也没有写一个部署脚本。我是这样做的:
首先,我在 GitHub 上创建了一个新仓库,并将我的 Nuxt 项目推了上去:
# 初始化 Git 仓库
git init
# 添加远程仓库地址
git remote add origin https://github.com/yourusername/your-repo.git
# 添加所有文件到暂存区
git add .
# 创建一个初始提交
git commit -m "Initial commit"
# 推送到远程主分支
git push -u origin main
在继续之前,请确保你的包含 Supabase 密钥的
.env
文件没有被上传到 GitHub。如果你使用 CLI 创建 Nuxt 项目并选择初始化 GitHub 仓库,它很可能已经为你生成了
.gitignore
文件。该文件应该已经排除了.env
、node_modules
以及其他你不想放在公共仓库里的东西。在推送前请务必仔细检查。即使是演示项目,保持密钥私有也是一个好习惯。
然后我访问 vercel.com,登录并点击 “New Project”。
Vercel 自动检测到了我的仓库。我只需要:
SUPABASE_URL
和 SUPABASE_KEY
)。就这样。不到一分钟,我的应用就上线了,并获得了一个 .vercel.app
的域名。
每当我向 GitHub 推送一个更改,Vercel 都会自动重新构建并重新部署应用。没有停机时间,没有手动部署,没有 DevOps 的头痛问题。
这真的是这个技术栈我最喜欢的部分之一。它让发布更新感觉就像是正常开发流程的一部分,而不是我必须去操心的额外步骤。
注意:如果你想让事情变得更加精致,Vercel 还允许你只需点击几下即可连接自定义域名。你可以使用他们的 DNS 或配置自己的,怎么方便怎么来。
这个项目最初只是一个快速的实验。但一切都行云流水。不需要设置服务器,也不需要从头开始设计界面。对我来说,这正是一个现代 Web 开发应有的感觉:快速上手,愉快构建,顺畅发布
完整的源代码在这里可以找到:👉 https://gitee.com/riki1020/nuxt-todo.git
随时可以克隆它、体验它,或将其用作你自己项目的起点。
感谢阅读!
评论: