构建 Vue 项目
Supabase 服务的 Vue 构建官方文档:https://supabase.com/docs/guides/getting-started/quickstarts/vue
创建了一个 Github 仓库,用来存放 Vue 前端项目
克隆项目
git clone git@github.com:sincerefly/vuebase-posty.git
初始化 Vue 项目
cd vuebase-posty
npm create vue@latest .
选项
回车确认后的 Oxlint(试验阶段)和 rolldown-vite(试验阶段)都不选择,示例代码也不需要
运行三连
# 安装依赖
npm install
# 格式化代码
npm run format
# 启动
npm run dev
引入 Supabase 依赖
安装库
npm install @supabase/supabase-js
创建环境变量文件
touch .env.local
将服务地址和 Supabase Publishable Key 填入(注意替换为自己的地址和密钥)
VITE_SUPABASE_URL=<SUBSTITUTE_SUPABASE_URL>
VITE_SUPABASE_PUBLISHABLE_KEY=<SUBSTITUTE_SUPABASE_PUBLISHABLE_KEY>
这里有一些容易混淆的地方需要注意
Supabase 教程页面上显示出用户的 Anon Key,看着需要使用这个 key 作为SUBSTITUTE_SUPABASE_PUBLISHABLE_KEY
,那上篇中的 “sb_publishable_JToCFTxxxxxx” 又是什么,用哪个呢?
这个以
sb_publishable_
开头的密钥,实际上就是anon key
(匿名公钥),只是 Supabase 在不同时期使用了不同的命名格式。
结论就是用谁都行,sb_publishable_ 是旧的,JWT 格式的密钥是更新的格式
新建 src/lib/supabaseClient.js 文件
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
修改 src/App.vue 文件
根据我的表 Posts 做了相应调整
<script setup>
import { ref, onMounted } from 'vue'
import { supabase } from './lib/supabaseClient'
const posts = ref([])
async function getPosts() {
const { data } = await supabase.from('posts').select()
posts.value = data
}
onMounted(async () => {
await getPosts()
})
</script>
<template>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>
显示出来三篇已发布文章(标题相同)
导出表 Types
https://supabase.com/docs/guides/api/rest/generating-types
如果网络抽风,使用本地代理 npm 设置如下 npm config set proxy socks5://127.0.0.1:7897 npm config set https-proxy socks5://127.0.0.1:7897
使用后清理 npm config delete proxy npm config delete https-proxy
# 安装命令行工具
npm i supabase@">=1.8.1" --save-dev
# 打开浏览器登录
npx supabase login
登录后,如果之前未执行过初始化,先在项目根目录运行
npx supabase init
按需选择,默认都是 N
Generate VS Code settings for Deno? [y/N]
Generate IntelliJ Settings for Deno? [y/N]
Finished supabase init.
获取数据库的类型定义
mkdir -p src/types
# 生成 Schema,注意替换 PROJECT_REF,就是服务器 API 地址子域名那串字符
npx supabase gen types typescript --project-id "$PROJECT_REF" --schema public > src/types/database.types.tss
Vibe Coding 环节
好了,Step by Step 到此,接下来开始氛围编程
这是一个 Vue 项目,后端是 Supabase 服务,请实现以下功能:
表结构如下:
CREATE TABLE users (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
username TEXT UNIQUE CHECK (char_length(username) >= 3),
email TEXT UNIQUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
content TEXT,
published_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
已经定义了策略,所有用户(匿名、登录)都可以获取到已发布的文章,即 published_at 字段不为空的记录;用户可以创建、修改自己的文章。
以上是服务端一些表结构,另外已经通过 supabase cli 导出了数据库字段类型的 Schema,在 src/types/database.types.tss
前端功能说明
1. 页面顶部有两栏,一个是“广场”,另一个是“我的”栏目,右上角有注册、登录功能,广场展示所有已发布的文章,我的栏目,如果未登录,通过页面上的文字提示请先登录,登录后展示所有用户的文章,登录后的右上角展示用户名,点击弹出下拉,有设置和登出、设置页面目前可以设置语言偏好;
2. 用户我的页面的文章,需要显示是否发布,可以根据单选项过滤全部、已发布、未发布三个状态,文章后方应该有编辑、发布的按钮,支持修改标题和内容;
3. 需要支持多语言,目前仅需要适配中文、英文两种语言,用户选择语言后,应该在浏览器本地进行缓存;
编码风格说明
1.首先,保持现代化、但不要使用过于花哨的颜色,简洁、小清新为主
2.代码实现应注意解耦合和封装,不要多个逻辑放到一个大文件中
3.API 接口和数据库操作需要符合 Supabase 的使用规范和习惯
正好打算试试 Trae,不过目前的智能程度,真是爱了... ⬇️
还是配置 Proxy,使用 Cursor
3 Hours Later...
页面功能初步完成
多语言也支持的良好
调整策略
因为上篇实验中,缺少部分策略,可以在 Supabase 面板删除掉所有策略,重新创建本示例所需的策略
TODO 再核对下:
-- posts 表
alter policy "允许匿名和登录用户查看所有已发布文章"
on "public"."posts"
to anon, authenticated
using (
(published_at IS NOT NULL)
);
create policy "允许登录用户创建自己的文章"
on "public"."posts"
for insert
to authenticated
with check (
-- 确保用户只能插入自己的帖子
user_id = auth.uid()
);
create policy "允许登录用户删除自己的文章"
on "public"."posts"
as PERMISSIVE
for DELETE
to authenticated
using (
auth.uid() = user_id
);
create policy "允许登录用户查看自己所有文章" -- 包含未发布
on "public"."posts"
as PERMISSIVE
for SELECT
to authenticated
using (
auth.uid() = user_id
);
alter policy "允许登录用户更新自己的帖子"
on "public"."posts"
to authenticated
using (
(auth.uid() = user_id)
with check (
(auth.uid() = user_id)
);
alter policy "用户每天只能插入10篇文章"
on "public"."posts"
to authenticated
with check (
((auth.uid() = user_id) AND (( SELECT count(*) AS count FROM posts posts_1 WHERE ((posts_1.user_id = auth.uid()) AND (posts_1.created_at > (now() - '1 day'::interval)))) < 10))
);
-- users 表
create policy "允许用户查看自己的用户信息"
on "public"."users"
for select
to authenticated
using (
(select auth.uid()) = id
);
CREATE POLICY "允许用户更新自己的用户信息"
ON "public"."users"
FOR UPDATE
USING (auth.uid() = id);
通过 Cloudflare Page 部署
因为编译后是纯前端页面,所以可以托管到 Pages 服务,可选择性很多,优先国外,因为国内 Page 服务可持续性 be like
Github Pages、Cloudflare Pages、Vercel 作为 Demo 放到哪里都足够,根据我的个人习惯,选择部署到 Cloudflare Pages,因为我有一个域名由 Cloudflare 管理,绑定自定义域名时可以纵享丝滑
先上传前端代码到 Github,我的仓库是:sincerefly/vuebase-posty
登录 Cloudflare 面板
选择 Workers & Pages,点击创建
注意先切换到 Pages,然后再点击 Get started
选择项目后下一步
选择 Vue Framawork,参数默认,应该还记得 .env.local 文件,将里面的 VITE_SUPABASE_URL
和 VITE_SUPABASE_PUBLISHABLE_KEY
设置到此处环境变量
点击部署,稍后可以看到服务已部署
服务地址:https://vuebase-posty.pages.dev
.pages.dev 是 Cloudflare 提供的域名,子域名是服务名,重复会追加随机字符。
添加自定义域名(可选)
由 CF 托管的域名,无需手动配置
稍等片刻
地址:https://posty.donx-done.xyz
配置 Supabase 服务 URL 地址
配置完成后,到页面进行注册测试,当头两棒子
{"code":"over_email_send_rate_limit","message":"For security purposes, you can only request this after 49 seconds."}
{"code":"over_email_send_rate_limit","message":"email rate limit exceeded"}
这是一个 Supabase 配置,位置在 Authentication 下的 Rate Limit
改成 20 封邮件后,可以找临时邮箱进行注册验证
Supabase 注册 URL 自动登录逻辑
- 用户点击邮件中的确认链接(http://web-host/#access_token=xxx&refresh_token=xxx&type=recovery)
- Supabase 客户端自动检测 URL 参数(detectSessionInUrl: true),自动创建 session 并触发 onAuthStateChange 事件
- 认证状态监听器处理 (src/stores/auth.ts)
- 应用初始化 (src/App.vue)
我也没仔细看,因为全程氛围编程,没写几行前端代码
记在最后
Vibe Coding 一些心得,描述需求时要全面,但让其实现代码时要分步实现。Debug 时,让其添加 Console 日志,将问题日志提交给它,定位会更快、更准确
前端使用 Vue 开发,部署到了 Cloudflare:https://posty.donx-done.xyz
前端代码仓库:sincerefly/vuebase-posty
本文阶段性的目标已达成,这篇想了想,定为「中篇」,Supabase 还有不少值得探索的功能,放到「下篇」学习记录。