npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

wxministore

v1.3.1

Published

小程序全局状态管理工具

Downloads

43

Readme

wxMiniStore

NPM version License

一个基于原生小程序的 mini 全局状态管理库,跨页面/组件数据共享渲染。

  • 全局状态 state 支持所有 Page 和 Component,更新时使用独有 diff 能力,性能更强。
  • 周期监听 pageListener 能监听所有页面的 onLoad、onShow 等周期事件,方便埋点、统计等行为。
  • 全局事件 methods,一处声明,所有 wxml 直接可用的函数。
  • 适合原生小程序,即使后期引入,也只需增加几行代码。

更新日志

1.3.1

[2021.1.13]
U:优化pageListener中的onShareAppMessage能力,使其支持自定义全局分享。

1.3.0

[2020.7.28]
A:新增 store.prototype.clearState 清除状态,by @zkl2333
F:新增polyfill,修复 #25
F:单词错误 pageLisener 改为 pageListener(已做向下兼容可放心升级)。

1.2.9

[2020.3.31] A: 新增debug 字段,用于开启/关闭 setState 时的 console。

导航

开始

在开始前,你可以 clone 或下载本项目,用微信开发工具打开 demo 目录来查看效果。

1.安装及引入

目前有两种引入方式:

npm

首先你需要 npm init 在项目目录下生成 package.json 后,再进行安装。

npm init
npm install wxministore -S

然后在微信小程序右上角详情中勾选 使用npm模块
接着选择左上角 工具-构建 npm。 这样你就可以在项目中导入了。

//app.js中
import Store from "wxministore";
//或者 const Store = require('wxministore');
App({});

clone

如果不太熟悉 npm 没关系,你可以将本项目中 lib/store.js 复制到你的项目中,并在app.js第一行引入:

//app.js中
import Store from "./util/store.js";
//或者 const Store = require('./util/store.js');
App({});

2. 实例化一个全局状态 state

Store 为构造函数,所以需要通过 new 关键字实例化,参数为 object 类型,下面我们初始化一个 state。

let store = new Store({
  state: {
    msg: "这是一个全局状态",
    user: {
      name: "李四",
    },
  },
});
console.log(store.getState().msg); //这是一个全局状态 1.2.6+
console.log(store.$state.msg); //这是一个全局状态 (不推荐)
App({});

初始化完成,我们如需在 js 中获取状态,可使用 store.getState() 获取全局状态,1.2.6+版本强烈推荐此方式。
store.$state 也可获取,但不建议使用。

3.在 App 中注入 store

这么做是为了在其他页面中使用 store。

App({
  onLaunch: function () {},
  store: store,
});

4.页面上使用

在所有 wxml 中,可使用$state.x。 其中$state 为全局状态的容器,里面包含了所有的全局状态。

<view>{{$state.user.name}}:{{$state.msg}}</view>

显示为 李四:这是一个全局状态。

如果在 template 文件中使用,需在属性 data 中引用$state

<!-- 这是一个template -->
<template name="t1">
  <view>{{$state.msg}}</view>
</template>

<!-- 这是引用位置 -->
<template is="t1" data="{{$state,arg1,arg2}}" />
<!--   相当于<template is="t1" data="{{$state:$state,arg1:arg1,arg2:arg2}}" /> -->

在版本 1.2.1+建议使用 App.Page 和 App.Component 创建页面和组件,当然也不是必须。详情查看nonWritable

// 没问题
Page({
  //...
});

// 更好
App.Page({
  //...
});

如果使用时,页面空白,说明你没有在 App 创建前 new Store。

5.如何修改状态

使用 app.store.setState 进行更新状态。如:

const app = getApp();
App.Page({
  data: {},
  onLoad: function () {
    //所有wxml中的$state.msg会同步更新
    app.store.setState({
      msg: "我被修改了,呜呜...",
    });
  },
});

修改状态注意事项

// 错误的示范 视图不会更新
let { user } = app.store.$state;
user.name = "张三";
app.store.setState({
  user,
});

//正确的示范
let { user } = app.store.getState();
user.name = "张三";
app.store.setState({
  user,
});

获取全局状态需使用 app.store.getState()。

周期监听 pageListener

在有的场景,我希望每个页面在 onLoad 时执行一个方法(如统计页面,监听等)。原本做法是一个一个的复制粘贴,很麻烦。
现在我们可以把某个周期,写入 pageListener 中,Store 会自动在相应周期优先执行pageListener然后再执行原页面周期内事件

1.加入监听

现在以监听 onLoad 为例, 在 Store 中新增一个 pageListener 对象,将需要监听的周期写入:

// store中
let store = new Store({
  //状态
  state: {
    //...
  },
  //方法
  methods: {
    //...
  },
  //页面监听
  pageListener: {
    onLoad(options) {
      console.log("我在" + this.route, "参数为", options);
    },
  },
});

就这样所有页面的 onLoad,将会优先执行此监听。接下来看页面内代码:

// index/index.js 页面
App.Page({
  onLoad() {
    console.log(2);
  },
});

执行结果为:

// 我在index/index 参数为 {...}
// 2

2.全局分享 1.3.1+

现支持全局分享功能,以方便开发者能一次性定义所有页面的分享功能。

// store中
let store = new Store({
  //页面监听
  pageListener: {
    onShareAppMessage(res){
      return {
        title: '全局分享',
        path: '/index/index'
      }
    }
  },
});

store中onShareAppMessage返回值的优先级是次于页面级的,所以当Page中有onShareAppMessage且有返回值,则会优先使用Page中的分享。

3.没有第二步...

总结:

  • 先执行 pageListener 监听,后执行原本页面中周期。
  • 还支持其他周期事件 ['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage', 'onPageScroll', 'onTabItemTap']

全局方法 methods

新增 methods,全局可使用。 适用于各个 wxml 中的交互事件(bindtap 等), 你可以封装一些常用的交互事件,如 行为埋点,类型跳转等。

1.创建一个全局方法

在原有状态基础上,新增一个 methods 对象,写入你的全局方法:

let store = new Store({
  //状态
  state: {
    msg: "这是一个全局状态",
  },
  //方法
  methods: {
    goAnyWhere(e) {
      wx.navigateTo({
        url: e.currentTarget.dataset.url,
      });
    },
    sayHello() {
      console.log("hello");
    },
  },
});

这里创建了一个全局封装的跳转 goAnyWhere。

2.使用全局方法

在 wxml 中,直接使用方法名调用:

<view bindtap="goAnyWhere" data-url="/index/index">
  首页
</view>

在 js 中,直接使用 this.方法名 来调用:

App.Page({
  onLoad() {
    this.sayHello();
  },
});

在非页面的 js 中,我们不建议使用 Store 中的全局方法。但你可使用 getCurrentPage().pop().sayHello() 来调用。

3.说明

  • 尽量封装复用率高的全局方法
  • 非交互型事件(即非 bindxx)的公用方法,建议不写入 Store 中。写入 App 中更好。

局部状态模式

在项目的组件和页面越来越多且复用率越来越高时,全局$state的利用率就很低,这时候就出现了一种情况,页面中的组件和页面达到百千量级,每个内部都有一个$state,而用到它的可能就只有 1 个或几个。就会引起各种性能问题。比如更新$state十分缓慢,且低效。
这时候你需要将$state 调整为部分组件和页面可用,而不是所有。

1.开启局部模式

let store = new Store({
  //。
  state: {
    msg: "这是一个全局状态",
  },
  openPart: true,
});

openPart 字段表示是否开启局部模式,默认值为 false。当我们想规定只有某些页面和组件使用$state 时,就需开启此模式,设置为 true。

2.设置范围

在需要使用$state的组件中,加入useStore: true,表示当前页面或组件可用$state。

// a.js
App.Page({
  useStore: true,
  onLoad() {
    console.log(this.data.$state); // { msg: '这是一个全局状态' }
    console.log(getApp().store.getState()); // { msg: '这是一个全局状态' }
  },
});

// b.js
App.Page({
  onLoad() {
    console.log(this.data.$state); // undefined
    console.log(getApp().store.getState()); // { msg: '这是一个全局状态' }
  },
});

a 页面设置了 Store 可用,所以可以通过 this.data.$state 获取。 b 页面没有设置,所以为 undefined,但两个页面均可通过 store.getState()读取全局状态。

<--! a页面有效 -->
<view>{{$state.msg}}</view>

<--! b页面无效 -->
<view>{{$state.msg}}</view>

3.注意事项

  • openPart 一旦开启,所有没有设置 useStore 的页面和组件将不能在 wxml 中使用$state。
  • 组件或页面.js 中,我们建议使用 getApp().store.getState()去获取全局状态,因为他没有限制。
  • 仅在 wxml 中需要用到$state 的页面和组件中开启 useStore。

你可以 clone 或下载本项目,用微信开发工具打开 demo 目录来查看具体用法。

页面中 useProp 属性 1.2.3+

useProp 用于控制当前页面/组件,使用哪些状态,不传则所有状态均可在当前页面中使用。

观察以下代码及注释:

// App.js
let store = new Store({
  state: {
    s1: "s1状态",
    s2: "s2状态",
  },
});

// A页面中
App.Page({
  useProp: ["s1"], //指定使用s1
  onLoad() {
    console.log(this.data.$state); // { s1: 's1状态' }
    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }
  },
});

// B页面中
App.Page({
  useProp: ["s2"], //指定使用s2
  onLoad() {
    console.log(this.data.$state); // { s2: 's2状态' }
    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }
  },
});

// C页面中
App.Page({
  onLoad() {
    console.log(this.data.$state); // { s1: 's1状态', s2: 's2状态' }
    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }
  },
});

useProp 是控制当前组件/页面使用哪些状态,而 useStore 是控制哪些组件/页面可使用 state 这个功能,两者可以同时作用。如:

// App.js中
let store = new Store({
  state: {
    s1: "s1状态",
    s2: "s2状态",
  },
  openPart: true,
});

// A页面中
App.Page({
  useStore: true,
  useProp: ["s1"], //指定使用s1
  onLoad() {
    console.log(this.data.$state); // { s1: 's1状态' }
    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }
  },
});

// B页面中
App.Page({
  useProp: ["s1"], //指定使用s1 但没设置useStore,所以无效
  onLoad() {
    console.log(this.data.$state); // undefined
    console.log(getApp().store.getState()); // { s1: 's1状态', s2: 's2状态' }
  },
});

其他 1.2.9+

实例化 Store 时,提供 debug 字段,用于开启/关闭框架内部 console 日志。 默认值为 true,即开启状态。如不需要,则设置 false 即可。

new Store({
  debug: false, // 关闭内部日志的输出。
});

non-writable 解决方案 1.2.1+

收到开发者的反馈,在小程序中使用插件时,会报错提示:

// [non-writable] modification of global variable "Page" is not allowed when using plugins at app.json.
// 在app.json中使用插件时,不允许修改全局变量 Page

原因是 store 源码重写了 Page、Component 方法。

1、开启防改写

在你的 store 配置中,加入 nonWritable: true

let store = new Store({
  nonWritable: true,
});

2、创建页面与组件调整

将你所有页面与组件创建方法改为App.Page(...) 和 App.Component(...)

//页面.js
App.Page({
  data: {},
  onLoad: function () {},
});

//组件.js
App.Component({
  data: {},
});

以上就解决了此问题。

api

这里列举了所有涉及到 Store 的属性与方法。

new Store(options: Object) *已更新

该函数使用 new 关键字返回一个 Store 类型的实例。 参数 options,为配置参数,
options.state 为初始全局状态。
options.methods 为全局方法。
options.openPart 状态局部模式。
options.pageListener 周期监听。
options.nonWritable 是否重写 Page,Componenet。

store.setState(data: Object, callback: Function)

用于修改全局状态,用法与微信小程序的 Page.prototype.setData 完全一致。 提示:页面中应避免使用 this.setData({$state: ...})去操作当前页面下的$state。如有相关需求,请使用页面其他状态存储。

store.$state: Object

该对象为实例.$state, 返回的是全局状态,应避免直接操作修改它。

store.$r: Array

该对象为所有页面或组件的实例。

store.getState: () => Object 1.2.6+

该 api 返回的是全局状态的拷贝。

store.clearState(callback: Function) 1.3.0+

用于清空全局状态,使所有$state下任意的状态为undefined。

总结及建议

考虑到后期的 app.js 内 store 不直观,可以把整套 store 单独写入一个 js 中,通过 require 引入。如:

// mystore.js中
const Store = require("../util/store.js");
module.exports = new Store({
  state: {},
  methods: {},
});
//---------------------------
// app.js中
let store = require("store/mystore.js");
App({
  store,
});

MiniStore 非常适合原生小程序。可以随时引入,不影响原有的业务,拓展性强。 欢迎 star、欢迎提 issue 甚至 pr...

License

MIT © Leisure