@entur/uniformen

中文文档
0.21.0发布于 2026/11/12EUPL-1.2TypeScriptES Module构建来源已验证近 30 天下载 2699

Entur apps的页眉和页脚通过Unixen布局服务.

中文使用文档
文档目录14 节
  1. 它会做什么
  2. 安装
  3. 使用量
  4. 获取布局
  5. 反应适配器
  6. 与 Next.js 一起使用
  7. 缓存和故障
  8. 边栏
  9. 语言
  10. 选择服务器上的语言
  11. 这个 entur_locale 饼干
  12. 项目结构
  13. 大楼
  14. 出版

@entur/uniformen (法语)

统一版式服务的客户端库. 它从 Unforden 获取共享头,页脚和头部资产,它提供 React 组件,使其在您的应用程序中实现.

服务使布局, 更改页眉和页脚到达您的应用程序 没有新发布此软件包。 这些变动列于 服务更改日志。 。 。 .

它会做什么

Unfunden是Entur门户网站应用程序的共享头和脚印. 此软件包从 Uniforden 服务中获取 SSR 版式, 并给予您输入的数据和 React 组件以添加到您的应用程序中 .

获取的布局包含:

  • headerHtml – 为顶端导航提供了 HTML
  • footerHtml — 为页脚提供 HTML
  • headAssets ——生 HTML (中文(简体) )<link>编号 :<style> 标记)将添加到 <head>
  • scripts - 未加工 <script> 要添加的 HTML </body>
  • csp ——每个指令的CSP来源(例如: { "style-src": [...], "script-src": [...] })合并到您页面的内容-安全政策标题中

安装

npm install @entur/uniformen
# or
bun add @entur/uniformen

该套件需要回放18或后作同级依赖.

使用量

获取布局

import { fetchUniformenLayout } from "@entur/uniformen";

const layout = await fetchUniformenLayout({
  token, // the signed-in user's Auth0 access token
  environment: "dev", // the Uniformen of the environment this app runs in
  params: {
    app: "partner",
    sidebar: true,
    locale: "nb-NO",
    availableLocales: ["nb-NO", "en-GB"],
    loginUrl: "/auth/login", // where the login link points when there is no token
    logoutUrl: "/auth/logout", // where the user menu's logout row points
  },
});

export default function app() {
  return html`
    <html lang="nb-NO">
      <head>
        ${layout.headAssets}
      </head>
      <body>
        ${layout.headerHtml}
        <main>{/* your app */}</main>
        ${layout.footerHtml}
        ${layout.scripts}
      </body>
    </html>
`;

将选项传递为一个对象 :

const layout = await fetchUniformenLayout({
  token, // Auth0 access token; adds the user to the nav
  environment: "dev", // "local" | "dev" | "staging" | "production" (default)
  params: { app: "partner", sidebar: true, locale: "nb-NO" }, // query params for the SSR endpoint
  timeoutMs: 1000, // how long to wait for the service (default)
});

timeoutMs (单位:千美元)number默认值 1000)是等待时间的几毫秒 服务,包括重试。 超时时,电话处理它像每一个 其他失败。 见 缓存和故障您的页面可以 而不是等待他们.

params.app 是要求布局的门户应用程序。 上行显示Entur标志旁边的应用名称. 应用程序切换器将此应用程序标记为当前应用程序,除非该应用程序未列出. 未知值使服务得到答案 400,然后调用返回 null。 。 。 .

带有参数 undefined 值不在查询字符串中。 矩阵参数为 以重复键发送( e)?key=a&key=b) (中文(简体) ).

应用程序切换器只显示给登录用户. 它链接到另一个门户 您从环境中获取了应用程序的布局。 例如,a dev 与 dev 实例的布局链接,绝不是生产.

标识旁边的环境徽章只在 Entur 中向用户显示 组织。 你不需要发送任何信息 因为服务内容 它来自你传递的标志。 其他用户看到上行没有徽章,但他们 仍然可以看到沿着顶部的彩色条纹显示页面不是生产. 何时 app 设置, 您的应用程序部署在环境的布局中 从,徽章是一个按钮。 它打开了您应用程序的链接列表 它被部署到的每一个环境。 否则,徽章是一个简单的标签.

params.sidebar (单位:千美元)boolean默认值 false)在最远处显示一个倒塌按钮 左侧的上边栏。 只有在您的应用程序有侧向导航时才设置它 。 校正让 按钮,而您的应用程序会让侧边栏。 见 边栏 详细情况.

params.simple (单位:千美元)boolean默认值 false隐藏应用程序切换器,通知, 边栏按钮和"Mine tigganger"。 顶端栏显示标志,应用程序 名称和登录链接 params.loginUrl,或者使用菜单的用户名 只有一个 params.logoutUrl 排队 它能覆盖 params.sidebar脚印 不变.

params.hideNotifications (单位:千美元)boolean默认值 false)使顶端栏没有 通知按钮,且服务不查询用户的通知. 在显示自己的通知的应用程序中使用, 因此页面没有两个 列表。 与 params.simple 设置时,按钮已被隐藏.

params.loginUrl (单位:千美元)string)是上行站点中的"洛格客栈"链接路径 改为无时 token 通过。 如果您将其删除, 则不显示登录链接 。 这一定是 一条你本源的路 绝对URL, //host 或者说 javascript: 制作 服务答案 400,然后调用返回 null。 。 。 .

params.logoutUrl (单位:千美元)string)是用户菜单点中"Logg ut"行的路径 当一个 token 通过。 如果您不显示, 则不显示登录行 。 这一定是 一条你本源的路 绝对URL, //host 或者说 javascript: 制作 服务答案 400,然后调用返回 null。 。 。 .

params.locale (BCP 47: (中文(简体) ) "nb-NO" | "nn-NO" | "en-GB"默认值 "nb-NO"这是 页眉和页脚的语言。 应用程序名称和环境标签不是 译出. 标签必须准确匹配。 譬如说, "nb" 提供服务答案 400设置 <html lang> 以同样价值计算.

params.availableLocales (单位:千美元)Locale[]) 显示一个完全带有这些语言切换器 标记,按此顺序。 如果将其删除或通过空列表,则不显示切换器 . 见 语言 用于显示和接受哪些价值观.

同样的选项适用于: fetchUniformenComponents 见下文.

反应适配器

import { fetchUniformenComponents } from "@entur/uniformen/react";

const { HeadAssets, Header, Footer, Scripts, csp } = await fetchUniformenComponents({
  token,
  environment: "dev",
  params: { app: "partner", sidebar: true, locale: "nb-NO" },
});

export default function App() {
  return (
    <>
      <head>
        <HeadAssets />
      </head>
      <body>
        <Header />
        <main>{/* your app */}</main>
        <Footer />
        <Scripts />
      </body>
    </>
  );
}

如果布局抓取失败, 所有组件都会不产生任何 csp 实值 {}。 。 。 .

适配器将布局 HTML 解析为真实的 React 元素.

csp 一样 Record<string, string[]> 那个 fetchUniformenLayout 返回,则 您只需要一个获取即可设置 Content-Security-Policy 头曰. 合并这些 输入您自己的指令.

  • 地点 <HeadAssets /> 内部 <head>。 。 。 .
  • 地点 <Header /> 内部 <body> 在此之前 <main>。 。 。 .
  • 必要时,地点 <Footer /> 内部 <body> 之后 <main>。 。 。 .
  • 地点 <Scripts /> 内部 <body> 作为最后一个元素.

与 Next.js 一起使用

与下一位.js,通过 Script 作为 loader 向 <HeadAssets> 财务报告和审定财务报表 <Scripts>。 。 。 . 否则Next.js可以改变脚本运行的顺序,因此脚本和组件的加载顺序错误.

import Script from "next/script";

// The head script sets the sidebar state before the first paint, so it must be in
// the initial HTML. The default Next.js strategy adds scripts after hydration, and
// the user would see the sidebar jump.
const BeforeInteractiveScript: typeof Script = (props) => <Script {...props} strategy="beforeInteractive" />;

<head>
  <HeadAssets loader={BeforeInteractiveScript} />
</head>
<body>
  <Header />
  <main>{/* your app */}</main>
  <Scripts loader={Script} />
</body>;

缓存和故障

fetchUniformenLayout 保留服务器程序中的内存布局。 每个 组合 environment 财务报告和审定财务报表 params 拥有自己的条目.

  • 匿名布局( 无) token)从内存中使用,只要服务 Cache-Control: max-age 允许。 今天是60秒钟。 之后,下一个电话 又取来.
  • 签入的布局包含用户名,因此在每个呼叫中都会取出. 这个 软件包将每个符号的最后一个保存一个小时,并只在 获取失败 。 它储存着一种SHA -256散列的代币,而不是代币本身.
  • 如果请求失败时出现网络错误或 5xx 状态, 软件包试一次 更在同一范围内 timeoutMsA级 4xx 状态没有重审.
  • 如果抓取失败, 调用会返回它为相同选项获得的最后一个布局, 对于一个签名的用户来说 也一样 保存此的匿名布局 一天时间。 如果没有这样的布局, 调用返回 null。 。 。 .
  • 使用相同的选项打电话, 对于一个签名的用户, 同样的代号, 运行 同时,向Unixen提出一个请求。 一个加入运行的电话 请求还使用该请求的 timeoutMs。 。 。 .
  • 内存最多持有100个匿名和100个签入版式. 当其中之一 它们是完整的,最近使用的布局被删除。 过期布局 每次存储新的布局都会被移除,所以签入的布局很快就消失了 时间过后,即使用户不回来.
  • 你得到的每个布局都是你自己的复制件,所以你可以将自己的来源添加到 layout.csp 不更改缓存布局.

要立即获取新布局, 例如在 unifen 部署后, 呼叫 clearUniformenLayoutCache()。 。 。 .

每个请求都发出一个 X-Uniformen-Client 带有软件包名称和 例如,版本 @entur/[email protected]。服务使用它来查看 版本正在使用中.

边栏

过 params.sidebar: true 显示顶端最左边的倒塌按钮 栏。 unifen不使侧边栏本身.

状态只存储于根元素上的此属性 :

<html data-uniformen-sidebar="expanded | collapsed"></html>

侧边栏代码在 headAssets这样 <HeadAssets /> 必须是在 <head>。 在第一次涂料之前设置存储状态, 因此崩溃的侧边栏不会跳出 . 它也开始在您自己的脚本运行前观看属性,所以它可以看到每个 写到属性,甚至早期.

基于属性的侧边栏样式 . 那么你不需要JavaScript状态, 而侧边栏在第一个相框上是正确的. 这在服务器上也能操作 应用程序,如果服务器不知道用户的偏好:

:root[data-uniformen-sidebar="collapsed"] .my-sidebar {
  width: 0;
  /* Also hide it from screen readers and the tab order. With only width: 0, the
     links can still get focus and screen readers still read them. */
  visibility: hidden;
}

如果崩溃是动画, 请在 visibility 一、导 言 宽度( n)transition: width 150ms ease, visibility 150ms ease) (中文(简体) ). 那内容就留下 在动画完成前可见 .

如果你的侧边栏 在一个阴影根 :root[…] 选择器无法到达 . 在根上设置自定义属性 。 自定义属性继承到 阴影根,所以里面的样式可以使用值:

:root {
  --my-sidebar-width: 240px;
}
:root[data-uniformen-sidebar="collapsed"] {
  --my-sidebar-width: 0px;
}

要使用不同的默认时 <html data-uniformen-sidebar="collapsed"> 打开 服务器。 校正只写用户存储的属性 偏好,或者当属性没有设置时,所以您的值被保留.

Unifen 将您提供的值作为起始状态,而不是改变。 没错 未保存到 localStorage,没有 uniformen:sidebar 事件为它起火。 当 用户第一次崩溃或扩展侧边栏,该选择 实值 已经保存, 从那时起,它就被使用,而不是你所作的默认。 如果你的应用商店 服务器上的首选项,并想要使用它自己的值,清除 uniformen:sidebar 当它保存自己的副本时键.

把它从其他地方砸倒,例如边栏中的关闭按钮,a 键盘快捷键或路由更改,写出相同的属性。 上行按钮 执行相同的:

document.documentElement.dataset.uniformenSidebar = "collapsed";

对脚本变化的反应听到这个事件。 当您使用上面的 CSS 时 能够。 从按钮或你的应用软件中, 事件会引发任何变化, 并拥有 新状态 :

window.addEventListener("uniformen:sidebar", (event) => {
  setCollapsed(event.detail.collapsed);
});

如果一个组件需要JavaScript中的状态,例如边栏组件 带有 collapsed 道克,读取属性并观看. A 类 MutationObserver 看见了 每写到属性。 与事件不同的是,它也在一个网页上工作 校正头脚本缺失, 例如布局抓取失败 。 礛 您的侧边栏和顶边栏一样工作。 在反应中 :

import { useCallback, useSyncExternalStore } from "react";

const ATTRIBUTE = "data-uniformen-sidebar";
const root = () => document.documentElement;

function subscribe(onChange: () => void) {
  const observer = new MutationObserver(onChange);
  observer.observe(root(), { attributeFilter: [ATTRIBUTE] });
  return () => observer.disconnect();
}

export function useSidebarCollapsed() {
  const collapsed = useSyncExternalStore(
    subscribe,
    () => root().getAttribute(ATTRIBUTE) === "collapsed",
    // The server does not know the user's preference.
    () => false,
  );
  const setCollapsed = useCallback((next: boolean) => {
    root().setAttribute(ATTRIBUTE, next ? "collapsed" : "expanded");
  }, []);
  return [collapsed, setCollapsed] as const;
}

在服务器发送的应用程序中,服务器会使扩展的侧边栏. 如果用户 偏好崩溃,水合后侧边栏会改变,用户会看到 跳下来。 CSS方法没有这个问题, 所以把它用于所有 CSS可以做到这一点。 在一个仅在浏览器中制作的单页应用程序中,状态是 从第一个角度来说是正确的.

event.detail 输入。 这个事件和 uniformen:locale 于 WindowEventMap,因此,在一个从软件包中导入的项目中,每个文件都得到 输入时没有额外输入或显示.

校正从属性中更新其余: 按钮,其 aria-expanded,以及它保存的偏好 localStorage 下级 uniformen:sidebar。 。 。 .

语言

params.locale 是服务所提供语言. params.availableLocales 添加用户可以更改语言的控制 :

const layout = await fetchUniformenLayout({
  token,
  params: { app: "partner", locale, availableLocales: ["nb-NO", "en-GB"] },
});

这些语言是一组广播节目。 服务器使 locale 作为 选中一个, 因此没有客户端状态, 标签从开始就是正确的 . 每个语言都以自己的语言来命名("Norsk bokmål","英语"). 控制权 总是被贴上"Språk / Language"的标签,不管是什么 locale 是,所以用户可以找到它 即使他们不能读取当前语言.

显示控件的地方取决于上行. 它只显示在一个 地点 :

  • 当一个用户被签入时,它是用户菜单中的"Språk / Language"一栏, 在登录行上方。 它呆在那里,当 params.simple 已经设定.
  • 在无菜单的匿名上行是左侧的单独控制 输入链接。 它显示一个地球和当前语言。 在小屏幕上 文字是隐藏的,地球表有一个 aria-label 与语言名称.

只列出您自己的应用程序的标签 。 制服支持三个,但 提供您页面所没有的语言比不提供更糟糕。 你选择 命令。 命令。 如果列表有重复的标签, 未知的标签或不包含 locale,服务答案 400 调用返回 null。 。 。 .

您的应用程序必须处理选择. 当用户选择语言时, 顶端栏 发送一个事件,而无所作为。 它没有设置饼干,写给 localStorage,重新装入或更改栏中的标签:

import { writeLocaleCookie } from "@entur/uniformen";

// The tags your app is translated into. Send them as `params.availableLocales`.
const SUPPORTED = ["nb-NO", "en-GB"] as const;

window.addEventListener("uniformen:locale", (event) => {
  writeLocaleCookie(event.detail.locale);
  window.location.reload();
});

重装会改变语言. 服务器使信头使用一种语言, ,模块装入时会加载自己的文本。 如果顶部栏变了 没有重新装入的标签,其余的页面仍将使用旧语言.

如果用户选择已经显示的语言,则没有发送事件,所以 页面不重新装入.

选择服务器上的语言

在下一次渲染时,将存储的选择发送为 params.locale 设置 <html lang> 以同样价值计算. readLocaleCookie 财务报告和审定财务报表 resolveLocale 用简单的绳子,所以他们 与任何服务器框架一起工作:

import { fetchUniformenLayout, readLocaleCookie, resolveLocale } from "@entur/uniformen";

const SUPPORTED = ["nb-NO", "en-GB"] as const;

const locale = resolveLocale({
  chosen: readLocaleCookie(request.headers.get("cookie")),
  acceptLanguage: request.headers.get("accept-language"),
  supported: SUPPORTED,
  fallback: "nb-NO",
});

const layout = await fetchUniformenLayout({
  token,
  params: { app: "partner", locale, availableLocales: [...SUPPORTED] },
});
// Render <html lang={locale}> with the layout.

resolveLocale 使用您的应用程序所支持的第一个 :

  1. chosen,用户选择的语言。 您的应用程序不支持的值是 忽略, 因此旧的或已更改的饼干无法打破页面 .
  2. 语文 acceptLanguage,按其分类 q 数值。 标记为 q=0 财务报告和审定财务报表 * 被排除在外.
  3. fallback。 。 。 .

当标签是同一标记时, 或当其语言部分时, 标签匹配 匹配 : en-US 给 en-GB,以及 nb 财务报告和审定财务报表 no 给 nb-NO。浏览器经常发送 仅这些短或区域标记,所以仅一个精确的匹配就会错过许多用户.

返回类型是您的类型 supported 列表,因此可以使用结果 您的应用程序期望它自己的语言类型.

在浏览器中使用相同的功能 如果你自己的文本选择他们的语言 在这里。 过 navigator.languages,按其自身顺序使用。 浏览器 正在建设 Accept-Language 从同一个列表,所以页面和页眉得到 同样的语言,同样在饼干设定前的第一次访问中:

const locale = resolveLocale({
  chosen: readLocaleCookie(document.cookie),
  acceptLanguage: navigator.languages,
  supported: SUPPORTED,
  fallback: "nb-NO",
});

writeLocaleCookie 写入 entur_locale 饼干LOCALE_COOKIE整个 为期一年 SameSite=Lax,并与 Secure 关于HTTPS。 只有你的应用 读它。 制服服务没有。 使用此名称可以让 Entur 应用程序全部存储 同样的选择.

默认饼干属于设定它的主机. 过 domain,例如 writeLocaleCookie(locale, { domain: "entur.org" }),与其他选择分享 应用程序在同一域. 某个域上的所有应用程序都必须这样做. 如果一个应用 将饼干设定为 domain 和另一个没有,浏览器发送两个饼干与 同样的名字, 和它们之间的顺序是不可靠的. readLocaleCookie 返回时 第一个. domain 必须是页面的主机或它的母机,所以无法工作 打开 localhost。在另一个域中,浏览器忽略了 cookie 没有出错.

如果您将应用程序从主机饼干移动到 domain,删除旧主机 在你写新书之前先吃饼干: document.cookie = "entur_locale=; path=/; max-age=0"。 。 。 .

项目结构

packages/uniformen/
├── src/
│   ├── index.ts               # Public API: fetchUniformenLayout, the locale helpers and the types
│   ├── types.ts               # UniformenLayout type definition
│   ├── index.test.ts          # Tests for fetchUniformenLayout
│   ├── locale.test.ts         # Tests for the locale helpers
│   ├── reactAdapter.tsx       # React adapter: fetchUniformenComponents
│   ├── version.ts             # Package version, updated by release-please
│   └── lib/
│       ├── fetchUniformenLayout.ts  # Fetches layout from the Uniformen SSR endpoint
│       ├── locale.ts                # Locale cookie and resolveLocale
│       └── layoutCache.ts           # In-memory cache of fetched layouts
├── dist/                      # Compiled output (generated by bun run build)
├── tsconfig.json              # TypeScript config for type checking and local dev
├── tsconfig.build.json        # TypeScript config for emitting .d.ts declaration files
└── package.json

大楼

bun run build

出版

释放 - 请创建所有释放。 不要用手释放: 不要运行 bun publish 或者说 npm publish ,不编辑 version 内 package.json 或者说 变动情况。 。 。 .

每一次推进 main,请释放 - 请收集更改的承诺 packages/uniformen/。它使下个版本的释放拉动请求打开 生成的更改日志 。 合并后,释放被标记 发布工作流程 建造、测试和出版 @entur/uniformen {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}请说.

该版本基于承诺主题,所以它们必须使用 常规武器委员会数字 :

feat(uniformen): add loginUrl to the render options
类型对已公布版本的影响
feat轻微起落
fix补丁凸起
feat!,或带有 BREAKING CHANGE: 页脚重大冲击
chore, (中文(简体) ), docs, (中文(简体) ), refactor, (中文(简体) ), test, (中文(简体) ), build, (中文(简体) ), ci无释放

见 捐款额 完全承诺和释放规则.

版本与发布记录

@entur/uniformen 共发布 23 个版本,首次发布于 2026/06/10;过去 12 个月发布了 23 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.21.0latest2026/11/121 天前
0.20.02026/11/049 天前
0.19.02026/11/0211 天前
0.18.02026/10/2717 天前
0.17.02026/10/1430 天前
0.16.02026/10/121 个月前

关于 @entur/uniformen 的常见问题

@entur/uniformen 是什么?

Entur apps的页眉和页脚通过Unixen布局服务.

如何安装 @entur/uniformen?

在项目目录运行 npm install @entur/uniformen,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(>=18)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@entur/uniformen 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@entur/uniformen 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@entur/uniformen 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@entur/uniformen 可以商用吗?

@entur/uniformen 使用 EUPL-1.2 许可。声明的许可为 EUPL-1.2,请阅读许可原文确认使用条件。以上为概括,不构成法律意见,请以许可原文为准。

@entur/uniformen 的下载量有多少?

根据 npm 官方下载统计,@entur/uniformen 在 2026/10/14 – 2026/11/12 这 30 天内被下载 2,699 次,日均约 90 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@entur/uniformen 还在维护吗?

最新版本 0.21.0 发布于 2026/11/12(1 天前);至今共发布 23 个版本,其中过去 12 个月发布了 23 个;首次发布于 2026/06/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。