npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@winner-fed/renderer-react

v1.0.3

Published

React renderer for WinJS framework

Downloads

16

Readme

@winner-fed/renderer-react

WinJS 框架的 React 渲染器,提供客户端渲染能力和 React Router 集成。

特性

  • React 19.x 支持 - 使用 React 19 的最新特性和并发渲染能力
  • 🚦 React Router v7 集成 - 最新的路由管理和导航能力
  • 🎯 多种路由模式 - 支持 Browser、Hash 和 Memory 路由
  • 🔄 插件系统集成 - 与 WinJS 插件系统无缝集成
  • 📦 TypeScript 支持 - 完整的类型定义
  • 🎨 应用上下文 - 通过 Context 访问路由和应用数据
  • 懒加载 - 内置代码分割和懒加载支持
  • 🔗 路由预加载 - 支持程序化路由预加载
  • 🌊 流式渲染 - 支持 React Suspense 流式渲染

安装

npm install @winner-fed/renderer-react

核心概念

客户端渲染

renderClient 是渲染 React 应用的主入口:

import { renderClient } from '@winner-fed/renderer-react';

renderClient({
  rootElement: document.getElementById('root'),
  routes: routesById,
  routeComponents: routeComponents,
  pluginManager: pluginManager,
  basename: '/app',
  history: history,
});

路由配置

路由定义采用 WinJS 路由结构,会自动转换为 React Router 格式:

const routesById = {
  'home': {
    id: 'home',
    path: '/',
    parentId: undefined,
  },
  'about': {
    id: 'about',
    path: '/about',
    parentId: undefined,
    clientLoader: async () => {
      return { data: 'About 页面数据' };
    },
  },
  'user': {
    id: 'user',
    path: '/user/:id',
    parentId: 'about',
  },
};

const routeComponents = {
  'home': HomePage,
  'about': AboutPage,
  'user': UserPage,
};

应用上下文

通过 useAppData Hook 访问应用数据:

import { useAppData } from '@winner-fed/renderer-react';

function MyComponent() {
  const { routes, clientRoutes, pluginManager, basename, history } = useAppData();
  
  return <div>当前 basename: {basename}</div>;
}

API 参考

函数

renderClient(options)

将 React 应用渲染到 DOM。

参数:

interface RenderClientOpts {
  // 公共路径配置
  publicPath?: string;
  // 是否运行时配置 publicPath
  runtimePublicPath?: boolean;
  // 挂载元素 ID(微前端场景可能变化)
  mountElementId?: string;
  // 挂载的 DOM 元素
  rootElement?: HTMLElement;
  // 路由配置(按 ID 索引)
  routes: IRoutesById;
  // 路由组件映射
  routeComponents: IRouteComponents;
  // 插件管理器实例
  pluginManager: any;
  // 路由 base 路径
  basename?: string;
  // 加载中显示的组件
  loadingComponent?: React.ReactNode;
  // History 实例(browserHistory/hashHistory/memoryHistory)
  history: History;
  // 是否启用流式渲染(默认 true)
  useStream?: boolean;
  // 是否仅返回组件(用于测试)
  components?: boolean;
  // 渲染完成回调
  callback?: () => void;
}

返回值:

  • 如果 components: true,返回 React 组件
  • 否则直接渲染到 DOM,无返回值

createClientRoutes({ routesById, routeComponents })

将 WinJS 路由格式转换为 React Router 格式。

参数:

{
  routesById: IRoutesById;
  routeComponents: Record<string, any>;
  parentId?: string;
  loadingComponent?: React.ReactNode;
  useStream?: boolean;
}

__getRoot()

获取当前的 React Root 实例(用于卸载等场景,如微前端)。

Hooks

useAppData()

获取应用上下文数据:

const { 
  routes,           // 路由配置
  routeComponents,  // 路由组件映射
  clientRoutes,     // 客户端路由树
  pluginManager,    // 插件管理器
  rootElement,      // 根元素
  basename,         // base 路径
  clientLoaderData, // 客户端加载的数据
  preloadRoute,     // 预加载路由函数
  history           // History 实例
} = useAppData();

useLoaderData()

获取当前路由的 clientLoader 加载的数据:

function UserPage() {
  const { data } = useLoaderData();
  return <div>用户: {data.name}</div>;
}

useClientLoaderData()

已废弃,请使用 useLoaderData()

useRouteData()

获取当前路由的上下文数据:

import { useRouteData } from '@winner-fed/renderer-react';

function MyComponent() {
  const { route } = useRouteData();
  return <div>当前路由 ID: {route.id}</div>;
}

useRouteProps()

获取当前路由的属性(不包括 element):

function MyComponent() {
  const routeProps = useRouteProps();
  return <div>{routeProps.someCustomProp}</div>;
}

useSelectedRoutes()

获取当前匹配的路由链(从根到当前路由):

function Breadcrumb() {
  const routes = useSelectedRoutes();
  return (
    <div>
      {routes.map(r => <span key={r.route.id}>{r.route.path}</span>)}
    </div>
  );
}

组件

<Link>

路由链接组件(从 react-router 导出):

import { Link } from '@winner-fed/renderer-react';

<Link to="/about">关于我们</Link>
<Link to="/user/123">用户页面</Link>
<Link to="/settings" state={{ from: 'home' }}>设置</Link>

withRouter(Component)

为类组件注入路由相关的 props:

import { withRouter, RouteComponentProps } from '@winner-fed/renderer-react';

class MyComponent extends React.Component<RouteComponentProps> {
  handleClick = () => {
    this.props.history.push('/home');
  };
  
  render() {
    const { location, params, navigate } = this.props;
    return <div>当前路径: {location.pathname}</div>;
  }
}

export default withRouter(MyComponent);

注入的 props:

  • history - 包含 back(), goBack(), push(), replace() 等方法
  • location - 当前位置对象
  • match - 包含路由参数
  • params - 路由参数对象
  • navigate - 导航函数

React Router 导出

直接从 react-router 重新导出的常用 API:

import {
  // Hooks
  useLocation,
  useNavigate,
  useParams,
  useSearchParams,
  useMatch,
  useOutlet,
  useOutletContext,
  useResolvedPath,
  useRoutes,
  
  // 组件
  Link,
  Navigate,
  NavLink,
  Outlet,
  
  // 工具函数
  createSearchParams,
  generatePath,
  matchPath,
  matchRoutes,
  resolvePath,
} from '@winner-fed/renderer-react';

History 导出

history 包重新导出:

import {
  createBrowserHistory,
  createHashHistory,
  createMemoryHistory,
  type History,
} from '@winner-fed/renderer-react';

类型定义

IRoute

路由定义接口:

interface IRoute {
  id: string;                    // 路由唯一标识
  path?: string;                 // 路由路径
  index?: boolean;               // 是否为索引路由
  parentId?: string;             // 父路由 ID
  redirect?: string;             // 重定向路径
  clientLoader?: ClientLoader;   // 客户端数据加载函数
  routeProps?: Record<string, any>; // 自定义路由属性
}

IClientRoute

客户端路由接口(扩展自 IRoute):

interface IClientRoute extends IRoute {
  element?: React.ReactNode;       // 路由元素
  Component?: React.ComponentType; // 路由组件
  children?: IClientRoute[];       // 子路由
  routes?: IClientRoute[];         // 子路由(遗留)
}

IRoutesById

路由映射表:

interface IRoutesById {
  [id: string]: IRoute;
}

IRouteComponents

路由组件映射表:

interface IRouteComponents {
  [id: string]: any; // React 组件
}

ClientLoader

客户端数据加载器:

type ClientLoader = (() => Promise<any>) & {
  hydrate?: boolean; // 是否需要水合
};

插件系统集成

渲染器与 WinJS 插件系统深度集成,提供多个插件钩子:

Provider 钩子(由内到外)

// 1. innerProvider - 最内层 Provider
pluginManager.applyPlugins({
  type: 'modify',
  key: 'innerProvider',
  initialValue: App,
  args: { routes, history, plugin },
});

// 2. i18nProvider - 国际化 Provider
pluginManager.applyPlugins({
  type: 'modify',
  key: 'i18nProvider',
  initialValue: App,
  args: { routes, history, plugin },
});

// 3. accessProvider - 权限控制 Provider
pluginManager.applyPlugins({
  type: 'modify',
  key: 'accessProvider',
  initialValue: App,
  args: { routes, history, plugin },
});

// 4. dataflowProvider - 数据流 Provider
pluginManager.applyPlugins({
  type: 'modify',
  key: 'dataflowProvider',
  initialValue: App,
  args: { routes, history, plugin },
});

// 5. outerProvider - 最外层 Provider
pluginManager.applyPlugins({
  type: 'modify',
  key: 'outerProvider',
  initialValue: App,
  args: { routes, history, plugin },
});

// 6. rootContainer - 根容器
pluginManager.applyPlugins({
  type: 'modify',
  key: 'rootContainer',
  initialValue: App,
  args: { routes, history, plugin },
});

事件钩子

// 修改客户端路由
pluginManager.applyPlugins({
  type: 'event',
  key: 'patchClientRoutes',
  args: { routes: clientRoutes },
});

// 路由变化事件
pluginManager.applyPlugins({
  type: 'event',
  key: 'onRouteChange',
  args: {
    routes,
    clientRoutes,
    location,
    action,
    basename,
    isFirst: boolean,
  },
});

使用示例

基础使用

import { renderClient } from '@winner-fed/renderer-react';
import { createBrowserHistory } from '@winner-fed/renderer-react';

const history = createBrowserHistory();

renderClient({
  rootElement: document.getElementById('root'),
  routes: {
    'home': { id: 'home', path: '/' },
    'about': { id: 'about', path: '/about' },
  },
  routeComponents: {
    'home': () => <div>首页</div>,
    'about': () => <div>关于</div>,
  },
  pluginManager: pluginManager,
  history: history,
  basename: '/',
});

带数据加载

const routes = {
  'user': {
    id: 'user',
    path: '/user/:id',
    clientLoader: async () => {
      const user = await fetchUser(params.id);
      return { user };
    },
  },
};

function UserPage() {
  const { data } = useLoaderData();
  return <div>用户: {data.user.name}</div>;
}

路由重定向

const routes = {
  'old-path': {
    id: 'old-path',
    path: '/old',
    redirect: '/new',
  },
  'new-path': {
    id: 'new-path',
    path: '/new',
  },
};

嵌套路由

const routes = {
  'layout': {
    id: 'layout',
    path: '/',
  },
  'home': {
    id: 'home',
    path: '/',
    index: true,
    parentId: 'layout',
  },
  'about': {
    id: 'about',
    path: 'about',
    parentId: 'layout',
  },
};

const routeComponents = {
  'layout': () => (
    <div>
      <nav>导航</nav>
      <Outlet />
    </div>
  ),
  'home': () => <div>首页内容</div>,
  'about': () => <div>关于内容</div>,
};

自定义路由属性

const routes = {
  'protected': {
    id: 'protected',
    path: '/protected',
    routeProps: {
      requireAuth: true,
      keepQuery: true,
    },
  },
};

function ProtectedPage() {
  const routeProps = useRouteProps();
  if (routeProps.requireAuth && !isLoggedIn()) {
    return <Navigate to="/login" />;
  }
  return <div>受保护的内容</div>;
}

预加载路由

import { Link, useAppData } from '@winner-fed/renderer-react';

function Navigation() {
  const { preloadRoute } = useAppData();
  
  // 程序化预加载
  const handleHover = () => {
    if (preloadRoute) {
      preloadRoute('/dashboard');
    }
  };
  
  return (
    <nav>
      <Link to="/home">首页</Link>
      <Link to="/products">产品</Link>
      
      {/* 程序化预加载 */}
      <button onMouseEnter={handleHover}>控制台</button>
    </nav>
  );
}

内部实现

渲染流程

  1. 路由转换:将 WinJS 的路由格式(IRoutesById)转换为 React Router 格式(IClientRoute[])
  2. 插件集成:应用插件系统的各种钩子(patchClientRoutes、onRouteChange 等)
  3. 数据加载:在路由变化时自动执行 clientLoader 并缓存数据
  4. Provider 包装:应用多层 Provider(innerProvider → i18nProvider → accessProvider → dataflowProvider → outerProvider → rootContainer)
  5. React 渲染:使用 React 18+ 的 createRoot API 渲染应用

路由转换规则

  • 根据 parentId 构建嵌套路由树
  • index: true 的路由转换为索引路由
  • redirect 字段转换为 Navigate 组件
  • clientLoader 保留在路由定义中,由渲染器管理数据加载

性能优化

  • 使用 useCallback 避免不必要的函数重建
  • clientLoader 数据全局缓存,避免重复加载
  • 支持流式渲染(useStream),提升首屏加载速度
  • 通过 preloadRoute 支持程序化路由预加载

依赖关系

  • react / react-dom:需要 React 19.0.0 或更高版本
  • react-router:使用 React Router v7 进行路由管理
  • history:使用 History v5,支持多种路由模式(Browser/Hash/Memory)
  • @winner-fed/winjs:与 WinJS 插件系统集成

常见问题

1. 如何在 clientLoader 中访问路由参数?

目前 clientLoader 不直接接收参数,但可以通过 window.location 或组件内部的 hooks 获取:

const routes = {
  'user': {
    id: 'user',
    path: '/user/:id',
    clientLoader: async () => {
      // 方案 1:从 URL 解析参数
      const id = window.location.pathname.split('/').pop();
      return { user: await fetchUser(id) };
    },
  },
};

2. 如何在微前端场景下使用?

使用 __getRoot() 获取 React Root 实例,在卸载时调用 root.unmount()

import { renderClient, __getRoot } from '@winner-fed/renderer-react';

// 挂载
renderClient({ /* ... */ });

// 卸载
const root = __getRoot();
if (root) root.unmount();

3. 如何处理路由守卫?

通过插件系统的 onRouteChange 钩子实现:

export default {
  onRouteChange({ location, routes }) {
    // 路由守卫逻辑
    if (needAuth && !isLoggedIn()) {
      history.push('/login');
    }
  },
};

4. 如何使用自定义 Loading 组件?

通过 loadingComponent 参数传入:

renderClient({
  // ...
  loadingComponent: <CustomSpinner />,
});

5. 如何实现路由预加载?

使用 useAppDatapreloadRoute 方法:

const { preloadRoute } = useAppData();

// 在鼠标悬停时预加载
const handleHover = () => {
  if (preloadRoute) {
    preloadRoute('/target-path');
  }
};

许可证

MIT