Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

License

NotificationsYou must be signed in to change notification settings

winjs-dev/winjs-renderer-react

Repository files navigation

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 格式:

constroutesById={'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',},};constrouteComponents={'home':HomePage,'about':AboutPage,'user':UserPage,};

应用上下文

通过useAppData Hook 访问应用数据:

import{useAppData}from'@winner-fed/renderer-react';functionMyComponent(){const{ routes, clientRoutes, pluginManager, basename, history}=useAppData();return<div>当前 basename:{basename}</div>;}

API 参考

函数

renderClient(options)

将 React 应用渲染到 DOM。

参数:

interfaceRenderClientOpts{// 公共路径配置publicPath?:string;// 是否运行时配置 publicPathruntimePublicPath?: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 加载的数据:

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

useClientLoaderData()

已废弃,请使用useLoaderData()

useRouteData()

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

import{useRouteData}from'@winner-fed/renderer-react';functionMyComponent(){const{ route}=useRouteData();return<div>当前路由 ID:{route.id}</div>;}

useRouteProps()

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

functionMyComponent(){constrouteProps=useRouteProps();return<div>{routeProps.someCustomProp}</div>;}

useSelectedRoutes()

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

functionBreadcrumb(){constroutes=useSelectedRoutes();return(<div>{routes.map(r=><spankey={r.route.id}>{r.route.path}</span>)}</div>);}

组件

<Link>

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

import{Link}from'@winner-fed/renderer-react';<Linkto="/about">关于我们</Link><Linkto="/user/123">用户页面</Link><Linkto="/settings"state={{from:'home'}}>设置</Link>

withRouter(Component)

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

import{withRouter,RouteComponentProps}from'@winner-fed/renderer-react';classMyComponentextendsReact.Component<RouteComponentProps>{handleClick=()=>{this.props.history.push('/home');};render(){const{ location, params, navigate}=this.props;return<div>当前路径:{location.pathname}</div>;}}exportdefaultwithRouter(MyComponent);

注入的 props:

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

React Router 导出

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

import{// HooksuseLocation,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,typeHistory,}from'@winner-fed/renderer-react';

类型定义

IRoute

路由定义接口:

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

IClientRoute

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

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

IRoutesById

路由映射表:

interfaceIRoutesById{[id:string]:IRoute;}

IRouteComponents

路由组件映射表:

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

ClientLoader

客户端数据加载器:

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

插件系统集成

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

Provider 钩子(由内到外)

// 1. innerProvider - 最内层 ProviderpluginManager.applyPlugins({type:'modify',key:'innerProvider',initialValue:App,args:{ routes, history, plugin},});// 2. i18nProvider - 国际化 ProviderpluginManager.applyPlugins({type:'modify',key:'i18nProvider',initialValue:App,args:{ routes, history, plugin},});// 3. accessProvider - 权限控制 ProviderpluginManager.applyPlugins({type:'modify',key:'accessProvider',initialValue:App,args:{ routes, history, plugin},});// 4. dataflowProvider - 数据流 ProviderpluginManager.applyPlugins({type:'modify',key:'dataflowProvider',initialValue:App,args:{ routes, history, plugin},});// 5. outerProvider - 最外层 ProviderpluginManager.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';consthistory=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:'/',});

带数据加载

constroutes={'user':{id:'user',path:'/user/:id',clientLoader:async()=>{constuser=awaitfetchUser(params.id);return{ user};},},};functionUserPage(){const{ data}=useLoaderData();return<div>用户:{data.user.name}</div>;}

路由重定向

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

嵌套路由

constroutes={'layout':{id:'layout',path:'/',},'home':{id:'home',path:'/',index:true,parentId:'layout',},'about':{id:'about',path:'about',parentId:'layout',},};constrouteComponents={'layout':()=>(<div><nav>导航</nav><Outlet/></div>),'home':()=><div>首页内容</div>,'about':()=><div>关于内容</div>,};

自定义路由属性

constroutes={'protected':{id:'protected',path:'/protected',routeProps:{requireAuth:true,keepQuery:true,},},};functionProtectedPage(){constrouteProps=useRouteProps();if(routeProps.requireAuth&&!isLoggedIn()){return<Navigateto="/login"/>;}return<div>受保护的内容</div>;}

预加载路由

import{Link,useAppData}from'@winner-fed/renderer-react';functionNavigation(){const{ preloadRoute}=useAppData();// 程序化预加载consthandleHover=()=>{if(preloadRoute){preloadRoute('/dashboard');}};return(<nav><Linkto="/home">首页</Link><Linkto="/products">产品</Link>{/* 程序化预加载 */}<buttononMouseEnter={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 获取:

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

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

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

import{renderClient,__getRoot}from'@winner-fed/renderer-react';// 挂载renderClient({/* ... */});// 卸载constroot=__getRoot();if(root)root.unmount();

3. 如何处理路由守卫?

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

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

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

通过loadingComponent 参数传入:

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

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

使用useAppDatapreloadRoute 方法:

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

许可证

MIT

About

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

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors2

  •  
  •  

[8]ページ先頭

©2009-2025 Movatter.jp