{"id":20882957,"url":"https://github.com/heineiuo/react-as-backend-framework","last_synced_at":"2026-05-15T20:05:56.541Z","repository":{"id":66092596,"uuid":"350553350","full_name":"heineiuo/react-as-backend-framework","owner":"heineiuo","description":"Use React as a backend framework","archived":false,"fork":false,"pushed_at":"2021-03-23T15:33:34.000Z","size":238,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-28T23:47:47.554Z","etag":null,"topics":["backend","framework","ioc","react"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/heineiuo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-23T02:19:15.000Z","updated_at":"2022-09-20T06:53:13.000Z","dependencies_parsed_at":"2023-03-23T21:27:34.655Z","dependency_job_id":null,"html_url":"https://github.com/heineiuo/react-as-backend-framework","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/heineiuo/react-as-backend-framework","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heineiuo%2Freact-as-backend-framework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heineiuo%2Freact-as-backend-framework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heineiuo%2Freact-as-backend-framework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heineiuo%2Freact-as-backend-framework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/heineiuo","download_url":"https://codeload.github.com/heineiuo/react-as-backend-framework/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heineiuo%2Freact-as-backend-framework/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33078046,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-15T20:05:40.333Z","status":"ssl_error","status_checked_at":"2026-05-15T20:05:38.672Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backend","framework","ioc","react"],"created_at":"2024-11-18T07:34:15.800Z","updated_at":"2026-05-15T20:05:56.524Z","avatar_url":"https://github.com/heineiuo.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 后端框架 —— React\n\n\n**TL;DR**\n\n这篇文章和SSR没有任何关系，和[React Server Component](https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html)也没有关系。是字面意思地用React作为后端框架开发`node.js`后端，基于React解耦和管理依赖、控制组件的生命周期、用JSX直观地定义服务器状态。\n\n## 出发点\n\n后端复杂之后，无可避免地需要引入依赖管理的机制，这时候OOP语言里经典的一些设计模式就派上用场了，比如工厂模式、IoC模式。一方面为了解耦，另一方面也能提高开发效率，从无止尽地`new`和传递参数中解放出来。\n\n`node.js`生态已经有了一些IoC实现，使用了decorators语法，比如[InversifyJS](https://github.com/inversify/InversifyJS)(满屏幕的Java味道)。\n\n那么，有没有可能不使用decorators实现IoC呢？这时候我又想到了Angular和React，Angular采用了类似Java DI的模式，而React却完全没有使用类似的模式，为何感觉同样拥有优雅的依赖管理？\n\n```ts\nconst Theme = createContext({})\nconst Router = createContext({})\n\n// Provider.js\nfunction Provider (props){\n  return (\n    \u003cTheme.Provider value={{ primaryColor: \"#1d7dfa\" }}\u003e\n        \u003cRouter.Provider value={{ currentPathname: \"/github\" }}\u003e\n          {props.children}\n        \u003c/Router.Provider\u003e\n    \u003c/Theme.Provider\u003e\n  )\n}\n\n// Consumer.js\nfunction Consumer (){\n  const theme = useContext(Theme)\n  const router = useContext(Router)\n}\n```\n\nContext API分离了Provider和Consumer，使用一个hook（useContext）将Provider的value「注入」到Consumer中。使得Consumer**没有在内部创建Provider**实例的时候得到了Provider的value，这...不就是**控制反转**吗...\n\n\n![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4c8f86cc6e7f48b599301d3bac3e600c~tplv-k3u1fbpfcp-watermark.image)\n\n## 下一步\n但是，众所周知，React是一个UI框架，是通过虚拟DOM来实现界面的。React的JSX使用的`div`，`span`这些关键词都对应着一个个的网页元素。这些年随着SSR的发展，也不乏开发者们在node.js服务端直接用ReactDOMServer去渲染出html，取代传统的模板....扯远了，既然文章的目的是实现后端，那么光搞个SSR没啥意义，至少要渲染出JSON来。\n\n既然拥有声明式的JSX语法，那么用JSX声明一个JSON自然就是最直接的选择。这让我想到了GraphQL，GraphQL通过定义schema和resolver，能够输出一个相当自由的JSON。\n\n\n**那么...**\n\n**那么我直接用GraphQL不就得了???**\n\n\n![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c7d1d45269ab45f283bb3b9ff25d61eb~tplv-k3u1fbpfcp-watermark.image)\n\n冷静一想，如果我是想用JSX声明一个JSON，其实并不能达到作为后端框架的目的，除了JSON之外的其他**Response**（比如text, arrayBuffer）也没那么容易用JSX声明。\n\n转变思路，去声明一个**ServerState**怎么样呢？服务器上的不同组件经过JSX的嵌套，共同构成一个服务器状态。嵌套关系就是依赖关系，而Request和Response可以交给抽象成Server的单个组件去接收和处理。\n\n```ts\n\n\u003cConfigProvider\u003e\n    \u003cDatabaseProvider\u003e\n        \u003cThirdPartyService\u003e\n            \u003cSessionProvider\u003e\n                \u003cServer onRequest={handleRequest}\u003e\n            \u003c/SessionProvider\u003e\n        \u003c/ThirdPartyService\u003e\n    \u003c/DatabaseProvider\u003e\n\u003c/ConfigProvider\u003e\n```\n\n## 抠腚开始\n\n其实有了上面这个结构之后，Coding是非常顺利的，Provider写起来都是固定的模式：\n\n```ts\n// Database.tsx\nimport { FC, useState, useContext, createContext } from \"react\";\nimport { ConfigContext } from \"./ConfigContext\";\n\nclass MockDatabaseClient {\n  options: any;\n  constructor(options: any) {\n    this.options = options;\n  }\n\n  async query\u003cT = any\u003e(): Promise\u003cT\u003e {\n    const result = {\n      count: Math.random() \u003c 0.5 ? 1 : 2,\n    } as unknown;\n    return result as T;\n  }\n}\n\ntype DatabaseState = {\n  db: MockDatabaseClient;\n};\n\nexport const DatabaseContext = createContext({} as DatabaseState);\n\n/**\n * 子组件通过useContext(DatabaseContext)得到db。\n */\nexport const DatabaseProvider: FC = (props) =\u003e {\n  const config = useContext(ConfigContext);\n  const [db] = useState(new MockDatabaseClient(config.dbOptions));\n  return (\n    \u003cDatabaseContext.Provider value={{ db }}\u003e\n      {props.children}\n    \u003c/DatabaseContext.Provider\u003e\n  );\n};\n\n\n```\n\n而Server相对特殊一点\n\n```ts\n// Server.tsx\nimport http from \"http\";\nimport { FC, useContext, useEffect, useRef } from \"react\";\nimport { ConfigContext } from \"./ConfigContext\";\nimport { DatabaseContext } from \"./DatabaseContext\";\n\n/**\n * \u003cHTTPServer port={8080} onRequest={requestHandler} key=\"KEY\"\u003e\n *\n * Won't update when `port` change.\n * Change `key` prop to close/update server.\n * @param props\n */\nexport const Server: FC = (props) =\u003e {\n  const ref = useRef\u003cany\u003e();\n  const config = useContext(ConfigContext);\n  const { db } = useContext(DatabaseContext);\n  const refPort = useRef\u003cnumber\u003e(config.port);\n\n  useEffect(() =\u003e {\n    ref.current = async (event: any) =\u003e {\n      const result = await db.query\u003c{ count: number }\u003e();\n      event.response.end(\n        `\u003c!DOCTYPE html\u003e\u003cmeta charset=\"utf8\"/\u003ehello ${Array.from({\n          length: result.count,\n        })\n          .fill(\"🐈\")\n          .join(\"\")} `\n      );\n    };\n  }, [db]);\n\n  useEffect(() =\u003e {\n    const httpServer = http.createServer((req, res) =\u003e {\n      if (ref.current) {\n        ref.current({\n          request: req,\n          response: res,\n        });\n      }\n    });\n\n    httpServer.listen(refPort.current, () =\u003e {\n      console.log(`HTTP server listening on port ${refPort.current}`);\n    });\n\n    return (): void =\u003e {\n      httpServer.close();\n    };\n  }, []);\n\n  return null;\n};\n\n\n\n```\n\n再写下handleRequest\n\n```\nconst handleRequest = useCallback((event) =\u003e {\n  event.response.end(\"hello 🐈\")\n}, [])\n```\n\n\n## 最后一步\n\n那么，怎么跑起来呢？\n\n借助[react-reconciler](https://github.com/facebook/react/tree/master/packages/react-reconciler)实现一个CustomRenderer是一个好方法，但是考虑到其实整个tree最终只是返回的一个null而已，可以先用简单的方法实现：\n\n```ts\n\nimport ReactDOM from 'react-dom'\nimport { JSDOM, DOMWindow } from 'jsdom'\nimport { App } from './App'\n\n// make ts happy\ndeclare const global: NodeJS.Global \u0026 { window: DOMWindow; document: Document }\n\nconst dom = new JSDOM(`\u003cdiv id=\"app\"\u003e\u003c/div\u003e`)\n/**\n * ReactDOM会访问window对象，所以注册到全局。\n */\nglobal.window = dom.window\nglobal.document = window.document\n\n/**\n * 之所以用#app而不直接用document.body的原因是什么呢？\n */\nReactDOM.render(\u003cApp\u003e\u003c/App\u003e, document.querySelector('#app'))\n\n```\n\n（jsdom真是个好东西呀）\n\n\n![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c9504585d13d4f2c9e8fa2ca02281bd2~tplv-k3u1fbpfcp-watermark.image)\n\n\n\n效果：\n\n![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e198ff7823fb4d86aedd370aaa547707~tplv-k3u1fbpfcp-watermark.image)\n\n以上代码可以直接从[heineiuo/react-as-backend-framework](https://github.com/heineiuo/react-as-backend-framework) 拿到，欢迎尝试。\n\n\n## 感想\n\nReact在很多人眼里不是一个完整的UI框架，而它在我眼里，甚至不是一个UI框架，而是一个「声明状态的框架」。所有的JSX构成的一个大树只是一个状态，这个状态可能渲染到DOM，也可能渲染到Native，也可能你和UI毫无关系，比如上面的代码里几乎全部由React Context构成，最终只返回了一个null，那这跟UI是没有丝毫关系的。\n\n\n\u003e **Learn Once, Write Anywhere**\n\n\n这次只是实现了一个hello world，体现了基于React的IoC机制，还缺少一些常用的组件，比如路由。下篇文章实现下路由。","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheineiuo%2Freact-as-backend-framework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fheineiuo%2Freact-as-backend-framework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheineiuo%2Freact-as-backend-framework/lists"}