首页/工具推荐/LupineJS-前后端协同的集大成之作

朋友们好,分享一个有趣,有用且强大的前后端协同的统一开发框架LupineJS,什么叫做前后协同呢,也就是说,只需要LupineJS一个框架,就能同时写前端+后端的全栈项目。

我们不需要去挑选什么前端框架,什么接口框架,一切尽在LupineJS

它有非常丰富的特点:

  • 轻量级 (7kb gzipped)
  • 极速无虚拟DOM
  • 前端类 React 体验
  • 后端类 Express体验
  • 内置服务端渲染 (SSR)
  • 页面无样式闪烁
  • 零配置 SEO
  • 社交分享开箱即用
  • 内置 CSS-in-JS 引擎
  • 自带响应式UI库
  • 内置多页面主题
  • 高性能列表
  • 组件自动适配多端
  • 自带管理面板
  • 自带78,000 个高质量图标
  • 前后的路由同构
  • 内置布局
  • 专业的文档站点
  • 等等等等更多特点

说实话,这些特点让我想起了我已经All in了一年的Bun框架,包罗万象,内置了非常多常用的功能,写起来方便快捷。

下面简单看一看LupineJS的用法示例。

CSS in JS 引擎

const Button = () => {
    const css = {
        backgroundColor: "#0ac92a",
        "&:hover": {
            backgroundColor: "#08a823"
        }
    };
    return <button css={css}>点击我</button>;
};

SSR 服务端渲染

Lupine.js 从一开始就将 服务端渲染 (SSR) 视为一等公民。

大多数 SPA 框架将 SSR 视为事后的补充,或者需要复杂的元框架(如 React 的 Next.js)支持,而 Lupine 则直接将 SSR 集成到其核心架构中(lupine.api 和 lupine.web)。

零配置 SEO

share-content.tsx 中,你可以根据正在查看的内容动态设置 meta 标签:

export const ShareContentPage = async (props: PageProps) => {
    // 1. 获取数据 (在服务端也可行!)
    const record = await fetchRecord(props.urlParameters["id"]);

    // 2. 定义 SEO/社交媒体元数据
    return (
        <div>
            <MetaData property="og:title" content={record.title} />
            <MetaData property="og:description" content={record.description} />
            <MetaData property="og:image" content={record.imageUrl} />
            <MetaData property="og:url" content={props.url} />

            <h1>{record.title}</h1>
            {/* ... 内容 ... */}
        </div>
    );
};

环境变量

Lupine 自动将你的服务端环境桥接到前端。

在 .env 文件中,以 WEB. 开头的变量被标记为前端可用:

# .env
WEB.API_BASE_URL=https://api.example.com
WEB.SITE_NAME=My Awesome App
SECRET_KEY=xxxx  # 这个保留在服务端!

在 SSR 期间(以及随后的浏览器中),可以通过 getWebEnv() 访问这些变量:

// 任意组件
import { getWebEnv } from "lupine.web";

const env = getWebEnv();
console.log(env.API_BASE_URL); // "https://api.example.com"

动态运行时配置

绑定 API (入口点):

// src/index.tsx
bindWebConfigApi("/api/admin/web-config");

使用配置 (组件):

// admin-poster-page.tsx
const PosterList = async () => {
    // 异步访问确保数据已就绪
    const maxRows = await WebConfig.get("poster_max_rows", 10);
    const category = await WebConfig.get("current_category", "default");

    // ... 使用配置 ...
};

非常适合我们临时修改页面数据,但又不想打包部署的时候。

智能缓存与性能

Lupine 的 SSR 不仅仅是为了渲染,更是为了速度。

模板缓存: 服务端缓存 index.html 的结构以减少文件系统读取。

它识别标题、Meta、CSS 和内容的“插槽” (packages/lupine.api/src/api/server-render.ts)。

路由同构

PageRouter 是一个强大的客户端路由系统,其逻辑设计与后端的 ApiRouter (位于 lupine.api 中) 保持 同构。

与通用的路由器(如 react-router 或 vue-router)相比,PageRouter与后端的 ApiRouter 共享完全相同的路由哲学。如果你知道如何编写 Lupine 后端 API,你就知道如何编写前端路由。

import { PageRouter, bindRouter } from "lupine.web";

// 1. 创建 Router
const pageRouter = new PageRouter();

// 2. 定义路由
pageRouter.use("/home", HomePage);
pageRouter.use("/about", AboutPage);
// 404 通配符
pageRouter.use("*", NotFoundPage);

// 3. 绑定到系统
bindRouter(pageRouter);

高性能列表

Lupine.js 采用了一种独特的 “组件级渲染 (Component-Level Rendering)” 策略,这与 React 或 Vue 等传统的虚拟 DOM 框架有显著不同。

这种方法在处理大型列表和复杂数据表格(例如管理后台 Admin Panel)时特别强大。

父组件负责初始化渲染列表,但当子项发生变化时,不需要重新渲染。

// BookList.tsx
export const BookList = () => {
    // 1. 初始数据加载
    let items = getSampleData();

    // 2. 渲染静态列表
    // 注意:如果我们只是编辑项目,我们不需要为整个列表维护 State!
    return (
        <div class="list">
            {items.map((item) => (
                <BookShowItem key={item.id} item={item} />
            ))}
        </div>
    );
};

子组件将内容包裹在 HtmlVar 中。这允许它独立地“重绘”自己。

// BookShowItem.tsx
import { HtmlVar, RefProps } from "lupine.components";

export const BookShowItem = (props: { item: SampleDataProps }) => {
    const ref: RefProps = { id: "" };

    // 1. 定义内部渲染逻辑
    const makeDom = (item: SampleDataProps) => (
        <div class="row-box">
            <div>Name: {item.name}</div>
            <button onClick={onEdit}>编辑</button>
        </div>
    );

    // 2. 为这一行创建一个 "响应式盒子 (Reactive Box)"
    const dom = new HtmlVar(makeDom(props.item));

    // 3. 更新逻辑:只更新这一个 DOM!
    const update = (newItem: SampleDataProps) => {
        // 手动触发仅针对此行的更新
        dom.value = makeDom(newItem);
    };

    const onEdit = () => {
        // 显示模态框,获取新数据,然后调用 update(newItem)
        showEditModal(props.item, (newItem) => {
            update(newItem);
        });
    };

    return (
        <div ref={ref} class="sample-data-row">
            {dom.node} {/* 使用 dom.node 嵌入响应式插槽 */}
        </div>
    );
};

虽然这种方法需要编写稍微多一点的“显式”代码(定义 HtmlVar 和 makeDom),但它为你提供了在重数据应用中难以匹敌的 控制力 和 高性能。

实际上,你就像是自己的“编译器”,精确地告诉浏览器在何时更新什么内容。

移动端和桌面端适配

Lupine.js 的核心设计理念之一是“一套代码,多端运行”。

通过内置的响应式设计工具和组件,你可以轻松构建同时适应 Web、移动端(iOS/Android)和桌面端(Electron)的应用程序。

你可以使用 MediaQueryRange 轻松编写响应式样式:

import { MediaQueryRange, MediaQueryMaxWidth } from "lupine.components";

const css: CssProps = {
    width: "100%",
    // 默认桌面端样式
    maxWidth: MediaQueryMaxWidth.DesktopMax,

    // 针对超小屏幕(手机竖屏)的调整
    [MediaQueryRange.ExtraSmallBelow]: {
        padding: "10px",
        fontSize: "14px"
    },

    // 针对平板及以下
    [MediaQueryRange.TabletBelow]: {
        flexDirection: "column"
    }
};

以上就是对LupineJS框架的基本介绍,是一个功能非常丰富,全面的框架。

如果对这个框架感兴趣,欢迎访问官网和开源仓库,点个小星星。

官方网站:https://uuware.github.io/lupine.js/zh/

开源地址:https://github.com/uuware/lupine.js