{"id":13511380,"url":"https://github.com/xiaoyao96/wxMiniStore","last_synced_at":"2025-03-30T20:33:08.415Z","repository":{"id":50308331,"uuid":"146731738","full_name":"xiaoyao96/wxMiniStore","owner":"xiaoyao96","description":"一个基于微信小程序的mini全局状态管理库","archived":true,"fork":false,"pushed_at":"2022-08-31T09:06:31.000Z","size":231,"stargazers_count":379,"open_issues_count":11,"forks_count":107,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-02T12:11:25.745Z","etag":null,"topics":["mini-app","miniprogram","setstate","store","store-management","wxstore"],"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/xiaoyao96.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":"2018-08-30T10:08:59.000Z","updated_at":"2025-02-20T05:07:34.000Z","dependencies_parsed_at":"2022-09-05T15:50:47.391Z","dependency_job_id":null,"html_url":"https://github.com/xiaoyao96/wxMiniStore","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xiaoyao96%2FwxMiniStore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xiaoyao96%2FwxMiniStore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xiaoyao96%2FwxMiniStore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xiaoyao96%2FwxMiniStore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xiaoyao96","download_url":"https://codeload.github.com/xiaoyao96/wxMiniStore/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246379379,"owners_count":20767694,"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":["mini-app","miniprogram","setstate","store","store-management","wxstore"],"created_at":"2024-08-01T03:00:48.797Z","updated_at":"2025-03-30T20:33:08.112Z","avatar_url":"https://github.com/xiaoyao96.png","language":"JavaScript","funding_links":[],"categories":["mini-programe"],"sub_categories":[],"readme":"# wxMiniStore \n\n[![NPM version](https://img.shields.io/npm/v/wxministore.svg)](https://www.npmjs.com/package/wxministore)\n[![License](https://img.shields.io/npm/l/wxministore.svg)](https://www.npmjs.com/package/wxministore)\n\n一个基于原生小程序的 mini 全局状态管理库，跨页面/组件数据共享渲染。\n\n- 全局状态 state 支持所有 Page 和 Component，更新时使用独有 diff 能力，性能更强。\n- 周期监听 pageListener 能监听所有页面的 onLoad、onShow 等周期事件，方便埋点、统计等行为。\n- 全局事件 methods，一处声明，所有 wxml 直接可用的函数。\n- 适合原生小程序，即使后期引入，也只需增加几行代码。\n\n## 更新日志\n\n自 2022.8.31 起本人将不再更新维护此库。\n推荐大家使用腾讯开源的 westore 来管理微信原生小程序状态，或直接使用 uniapp、taro 等跨平台开发框架来开发小程序更佳。\n感谢之前使用过此库的开发者，以及提过pr和issue的贡献者。\n\n### 1.3.1\n\n\\[2021.1.13\\]  \n`U`：优化pageListener中的onShareAppMessage能力，使其支持自定义[全局分享](#share)。\n\n### 1.3.0\n\n\\[2020.7.28\\]  \n`A`：新增 [store.prototype.clearState](#clearState) 清除状态，by [@zkl2333](https://github.com/zkl2333)  \n`F`：新增polyfill，修复 [#25](https://github.com/xiaoyao96/wxMiniStore/issues/25)。  \n`F`：单词错误 pageLisener 改为 pageListener（已做向下兼容可放心升级）。  \n\n### 1.2.9\n\n\\[2020.3.31\\] `A`: 新增[debug 字段](#other)，用于开启/关闭 setState 时的 console。  \n\n### 导航\n\n- [全局状态开始](#start)\n  - [安装及引入](#start-1)\n  - [实例化](#state)\n  - [App 中注入](#start-3)\n  - [页面上使用](#start-4)\n  - [修改状态](#start-5)\n  - [修改状态注意事项](#start-6)\n- [页面周期监听](#lisener)\n  - [全局分享](#share)\n- [全局方法](#f)\n- 性能优化\n  - [局部状态模式](#part)\n  - [useProp](#useProp)\n- [其他](#other)\n- [non-writable 解决方案](#nonWritable)\n- [Api 说明](#api)\n- [总结及建议](#end)\n\n## \u003cdiv id=\"start\"\u003e开始\u003c/div\u003e\n\n在开始前，你可以 clone 或下载本项目，用微信开发工具打开 demo 目录来查看效果。\n\n### \u003cdiv id=\"start-1\"\u003e1.安装及引入\u003c/div\u003e\n\n目前有两种引入方式：\n\n#### npm\n\n首先你需要 npm init 在项目目录下生成 package.json 后，再进行安装。\n\n```cmd\nnpm init\nnpm install wxministore -S\n```\n\n然后在微信小程序右上角详情中勾选 `使用npm模块`。  \n 接着选择左上角 工具-构建 npm。\n这样你就可以在项目中导入了。\n\n```js\n//app.js中\nimport Store from \"wxministore\";\n//或者 const Store = require('wxministore');\nApp({});\n```\n\n#### clone\n\n如果不太熟悉 npm 没关系，你可以将本项目中 lib/store.js 复制到你的项目中，并在`app.js第一行`引入：\n\n```js\n//app.js中\nimport Store from \"./util/store.js\";\n//或者 const Store = require('./util/store.js');\nApp({});\n```\n\n### \u003cdiv id=\"state\"\u003e2. 实例化一个全局状态 state\u003c/div\u003e\n\nStore 为构造函数，所以需要通过 new 关键字实例化，参数为 object 类型，下面我们初始化一个 state。\n\n```js\nlet store = new Store({\n  state: {\n    msg: \"这是一个全局状态\",\n    user: {\n      name: \"李四\",\n    },\n  },\n});\nconsole.log(store.getState().msg); //这是一个全局状态 1.2.6+\nconsole.log(store.$state.msg); //这是一个全局状态 （不推荐）\nApp({});\n```\n\n初始化完成，我们如需在 js 中获取状态，可使用 `store.getState()` 获取全局状态，`1.2.6+`版本强烈推荐此方式。  \nstore.\\$state 也可获取，但不建议使用。\n\n### \u003cdiv id=\"start-3\"\u003e3.在 App 中注入 store\u003c/div\u003e\n\n这么做是为了在其他页面中使用 store。\n\n```js\nApp({\n  onLaunch: function () {},\n  store: store,\n});\n```\n\n### \u003cdiv id=\"start-4\"\u003e4.页面上使用\u003c/div\u003e\n\n在所有 wxml 中，可使用$state.x。\n其中$state 为全局状态的容器，里面包含了所有的全局状态。\n\n```html\n\u003cview\u003e{{$state.user.name}}：{{$state.msg}}\u003c/view\u003e\n```\n\n显示为 李四：这是一个全局状态。\n\n如果在 template 文件中使用，需在属性 data 中引用\\$state\n\n```html\n\u003c!-- 这是一个template --\u003e\n\u003ctemplate name=\"t1\"\u003e\n  \u003cview\u003e{{$state.msg}}\u003c/view\u003e\n\u003c/template\u003e\n\n\u003c!-- 这是引用位置 --\u003e\n\u003ctemplate is=\"t1\" data=\"{{$state,arg1,arg2}}\" /\u003e\n\u003c!--   相当于\u003ctemplate is=\"t1\" data=\"{{$state:$state,arg1:arg1,arg2:arg2}}\" /\u003e --\u003e\n```\n\n在版本 1.2.1+建议使用 App.Page 和 App.Component 创建页面和组件，当然也不是必须。详情查看[nonWritable](#nonWritable)\n\n```js\n// 没问题\nPage({\n  //...\n});\n\n// 更好\nApp.Page({\n  //...\n});\n```\n\n如果使用时，页面空白，说明你没有在 App 创建前 new Store。\n\n### \u003cdiv id=\"start-5\"\u003e5.如何修改状态\u003c/div\u003e\n\n使用 app.store.setState 进行更新状态。如:\n\n```js\nconst app = getApp();\nApp.Page({\n  data: {},\n  onLoad: function () {\n    //所有wxml中的$state.msg会同步更新\n    app.store.setState({\n      msg: \"我被修改了，呜呜...\",\n    });\n  },\n});\n```\n\n### \u003cdiv id=\"start-6\"\u003e修改状态注意事项\u003c/div\u003e\n\n```js\n// 错误的示范 视图不会更新\nlet { user } = app.store.$state;\nuser.name = \"张三\";\napp.store.setState({\n  user,\n});\n\n//正确的示范\nlet { user } = app.store.getState();\nuser.name = \"张三\";\napp.store.setState({\n  user,\n});\n```\n\n获取全局状态需使用 app.store.getState()。\n\n## \u003cdiv id=\"lisener\"\u003e周期监听 pageListener\u003c/div\u003e\n\n在有的场景，我希望每个页面在 onLoad 时执行一个方法（如统计页面，监听等）。原本做法是一个一个的复制粘贴，很麻烦。  \n现在我们可以把某个周期，写入 pageListener 中，Store 会自动在`相应周期优先执行pageListener然后再执行原页面周期内事件`。\n\n### 1.加入监听\n\n现在以监听 onLoad 为例， 在 Store 中新增一个 pageListener 对象，将需要监听的周期写入:\n\n```js\n// store中\nlet store = new Store({\n  //状态\n  state: {\n    //...\n  },\n  //方法\n  methods: {\n    //...\n  },\n  //页面监听\n  pageListener: {\n    onLoad(options) {\n      console.log(\"我在\" + this.route, \"参数为\", options);\n    },\n  },\n});\n```\n\n就这样所有页面的 onLoad，将会优先执行此监听。接下来看页面内代码：\n\n```js\n// index/index.js 页面\nApp.Page({\n  onLoad() {\n    console.log(2);\n  },\n});\n```\n\n执行结果为:\n\n```js\n// 我在index/index 参数为 {...}\n// 2\n```\n\n### \u003cdiv id=\"share\"\u003e2.全局分享 `1.3.1+`\u003c/div\u003e\n\n现支持全局分享功能，以方便开发者能一次性定义所有页面的分享功能。  \n```js\n// store中\nlet store = new Store({\n  //页面监听\n  pageListener: {\n    onShareAppMessage(res){\n      return {\n        title: '全局分享',\n        path: '/index/index'\n      }\n    }\n  },\n});\n```\nstore中onShareAppMessage返回值的优先级是`次于`页面级的，所以当Page中有onShareAppMessage且有返回值，则会优先使用Page中的分享。\n\n### 3.没有第二步...\n\n总结：\n\n- 先执行 pageListener 监听，后执行原本页面中周期。\n- 还支持其他周期事件 ['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage', 'onPageScroll', 'onTabItemTap']\n\n## \u003cdiv id=\"f\"\u003e全局方法 methods\u003c/div\u003e\n\n新增 methods，全局可使用。\n适用于各个 wxml 中的交互事件(bindtap 等), 你可以封装一些常用的交互事件，如 行为埋点，类型跳转等。\n\n### 1.创建一个全局方法\n\n在原有状态基础上，新增一个 methods 对象，写入你的全局方法：\n\n```js\nlet store = new Store({\n  //状态\n  state: {\n    msg: \"这是一个全局状态\",\n  },\n  //方法\n  methods: {\n    goAnyWhere(e) {\n      wx.navigateTo({\n        url: e.currentTarget.dataset.url,\n      });\n    },\n    sayHello() {\n      console.log(\"hello\");\n    },\n  },\n});\n```\n\n这里创建了一个全局封装的跳转 goAnyWhere。\n\n### 2.使用全局方法\n\n在 wxml 中，直接使用`方法名`调用:\n\n```html\n\u003cview bindtap=\"goAnyWhere\" data-url=\"/index/index\"\u003e\n  首页\n\u003c/view\u003e\n```\n\n在 js 中，直接使用 `this.方法名` 来调用:\n\n```js\nApp.Page({\n  onLoad() {\n    this.sayHello();\n  },\n});\n```\n\n在非页面的 js 中，我们不建议使用 Store 中的全局方法。但你可使用 getCurrentPage().pop().sayHello() 来调用。\n\n### 3.说明\n\n- 尽量封装复用率高的全局方法\n- 非交互型事件（即非 bindxx）的公用方法，建议不写入 Store 中。写入 App 中更好。\n\n## \u003cdiv id=\"part\"\u003e局部状态模式\u003c/div\u003e\n\n在项目的组件和页面越来越多且复用率越来越高时，全局$state的利用率就很低，这时候就出现了一种情况，页面中的组件和页面达到百千量级，每个内部都有一个$state，而用到它的可能就只有 1 个或几个。就会引起各种性能问题。比如更新$state十分缓慢，且低效。  \n这时候你需要将$state 调整为部分组件和页面可用，而不是所有。\n\n### 1.开启局部模式\n\n```js\nlet store = new Store({\n  //。\n  state: {\n    msg: \"这是一个全局状态\",\n  },\n  openPart: true,\n});\n```\n\nopenPart 字段表示是否开启局部模式，默认值为 false。当我们想规定只有某些页面和组件使用\\$state 时，就需开启此模式，设置为 true。\n\n### 2.设置范围\n\n在需要使用$state的组件中，加入`useStore: true`，表示当前页面或组件可用$state。\n\n```js\n// a.js\nApp.Page({\n  useStore: true,\n  onLoad() {\n    console.log(this.data.$state); // { msg: '这是一个全局状态' }\n    console.log(getApp().store.getState()); // { msg: '这是一个全局状态' }\n  },\n});\n\n// b.js\nApp.Page({\n  onLoad() {\n    console.log(this.data.$state); // undefined\n    console.log(getApp().store.getState()); // { msg: '这是一个全局状态' }\n  },\n});\n```\n\na 页面设置了 Store 可用，所以可以通过 this.data.\\$state 获取。\nb 页面没有设置，所以为 undefined，但两个页面均可通过 store.getState()读取全局状态。\n\n```html\n\u003c--! a页面有效 --\u003e\n\u003cview\u003e{{$state.msg}}\u003c/view\u003e\n\n\u003c--! b页面无效 --\u003e\n\u003cview\u003e{{$state.msg}}\u003c/view\u003e\n```\n\n### 3.注意事项\n\n- openPart 一旦开启，所有没有设置 useStore 的页面和组件将不能在 wxml 中使用\\$state。\n- 组件或页面.js 中，我们建议使用 getApp().store.getState()去获取全局状态，因为他没有限制。\n- 仅在 wxml 中需要用到\\$state 的页面和组件中开启 useStore。\n\n你可以 clone 或下载本项目，用微信开发工具打开 demo 目录来查看具体用法。\n\n## \u003cdiv id=\"useProp\"\u003e 页面中 useProp 属性 `1.2.3+`\u003c/div\u003e\n\nuseProp 用于控制当前页面/组件，使用哪些状态，不传则所有状态均可在当前页面中使用。\n\n观察以下代码及注释：\n\n```js\n// App.js\nlet store = new Store({\n  state: {\n    s1: \"s1状态\",\n    s2: \"s2状态\",\n  },\n});\n\n// A页面中\nApp.Page({\n  useProp: [\"s1\"], //指定使用s1\n  onLoad() {\n    console.log(this.data.$state); // { s1: 's1状态' }\n    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }\n  },\n});\n\n// B页面中\nApp.Page({\n  useProp: [\"s2\"], //指定使用s2\n  onLoad() {\n    console.log(this.data.$state); // { s2: 's2状态' }\n    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }\n  },\n});\n\n// C页面中\nApp.Page({\n  onLoad() {\n    console.log(this.data.$state); // { s1: 's1状态', s2: 's2状态' }\n    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }\n  },\n});\n```\n\nuseProp 是控制当前组件/页面使用哪些状态，而 useStore 是控制哪些组件/页面可使用 state 这个功能，两者可以同时作用。如：\n\n```js\n// App.js中\nlet store = new Store({\n  state: {\n    s1: \"s1状态\",\n    s2: \"s2状态\",\n  },\n  openPart: true,\n});\n\n// A页面中\nApp.Page({\n  useStore: true,\n  useProp: [\"s1\"], //指定使用s1\n  onLoad() {\n    console.log(this.data.$state); // { s1: 's1状态' }\n    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }\n  },\n});\n\n// B页面中\nApp.Page({\n  useProp: [\"s1\"], //指定使用s1 但没设置useStore，所以无效\n  onLoad() {\n    console.log(this.data.$state); // undefined\n    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }\n  },\n});\n```\n\n## \u003cdiv id=\"other\"\u003e其他 `1.2.9+`\u003c/div\u003e\n\n实例化 Store 时，提供 debug 字段，用于开启/关闭框架内部 console 日志。 默认值为 true，即开启状态。如不需要，则设置 false 即可。\n\n```js\nnew Store({\n  debug: false, // 关闭内部日志的输出。\n});\n```\n\n## \u003cdiv id=\"nonWritable\"\u003enon-writable 解决方案 `1.2.1+`\u003c/div\u003e\n\n收到开发者的反馈，在小程序中使用插件时，会报错提示:\n\n```js\n// [non-writable] modification of global variable \"Page\" is not allowed when using plugins at app.json.\n// 在app.json中使用插件时，不允许修改全局变量 Page\n```\n\n原因是 store 源码重写了 Page、Component 方法。\n\n### 1、开启防改写\n\n在你的 store 配置中，加入 `nonWritable: true`。\n\n```js\nlet store = new Store({\n  nonWritable: true,\n});\n```\n\n### 2、创建页面与组件调整\n\n将你所有页面与组件创建方法改为`App.Page(...) 和 App.Component(...)`。\n\n```js\n//页面.js\nApp.Page({\n  data: {},\n  onLoad: function () {},\n});\n\n//组件.js\nApp.Component({\n  data: {},\n});\n```\n\n以上就解决了此问题。\n\n## \u003cdiv id=\"api\"\u003eapi\u003c/div\u003e\n\n这里列举了所有涉及到 Store 的属性与方法。\n\n### new Store(options: Object) \\*已更新\n\n该函数使用 new 关键字返回一个 Store 类型的实例。\n参数 options，为配置参数，  \noptions.state 为初始全局状态。  \noptions.methods 为全局方法。  \noptions.openPart 状态局部模式。  \noptions.pageListener 周期监听。  \noptions.nonWritable 是否重写 Page，Componenet。\n\n### store.setState(data: Object, callback: Function)\n\n用于修改全局状态，用法与微信小程序的 Page.prototype.setData 完全一致。\n_提示：页面中应避免使用 this.setData({\\$state: ...})去操作当前页面下的\\$state。如有相关需求，请使用页面其他状态存储。_\n\n### store.\\$state: Object\n\n该对象为实例.\\$state， 返回的是全局状态，应避免直接操作修改它。\n\n### store.\\$r: Array\n\n该对象为所有页面或组件的实例。\n\n### store.getState: () =\u003e Object `1.2.6+`\n\n该 api 返回的是全局状态的拷贝。\n\n### \u003cdiv id=\"clearState\"\u003estore.clearState(callback: Function) `1.3.0+`\u003c/div\u003e\n\n用于清空全局状态，使所有$state下任意的状态为undefined。\n\n## \u003cdiv id=\"end\"\u003e总结及建议\u003c/div\u003e\n\n考虑到后期的 app.js 内 store 不直观，可以把整套 store 单独写入一个 js 中，通过 require 引入。如：\n\n```js\n// mystore.js中\nconst Store = require(\"../util/store.js\");\nmodule.exports = new Store({\n  state: {},\n  methods: {},\n});\n//---------------------------\n// app.js中\nlet store = require(\"store/mystore.js\");\nApp({\n  store,\n});\n```\n\nMiniStore 非常适合原生小程序。可以随时引入，不影响原有的业务，拓展性强。\n欢迎 star、欢迎提 issue 甚至 pr...\n\n## License\n\nMIT © [Leisure](https://github.com/xiaoyao96)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxiaoyao96%2FwxMiniStore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxiaoyao96%2FwxMiniStore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxiaoyao96%2FwxMiniStore/lists"}