---
title: "Routage — Docs zenit Zig UI"
description: "Décrivez les pages sous forme d’enum, exposez la page courante et ses paramètres comme deux Signals, et obtenez précédent / suivant grâce à une pile…"
url: https://zenit.z.express/fr/docs/advanced/routing
language: fr
alternate_en: https://zenit.z.express/docs/advanced/routing.md
alternate_zh: https://zenit.z.express/zh/docs/advanced/routing.md
alternate_es: https://zenit.z.express/es/docs/advanced/routing.md
alternate_ja: https://zenit.z.express/ja/docs/advanced/routing.md
alternate_ko: https://zenit.z.express/ko/docs/advanced/routing.md
alternate_de: https://zenit.z.express/de/docs/advanced/routing.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# Routage dans l’application

Décrivez les pages sous forme d’enum, exposez la page courante et ses paramètres comme deux Signals, et obtenez précédent / suivant grâce à une pile d’historique de capacité fixe.

## Le modèle Router

`ui.fx.Router(Page)` est un routeur en mémoire piloté par un enum — pas un routeur d’URL web. Il expose deux Signals, `current: *Signal(Page)` et `params: *Signal(RouteParams)`, et conserve une pile d’historique de 64 entrées au plus.

`app.zig`

```zig
const std = @import("std");
const ui = @import("ui");

const Page = enum { welcome, editor, settings };
const Router = ui.fx.Router(Page);

fn mountApp(cx: *ui.Cx, scope: *ui.Scope) anyerror!*ui.Node {
    // The router's two Signals are created in `scope`, so pass a scope that
    // lives as long as the window. The router value itself is large (~290 KB of
    // inline history), so keep it in Cx-owned state and pass the pointer around.
    const router = try cx.bindState(Router, try Router.init(scope, .welcome));

    const shell = try ui.box(cx, .{
        .width = .fill(),
        .height = .fill(),
        .direction = .column,
    }, .{});
    // ...append a toolbar and the page host (see "Rendering pages")
    _ = router;
    return shell;
}
```

> NOTE
> 
> **Le routeur est une valeur volumineuse.** L’historique est un tableau en ligne : 64 entrées, chacune portant un `RouteParams` d’au plus 8 paramètres dans des tampons en ligne (clé de 64 octets + valeur de 512 octets), soit environ 290 KB au total. Conservez-le dans un stockage de longue durée comme `cx.bindState` et passez un pointeur ; ne le copiez pas par valeur.

## Navigation et paramètres

`navigate.zig`

```zig
var params: ui.fx.router.RouteParams = .{};
try params.set("file", "draft.md"); // copies the bytes; errors on overflow

router.navigate(.editor, params); // page + params
router.navigateTo(.settings); // page, empty params

_ = router.back(); // false when already at the oldest entry
_ = router.forward(); // false when there is no forward branch

if (router.canGoBack()) {
    // e.g. enable a Back button
}
```

`RouteParams.set` copie les octets de la clé et de la valeur dans ses propres tampons : les modifications ultérieures du tampon de l’appelant ne l’affectent pas ; définir une clé existante écrase sa valeur. La capacité est fixe, et un dépassement renvoie une erreur au lieu de tronquer :

| Limite | Valeur | En cas de dépassement |
| --- | --- | --- |
| Paramètres par entrée | `max_params = 8` | `error.TooManyParams` |
| Longueur de la clé | `max_key_len = 64` | `error.KeyTooLong` |
| Longueur de la valeur | `max_value_len = 512` | `error.ValueTooLong` |

`navigate`, `back` et `forward` écrivent tous dans les deux Signals. `canGoBack()` / `canGoForward()` sont de simples lectures, pas des Signals. Pour piloter le routeur depuis un handler, gardez le pointeur dans une struct `cx.bindState` :

`back_button.zig`

```zig
const Nav = struct {
    router: *Router,

    fn goBack(self: *Nav) void {
        _ = self.router.back();
    }
};

const nav = try cx.bindState(Nav, .{ .router = router });
const back_button = try ui.widgets.Button(.{
    .label = "Back",
    .on_click = cx.on(Nav, nav, Nav.goBack),
}).mount(scope, cx);
```

## Afficher les pages

La page courante est un `Signal(Page)` ; basculez entre les sous-arbres de page avec `ui.Match`. Match donne à chaque page un Scope enfant ; lors d’un changement, il dispose d’abord l’ancien Scope (ses Signals, Effects et ressources partent avec lui), puis libère les anciens nœuds et monte la nouvelle page.

`pages.zig`

```zig
const page_host = try ui.box(cx, .{
    .width = .fill(),
    .height = .fill(),
}, .{});

// Match appends the page to page_host and rebuilds it when router.current
// changes; each page gets a fresh child Scope that is disposed on leave.
_ = try ui.Match(Page, scope, page_host, router.current, cx, struct {
    fn build(child_scope: *ui.Scope, c: *ui.Cx, page: Page) anyerror!*ui.Node {
        return switch (page) {
            .welcome => mountWelcome(c, child_scope),
            .editor => mountEditor(c, child_scope),
            .settings => mountSettings(c, child_scope),
        };
    }
}.build);
```

Match ne reconstruit que lorsque la valeur de l’enum change : naviguer de `.editor` vers `.editor` avec d’autres paramètres garde la page montée. Une page qui dépend des paramètres doit s’abonner elle-même à `router.params` :

`editor_page.zig`

```zig
// editor -> editor with other params does not rebuild the page,
// so subscribe to the params Signal inside the page.
try scope.createEffect(.{ .params = router.params }, struct {
    fn run(ctx: anytype) void {
        // get() returns a copy; keep it in a local so the slices stay valid.
        const params = ctx.params.get();
        const file = params.get("file") orelse "untitled.md";
        std.log.info("editor shows {s}", .{file});
    }
}.run);
```

## Règles de l’historique

HISTORY STACK

back / forward ne déplacent que le curseur ; un navigate après back coupe d’abord history\_len au curseur, puis empile la nouvelle entrée.

-   **Une nouvelle navigation tronque la branche suivante.** Naviguer après back supprime les anciennes entrées forward.
    
-   **Chaque navigate empile.** La même page avec d’autres paramètres — voire une cible identique — devient une nouvelle entrée.
    
-   **À 64 entrées, la moitié la plus ancienne disparaît.** Avant le 65e empilement, les 32 plus récentes sont conservées et le curseur se décale avec elles.
    
-   **Pas d’URL.** Les liens profonds et les schémas d’URL du système doivent être associés à `navigate` par l’application.
    

CAPACITY 64

La suppression se fait par lot : au lieu d’évincer une entrée à chaque empilement, elle libère la moitié de la capacité d’un coup.

## Tracer la navigation

Le routeur ne journalise rien de lui-même. Pour déboguer la navigation, écrivez les transitions utiles dans la Console du Cx courant sous un scope `router` ; la Console de DevTools peut alors filtrer par scope et afficher le niveau et le point d’appel.

`trace.zig`

```zig
router.navigate(.editor, params);
cx.console().scoped("router").debug("navigate -> {s}", .{@tagName(Page.editor)});
```

Consultez [DevTools](https://zenit.z.express/fr/docs/advanced/devtools) pour l’API complète de la Console.

> WARNING
> 
> **Deux routeurs différents.** Ce site de documentation utilise un path router de navigateur ; le `ui.fx.Router` d’une application de bureau zenit fonctionne avec des enums et des Signals et ignore la notion d’URL.
