1 个不稳定版本

0.1.0 2024 年 8 月 1 日

#369Web 编程

Download history 107/week @ 2024-07-28 3/week @ 2024-08-04

每月 110 次下载

MIT 许可证

50KB
893

ssrkit

ssrkit 是一个强大且灵活的 Rust 函数库,专门为简化服务器端渲染(SSR)的实现流程而设计。它基于 ssr-rs 项目,进一步扩展了功能和易用性。ssrkit 提供了一套完整的工具,包括参数处理系统、Island 架构支持和模板渲染功能,可以无缝集成到各种 Web 框架中。

特性

  • 轻量级和高效: 最佳化的性能,最小化运行时开销
  • 灵活的参数处理: 自定义路由参数处理逻辑
  • Island 架构: 支持部分页面的客户端交互,提高应用程序的交互性
  • 模板渲染: 内置模板系统,支持自定义内容插入
  • 易于集成: 设计用于与各种 Rust Web 框架和前端框架无缝协作
  • 可扩展性: 模块化设计,易于扩展和自定义
  • 线程安全: 支持多线程环境,适用于高并发场景
  • 类型安全: 利用 Rust 的类型系统确保运行时安全

安装

将以下内容添加到您的 Cargo.toml 文件中:

[dependencies]
ssrkit = { git = "https://github.com/jeromeleong/ssrkit.git" }
ssr = "0.5.7" # 確保使用與 ssrkit 相容的 ssr 版本

注意:ssrkit 依赖于 ssr-rs 和其他 SSR 相关函数库。请确保您的项目中包含了所有必要的依赖。

快速开始

以下是一个基本的使用示例,展示了 ssrkit 的核心功能:

use serde_json::json;
use ssr::Ssr;
use ssrkit::prelude::*;
use std::collections::HashMap;
use std::sync::Arc;

struct BasicParamsProcessor;
impl ParamsProcessor for BasicParamsProcessor {
    fn process(
        &self,
        _path: &str,
        params: &HashMap<String, String>,
    ) -> serde_json::Map<String, Value> {
        params
            .iter()
            .map(|(k, v)| (k.clone(), Value::String(v.clone())))
            .collect()
    }
}

struct ExampleIslandProcessor;
impl IslandProcessor for ExampleIslandProcessor {
    fn process(&self, island_manager: &Arc<IslandManager>, context: &ProcessContext) -> Value {
        let mut islands = serde_json::Map::new();

        if context.path == "/example" {
            // Counter Island
            let counter = get_or_render_island("counter", || {
                island_manager
                    .render_island(
                        "Counter",
                        &json!({
                            "initialCount": 0,
                            "client": "load"
                        }),
                    )
                    .unwrap_or_default()
            });

            islands.insert(
                "counter".to_string(),
                json!({
                "id": "Counter",
                "html": counter
                }),
            );
        }

        Value::Object(islands)
    }
}

fn main() {
    // 初始化 SSR 元件
    init_ssr(
        || Box::new(CombinedParamsProcessor::new().add("/", BasicParamsProcessor)),
        || {
            let manager = IslandManager::new();

            manager.register("Counter", |_, props| {
                let initial_count = props["initialCount"].as_i64().unwrap_or(0);
                let instance_id = props["instanceId"].as_str().unwrap_or("");
                Ok(format!(
                    r#"<div id="{}" data-island="Counter" data-props='{}'>
                        <button>遞增</button>
                        <span>{}</span>
                    </div>"#,
                    instance_id,
                    serde_json::to_string(props).unwrap(),
                    initial_count
                ))
            });

            manager
                .add_island("Counter", Some(json!({"initialCount": 0})))
                .unwrap();

            manager
        },
        Template::new,
        None, // 可選的 SsrkitConfig
    );

    let renderer = get_renderer();

    // 模擬請求
    let path = "/example";
    let mut params = HashMap::new();
    params.insert("user".to_string(), "Alice".to_string());

    // 執行渲染
    let result = renderer.process_and_render(&ExampleIslandProcessor, path, params, |props| {
        let parsed_props: Value = serde_json::from_str(props).unwrap();
        let user = parsed_props["params"]["user"].as_str().unwrap_or("訪客");
        let content = format!("歡迎,{}!這是一個互動計數器:", user);

        // 使用 ssr-rs 進行實際的 SSR 渲染
        let ssr = Ssr::new("path/to/your/frontend/bundle.js", "render").unwrap();
        let rendered = ssr.render(props).unwrap();

        Ok(json!({
        "html": format!(
        r#"<h1>{}</h1>
            <div data-island="Counter" data-name="counter" data-props='{{"initialCount": 0}}'></div>
        {}"#,
        content,
        rendered
        ),
        "css": ".counter { font-weight: bold; }",
        "head": "<meta name='description' content='SSRKit 範例與計數器'>"
        })
        .to_string())
    });

    match result {
        Ok(html) => println!("渲染的 HTML:\n{}", html),
        Err(e) => println!("渲染錯誤:{}", e),
    }
}

核心概念

初始化 SSR

初始化 SSR 是使用 ssrkit 的第一步,它设定了整个 SSR 系统的核心组件:

init_ssr(
    params_processor_init: impl FnOnce() -> Box<dyn ParamsProcessor>,
    island_manager_init: impl FnOnce() -> IslandManager,
    template_init: impl FnOnce() -> Template,
    config: Option<&SsrkitConfig>,
)
  • params_processor_init: 初始化参数处理器
  • island_manager_init: 初始化 Island 管理器
  • template_init: 初始化模板
  • config: 可选的 SSRKit 配置

参数处理 (Params Processing)

参数处理允许您根据路由和请求参数自定义逻辑:

struct UserParamsProcessor;
impl ParamsProcessor for UserParamsProcessor {
    fn process(
        &self,
        _path: &str,
        params: &HashMap<String, String>,
    ) -> serde_json::Map<String, serde_json::Value> {
        let mut processed = serde_json::Map::new();
        if let Some(user_id) = params.get("id") {
            processed.insert("user_id".to_string(), user_id.clone().into());
            processed.insert("is_admin".to_string(), (user_id == "admin").into());
        }
        processed
    }
}

Island 架构

Island 架构允许您在服务器端渲染的页面中嵌入可交互的客户端组件:

island_manager.register("Counter", |_, props| {
    let initial_count = props["initialCount"].as_i64().unwrap_or(0);
    let instance_id = props["instanceId"].as_str().unwrap_or("");
    Ok(format!(
        r#"<div id="{}" data-island="Counter" data-props='{}'>
            <button>遞增</button>
            <span>{}</span>
        </div>"#,
        instance_id,
        serde_json::to_string(props).unwrap(),
        initial_count
    ))
});

island_manager.add_island("Counter", Some(json!({"initialCount": 0}))).unwrap();

模板渲染

ssrkit 使用内置的模板系统来组合最终的 HTML 输出:

let template = Template::new();

模板系统会自动处理 HTML、CSS 和额外的头部内容。

SSR 渲染器

SSR 渲染器是 ssrkit 的核心,它协调参数处理、Island 渲染和模板填充:

let renderer = get_renderer();

let result = renderer.process_and_render(
    &island_processor,
    path,
    params,
    |props| {
    // 實作渲染邏輯,這裡通常會呼叫 ssr-rs 的 SSR 功能
    // let ssr = Ssr::new("path/to/your/frontend/bundle.js", "render").unwrap();
    // let rendered = ssr.render(props).unwrap();  
    //
    Ok(json!({
            "html": "<h1>你好,世界!</h1>",
            "css": ".greeting { color: blue; }",
            "head": "<meta name='description' content='我的 SSR 頁面'>"
        }).to_string())
    }
);

高级使用

自定义参数处理器

对于更复杂的应用,您可以组合多个参数处理器:

let params_processor = CombinedParamsProcessor::new()
    .add("/user", UserParamsProcessor)
    .add("/blog", BlogParamsProcessor);

Island 处理器

Island 处理器允许您在渲染过程中动态处理 Island 组件:

struct IslandDemoProcessor;

impl IslandProcessor for IslandDemoProcessor {
    fn process(&self, island_manager: &Arc<IslandManager>, context: &ProcessContext) -> Value {
        let mut islands = serde_json::Map::new();
        if context.path == "/demo" {
            let counter = get_or_render_island("counter", || {
                island_manager.render_island("Counter", &json!({"initialCount": 5})).unwrap_or_default()
            });
            islands.insert("counter".to_string(), json!({"id": "Counter", "html": counter}));
        }
        Value::Object(islands)
    }
}

与 Web 框架集成

以下是一个使用 Salvo 框架的示例:

use salvo::prelude::*;
use ssrkit::prelude::*;
use ssr::Ssr;

#[handler]
pub async fn handle_ssr(req: &mut Request, res: &mut Response) {
    let path = req.uri().path().to_string();
    let params: std::collections::HashMap<String, String> = req.params().iter()
        .map(|(k, v)| (k.to_string(), v.to_string()))
        .collect();

    let renderer = get_renderer();
    let island_processor = IslandDemoProcessor;

    let result = renderer.process_and_render(
        &island_processor,
        &path,
        params,
        |props| {
            // 使用 ssr-rs 進行實際的 SSR 渲染
            let ssr = Ssr::new("path/to/your/frontend/bundle.js", "render").unwrap();
            let rendered = ssr.render(props).unwrap();

            Ok(json!({
                "html": format!("<h1>你好,Salvo!</h1>{}", rendered),
                "css": ".greeting { color: green; }",
                "head": "<meta name='description' content='Salvo SSR 範例'>"
            }).to_string())
        }
    );

    match result {
        Ok(html) => {
            res.render(Text::Html(html));
        },
        Err(e) => {
            res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
            res.render(Text::Plain(format!("內部伺服器錯誤:{}", e)));
        }
    }
}

前端集成

ssrkit 需要前端框架的配合才能实现完整的服务器端渲染。以下是一个使用 Svelte 的示例:

前端 SSR 入口点(例如 ssr.js

import App from './App.svelte';
import * as routes from './routes';

export function render(props) {
    const { url, params, islands } = JSON.parse(props);

    let component = routes.NotFound;
    let componentProps = { ...params };

    // 根據 URL 選擇適當的元件
    if (url === '/') {
        component = routes.Index;
    } else if (url === '/about') {
        component = routes.About;
    } else if (url.startsWith('/blog')) {
        component = routes.BlogPost;
    } else if (url.startsWith('/user')) {
        component = routes.User;
    } else if (url === '/island-demo') {
        component = routes.IslandDemo;
        componentProps.initialCount = 10; // 設定計數器的初始值
    }

    const rendered = App.render({
        url,
        component,
        props: componentProps,
        islands
    });

    return JSON.stringify({
        html: rendered.html,
        css: rendered.css.code,
        head: rendered.head
    });
}

在 ssrkit 中使用前端渲染

在 Rust 后端中,您可以使用以下方式使用前端 SSR 渲染:

let result = renderer.process_and_render(
    &island_processor,
    &path,
    params,
    |props| {
        // 使用 ssr-rs 呼叫前端的 SSR 函數
        let ssr = Ssr::new("path/to/your/frontend/bundle.js", "render").unwrap();
        let ssr_result = ssr.render(props)?;

        // 解析前端傳回的 JSON
        let rendered: serde_json::Value = serde_json::from_str(&ssr_result)?;

        Ok(rendered.to_string())
    }
);

性能考虑

  • 可选水合: Island 架构允许有选择性地水合页面的特定部分,减少客户端 JavaScript 的大小和执行时间。
  • 串流 SSR: 虽然 ssrkit 本身不直接提供串流 SSR,但可以与支持串流输出的 Web 框架结合使用,提高首次内容绘制(FCP)时间。

性能优化技巧

  1. 缓存策略优化:
  • 使用粗粒度的时间戳来增加缓存命中率。
  • 实现自定义缓存键生成策略,排除频繁变化的数据。
  1. 减少动态内容:
  • 将页面分为静态部分和动态部分,静态部分可以长期缓存。
  • 使用客户端 JavaScript 更新动态内容,例如时间戳。
  1. 使用 ETags:
  • 实现 ETags 来允许客户端缓存页面,只有当内容真正变化时才发送新的响应。
  1. 增加缓存时间:
  • 如果内容不需要即时更新,可以增加缓存的有效期限。
  1. 批量处理:
  • IslandProcessor 中实现批量处理逻辑,减少数据库查询次数。
  1. 优化前端代码:
  • 确保前端 SSR 代码高效,避免不必要的计算和渲染。

与 ssr-rs 的关系

ssrkit 基于 ssr-rs 项目,并对其进行了扩展。以下是 ssrkit 与 ssr-rs 的主要区别和改进:

  1. 参数处理系统:
  • ssrkit 引入了更灵活的参数处理系统,允许根据路由自定义参数处理逻辑。
  1. Island 架构:
  • ssrkit 新增了 Isl​​and 架构支持,实现了更细粒度的客户端交互。
  1. 模板系统:
  • ssrkit 提供了内置的模板系统,简化了 HTML、CSS 和头部内容的组合。
  1. 状态管理:
  • ssrkit 引入了全局状态管理,便于在不同组件间共享数据。
  1. 扩展性:
  • ssrkit 设计了更多的扩展点,便于与其他函数库和框架整合。
  1. 类型安全:
  • ssrkit 更多地利用了 Rust 的类型系统,提供了更强的类型安全保证。

尽管 ssrkit 增加了这些特性,但它仍然依赖于 ssr-rs 作为底层 SSR 引擎。在使用 ssrkit 时,您需要同时引入 ssr-rs 作为依赖。

最佳实践

  1. 组件化设计: 将应用程序分解为小型、可重复使用的组件,以便于维护和测试。

  2. 提前准备数据: 在调用 SSR 渲染之前,尽可能准备好所需的所有数据。

  3. 错误处理: 实现全面的错误处理策略,确保在 SSR 失败时有适当的回退机制。

  4. 性能监控: 使用性能监控工具跟踪 SSR 的执行时间和资源使用情况。

  5. 代码分割:利用动态导入和懒加载技术减少初始加载时间。

  6. SSR 与 CSR 结合:对于不需要 SEO 的页面部分,请考虑使用客户端渲染。

  7. 合理使用 Islands:只对真正需要交互的组件使用 Island 架构。

常见问题解答

  1. **Q: ssrkit 可以与哪些 Web 框架一起使用? ** A: ssrkit 设计为框架无关的,可以与大多数 Rust Web 框架(如 Actix, Rocket, Warp 等)整合。

  2. **Q: 如何处理 SEO 问题? ** A: 使用 ssrkit 的服务器端渲染可以确保搜索引擎能够爬取到完整的页面内容。确保在模板中包含必要的 meta 标签。

  3. **Q: ssrkit 支持增量静态再生(ISR)吗? ** A: 目前 ssrkit 主要专注于动态 SSR。ISR 可能会在未来版本中考虑支持。

  4. **Q: 如何处理大型应用的性能问题? ** A: 利用 ssrkit 的缓存机制、考虑在 IslandProcessor 中实现并行处理,并使用 Island 架构来最小化客户端 JavaScript。

  5. **Q: ssrkit 如何处理前端路由? ** A: ssrkit 通过与前端框架的整合来处理路由。在服务器端,你可以根据 URL 选择适当的组件进行渲染。

  6. **Q: 如何自定义 Island 的客户端行为? ** A: Island 的客户端行为应在前端框架中实现。ssrkit 负责服务器端渲染和初始状态的传递。

  7. **Q: ssrkit 是否支持部分页面更新? ** A: ssrkit 主要关注完整页面的 SSR。部分页面更新通常应由客户端 JavaScript 处理。

  8. **Q: 如何处理认证和授权? ** A: 认证和授权逻辑应在 ParamsProcessor 或你的 Web 框架中实现。ssrkit 可以根据这些逻辑的结果来渲染对应的内容。

贡献

我们欢迎社区贡献!如果你有任何改进建议或发现 bug,请开启一个 issue 或提交一个 pull request。

许可

ssrkit 使用 MIT 许可证。

致谢

特别感谢 ssr-rs 项目的开发者,他的工作为 ssrkit 奠定了基础。

相关项目

  • ssr-rs: ssrkit 的基础 SSR 引擎
  • salvo: 一个兼容于 ssrkit 的 Rust Web 框架
  • Svelte: 一个受欢迎的前端框架,可以与 ssrkit 搭配使用

更新日志

请查看 CHANGELOG.md 文件以了解最新的变更和版本信息。

联系我们

如果你有任何问题或建议,可以通过以下方式联系我:

感谢你使用 ssrkit!我们期待看到你用它构建的出色应用。

依赖

~5.5–8MB
~136K SLoC