@arnosaine/is

中文文档
0.2.9发布于 2025/01/23ISCTypeScriptES Module近 30 天下载 3409

地物旗,角色和权限的渲染,A/B测试,实验地物等,多见于React.

中文使用文档
文档目录28 节
  1. 关键特性
  2. 目录
  3. 演示文稿
  4. 开始
  5. 创建 <Is> 打开 useIs
  6. 使用 <Is> 打开 useIs
  7. 想法
  8. 特性旗
  9. 域的应用备选案文
  10. 用户角色和权限
  11. 是特定的日子
  12. 快捷键组件和钩
  13. 装入器( 反应路由器 / Remix)
  14. 设置
  15. 使用 loadIs
  16. 公用事业
  17. API 密码
  18. create
  19. createFromLoader
  20. <Is>
  21. useIs
  22. loadIs
  23. is
  24. toBooleanValues
  25. 类型
  26. Value
  27. Values
  28. Conditions

@arnosaine/is (美国英语)

特性旗, (中文(简体) ), 基于任务和权限的渲染, (中文(简体) ), A/B 测试,实验特征,以及 更多 在反应.

关键特性

  • 有条件渲染组件的宣告语法
  • 支持各种数据来源,包括上下文、钩子和API响应
  • 可自定义默认条件和动态值

创建 习惯 <Is> 构成部分和 useIs 任何有条件渲染用途的钩子 .

或者创造 快捷键组件 喜欢 <IsAuthenticated>, (中文(简体) ), <HasRole> 编号 : <Role> 财务报告和审定财务报表 <HasPermission> 编号 : <Can>和钩子一样 useIsAuthenticated, (中文(简体) ), useHasRole 编号 : useRole 财务报告和审定财务报表 useHasPermission 编号 : useCan,用于最常见的案件.

如果您正在使用 React 路由器或 Remix, 请使用 createFromLoader 创建 loadIs 装载机和公用设备功能 authenticated。 。 。 .

目录

演示文稿

开始

在这里,我们创建一个组件和一个钩子来检查用户是否经过认证,或者是否启用实验特性. 我们把用户从 UserContext。实验特性启用于 preview.* 例如,http://preview.localhost:5173.

创建 <Is> 打开 useIs

./is.ts数字 :

import { create } from "@arnosaine/is";
import { use } from "react";
import UserContext from "./UserContext";

const [Is, useIs] = create(function useValues() {
  const user = use(UserContext);

  const isExperimental = location.hostname.startsWith("preview.");

  // Or, get the value from the user context, a hook call, or another
  // source.
  // const isExperimental = user?.roles?.includes("developer") ?? false;

  return {
    // The property names become the prop and hook param names.
    // Allowed types: boolean | number | string | boolean[] | number[] |
    // string[].
    authenticated: Boolean(user),
    experimental: isExperimental,
    // ...
  };
});

export { Is, useIs };

使用 <Is> 打开 useIs

import { Is, useIs } from "./is";

// Component

<Is authenticated fallback="Please log in">
  Welcome back!
</Is>;

<Is experimental>
  <SomeExperimentalFeature />
</Is>;

// Hook

const isAuthenticated = useIs({ authenticated: true }); // boolean
const isExperimental = useIs({ experimental: true }); // boolean

i️ 在条件代码变大时考虑懒惰加载. 否则,有条件的代码就包含在捆绑中,即使没有被渲染出. 此外,如果未交代的代码应保密,则不要使用这种方法.

想法

特性旗

硬码特性

硬码特性列表也许是最简单的方法,仍然可以改进项目工作流程. 例如,可以在 release 分支,同时可以在 main 或者说 feature 树枝.

./is.ts数字 :

import { create } from "@arnosaine/is";

const [Is, useIs] = create(function useValues() {
  return {
    // Hardcoded features
    feature: ["feature-abc", "feature-xyz"] as const,
    // ...
  };
});

export { Is, useIs };

构建时间特征

从构建时的环境变量读取已启用的特性 :

.env数字 :

FEATURES=["feature-abc","feature-xyz"]

./is.ts数字 :

import { create } from "@arnosaine/is";

const [Is, useIs] = create(function useValues() {
  return {
    // Read the enabled features from an environment variable at build
    // time
    feature: JSON.parse(import.meta.env.FEATURES ?? "[]"),
    // ...
  };
});

export { Is, useIs };

运行时间特征

运行时从配置文件或 API 读取启用的特性 :

public/config.json数字 :

{
  "features": ["feature-abc", "feature-xyz"]
}

./is.ts数字 :

import { create } from "@arnosaine/is";
import { use } from "react"; // React v19

async function getConfig() {
  const response = await fetch(import.meta.env.BASE_URL + "config.json");
  return response.json();
}

const configPromise = getConfig();

const [Is, useIs] = create(function useValues() {
  const config = use(configPromise);

  return {
    feature: config.features,
    // ...
  };
});

export { Is, useIs };

A/B 测试,实验特征

启用基于其他值的一些特性 :

./is.ts数字 :

import { create } from "@arnosaine/is";

const [Is, useIs] = create(function useValues() {
  const features = [
    /*...*/
  ];

  // Enable some features only in development mode:
  if (import.meta.env.MODE === "development") {
    features.push("new-login-form");
  }

  // Or, enable some features only on `dev.*` domains, for example, at
  // http://dev.localhost:5173:
  if (location.hostname.startsWith("dev.")) {
    features.push("new-landing-page");
  }

  return {
    feature: features,
    // ...
  };
});

export { Is, useIs };

在预览模式中启用所有特性

./is.ts数字 :

import { create } from "@arnosaine/is";

const [Is, useIs] = create(function useValues() {
  const features = [
    /*...*/
  ];

  const isPreview = location.hostname.startsWith("preview.");

  return {
    feature: isPreview
      ? // In preview mode, all features are enabled.
        // Typed as string to accept any string as a feature name.
        (true as unknown as string)
      : features,
    // ...
  };
});

export { Is, useIs };

使用量

如何界定特征并不重要 <Is> 财务报告和审定财务报表 useIs 同为:

import { Is, useIs } from "./is";

// Component

<Is feature="new-login-form" fallback={<OldLoginForm />}>
  <NewLoginForm />
</Is>;

// Hook

const showNewLoginForm = useIs({ feature: "new-login-form" });

域的应用备选案文

在浏览器中, location.hostname 是常数,而 location.hostname === "example.com" && <p>This appears only on example.com</p> 可能是你所需要的一切。 您仍可选择使用Is模式来表示一致性以及服务器侧动作和加载器.

./is.ts数字 :

import { create } from "@arnosaine/is";

const [Is, useIs] = create(function useValues() {
  const domain = location.hostname.endsWith(".localhost")
    ? // On <domain>.localhost, get subdomain.
      location.hostname.slice(0, -".localhost".length)
    : location.hostname;

  return {
    variant: domain,
    // ...
  };
});

export { Is, useIs };

使用量

import { Is, useIs } from "./is";

// Component

<Is variant="example.com">
  <p>This appears only on example.com</p>
</Is>;

// Hook

const isExampleDotCom = useIs({ variant: "example.com" });

用户角色和权限

./is.ts数字 :

import { create } from "@arnosaine/is";
import { use } from "react";
import UserContext from "./UserContext";

const [Is, useIs] = create(function useValues() {
  const user = use(UserContext);

  return {
    authenticated: Boolean(user),
    role: user?.roles, // ["admin", ...]
    permission: user?.permissions, // ["create-articles", ...]
    // ...
  };
});

export { Is, useIs };

使用量

import { Is, useIs } from "./is";

// Component

<Is authenticated fallback="Please log in">
  Welcome back!
</Is>;

<Is role="admin">
  <AdminPanel />
</Is>;

<Is permission="update-articles">
  <button>Edit</button>
</Is>;

// Hook

const isAuthenticated = useIs({ authenticated: true });
const isAdmin = useIs({ role: "admin" });
const canUpdateArticles = useIs({ permission: "update-articles" });

是特定的日子

./is.ts数字 :

import { create } from "@arnosaine/is";
import { easter } from "date-easter";
import { isSameDay } from "date-fns";

const [Is, useIs] = create(function useValues() {
  return {
    easter: isSameDay(new Date(easter()), new Date()),
    // ...
  };
});

export { Is, useIs };

使用量

import { Is, useIs } from "./is";

// Component

<Is easter>🐣🐣🐣</Is>;

// Hook

const isEaster = useIs({ easter: true });

快捷键组件和钩

import { create } from "@arnosaine/is";
import { use } from "react";
import UserContext from "./UserContext";

const [IsAuthenticated, useIsAuthenticated] = create(
  function useValues() {
    const user = use(UserContext);

    return { authenticated: Boolean(user) };
  },
  { authenticated: true } // Default props / hook params
);

<IsAuthenticated fallback="Please log in">Welcome back!</IsAuthenticated>;

const isAuthenticated = useIsAuthenticated();
import { create, toBooleanValues } from "@arnosaine/is";
import { use } from "react";
import UserContext from "./UserContext";

const [HasRole, useHasRole] = create(function useValues() {
  const user = use(UserContext);

  // Create object { [role: string]: true }
  return Object.fromEntries((user?.roles ?? []).map((role) => [role, true]));
});

<HasRole admin>
  <AdminPanel />
</HasRole>;

const isAdmin = useHasRole({ admin: true });

// Same with toBooleanValues utility
const [Role, useRole] = create(() => toBooleanValues(use(UserContext)?.roles));

<Role admin>
  <AdminPanel />
</Role>;

const isAdmin = useRole({ admin: true });
import { create, toBooleanValues } from "@arnosaine/is";
import { use } from "react";
import UserContext from "./UserContext";

const [HasPermission, useHasPermission] = create(function useValues() {
  const user = use(UserContext);

  // Create object { [permission: string]: true }
  return Object.fromEntries(
    (user?.permissions ?? []).map((permission) => [permission, true])
  );
});

<HasPermission update-articles>
  <button>Edit</button>
</HasPermission>;

const canUpdateArticles = useHasPermission({ "update-articles": true });

// Same with toBooleanValues utility
const [Can, useCan] = create(() =>
  toBooleanValues(use(UserContext)?.permissions)
);

<Can update-articles>
  <button>Edit</button>
</Can>;

const canUpdateArticles = useCan({ "update-articles": true });

用于非常特定用途

import { create } from "@arnosaine/is";
import { use } from "react";
import UserContext from "./UserContext";

const [CanUpdateArticles, useCanUpdateArticles] = create(
  function useValues() {
    const user = use(UserContext);

    return {
      updateArticles: user?.permissions?.includes("update-articles") ?? false,
    };
  },
  { updateArticles: true } // Default props / hook params
);

<CanUpdateArticles>
  <button>Edit</button>
</CanUpdateArticles>;

const canUpdateArticles = useCanUpdateArticles();

装入器( 反应路由器 / Remix)

设置

  1. 创建 <Is>, (中文(简体) ), useIs 打开 loadIs 使用 createFromLoader。 。 。 .

    ./app/is.ts数字 :

    import { createFromLoader } from "@arnosaine/is";
    import { loadConfig, loadUser } from "./loaders";
    
    const [Is, useIs, loadIs] = createFromLoader(async (args) => {
      const { hostname } = new URL(args.request.url);
      const isPreview = hostname.startsWith("preview.");
      const user = await loadUser(args);
      const config = await loadConfig(args);
    
      return {
        authenticated: Boolean(user),
        feature: config?.features,
        preview: isPreview,
        role: user?.roles,
        // ...
      };
    });
    
    export { Is, useIs, loadIs };
    

    ./app/root.tsx数字 :

  2. 返回时 ...is 从根 loader 编号 : clientLoader。见 选项 以使用其他路线.

    import { loadIs } from "./is";
    
    export const loader = async (args: LoaderFunctionArgs) => {
      const is = await loadIs(args);
    
      return {
        ...is,
        // ...other loader data...
      };
    };
    

i️ 根 ErrorBoundary 无法访问根 loader 数据。 从根起 Layout 导出与 root 共享 ErrorBoundary,如果使用 <Is> 或者说 useIs 输入 Layout 导出,考虑将所有路由前缀为 _. (路径)和使用 ErrorBoundary 内 routes/_.tsx 在到达根之前捕捉出错 ErrorBoundary。 。 。 .

使用 loadIs

import { loadIs } from "./is";

// Or clientLoader
export const loader = async (args: LoaderFunctionArgs) => {
  const is = await loadIs(args);

  const isAuthenticated = is({ authenticated: true });
  const hasFeatureABC = is({ feature: "feature-abc" });
  const isPreview = is({ preview: true });
  const isAdmin = is({ role: "admin" });

  // ...
};

公用事业

i️ 参见 Remix 示例 倾斜/倾斜 财务报告和审定财务报表 利用/反应.ts 需要更多的实例.

./app/utils/auth.tsx数字 :

import { loaderFunctionArgs } from "@remix-run/node";
import { loadIs } from "./is";

export const authenticated = async (
  args: LoaderFunctionArgs,
  role?: string | string[]
) => {
  const is = await loadIs(args);

  // Ensure user is authenticated
  if (!is({ authenticated: true })) {
    throw new Response("Unauthorized", {
      status: 401,
    });
  }

  // If the optional role parameter is available, ensure the user has
  // the required roles
  if (!is({ role })) {
    throw new Response("Forbidden", {
      status: 403,
    });
  }
};
import { authenticated } from "./utils/auth";

export const loader = async (args: LoaderFunctionArgs) => {
  await authenticated(args, "admin");

  // User is authenticated and has the role "admin".

  // ...
};

API 密码

create

打电话 create 宣布: Is 构成部分和 useIs 钩.

const [Is, useIs] = create(useValues, defaultConditions?);

名称 Is 财务报告和审定财务报表 useIs 建议采用多用途组件和钩子。 对于单用途,可以相应命名. 可选 defaultConditions 参数对于单一目的的执行也往往有用.

const [IsAuthenticated, useIsAuthenticated] = create(
  () => {
    // Retrieve the user. Since this is a hook, using other hooks and
    // context is allowed.
    const user = { name: "Example" }; // Example: use(UserContext)
    return { authenticated: Boolean(user) };
  },
  { authenticated: true }
);

参数

  • useValues: 获取并计算电流的反向钩 values 用于比较逻辑.
  • 选项 defaultConditions: 默认道具/参数 Is 财务报告和审定财务报表 useIs。 。 。 .
  • 选项 options: 用于配置行为的一个选项对象.
    • 选项 method (单位:千美元)"every" | "some": 默认 : "some"。指定如何匹配数组类型值和条件。 使用 "some" 仅要求一些条件与数值相匹配,或 "every" 要求所有条件匹配.

回返

create 返回包含 Is 构成部分和 useIs 钩.

createFromLoader

打电话 createFromLoader 宣布: Is 构成部分 useIs 钩子和 loadIs 加载器.

const [Is, useIs, loadIs] = createFromLoader(loadValues, defaultConditions?, options?);

名称 Is, (中文(简体) ), useIs 财务报告和审定财务报表 loadIs 建议采用多用途部件、钩和装载器。 对于单用途,可以相应命名. 可选 defaultConditions 参数对于单一目的的执行也往往有用.

const [IsAuthenticated, useIsAuthenticated, loadIsAuthenticated] =
  createFromLoader(
    async (args) => {
      // Retrieve the user. Since this is a loader, using await and
      // other loaders is allowed.
      const user = await loadUser(args);
      return { authenticated: Boolean(user) };
    },
    { authenticated: true }
  );

参数

  • loadValues: 反应路由器 / Remix 加载器函数,用于获取和计算电流 values 用于比较逻辑.
  • 选项 defaultConditions: 默认道具/参数 Is, (中文(简体) ), useIs 财务报告和审定财务报表 is。 。 。 .
  • 选项 options: 用于配置行为的一个选项对象.
    • 选项 method (单位:千美元)"every" | "some": 默认 : "some"。指定如何匹配数组类型值和条件。 使用 "some" 仅要求一些条件与数值相匹配,或 "every" 要求所有条件匹配.
    • 选项 prop默认值 : "__is_values"。 。 。 . is 对象(功能)属性,预计将在根加载器数据中返回.
    • 选项 routeId: 默认: 根路由 ID ("root" 或者说 "0") (中文(简体) ). 提供 is.__is_values 从它的装载器。 示例 "routes/admin"。 。 。 .

回返

createFromLoader 返回包含 Is 构成部分 useIs 钩子和 loadIs 加载器.

<Is>

道具

  • ...conditions: 条件与 defaultConditions 然后比较 useValues 编号 : loadValues 返回值。 如果给出了多个条件,所有条件都必须与其相应的值相匹配. 对于任何阵列类型条件:
    • 如果相应的值也是数组和 options.method 实值 "some" (默认),值数组必须至少包含一个条件项. 若为 options.method 实值 "every",值阵列必须包括所有条件项.
    • 如果对应的值不是数组,则该值必须是条件项之一.
  • 选项 children: 如果所有条件都相符, 您打算做的 UI .
  • 选项 fallback: 如果某些条件不匹配, 您打算做的 UI .

使用量

<Is authenticated fallback="Please log in">
  Welcome back!
</Is>

<IsAuthenticated fallback="Please log in">Welcome back!</IsAuthenticated>

useIs

参数

  • conditions: 条件与 defaultConditions 然后比较 useValues 编号 : loadValues 返回值。 如果给出了多个条件,所有条件都必须与其相应的值相匹配. 对于任何阵列类型条件:
    • 如果相应的值也是数组和 options.method 实值 "some" (默认),值数组必须至少包含一个条件项. 若为 options.method 实值 "every",值阵列必须包括所有条件项.
    • 如果对应的值不是数组,则该值必须是条件项之一.

回返

useIs 返回时 true 如果所有条件都相符, false 否则.

使用量

const isAuthenticated = useIs({ authenticated: true });
const isAuthenticated = useIsAuthenticated();

loadIs

参数

  • args: 反应路由器/重混 LoaderFunctionArgs, (中文(简体) ), ActionFunctionArgs, (中文(简体) ), ClientLoaderFunctionArgs,或 ClientActionFunctionArgs。 。 。 .

回返

loadIs 返回 a Promise 决定 is 函数.

使用量

export const loader = async (args: LoaderFunctionArgs) => {
  const is = await loadIs(args);
  const authenticated = await loadIsAuthenticated(args);

  const isAuthenticated = is({ authenticated: true });
  const isAuthenticated = authenticated();
  // ...
};

is

is 函数是正在等待的调用返回值 loadIs。 。 。 .

参数

  • conditions: 条件与 defaultConditions 然后比较 useValues 编号 : loadValues 返回值。 如果给出了多个条件,所有条件都必须与其相应的值相匹配. 对于任何阵列类型条件:
    • 如果相应的值也是数组和 options.method 实值 "some" (默认),值数组必须至少包含一个条件项. 若为 options.method 实值 "every",值阵列必须包括所有条件项.
    • 如果对应的值不是数组,则该值必须是条件项之一.

回返

is 返回 a true 如果所有条件都相符, false 否则.

使用量

单位 root.tsx 您也必须返回 ...is 从 loader 编号 : clientLoader。见 选项 以使用其他路线.

export const loader = async (args: LoaderFunctionArgs) => {
  const is = await loadIs(args);

  return {
    ...is,
    // ...other loader data...
  };
};

toBooleanValues

打电话 toBooleanValues 以 true 数值.

const permissionList = [
  "create-articles",
  "read-articles",
  "update-articles",
  "delete-articles",
];
const permissionValues = toBooleanValues(permissions);
// { "create-articles": true, "read-articles": true, ... }

参数

  • 选项 strings: 一系列字符串.

回返

toBooleanValues 返回对象 true 数值.

类型

Value

  • 类型 Value 实值 boolean | number | string。 。 。 .
  • 也可能更具体一些,比如: string 数值.

示例

const features = ["feature-abc", "feature-xyz"] as const;

// "feature-abc" | "feature-xyz"
type Feature = (typeof features)[number];

Values

  • 类型 Values 实值 Record<string, Value | Value[]>。 。 。 .

示例

{
  "authenticated": true,
  "roles": ["admin"],
  "permissions": [
    "create-articles",
    "read-articles",
    "update-articles",
    "delete-articles"
  ]
}

Conditions

  • 类型 Conditions 实值 Partial<Values>。 。 。 .

示例

{
  "roles": "admin"
}

版本与发布记录

@arnosaine/is 共发布 14 个版本,首次发布于 2024/05/25;过去 12 个月发布了 0 个版本。

版本发布日期距今
0.2.9latest2025/01/231.8 年前
0.2.82025/01/231.8 年前
0.2.72025/01/171.8 年前
0.2.62024/08/022 年前
0.2.52024/08/022 年前
0.2.42024/07/312 年前

关于 @arnosaine/is 的常见问题

@arnosaine/is 是什么?

地物旗,角色和权限的渲染,A/B测试,实验地物等,多见于React.

如何安装 @arnosaine/is?

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

@arnosaine/is 支持 TypeScript 吗?

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

@arnosaine/is 支持 ESM 还是 CommonJS?

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

@arnosaine/is 可以商用吗?

@arnosaine/is 使用 ISC 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@arnosaine/is 的下载量有多少?

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

@arnosaine/is 还在维护吗?

最新版本 0.2.9 发布于 2025/01/23(1.8 年前);至今共发布 14 个版本,其中过去 12 个月发布了 0 个;首次发布于 2024/05/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。