{"id":18011236,"url":"https://github.com/voncheng/tomatobean","last_synced_at":"2025-06-16T08:36:37.957Z","repository":{"id":27346742,"uuid":"113542425","full_name":"voncheng/Tomatobean","owner":"voncheng","description":"集成react + react-router + react-redux的轻量级前端框架。提供keep-live功能，快速构建项目。在构建中大型项目时优势明显。","archived":false,"fork":false,"pushed_at":"2023-01-03T19:48:43.000Z","size":589,"stargazers_count":14,"open_issues_count":14,"forks_count":9,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-05-21T08:54:48.001Z","etag":null,"topics":["keeper-live","notification","react","react-router","redux","router-keeper"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/voncheng.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-08T06:56:56.000Z","updated_at":"2024-05-04T15:24:40.000Z","dependencies_parsed_at":"2023-01-14T06:32:38.367Z","dependency_job_id":null,"html_url":"https://github.com/voncheng/Tomatobean","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/voncheng/Tomatobean","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voncheng%2FTomatobean","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voncheng%2FTomatobean/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voncheng%2FTomatobean/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voncheng%2FTomatobean/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/voncheng","download_url":"https://codeload.github.com/voncheng/Tomatobean/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voncheng%2FTomatobean/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260126833,"owners_count":22962697,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["keeper-live","notification","react","react-router","redux","router-keeper"],"created_at":"2024-10-30T03:07:50.401Z","updated_at":"2025-06-16T08:36:37.935Z","avatar_url":"https://github.com/voncheng.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tomatobean\n[![npm](https://img.shields.io/npm/v/tomatobean.svg)](https://www.npmjs.com/package/tomatobean)\n## \nTomatobean是一个react + redux + react-router的集成框架。它简化了三者的配置，在项目中只需少量代码就能实现复杂的功能，并且能够帮助开发者梳理工程的结构使其易于维护。tomatobean提供了大量的装饰器，为react组件提供更强大的功能。简单的语意，和使用规则让开发者迅速上手开发实现零成本学习。其中创新的使用了智能化自动装载机制，解放了程序员的双手。话不多说开搞。\n\n\n版本更新历史：\n\n[V0.9.0更新文档](https://github.com/VonCheng/Tomatobean/blob/master/doc/update-doc-0.9.0.md)\n\nExamples:\n\n[example--v0.9.2](https://github.com/VonCheng/TBExample)\n\n**主要目录如下：**\n\n* [安装](#install)\n* [快速开始](#start)\n* [进阶](#advance)\n  - [routerConfig](#routerconfig)\n  - [models](#models)\n  - [action](#action)\n  - [api](#api)\n* [装饰器](#decorate)\n  - [BaseAction](#baseaction)\n  - [Selecter](#selecter)\n  - [RootRouteConnect](#rootrouteconnect)\n  - [Configuration](#configuration)\n  - [Tabbar](#tabbar)\n  - [Notification](#notification)\n  \n* [配置类](#configure)\n  - [AuthorityInterceptor](#authorityinterceptor)\n* [工具方法](#tools)\n  - [combinModals](#combinmodals)\n\n## \u003cspan id=\"install\"\u003e安装\u003c/span\u003e\n\n安装命令: \n```\nnpm install tomatobean\n```\n\n## \u003cspan id=\"start\"\u003e快速开始\u003c/span\u003e\n\n\u003e加载配置文件生成App，就像这样\n\n```javascript\nimport createApp, { combinModals } from 'tomatobean';\nimport { routerConfig } from '../config/routerConfig';\nimport models from './models';\nimport host from '../config/remoteHost';\n\nconst app = createApp();\n// 加载路由配置\napp.router(routerConfig);\n// 加载数据模型\napp.model(models);\n// 配置remote host\napp.setHost(host);\n// 运行App\napp.run();\n```\napp的运行依赖于这三个文件。其中model配置文件涉及到开发者具体项目的业务部分，也是开发者最关系的事情，接下来我会逐一解释这三个文件的用法和作用。\n\n## \u003cspan id=\"advance\"\u003e进阶\u003c/span\u003e\n\n### \u003cspan id=\"routerconfig\"\u003erouterConfig\u003c/span\u003e\n\nrouterConfig 这个文件主要是配置工程的路由部分。由于tomato框架集成的是react-router组件，所以在配置上跟react-router有一些相似之处。只不过为了一些特殊的功能的实现，再其基础上进行了包装。整个配置文件看起来会是这样。\n\n```javascript\nexport const routerConfig = {\n  routes: [\n    {\n      path: '/',\n      component: 'App',\n      indexRoute: { redirect: '/home' },\n      childRoutes: [\n        {\n          path: '/home',\n          component: 'home',\n          state: {\n            mark: '首页',\n          },\n        },\n      ],\n    }, {\n      path: '/login',\n      component: 'login',\n      state: {\n        checkAuthority: false,\n      },\n    },\n  ],\n  initializationTabs: [\n    {\n      pathname: '/home',\n      state: {\n        mark: '首页',\n      },\n    },\n  ],\n};\n\n```\n*配置规则*\n\n\u0026#8195;\u0026#8195;属性名称\u0026#8195;| 类型 | 默认值| 描述\n--- | --- | -- |---\nroutes | Object | \u0026#8195;\u0026#8195;\u0026#8195;| 路由主体\ninitializationTabs| Object || 初始状态下Tabbar的展示项（可选）\npath| string|| 匹配路径\ncomponent| string || React页面的放置路径\nindexRoute| Object ||需要做重定向的操作对象\nredirect| string | |重定向匹配路径\nstate| object || 浏览器Location的状态，可以在页面跳转的时候最为传值对象。`注:` 按需要内部可以添加任意多个值，在这里配置作为初始值使用\nmark| sting ||是否作为Tabbar的展示项，如果有内容则值作为Tabbar的展示内容（可选）\nchildRoutes| Object || 子路由\ncheckAuthority| boolean|true| 需不需要做用户登录认证，也就是说在进入该页面之前是否判断当前用户已登录，没有登录将会跳转登录操作。用户认证的[具体配置](#authority)\n\n### \u003cspan id=\"models\"\u003emodels\u003c/span\u003e\nmodels作为工程业务的主体部分，也是重点说明的模块。首先还是看一下代码。\n\n```javascript\n// home.js\n\nexport default {\n  namespace: 'home', // 必须唯一 建议与文件名相同\n  state: {\n    enters: [],\n  },\n  reducers: {\n    enters(state, action) {\n      return {\n        ...state,\n        enters: action.data.data,\n      };\n    },\n  },\n};\n```\n\n一个最为基础的model看起来就是这个样子。你可以把它理解为托管一个React组件状态的机器，或者看做MVC中的M，总之它管控着React组件数据流。\n\n*属性说明*\n\n属性名称\u0026#8195; | 类型 | 默认值 | 描述\n--- | --- |---|---\nnamespace |string |\u0026#8195;无\u0026#8195;\u0026#8195; | 命名空间这里也用做model的名称，需要保证唯一性\n state | object |{}| 数据模型，可以理解为需要托管的状态\n cache | boolean | false | 缓存类型（与`autowrite`属性关联）\n autowrite | object |null | 设置需要懒加载的`state`（与`cache`属性关联）\nreducers | object |{}| 存放响应器[reducer](#reducer)。响应器是一个用于响应一个action事件，并更新states值的函数。\n\n 注：\n \u003e 1. 当cache: true ，在整个工程范围内，已设置的懒加载state，每条数据只会加载一次，（如果一次没有装载成功，那么接下来的取值还会继续装载，直到装载成功）\n \u003e 当cache: false，但autowrite有值，那么装载机制的作用范围，就是以绑定模型的组件，而不是整个工程。\n \n \u003e2. \u003cspan id=\"reducer\"\u003ereducer\u003c/span\u003e是一个用于响应[action](#action)事件的函数，它被注入了两个参数 `state` 和 `action`。其中`state`为当前的所有状态，`action`参数为[action](#action)事件传入的值。最后reducer返回一个全新的`state`。（温馨提示：请参照上方的代码进行理解）\n\n \n### \u003cspan id=\"action\"\u003eaction\u003c/span\u003e\n\n`action`最基础的作用就是发起变更状态的请求。在具体的项目中，你可以将以一些了业务逻辑放在这里，也可以做单纯的数据结构处理。单独作为一个块，这样设计最初的目的也是为了解耦和复用。但多数情况下，`action`和`model`关系紧密，所以可以将两者放入一个文件中。但同时不要错误的将两者混为一谈。下面就是一个`action`\n\n```javascript\n\n// homeAction\nimport { getEntersListRequest } from '../api/homeApi';\n\nexport async function getOpportunityList(params, update) {\n  const response = await getEntersListRequest(params);\n  update({\n    type: 'home/enters',\n    data: response,\n  });\n  return response;\n}\n\n```\n\n`action`尽量使用`async`声明，对于异步加载很方便。每一个`action`都被注入了一个参数`update`。`update`是一个函数，他只接收一个参数，这个参数是一个对象。其中`type`为必须属性，它的值指向的是用来响应它请求的，具体某个`model`下的某个`reducer`响应器。其他属性为携带参数，可以任意添加。\n\n### \u003cspan id=\"api\"\u003eapi\u003c/span\u003e\n\n你可以理解为持久层，他负责对接后台服务，也可以想象成数据源。将它但作为一块抽离出来目的是解耦，实现复用。\n\n``` js\n\n// 公共接口\nimport request from '../util/request';\nimport { urlAppendQuery } from '../util/tools';\n/**\n * 根据用户Id查询用户信息\n */\nexport async function queryUserByIdRequest() {\n  return request.GET(`${host}/user-service/user/list`);\n}\n\n/**\n * 创建用户信息\n */\nexport async function saveUserRequest(params) {\n  return request.PSOT(`${host}/user-service/save`, params);\n}\n\n```\n\n## \u003cspan id=\"decorate\"\u003e装饰器\u003c/span\u003e\n\n`Tomato`提供的一些具体的方法、装饰器。这些东西能够为你的组件提供一些特殊的功能，比如：状态回滚、消息通知、路由跳转、location监听等等。\n\n### \u003cspan id=\"baseaction\"\u003eBaseAction\u003c/span\u003e\n`BaseAction`提供基础功能，包括一下方法：\n\n方法| 参数 | 返回值 | 功能说明\n--- | --- | --- | ---\n[linkTo](#linkTo)| (location) | -- | 跳转 \n[redirect](#redirect)|(location)|-- | 重定向\n[go](#go)|(number)| -- | 跳转指定浏览历史记录\n[goBack](#goBack)| -- | -- | 回退 \n[goForward](#goForward)| -- | -- | 前进\n[rollBack](#rollBack)|(namespace)| -- | 组件状态回滚，回到最初状态（仅限于托管的状态）\n \n\u003e#### \u003cspan id=\"linkTo\"\u003elinkTo\u003c/span\u003e\n\n**语法**\n\n``` javascript\nthis.props.baseAction.linkTo(location);\n// 例子\nthis.props.baseAction.linkTo(\"/home\");\nthis.props.baseAction.linkTo({pathname: \"/home\", state: \"hello world\"});\n```\n\n**参数**\n*location*\n路由地址信息\n\n**返回值**\n无\n\u003e#### \u003cspan id=\"redirect\"\u003eredirect\u003c/span\u003e\n\n**语法**\n \n``` javascript\nthis.props.baseAction.redirect(location);\n// 例子\nthis.props.baseAction.redirect(\"/home\");\nthis.props.baseAction.redirect({pathname: \"/home\", state: \"hello world\"});\n```\n\n**参数**\n*location*\n路由地址信息\n\n**返回值**\n无\n\u003e#### \u003cspan id=\"go\"\u003ego\u003c/span\u003e\n\n**语法**\n\n``` javascript\nthis.props.baseAction.go(number);\n// 例子\nthis.props.baseAction.go(1);\n```\n\n**参数**\n*number*\n回退步数\n\n**返回值**\n无\n\u003e#### \u003cspan id=\"goBack\"\u003egoBack\u003c/span\u003e\n\n**语法**\n\n``` javascript\nthis.props.baseAction.goBack();\n```\n\n**参数**\n无\n\n**返回值**\n无\n\u003e#### \u003cspan id=\"goForward\"\u003egoForward\u003c/span\u003e\n\n**语法**\n\n``` javascript\nthis.props.baseAction.goForward();\n```\n\n**参数**\n无\n\n**返回值**\n无\n\n\u003e#### \u003cspan id=\"rollBack\"\u003erollBack\u003c/span\u003e\n\n**语法**\n\n``` javascript\nthis.props.baseAction.rollBack(namespace);\n// 例子\nthis.props.baseAction.rollBack(\"home\");\n```\n\n**参数**\n*modelName*\n模型的命名空间，也就是模型的名称\n\n**返回值**\n无\n#### \u003cspan id=\"selecter\"\u003eSelecter\u003c/span\u003e\n绑定`model`，`view`，`action`三者得工具。由`Tomato`划分出来的四大模块都是独立的，每一部分都不能独立工组，因为他们不是一个完整的系统。只有通过绑定，引用这些方式，组合在一起才能构成一个完整的组件。\n\n使用实例：\n\n``` javascript\n\nimport React, { Component } from 'react';\nimport { Selecter } from 'tomatobean';\nimport BaseActions from 'tomatobean/enhance';\nimport { getOpportunityList } from '../../models/home';\n\n@Selecter(['home'], { getOpportunityList })\nexport class View extends Component {\n  componentDidMount() {\n    const { getOpportunityList } = this.props.actions;\n    getOpportunityList();\n  }\n\n  render() {\n    const { enters } = this.props.home;\n    return (\n      \u003cdiv className=\"home-page\"\u003e\n        \u003cp className=\"group-title\"\u003e快捷进入\u003c/p\u003e\n          {enters}\n        \u003c/div\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n\n```\n#### \u003cspan id=\"rootrouteconnect\"\u003eRootRouteConnect\u003c/span\u003e\n\n标记根路由组件\n\n#### \u003cspan id=\"configuration\"\u003eConfiguration\u003c/span\u003e\n\n标记当前类为配置类，重写系统的约定配置。\n\n#### \u003cspan id=\"tabbar\"\u003eTabbar\u003c/span\u003e\n\n提供Tabbar数据，如果需要重写Tabber组件，可通过@Tabbar装饰器修饰以获取系统数据。需要注意的是`Tomato`内部提供了一个Tabber样式组件可直接使用。\n\n#### \u003cspan id=\"notification\"\u003eNotification\u003c/span\u003e\n\n通知中心，提供更便捷的通知服务。\n\n`Notification`通知服务，其中包括一下方法：\n\n方法 | 参数 | 返回值 | 功能说明\n--- | --- | --- | ---\n[observer](#observer)|(name,func)| -- | 注册观察者\n[postNotification](#postNotification)|(name,...params)|-- | 发送消息通知\n[removeObserver](#removeObserver)| (name) | -- | 移除观察者\n\n\u003e#### \u003cspan id=\"observer\"\u003eobserver\u003c/span\u003e\n\n**语法**\n\n``` javascript\nconst { observer } = this.props.notification;\nconst active = (message) =\u003e {\n    console.log(message); //打印接收到的消息\n    ...\n}\nobserver('name', active);\n```\n\n**参数**\n*name*\n观察者名称\n*active*\n观察者你收到通知后做出响应\n\n**返回值**\n无\n\u003e#### \u003cspan id=\"postNotification\"\u003epostNotification\u003c/span\u003e\n\n**语法**\n\n``` javascript\n\nconst { postNotification } = this.props.notification;\npostNotification('name', message);\n        \n```\n\n**参数**\n*name*\n观察者名称\n*message*\n被发送的消息\n\n**返回值**\n无\n\u003e#### \u003cspan id=\"removeObserver\"\u003eremoveObserver\u003c/span\u003e\n\n**语法**\n\n``` javascript\n\nconst { removeObserver } = this.props.notification;\nremoveObserver('name');\n        \n```\n\n**参数**\n*name*\n观察者名称\n\n**返回值**\n无\n\n### \u003cspan id=\"configure\"\u003e配置类\u003c/span\u003e\n\n#### \u003cspan id=\"authorityinterceptor\"\u003eAuthorityInterceptor\u003c/span\u003e\n方法1\n\u003e方法：`static checkAuthority(author, redirect)`;\u003cbr/\u003e\n\u003e用途：是不是有效用户权限（只会在第一次进入系统时是调用);\u003cbr/\u003e\n\u003e 参数：\n\u003e`@author`进入系统的遥控器(此方法可以作为信物传递)，只有当 author(true)时才会打开系统。\u003cbr/\u003e\n\u003e`@redirect`重定向方法；\u003cbr/\u003e\n\n方法2\n\u003e方法：`static preHandle(location, redirect)`;\u003cbr/\u003e\n\u003e用途：每个页面进入之前的预处理（可以在此处做权限控制）；\u003cbr/\u003e\n\u003e参数：\n\u003e`@location`当前访问的地址信息\u003cbr/\u003e\n\u003e`@redirect`重定向方法\u003cbr/\u003e\n\u003e`@author`进入系统的遥控器(此方法可以作为信物传递)，只有当 author(true)时才会打开系统。需要注意的是，系统必须存在一个状态，非开即关。所以无论如何author方法必须被调用。\u003cbr/\u003e\n\u003e`@debut`是不是第一次进入系统。\u003cbr/\u003e\n\n\u003e \u003cspan id=\"authority\"\u003e用户登录认证\u003c/span\u003e\n\n```javascript\n\nimport { Configuration, AuthorityInterceptor } from 'tomatobean';\n\n@Configuration\nexport class Ub extends AuthorityInterceptor {\n  /**\n   * 是不是有效用户权限\n   * @param {Func} author 进入系统的遥控器(此方法可以作为信物传递)，只有当 author(true)时才会打开系统。\n   *              需要注意的是，系统必须存在一个状态，非开即关。所以无论如何author方法必须被调用。\n   * @param {Func} redirect 从定向方法\n   */\n  static checkAuthority(author, redirect) {\n    // 示例代码...\n    // 模拟异步请求\n    setTimeout(() =\u003e {\n      if (true) { // 有效用户\n        author(true);\n        // 从服务器获取的权限\n        Ub.AJ = ['/home'];\n      } else { // 无效用户\n        author(false);\n        redirect({ pathname: '/login' });\n      }\n    }, 100);\n  }\n  /**\n   * 每个页面进入之前的预处理（可以在此处做权限控制）\n   * @param {Object} location 当前访问的地址信息\n   * @param {Func} redirect 重定向方法\n   * @param {Func} author  ...(同上)\n   * @param {Func} debut 是不是第一次进入系统\n   */\n  static preHandle(location, redirect) {\n    // 示例代码...\n    if (Ub.AJ \u0026\u0026 location.pathname === Ub.AJ[0]) {\n      redirect('/error');\n    }\n  }\n}\n\n```\n\n### \u003cspan id=\"tools\"\u003e工具方法\u003c/span\u003e\n\n1.\u003cspan id=\"combinmodals\" \u003ecombinModals\u003c/span\u003e\n\n## \u003cspan id=\"support\"\u003eSupport\u003c/span\u003e\n\n该框架拥有多个项目的成功案例，如果在项目中开发者遇到问题，可通过下面的联系方式与作者沟通。\n\n联系方式：\n\nwachat: chengpu552877\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoncheng%2Ftomatobean","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvoncheng%2Ftomatobean","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoncheng%2Ftomatobean/lists"}