Remix框架:高性能React全栈开发实战

引言

在Web开发快速发展的今天,React生态系统中涌现出众多优秀框架,其中Remix凭借独特的设计理念和强大功能,正成为构建现代Web应用的热门选择。这款由React Router团队打造的全栈框架,聚焦性能优化、开发体验和Web标准,为开发者提供了一站式解决方案,涵盖路由管理、数据加载、表单处理到部署优化等核心环节。

本文将系统解析Remix框架的核心特性与实现原理,并通过生态对比和实践案例,带您全方位认识这一工具。主要内容包括:

  • Remix的核心设计理念与架构优势
  • 嵌套路由与数据加载的集成实现
  • 服务端渲染(SSR)与客户端渲染(CSR)的协同机制
  • 与Next.js等主流框架的对比分析
  • 项目实战中的最佳实践
  • 性能调优与部署方案

无论您是技术选型决策者,还是追求开发效率的全栈工程师,本文都将提供实用洞见,助您高效运用Remix框架。

一、Remix框架概述与核心特性

1.1 Remix的起源与设计哲学

Remix诞生于React Router团队,这个团队在客户端路由领域有着深厚的积累和经验。正是基于对React应用路由管理的深刻理解,Remix被设计为一个全栈Web框架,而不仅仅是另一个React框架。它的核心理念是"回归Web本质",强调利用浏览器和HTTP协议的原生能力,而非与之对抗。

与传统的单页应用(SPA)框架不同,Remix采用了渐进增强 (Progressive Enhancement)的策略,确保应用即使在JavaScript不可用或加载失败的情况下也能正常工作。这种设计哲学使Remix应用具有更好的韧性 (Robustness)和可访问性(Accessibility)。

1.2 核心特性深度解析

1.2.1 嵌套路由与布局系统

Remix的路由系统是其最强大的特性之一。它采用基于文件系统的路由配置,类似于Next.js,但提供了更灵活的嵌套路由支持。在Remix中,文件系统的结构直接映射到URL路由:

bash 复制代码
app/routes/
  ├── index.tsx        -> /
  ├── about.tsx        -> /about
  └── dashboard/
       ├── index.tsx   -> /dashboard
       └── settings.tsx -> /dashboard/settings

嵌套路由的关键优势在于代码和资源的按需加载 。当用户访问/dashboard/settings时,Remix只会加载与dashboardsettings相关的代码,而不会加载应用中其他不相关的部分。这种设计显著减少了初始加载时间。

在布局方面,父路由可以通过<Outlet />组件渲染子路由的内容,实现布局继承和局部更新。例如,一个管理后台可能具有固定的侧边栏导航和顶部栏,只有内容区域会根据路由变化:

js 复制代码
// app/routes/dashboard.tsx
export default function Dashboard() {
  return (
    <div className="dashboard-layout">
      <Sidebar />
      <div className="content-area">
        <Outlet /> {/* 子路由内容将在这里渲染 */}
      </div>
    </div>
  );
}

这种设计不仅提高了用户体验(避免了全页面刷新),还增强了代码的可维护性,因为共享的布局逻辑只需在父路由中定义一次。

1.2.2 数据加载与提交一体化

Remix创新性地将数据加载 (Data Loading)和数据变更(Data Mutation)与路由系统紧密集成。每个路由可以定义两个关键函数:

  1. loader:负责为路由提供所需数据
  2. action:处理路由接收的表单提交
js 复制代码
// app/routes/posts/$id.tsx
import { json } from "@remix-run/node";
import { useLoaderData, Form } from "@remix-run/react";

// 数据加载
export async function loader({ params }) {
  const post = await db.posts.findUnique({ where: { id: params.id } });
  return json(post);
}

// 数据变更
export async function action({ request }) {
  const formData = await request.formData();
  await db.posts.update({
    where: { id: formData.get("id") },
    data: { title: formData.get("title") }
  });
  return redirect("/posts");
}

export default function Post() {
  const post = useLoaderData<typeof loader>();
  
  return (
    <Form method="post">
      <input name="id" defaultValue={post.id} hidden />
      <input name="title" defaultValue={post.title} />
      <button type="submit">保存</button>
    </Form>
  );
}

这种设计有几个显著优势:

  • 数据与UI耦合:每个路由明确定义了其数据需求,便于理解和维护
  • 服务端优先:数据获取和变更逻辑主要在服务端执行,减少客户端复杂度
  • 自动重新验证 :在action执行后,Remix会自动重新调用相关路由的loader获取最新数据
1.2.3 渐进式增强与韧性设计

Remix坚持渐进增强(Progressive Enhancement)原则,确保应用在多种环境下都能正常工作。最典型的例子是表单处理:

js 复制代码
// 传统React表单
<form onSubmit={async (e) => {
  e.preventDefault();
  await fetch('/api/submit', { method: 'POST', body: JSON.stringify(data) });
  // 处理响应...
}}>
js 复制代码
// Remix表单
<Form method="post">
  {/* 表单字段 */}
</Form>

传统React表单完全依赖JavaScript,如果JS加载失败或执行出错,表单将无法提交。而Remix的表单即使在没有JavaScript的情况下也能正常工作,因为浏览器会执行默认的表单提交行为。

这种设计不仅提高了应用的韧性 ,还对SEO可访问性有积极影响。搜索引擎爬虫和屏幕阅读器等辅助技术能够更好地理解和使用Remix构建的应用。

1.2.4 性能优化机制

Remix内置了多种性能优化策略:

  1. 自动代码拆分:基于路由的代码拆分确保用户只下载当前页面所需的代码
  2. 资源预加载 :当用户鼠标悬停在<Link>上时,Remix会自动预加载目标路由的代码和数据
  3. 并行数据加载 :嵌套路由的loader会并行执行,而非串行等待
  4. 智能缓存 :利用HTTP缓存头(Cache-Control)实现高效的资源缓存策略

这些优化共同作用,使得Remix应用即使在较慢的网络环境下也能提供流畅的用户体验。

二、Remix架构与工作原理

2.1 服务端与客户端协作模型

Remix采用了独特的服务端与客户端协作渲染模型,结合了服务器端渲染(SSR)和客户端导航的优点。让我们通过一个典型的页面加载流程来理解这一机制:

  1. 初始请求(SSR阶段)
    • 浏览器请求/dashboard/analytics
    • 服务端执行匹配路由的loader函数获取数据
    • 服务端渲染完整的HTML文档
    • 响应包含HTML和序列化的loader数据

Browser Server Database GET /dashboard/analytics 执行loader查询数据 返回数据 渲染React组件为HTML 返回HTML + 内联数据 Browser Server Database

  1. 客户端导航(CSR阶段)
    • 用户点击<Link to="/dashboard/settings">
    • Remix拦截导航,避免全页面刷新
    • 客户端获取/dashboard/settings的loader数据(JSON格式)
    • React仅更新变化的部分UI

Browser Server GET /dashboard/settings (数据请求) 执行loader函数 返回JSON数据 React协调更新DOM Browser Server

这种协作模型的关键优势在于:

  • 快速首屏渲染:SSR确保用户立即看到内容,无需等待JS加载执行
  • 流畅的后续导航:客户端导航避免了全页面刷新,提供类似SPA的体验
  • 数据一致性:无论是SSR还是CSR,数据获取逻辑保持一致(相同的loader函数)

2.2 编译器与运行时架构

Remix的架构可以清晰地分为编译器运行时两部分:

2.2.1 编译器(Compiler)

Remix编译器基于esbuild构建,负责:

  1. 代码拆分:根据路由结构自动拆分应用代码
  2. 服务端/客户端分离
    • 服务端包:包含loader/action逻辑,仅在服务端运行
    • 客户端包:包含UI组件和交互逻辑
  3. 资源优化:处理CSS、图像等静态资源

这种分离确保了客户端包不会包含任何服务端逻辑,减少了不必要的代码传输。

2.2.2 运行时(Runtime)

Remix运行时包括:

  1. 服务端适配器

    • @remix-run/node:Node.js环境
    • @remix-run/cloudflare:Cloudflare Workers环境
    • 其他平台适配器

    这些适配器将平台特定的请求/响应对象转换为Remix的标准Fetch API格式。

  2. 客户端路由

    • 基于React Router的增强实现
    • 处理客户端导航、预加载、滚动恢复等
  3. 数据管理

    • 处理loader数据的序列化/反序列化
    • 管理客户端数据缓存

2.3 错误处理与边界

Remix提供了强大的错误边界(Error Boundaries)机制,允许开发者在不同粒度上处理错误:

  1. 路由级错误边界
    每个路由可以定义ErrorBoundary组件,捕获该路由及其子路由的错误
js 复制代码
// app/routes/dashboard.tsx
import { useRouteError } from "@remix-run/react";

export function ErrorBoundary() {
  const error = useRouteError();
  return (
    <div className="error-container">
      <h1>出错了!</h1>
      <p>{error.message}</p>
    </div>
  );
}
  1. 全局错误边界

    根路由(app/root.tsx)的ErrorBoundary会捕获未被其他边界处理的错误

  2. Catch边界

    对于预期的错误(如404),可以使用CatchBoundary提供更友好的UI

js 复制代码
import { useCatch } from "@remix-run/react";

export function CatchBoundary() {
  const caught = useCatch();
  return (
    <div>
      <h1>{caught.status} {caught.statusText}</h1>
      <p>{caught.data}</p>
    </div>
  );
}

这种分层次的错误处理机制使得开发者可以精细控制错误恢复策略,避免因局部错误导致整个应用崩溃。

三、Remix与生态系统的对比

3.1 Remix vs Next.js:架构与特性对比

作为React生态中最受欢迎的两个全栈框架,Remix和Next.js各有侧重。以下从多个维度进行详细对比:

路由系统
  • Remix :采用嵌套路由设计,通过文件系统约定自动生成路由结构。例如app/routes/posts/$id.tsx会自动映射到/posts/:id路径。嵌套路由支持布局共享和数据继承。
  • Next.js :早期基于pages目录(如pages/posts/[id].js),13版本后引入App Router,支持更灵活的布局系统。App Router同样采用文件系统约定,但支持并行路由和拦截路由等高级特性。
数据加载
  • Remix :每个路由可定义loader函数处理数据获取,action处理表单提交。数据与路由强绑定,例如:

    javascript 复制代码
    export async function loader({ params }) {
      return getPost(params.id);
    }
  • Next.js :传统方式使用getServerSideProps(SSR)或getStaticProps(SSG)。App Router中改用async组件,如:

    javascript 复制代码
    export default async function Page({ params }) {
      const post = await getPost(params.id);
      return <PostDetail post={post} />;
    }
表单处理
  • Remix :深度集成HTML原生表单,支持<Form>组件和action处理。提交时自动处理CSRF防护,典型流程:
    1. 用户提交表单
    2. 触发路由action函数
    3. 自动重新执行相关loader
  • Next.js :默认无内置表单处理,需配合useState或React Hook Form等库。App Router引入服务端Actions后,可通过"use server"实现类似功能。
渲染策略
  • Remix :默认SSR渲染,支持动态JS加载(如:clientLoader)。适合内容频繁变化的场景,如电商产品页。
  • Next.js
    • SSG:构建时生成静态HTML(博客文档)
    • ISR:静态页面增量更新(商品列表)
    • CSR:客户端动态渲染(用户仪表盘)
部署适配
  • Remix :通过适配器支持多种运行时:
    • @remix-run/node:传统Node服务
    • @remix-run/deno:Deno环境
    • @remix-run/cloudflare:Edge环境
  • Next.js:在Vercel平台有自动优化(如边缘函数、图片优化)。也可部署到Node服务器或其他支持静态托管的平台。
典型应用场景
  • 选择Remix
    • 表单密集型应用(后台管理系统)
    • 需要精细错误处理的金融系统
    • 多平台部署需求(需同时支持Node和Edge)
  • 选择Next.js
    • 内容营销站点(利用SSG/ISR)
    • Vercel生态深度集成项目
    • 需要混合渲染策略的复杂应用
特性 Remix Next.js
路由系统 嵌套路由,文件系统约定 早期基于pages,现支持App Router
数据加载 路由级loader/action getServerSideProps/getStaticProps
表单处理 内置HTML表单+action处理 依赖客户端状态管理
渲染策略 SSR为主,支持CSR SSR/SSG/ISR/CSR混合
代码拆分 基于路由自动拆分 基于路由自动拆分
部署目标 多平台适配(Node, Edge等) 原生优化Vercel部署
错误处理 路由级ErrorBoundary 全局错误页面
API路由 无单独API路由,action即API 单独的pages/api目录
静态导出 支持但非重点 核心功能(SSG)
学习曲线 中等,需理解嵌套路由 较低,文档完善

3.2 适用场景分析

根据架构差异,两个框架各有最适合的应用场景:

选择Remix当:

  1. 应用高度交互:需要复杂表单处理、频繁数据变更的管理系统
  2. 嵌套布局复杂:如仪表盘、多级导航的管理后台
  3. 重视渐进增强:需要确保基础功能在无JS环境下可用
  4. 部署灵活性:需要部署到多种环境(Node, Edge, Serverless等)

选择Next.js当:

  1. 内容为主:博客、营销网站等静态内容较多的场景
  2. 需要ISR:增量静态再生对于频繁更新的内容很有效
  3. Vercel生态:计划使用Vercel部署并利用其优化功能
  4. 图像优化:需要自动图像优化和响应式图片

3.3 性能对比

在性能方面,两个框架各有优势:

  1. 初始加载性能

    • Next.js的SSG/ISR可以提供最快的初始加载,因为内容已预生成
    • Remix的SSR需要执行loader,但通过并行数据加载优化
  2. 后续导航性能

    • Remix的嵌套路由和预加载策略使客户端导航极其流畅
    • Next.js的客户端导航同样快速,但缺少Remix的细粒度数据预加载
  3. 资源效率

    • Remix的代码拆分更细粒度(到组件级别)
    • Next.js的代码拆分基于路由,但支持动态导入

实际性能差异取决于具体实现,但两者都属于高性能框架范畴。选择的关键在于应用的特性和开发团队的偏好。

四、Remix实践指南

4.1 项目初始化与开发流程

开始一个Remix项目非常简单:

bash 复制代码
# 创建新项目
npx create-remix@latest

# 进入项目目录
cd my-remix-app

# 安装依赖(如果未自动安装)
npm install

# 启动开发服务器
npm run dev

Remix提供了多种模板选择,包括:

  1. 基础模板:最简Remix设置
  2. TypeScript模板:包含TypeScript配置
  3. Express模板:集成Express服务器
  4. Cloudflare Workers模板:针对边缘计算环境

4.2 数据库集成实践

Remix与各种数据库都能良好集成。以下是使用Prisma(流行的ORM)的示例:

  1. 安装Prisma:
bash 复制代码
npm install prisma @prisma/client
  1. 初始化Prisma:
bash 复制代码
npx prisma init
  1. 定义数据模型(prisma/schema.prisma):
prisma 复制代码
model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
}
  1. 在loader中使用:
typescript 复制代码
import { prisma } from "~/db.server";

export async function loader() {
  const posts = await prisma.post.findMany({
    where: { published: true },
    orderBy: { createdAt: "desc" }
  });
  return json(posts);
}

4.3 身份验证实现

身份验证是大多数应用的核心需求。Remix提供了灵活的认证方案:

  1. 基于Session的认证
typescript 复制代码
// app/session.server.ts
import { createCookieSessionStorage } from "@remix-run/node";

const sessionStorage = createCookieSessionStorage({
  cookie: {
    name: "__session",
    secure: process.env.NODE_ENV === "production",
    secrets: [process.env.SESSION_SECRET],
    sameSite: "lax",
    path: "/",
    maxAge: 60 * 60 * 24 * 30, // 30天
    httpOnly: true
  }
});

export { sessionStorage };
  1. 登录Action
typescript 复制代码
export async function action({ request }: ActionArgs) {
  const formData = await request.formData();
  const email = formData.get("email");
  const password = formData.get("password");
  
  const user = await authenticateUser(email, password);
  if (!user) return json({ error: "Invalid credentials" }, { status: 401 });
  
  const session = await sessionStorage.getSession(request.headers.get("Cookie"));
  session.set("userId", user.id);
  
  return redirect("/dashboard", {
    headers: {
      "Set-Cookie": await sessionStorage.commitSession(session)
    }
  });
}
  1. 保护路由
typescript 复制代码
export async function loader({ request }: LoaderArgs) {
  const session = await sessionStorage.getSession(request.headers.get("Cookie"));
  const userId = session.get("userId");
  if (!userId) throw new Response("Unauthorized", { status: 401 });
  
  return json(await getDashboardData(userId));
}

4.4 测试策略

Remix应用可以采用多种测试策略:

  1. 单元测试:使用Jest/Vitest测试独立函数
typescript 复制代码
import { loader } from "./posts.route";

test("loader returns published posts", async () => {
  const posts = await loader();
  expect(posts).toEqual(expect.arrayContaining([
    expect.objectContaining({ published: true })
  ]));
});
  1. 集成测试:测试loader/action与数据库的交互
typescript 复制代码
import { createTestClient } from "@remix-run/testing";

test("submit post creates new record", async () => {
  const client = createTestClient();
  const response = await client.post("/posts/new", {
    title: "Test Post",
    content: "Test content"
  });
  
  expect(response.status).toBe(200);
  expect(await db.post.count()).toBe(1);
});
  1. 端到端测试:使用Cypress或Playwright测试完整流程
typescript 复制代码
// Cypress测试示例
describe("Post Creation", () => {
  it("should create a new post", () => {
    cy.login();
    cy.visit("/posts/new");
    cy.get("input[name=title]").type("Test Post");
    cy.get("textarea[name=content]").type("Test content");
    cy.get("button[type=submit]").click();
    cy.url().should("include", "/posts");
    cy.contains("Test Post").should("exist");
  });
});

4.5 部署策略

Remix支持部署到多种平台:

  1. Node.js服务器
bash 复制代码
npm run build
npm run start
  1. Serverless平台

    • Vercel:配置vercel.json
    json 复制代码
    {
      "version": 2,
      "builds": [{ "src": "build/index.js", "use": "@vercel/node" }],
      "routes": [{ "src": "/(.*)", "dest": "build/index.js" }]
    }
  2. 边缘计算

    • Cloudflare Workers:使用@remix-run/cloudflare适配器
    typescript 复制代码
    // worker.ts
    import { createEventHandler } from "@remix-run/cloudflare";
    import * as build from "../build";
    
    addEventListener("fetch", createEventHandler({ build }));
  3. 静态站点:对于内容为主的站点,可以导出静态HTML

bash 复制代码
npm run build
npm run remix export

五、高级主题与最佳实践

5.1 性能优化进阶

除了内置的优化机制,Remix应用还可以采用以下高级优化策略:

  1. 缓存策略
    在loader中设置适当的HTTP缓存头,减少重复请求
typescript 复制代码
export async function loader({ request }: LoaderArgs) {
  const data = await getData();
  return json(data, {
    headers: {
      "Cache-Control": "public, max-age=60, stale-while-revalidate=86400"
    }
  });
}
  1. 关键CSS内联
    对于关键路径CSS,可以内联到HTML中避免额外请求
typescript 复制代码
export function links() {
  return [
    {
      rel: "stylesheet",
      href: stylesHref,
      "data-inline": "true" // 标记为内联
    }
  ];
}
  1. 资源预取
    使用<Link prefetch="intent">实现智能预加载
js 复制代码
<Link to="/about" prefetch="intent">关于我们</Link>

5.2 状态管理策略

虽然Remix减少了客户端状态管理的需求,但复杂场景仍需要状态管理方案:

  1. URL状态
    对于UI状态(如筛选条件),可以存储在URL查询参数中
js 复制代码
function Filter() {
  const [searchParams, setSearchParams] = useSearchParams();
  const filter = searchParams.get("filter") || "all";
  
  const handleChange = (value) => {
    setSearchParams({ filter: value });
  };
  
  return <select value={filter} onChange={(e) => handleChange(e.target.value)}>...</select>;
}
  1. 客户端缓存
    使用useFetcher实现乐观UI更新
js 复制代码
function LikeButton({ postId }) {
  const fetcher = useFetcher();
  
  return (
    <fetcher.Form method="post" action={`/posts/${postId}/like`}>
      <button type="submit">
        {fetcher.state === "submitting" ? "处理中..." : "点赞"}
      </button>
    </fetcher.Form>
  );
}
  1. 全局状态
    对于真正的全局状态(如主题偏好),可以使用Context或状态管理库
js 复制代码
const ThemeContext = createContext();

function App() {
  const [theme, setTheme] = useState("light");
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {/* 应用内容 */}
    </ThemeContext.Provider>
  );
}

5.3 国际化(i18n)实现

Remix没有内置i18n支持,但可以轻松集成解决方案:

  1. 基于路由的国际化
typescript 复制代码
// app/routes/[lang]/about.tsx
export async function loader({ params }: LoaderArgs) {
  const translations = await getTranslations(params.lang);
  return json(translations);
}
  1. Cookie/Header检测
typescript 复制代码
export async function loader({ request }: LoaderArgs) {
  const lang = detectLanguage(request);
  const translations = await getTranslations(lang);
  return json(translations);
}
  1. 客户端切换
js 复制代码
function LanguageSwitcher() {
  const locales = ["en", "zh", "es"];
  
  return (
    <Form method="post" action="/set-locale">
      <select name="locale">
        {locales.map((locale) => (
          <option key={locale} value={locale}>
            {locale}
          </option>
        ))}
      </select>
      <button type="submit">切换语言</button>
    </Form>
  );
}

5.4 监控与错误追踪

生产环境应用需要完善的监控:

  1. 错误追踪
typescript 复制代码
export function ErrorBoundary() {
  const error = useRouteError();
  
  // 上报错误到监控系统
  useEffect(() => {
    trackError(error);
  }, [error]);
  
  return <div>...</div>;
}
  1. 性能监控
typescript 复制代码
export function links() {
  return [
    {
      rel: "preconnect",
      href: "https://analytics.example.com",
      crossOrigin: "anonymous"
    }
  ];
}
  1. 真实用户监控(RUM)
typescript 复制代码
export default function App() {
  useEffect(() => {
    initAnalytics();
  }, []);
  
  return <Outlet />;
}

六、总结与展望

6.1 Remix的核心价值

通过对Remix的全面分析,我们可以总结出它的核心价值主张:

  1. 开发者体验(DX)

    • 简洁的API设计,减少样板代码
    • 约定优于配置,降低决策疲劳
    • 热模块替换(HMR)和快速重载提升开发效率
  2. 用户体验(UX)

    • 快速的初始加载和流畅的导航
    • 渐进增强确保基础功能可用
    • 自动处理加载状态和错误恢复
  3. 架构优势

    • 前后端关注点分离但紧密集成
    • 内置最佳实践(如代码拆分、缓存策略)
    • 灵活的部署选项适应各种环境

6.2 适用场景再评估

基于实际使用经验,Remix特别适合以下场景:

  1. 数据密集型应用:如CRM、ERP等业务系统
  2. 表单丰富的应用:如调查问卷、数据录入界面
  3. 需要渐进增强的项目:对可访问性和SEO要求高的网站
  4. 全栈团队:希望统一前后端开发流程的团队

6.3 学习路径建议

对于想要掌握Remix的开发者,建议的学习路径:

  1. 基础阶段

    • 熟悉React和React Router
    • 完成Remix官方教程
    • 构建简单的CRUD应用
  2. 进阶阶段

    • 深入理解loader/action模型
    • 实践嵌套路由和布局
    • 集成认证和数据库
  3. 专家阶段

    • 自定义适配器
    • 性能优化和监控
    • 复杂状态管理方案

6.4 未来展望

随着Web开发的演进,Remix可能会在以下方面继续发展:

  1. 更紧密的React集成:如React Server Components的官方支持
  2. 边缘计算优化:更好的边缘部署和运行时
  3. 开发者工具增强:更强大的调试和分析工具
  4. 生态系统扩展:更多官方和社区插件

6.5 最终建议

对于正在考虑采用Remix的团队,我们建议:

  1. 从小开始:在非关键项目上试用,评估团队适应度
  2. 全栈思维:充分利用Remix的全栈能力,重新思考数据流
  3. 性能预算:建立性能基准,监控关键指标
  4. 社区参与:加入Remix社区,分享经验和最佳实践

Remix代表了Web框架发展的一个重要方向------回归Web本质,同时利用现代开发工具和模式。它可能不是所有场景的最佳选择,但对于适合的项目,Remix能够显著提升开发效率和用户体验。作为开发者,理解并掌握这样的工具,将有助于我们在快速变化的技术 landscape 中保持竞争力。

相关推荐
ywf12151 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭1 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf7 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
华洛8 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq9 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A9 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常10 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端