从 Vue 到 React:Tailwind CSS 布局 + BFF 代理实战

从 Vue 到 React:Tailwind CSS 布局 + BFF 代理实战

前言

上一篇文章写了 Vue 3 + DeepSeek API 的流式输出,今天切换到 React 生态继续学习。在动手写 React 代码前,先搞清楚几个重要的工程化概念:Tailwind CSS 原子化样式BFF 层代理 ,以及 React 和 Vue 在响应式数据上的异同


一、Tailwind CSS 原子化布局

React 项目里经常用 Tailwind CSS,它跟传统写 CSS 完全不同------不需要写 CSS 文件,直接用类名拼布局

1.1 Flex 布局核心类

tsx 复制代码
<div className="flex flex-col justify-center items-center">
  {/* 子元素会纵向排列,水平和垂直都居中 */}
</div>

逐词拆解:

类名 CSS 等价 含义
flex display: flex 开启弹性布局
flex-col flex-direction: column 主轴改为垂直方向
justify-center justify-content: center 主轴居中
items-center align-items: center 交叉轴居中

主轴 vs 交叉轴 :默认主轴是水平方向,加了 flex-col 后主轴变成垂直方向。

  • justify-* 控制主轴
  • items-* 控制交叉轴

1.2 常用工具类速查

tsx 复制代码
<div className="
  h-screen        // height: 100vh --- 撑满整个屏幕
  h-full          // height: 100% --- 撑满父容器
  mx-auto         // margin-left/right: auto --- 水平居中
  mb-2            // margin-bottom: 0.5rem (8px)
  overflow-auto   // 内容超出显示滚动条
  relative        // position: relative --- 相对定位锚点
  justify-end     // 主轴尾部对齐(配合 flex-col 即底部对齐)
  text-center     // text-align: center --- 文字居中
  text-red-500    // color: #ef4444 --- 红色文字
">

记忆诀窍:{属性缩写}-{方向/值}-{大小},比如 mb-2 = margin-bottom 2 档 = 8px。


二、React vs Vue 响应式数据

两者都是响应式框架,但 API 不同:

Vue 3

js 复制代码
import { ref } from 'vue'
const count = ref(0)    // 用 ref 包装
count.value++           // 通过 .value 读写

React

tsx 复制代码
import { useState } from 'react'
const [count, setCount] = useState(0)  // 解构出值和 setter
setCount(count + 1)                     // 通过 setter 修改

核心差异:Vue 通过 .value 直接改,React 必须用 setCount 方法触发重渲染。

小技巧:!! 双取反

js 复制代码
!!"hello"   // true  --- 非空字符串转 true
!!0         // false --- 0 转 false
!!undefined // false

! 取反一次得到布尔值(但是反的),再 ! 翻回来,等价于 Boolean(x),常用于条件判断。


三、BFF 层 + Vite 代理

3.1 为什么需要 BFF?

scss 复制代码
浏览器 ─→ BFF(Node) ─→ DeepSeek API / 后端服务
(无 Key)    (Key 安全存这里)

BFF = Backend For Frontend,面向前端的后端层。API Key 不能暴露在浏览器,所以前端不直接调 DeepSeek,而是通过 BFF 中转。

3.2 Vite 代理配置

前端 Vite 跑在 5173 端口,BFF 跑在 3000 端口。浏览器有同源策略,不能跨端口请求。Vite 代理解决跨域

js 复制代码
// vite.config.js
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',  // 转发到后端
        secure: false,
        rewrite: (path) => path.replace(/^\/api/, ''),  // 去掉 /api 前缀
      }
    }
  }
})

效果:

bash 复制代码
前端请求:  /api/stream
    ↓ Vite 代理转发 + rewrite 去前缀
后端收到:  /stream

前端感觉自己在同源请求,实际是 Vite 偷偷转了。

3.3 Express 后端路由

js 复制代码
import express from 'express'
const app = express()

app.get('/stream', async (req, res) => {
  // 调用 DeepSeek API,流式返回给前端
  const response = await fetch('https://api.deepseek.com/chat/completions', {
    headers: { Authorization: `Bearer ${process.env.API_KEY}` },
    body: JSON.stringify({ stream: true, /* ... */ })
  })
  // 逐块转发
  const reader = response.body.getReader()
  while (true) {
    const { done, value } = await reader.read()
    if (done) break
    res.write(value)
  }
  res.end()
})

app.listen(3000)

路由就是 URL 和处理函数的映射------用户访问什么路径,服务器执行什么代码。配合 RESTful 风格,URL 表示资源,HTTP 方法表示操作:

bash 复制代码
GET    /users     → 获取用户列表
POST   /users     → 创建用户
PUT    /users/1   → 更新用户
DELETE /users/1   → 删除用户

四、RAG 相关概念速览

项目中还涉及了 RAG(检索增强生成)的基础流程:

概念 一句话解释
Loader 负责读取不同格式文件(PDF、CSV、网页)
Cheerio Node.js 版 jQuery,把 HTML 字符串解析成 DOM 树
CSS Selector 定位 HTML 元素,如 .main-area p 选文章段落
Chunk 大文件切成的具有语义完整性的小段文本,是 RAG 检索的最小单位
蒸馏(Distillation) 用大模型"教"小模型,让小的也有大模型大部分能力

五、总结

今天在技术栈上做了横向和纵向的扩展:

维度 内容
框架 Vue → React,理解了 useStateref 的异同
样式 从手写 CSS 到 Tailwind 原子化类名
架构 从纯前端直接调 API 到 BFF 层代理转发
跨域 从 Vite proxy 理解前后端分离中的网络瓶颈
AI 从 RAG 流程(Loader → Splitter → Chunk)了解知识库底层

前端入门只是第一步,理解架构才能写出安全的代码。欢迎交流!

相关推荐
摸鱼老王不带86 小时前
不用后端,纯前端读出"网站眼里你的真实出口 IP"——聊聊 Cloudflare 的 cdn-cgi/trace
前端
程序员黑豆6 小时前
鸿蒙开发入门:Row 和 Column 布局组件详解
前端·harmonyos
hoLzwEge7 小时前
前端环境变量配置指南:.env 文件详解
前端·前端框架
hunterandroid7 小时前
Paging 3 RemoteMediator 实战:构建离线优先的分页列表
android·前端
Underwood_177 小时前
星级评价——了解useState
前端
hunterandroid7 小时前
[鸿蒙从零到一] ArkUI 组件化实战:构建可复用、可组合的自定义组件
前端·华为·架构
一只公羊7 小时前
iPhone摄像头 在开发/调试过程中强行停止 App,导致 `AVCaptureSession` 没有被正常释放
前端
浮江雾8 小时前
Flutter第十节-----Flutter布局与组件全解析
android·开发语言·前端·学习·flutter·入门