{"id":20022240,"url":"https://github.com/lucifier129/refer","last_synced_at":"2025-07-29T14:11:46.733Z","repository":{"id":57352036,"uuid":"41463100","full_name":"Lucifier129/refer","owner":"Lucifier129","description":"redux-like library for handling global state on functional style","archived":false,"fork":false,"pushed_at":"2016-01-25T11:50:58.000Z","size":59,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-28T08:47:23.206Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/Lucifier129.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":"2015-08-27T03:13:09.000Z","updated_at":"2024-02-12T23:53:32.000Z","dependencies_parsed_at":"2022-09-19T16:21:13.351Z","dependency_job_id":null,"html_url":"https://github.com/Lucifier129/refer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Lucifier129/refer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Frefer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Frefer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Frefer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Frefer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lucifier129","download_url":"https://codeload.github.com/Lucifier129/refer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Frefer/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267701315,"owners_count":24130449,"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","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2024-11-13T08:39:40.197Z","updated_at":"2025-07-29T14:11:46.697Z","avatar_url":"https://github.com/Lucifier129.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# refer\nredux-like library for handling global state on functional style\n\n## refer 介绍\n\nrefer 是一个状态管理库，受到了 [redux](https://github.com/rackt/redux) 的启发和影响，源码结构以及 API 也高度相似。所不同的是，refer 体积更小，概念更少，灵活性更强。\n\n通常来说，用户只需要用到 `createStore` 一个 api。去掉了 redux 的 combineReducers、bindActionCreators 以及 applyMiddleware 等动作。\n\n在深入了解 refer 之后，你将发现，那些概念是不必要的。每个动作都是一个 handler，所谓的 middleware、actionCreator 和 reducer 等， 只是一串相继执行的 handlers 的不同阶段而已。\n\n## 引入 refer\n\n```shell\nnpm install refer\n```\n\nrefer 遵循 UMD 模块规范。在没有模块化环境时，将输出到全局变量 `Refer`。\n\n### `script` 标签引入\n\n```html\n\u003cscript src=\"refer.min.js\"\u003e\u003c/script\u003e\n\u003cscript\u003econsole.log(Refer)\u003c/script\u003e\n```\n\n### AMD 模块加载\n\n```javascript\ndefine(['refer'], function(Refer) {\n\tconsole.log(Refer)\n})\n```\n\n### commonjs\n\n```javascript\nvar Refer = require('refer')\nconsole.log(Refer)\n```\n\n### ES2015\n\n```javascript\nimport Refer from 'refer'\nconsole.log(Refer)\n```\n\n## 理解 refer\n\nrefer 跟 redux 一样，崇尚(pure function)纯函数。\n\n### 什么是 refer handler？\n\n`refer handler` 是接受一个参数并返回一个值的普通函数，或者这类函数所组成的数组，甚至可以任意嵌套。\n\n```javascript\n// 返回一个数的后继\nlet succ = n =\u003e n + 1\nlet double = x =\u003e x * 2\nlet succ_double = [succ, double]\nlet handler1 = [succ, [double, succ], double]\n```\n\n### 什么是 refer handlers？\n\n`refer handlers` 是一个 key-value 对象。value 为 `refer handler`， key 则是其字符串代号。\n\n```javascript\nlet handlers = {\n\tsucc: n =\u003e n + 1,\n\tdouble: x =\u003e x * 2,\n\tsucc_double: ['succ', 'double'],\n\tkey1: 'succ',\n\tkey2: ['succ', [x =\u003e x - 1, 'key1']]\n}\n```\n\n`refer handlers` 的 key 可以作为 value，或者 value 数组里的一项，增加复用能力。\n\n### 你已经掌握了 refer 的一半。\n\n`refer` 的 `createStore` 函数，接受两个参数: `handlers` 和 `initialState`，返回一个对象。\n\n`handlers` 就是你刚才所掌握的，单参数函数的排列组合与命名。`initialState` 则默认为空对象`{}`\n\n`createStore` 返回的对象拥有几个属性。\n\n#### getState 函数\n\n返回当前 state 数据，默认是 `initialState`，除非 state 被改变。\n\n#### subscribe(callback) 函数\n\n订阅 state 变化的消息，callback 被调用时不接受任何参数。该订阅函数将返回一个解除订阅的函数。\n\n#### replaceState(nextState, silent) 函数\n\n用 nextState 替换当前的 state，如果 silent 为 true，则不触发消息发布\n\n#### dispatch(key, value) 函数\n\n接受两个参数 key 与 value，key 匹配出 `refer handlers`对象里的 `handler`，然后以 `pipe` 的形式依次执行里面的函数。\n\n第一个函数接收的参数为 value，第二个函数接收的参数为第一个函数的返回值，以此类推。\n\n注意：最后一个函数必须返回一个更新 state 的函数。dispatch 方法在同步 handler 时返回更新后的 state\n\n```javascript\nlet handlers = {\n\tadd: [n =\u003e n * 2, n =\u003e n / 2, n =\u003e state =\u003e state + n],\n\treduce: n =\u003e state =\u003e state - n\n}\n// initialState 不要求必须是对象，可以是任意值\nlet store = createStore(handlers, 0)\nstore.dispatch('add', 10) // 返回最新的 state: 10\nstore.dispatch('reduce', 100) // 返回最新的 state: 99\n```\n\nhandler 里的任意函数都可以返回一个 promise，它后面的函数将会在 promise.then 之后继续进行。不需要像`redux-promise`这类中间件的支持，refer 天生支持异步。\n\n如果有函数返回 promise，dispatch 方法返回的也是 promise。\n\n注意：在两种情况下，dispatch 返回的是 promise 对象\n\n- handler 里的某个函数返回 promise\n- handler 里任意函数抛出错误，这个错误将以 Promise.reject(error) 形式返回。\n\n```javascript\nlet handlers = {\n\tadd: [n =\u003e n * 2, n =\u003e n / 2, n =\u003e state =\u003e state + n],\n\treduce: n =\u003e state =\u003e state - n,\n\tasyncAdd: n =\u003e state =\u003e Promise.resolve(state + n),\n\terrorAdd: n =\u003e state =\u003e { throw new Error('I am error')}\n}\nlet store = createStore(handlers, 0)\n\nstore.dispatch('add', 10) // 返回最新的 state: 10\n\n// 异步返回最新的 state\nstore.dispatch('asyncAdd', 10)\n\t.then(state =\u003e console.log(state)) // 20\nstore.dispatch('errorAdd', 100)\n\t.catch(error =\u003e console.log(error.message)) // I am error\n```\n\n由于 dispatch 函数在同步和异步两种情况下都可能返回 promise。可以用下面列举的方式，兼顾成功与失败两种情形。\n\n- 确定 handler 中所有函数都是同步执行的情况\n\t* 判断 dispatch 的返回值是不是 thenable 对象，如果是，则说明存在错误\n\n- handler 里的函数有可能同步也有可能异步的情况\n\t* 用 Promise.resolve(dispatch(key, value)).then(success).catch(fail) 处理\n\n- handler 里存在异步情况\n\t* dispatch(key, value).then(success).catch(fail)\n\n#### actions 对象\n\n`actions` 对象的 key 跟 `refer handlers` 完全一致，它的值则是一个函数: value =\u003e dispatch(key, value)。\n\n因此，你再需要使用 `bindActionCreators` 函数手动构造一个 `actions`。refer 已经提供好了。\n\n```javascript\nlet handlers = {\n\tadd: [n =\u003e n * 2, n =\u003e n / 2, n =\u003e state =\u003e state + n],\n\treduce: n =\u003e state =\u003e state - n,\n\tasyncAdd: n =\u003e state =\u003e Promise.resolve(state + n),\n\terrorAdd: n =\u003e state =\u003e { throw new Error('I am error')}\n}\nlet store = createStore(handlers, 0)\nlet { add, asyncAdd, errorAdd } = store.actions\n\nadd(10) // 返回最新的 state: 10\n\n// 异步返回最新的 state\nasyncAdd(10)\n\t.then(state =\u003e console.log(state)) // 20\nerrorAdd(100)\n\t.catch(error =\u003e console.log(error.message)) // I am error\n```\n\n### 你已经掌握了 refer 的八成\n\nrefer 的理念非常简单，就是依次执行一组函数，根据 key 来调节各组函数的执行时机，根据各个函数是否返回 promise 来决定下一个函数是同步还是异步执行。\n\n## refer 的高级用法\n\n### 如何写中间件？\n\nredux 的中间件是一个函数，而 refer 的中间件则是一个可复用的 `refer handlers`\n\n`createStore(handlers, initialState)` 的 handlers 参数，支持数组类型，也就是说，你可以把多个 handlers 打包成一个数组传入。\n\n```javascript\ncreateStore([handlers1, handlers2, handlers3])\n```\n\n各个 handlers 的同名 handler，会按照 handlers 在数组里的索引顺序拼接起来。\n\n```javascript\n// 多个 handlers 将拼接成一个更大的 handlers 对象\n{\n\t[key]: [handler1.key, handler2.key, handler3.key]\n}\n```\n\n也就是说，handler2 的某个 key 所对应的 handler 的第一个函数，其接收的参数将收到 handlers1 的影响，如果它没有同名的 handler，则接收 value，如果它有，则接受 handers1[key] 所返回的值。\n\n值得注意的是，每个 handlers 的 key ，其代号意义，只在该 handlers 对象中有效。不能在 handlersA 中使用它不存在，但 handlersB 中存在的 handler 代号。\n\n### 了解 refer 的生命周期钩子\n\nhandlers 的 key 没有限制。但 refer 选取了几个特殊的 key 值，它们不参与更新 state。\n\n`handlers[lifeCycleHook]` 将在 dispatch 函数执行的各个特定阶段依次被调用，它们会被传入相应一个对象参数 `data`\n\n- `data.key`: dispatch 的 key 参数\n- `data.value`: dispatch 的 value 参数\n- `data.currentState`: 当前的 state\n- `data.nextState`: 下一个 state\n- `data.error`: dispatch 出错时的错误信息\n\n\n#### @SHOULD_DISPATCH\n\n在 dispatch 执行时，最先被调用的生命周期函数，如果它返回 false，将终止此次 dispatch。该函数的 data 参数只有 key 、value、 currentState 三个属性。\n\n#### @DISPATCH\n\n紧接着 @SHOULD_DISPATCH 之后，该生命周期函数被调用，data 参数的属性与 @SHOULD_DISPATCH 相同。\n\n#### @SHOULD_UPDATE\n\n在 nextState 存在时被调用，data 参数的属性有 key、 value、 currentState 与 nextState 四个，如果它返回 false，将终止更新 state。\n\n#### @WILL_UPDATE\n\n在 currentState 即将被 nextState 所替换前调用，data 参数与 @SHOULD_UPDATE 相同。\n\n#### @DID_UPDATE\n\n在 currentState 被 nextState 所替换后调用，data 参数与 @SHOULD_UPDATE 相同。注意：此时的 `data.nextState` 才是跟 store.getState() 返回值相等。\n\n#### @SYNC\n\n在 currentState 以同步方式被更新后调用，参数与 @SHOULD_UPDATE 相同。\n\n#### @ASYNC_START\n\n在 currentState 以异步方式更新开始阶段被调用，data 参数为 key、value、currentState 与 nextState，其中 nextState 为 promise 对象。\n\n#### @ASYNC_END\n\n在 currentState 以异步方式更新结束时调用，data 参数为 key、value、currentState 与 (nextState || error )。当异步更新成功时，nextState 属性存在，error 不存在；当异步更新存在错误时，nextState 不存在，error 存在。\n\n#### @THROW_ERROR\n\n当 dispatch 中存在错误时调用，该生命周期函数将接收一个 error 参数对象。该函数的返回值将被 Promise.reject 包裹，作为 dispatch 的返回值。\n\n也就是说，用户可以通过 @THROW_ERROR 钩子，控制 dispatch 出错时返回的 promise 对象携带的错误信息是什么。用户可以 `dispatch(key, value).catch(fail)` 进行处理。\n\n注意：dispatch 时的所有错误信息都会进入这个生命周期钩子函数，包括 handler 里的函数故意抛出的错误，或者返回 Promise.reject。\n\n## 了解 refer 的生态\n\nrefer 还比较新，目前并没有社区生态，希望你能加入。\n\n[refer-logger](https://github.com/Lucifier129/refer-logger) 是根据 refer 生命周期钩子写的日志中间件。\n\n```javascript\nimport createLogger from 'refer-logger'\nlet logger = createLogger({\n\tscope: 'scopeName',\n\tdebug: true\n})\n```\n\n`createLogger` 接受一个对象参数，scope 属性默认为 `Root`， debug 为 true 时则在 @THROW_ERROR 钩子中抛出错误（因为部分浏览器不显示 promise 里包含的错误信息）。\n\n当 action 被调用时，控制台将输出 key、value、nextState、currentState 以及此 action 的发生时间，消耗时长等信息。\n\n[refer-dom](https://github.com/Lucifier129/refer-dom) 是 refer 与 virtual-dom 结合的产物，它完整模拟了 `react` 的 component api，可以无缝跑起 react component 代码，只需要将 react 依赖指向 refer-dom 即可。\n\nrefer-dom 的 component api 是 react component 的超集，你可以在组件的 getHandlers 方法返回自定义的 handlers，然后用组件的 actions 属性里的函数来更新组件的 state。setState 方法在 refer-dom 里，只是更新 state 的其中一个 action 而已。\n\n基于 refer 是如此简单易用，相信你能发挥出更大的威力。一起来构建 refer 生态吧。","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucifier129%2Frefer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flucifier129%2Frefer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucifier129%2Frefer/lists"}