精华内容
下载资源
问答
  • 原生小程序状态框架 mpstore 简介 小程序工程越来越复杂,数据共享和通信成为开发的一大难题,像Mpvue, Taro, Uni-App等虽然可以使用Vuex/Redux管理状态,但是对于小程序项目是侵入式的,必须要将整个项目接入框架,...
  • 原生微信小程序全局状态管理

    千次阅读 2020-05-06 15:49:09
    1、因为项目需要后台通过webscoket推送信息,小程序不管在什么页面都要把信息弹出。 2.这就需要全局状态管理。 3.

    1、因为项目需要后台通过webscoket推送信息,小程序不管在什么页面都要把信息弹出,在任何一个页面关闭弹窗,其他页面都不再显示。
    2.这就需要全局状态管理。
    3.第一步需要在项目根目录下创建一个存放公共代码的文件例如utils,在文件下存放create,js和diff.js文件。
    create.js文件和diff.js在下文的GitHub项目中下载。
    在这里插入图片描述
    4.在根目录下创建store.js文件用于全局数据管理。
    在这里插入图片描述
    5.创建一个弹框组件(不会封装组件的,可以查询其他文章)
    在这里插入图片描述
    6.在需要弹窗的页面引入弹窗组件,并在js文件中引入store.js文件和create.js文件。将代码包裹在create对象中。
    7.注意在组件内部使用this修改全局变量如下:
    this.store.data.bbb = ‘在组件中修改全局变量bbb’
    this.update()
    不在组件中修改用store修改全局变量如下:
    store.data.bbb = ‘成功在首页进行更新bbb’
    //这里只能用 store.update 而不是 this.update
    store.update()
    8.demo代码https://github.com/hahaha666666/westroe-globalData

    展开全文
  • 原生小程序开发可以通过复制git项目中packages/xxx/dist中的包文件到实际项目中进行引用 框架加入DEMO 特征 上手简单,仅需要了解4个api 更易用的action 更简单的数据处理 独立存在的事件中心 强大的钩机制 链接 ...
  • 微信小程序的开发体验类似vue和react,但是却没有提供全局状态管理的机制,所以状态的共享只能通过属性传递的方式来实现。这种做法在小规模的应用中尚可以满足开发效率,但是在复杂的应用中组件的嵌套层次很深,属性...
  • 微信小程序的全局状态管理库,model方式开发数据管理,数据变更同步
  • 一个基于原生小程序的 mini 全局状态管理库,跨页面/组件数据共享渲染。 全局状态 state 支持所有 Page 和 Component,更新时使用独有 diff 能力,性能更强。 周期监听 pageListener 能监听所有页面的 onLoad、...
  • 一个基于原生小程序的mini全局状态管理库,五行代码即可引入。

    wxMiniStore

    一个基于原生小程序的 mini 全局状态管理库,跨页面/组件数据共享渲染。
    点击此处进入github地址

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

    导航

    开始

    在开始前,你可以 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 中,可使用 s t a t e . x 。 其 中 state.x。 其中 state.xstate 为全局状态的容器,里面包含了所有的全局状态。

    <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.没有第二步…

    总结:

    • 先执行 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 中更好。

    局部状态模式

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

    1.开启局部模式

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

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

    2.设置范围

    在需要使用 s t a t e 的 组 件 中 , 加 入 ‘ u s e S t o r e : t r u e ‘ , 表 示 当 前 页 面 或 组 件 可 用 state的组件中,加入`useStore: true`,表示当前页面或组件可用 stateuseStore:truestate。

    // 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

    展开全文
  • 我在帮助Nike,沃尔玛以及一些创业公司开发小程序后,依旧认为使用小程序原生框架是一个更高效,稳定的选择,而使用原生框架唯独缺少一个好的状态管理库,如果不引入状态管理则会让我们在模块化,项目结构以及单元...
  • 一个基于原生小程序的mini全局状态管理库,五行代码即可引入。git地址戳我 全局状态state支持所有Page和Component,状态完全同步,并提供api更新状态。 周期监听pageLisener能监听所有页面的onLoad,onShow等周期...

    wxMiniStore
    一个基于原生小程序的mini全局状态管理库,五行代码即可引入。git地址戳我

    全局状态state支持所有Page和Component,状态完全同步,并提供api更新状态。
    周期监听pageLisener能监听所有页面的onLoad,onShow等周期事件。
    全局事件methods,全局可用的方法。
    适合原生小程序,可以随时引入,不影响原有的业务,拓展性强。
    导航
    开始
    全局状态
    局部状态模式
    全局页面周期
    全局方法
    non-writable解决方案
    Api说明
    总结及建议
    开始
    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没关系,你可以将本项目中util下的store.js复制到你的项目中,并在app.js第一行引入:

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

    2. 实例化一个全局状态 state
    Store 允许传一个参数,类型为Object,全局状态写入对象state中,读取请使用store.$state。

    let store = new Store({
      //。
      state: {
        msg: '这是一个全局状态'
      }
    })
    console.log(store.$state.msg); //这是一个全局状态
    App({
    })

    3.在App中注入store
    这么做是为了在其他页面中使用store。

    App({
      onLaunch: function () {

      },
      store: store
    })

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

      <view>{{$state.user.name}}:{{$state.msg}}</view>
    1
    显示为 李四:这是一个全局状态。

    如果在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}}" /> -->

    5.如何修改状态
    js中使用app中的store来进行操作状态。具体参见下面api说明。

    const app = getApp()
    Page({
      data: {

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


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

    1.开启局部模式
    let store = new Store({
      //。
      state: {
        msg: '这是一个全局状态'
      },
      openPart: true
    })

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

    2.设置范围
    在需要使用state的组件中,加入‘userStore:true‘,表示当前页面或组件可用 state的组件中,加入`userStore: true`,表示当前页面或组件可用state的组件中,加入‘userStore:true‘,表示当前页面或组件可用state。

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

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

    a页面设置了Store可用,所以可以通过this.data.state获取。b页面没有设置,所以为undefined,但两个页面均可通过store. state获取。b页面没有设置,所以为undefined,但两个页面均可通过store.state获取。b页面没有设置,所以为undefined,但两个页面均可通过store.state获取。

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

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

    3.注意事项
    openPart一旦开启,所有没有设置useStore的页面和组件将不能在wxml中使用$state。
    组件或页面.js中,我们建议使用getApp().store.$state去获取全局状态,因为他没有限制。
    仅在wxml中需要用到$state的页面和组件中开启useStore。
    周期监听 pageLisener
    在有的场景,我希望每个页面在onLoad时执行一个方法(如统计页面,监听等)。原本做法是一个一个的复制粘贴,很麻烦。
    现在我们可以把某个周期,写入pageLisener中,Store会自动在相应周期优先执行pageLisnner然后再执行原页面周期内事件。

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

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

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

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

    执行结果为:

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

    2.没有第二步…
    总结:

    先执行pageLisener监听,后执行原本页面中周期。
    还支持其他周期事件 [‘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.方法名 来调用:

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

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

    3.说明
    尽量封装复用率高的全局方法
    非交互型事件(即非bindxx)的公用方法,建议不写入Store中。写入App中更好。
    non-writable解决方案
    收到开发者的反馈,在小程序中使用插件时,会报错提示:

     // [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
    const app = getApp()
    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.pageLisener 周期监听
    options.nonWritable 是否重写Page,Componenet。

    Store.prototype.setState(Object data, Function callback)
    用于修改全局状态,用法与微信小程序的 Page.prototype.setData完全一致。在页面中调用setState的数据为同步,渲染为异步。在页面未加载完成时,调用setState的数据为异步(页面周期attached时完成),渲染为异步。
    提示:页面中应避免使用this.setData({$state: …})去操作当前页面下的$state。如有相关需求,请使用页面其他状态存储。

    store.$state : Object
    该对象为实例.$state, 返回的是全局状态(1.2版完全引用)。应避免直接操作修改它。

    store.$r : Object
    该对象为所有页面或组件的实例。

    总结及建议
    考虑到后期的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非常适合原生小程序。可以随时引入,不影响原有的业务,拓展性强。
    --------------------- 
    作者:剪云者。 
    来源:CSDN 
    原文:https://blog.csdn.net/qq_35173602/article/details/82349742 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • 于是乎网上寻找有没有更好的解决方案,最终找到了库,这是由腾讯开源团队研发的微信小程序解决方案,其中针对状态管理的实现很不错,而且还使用专门为小程序开发的保证每次以最小的数据量更新状态,比原生setData的...
  • 微信小程序注册登录思路 (这是根据自身的项目的思路,不一定每个项目都适用) 1.制作授权登录框,引导用户点击按钮 2.uni.login获取code 3.把code传给后端接口,后端返回如下数据 openid: ogtVM5RWdfadfasdfadfadV5s ...
  • 小程序全局状态管理

    千次阅读 2018-08-17 07:30:22
    小程序全局状态管理 缘由 使用vue的朋友可能用过vuex,使用react的朋友可能用redux,好处自然不用多说,用过的都说好。 微信小程序,我去年就曾接触过,那时候小程序连组件的概念都没有,现在小程序似乎更火了,也...

    小程序全局状态管理

    缘由

    使用vue的朋友可能用过vuex,使用react的朋友可能用redux,好处自然不用多说,用过的都说好。
    微信小程序,我去年就曾接触过,那时候小程序连组件的概念都没有,现在小程序似乎更火了,也增加了很多新概念,包括自定义组件。
    小程序的入口文件app.js里会调用App()方法创建一个应用程序实例,有一些公共的、全局的数据可以保存在app.globalData属性里,但是globalData并不具备响应式功能,数据变化时,不会自动更新视图。多个页面或者组件共享同一状态时,处理起来也是相当麻烦的。
    所以,我花了一点时间,简单实现了一个适用于小程序的状态管理。

    demo

    app.js

    //app.js
    import store from './store/index';
    
    // 创建app
    App(store.createApp({
      globalData: {
        userInfo: {},
        todos: [{
          name: '刷牙',
          done: true
        }, {
          name: '吃饭',
          done: false
        }]
      }
      // ...其他
    }))
    复制代码

    pages/index/index.wxml

    <view class="container">
      <view>
        {{title}}
      </view>
      <view class="info">
        <view>
          姓名:{{userInfo.name}}
        </view>
        <view>
          年龄:{{userInfo.age}}
        </view>
      </view>
      <button type="button" bindtap="addTodo">增加todo</button>
      <!-- todos组件 -->
      <todos />
    </view>
    复制代码

    pages/index/index.js

    //index.js
    import store from '../../store/index';
    
    // 创建页面
    Page(store.createPage({
      data: {
        title: '用户信息页'
      },
      // 依赖的全局状态属性 这些状态会被绑定到data上
      globalData: ['userInfo', 'todos'],
      // 这里可以定义需要监听的状态,做一些额外的操作,this指向了当前页面实例
      watch: {
        userInfo(val) {
          console.log('userInfo更新了', val, this);
        }
      },
      onLoad() {
        this.getUserInfo().then(userInfo => {
          // 通过dispatch更新globalData数据
          store.dispatch('userInfo', userInfo);
        })
      },
      // 模拟从服务端获取用户信息
      getUserInfo() {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve({
              name: '小明',
              age: 20
            })
          }, 100)
        })
      },
      // 增加todo
      addTodo() {
        // 注意:这里从this.data上获取todos而不是globalData
        const todos = [...this.data.todos, {
          name: '学习',
          donw: false
        }];
        store.dispatch('todos', todos);
      }
      // 其他...
    }))
    复制代码

    components/todos/index.wxml

    <view class="container">
        <view class="todos">
            <view wx:for="{{todos}}" wx:key="{{index}}">
                <view>{{item.name}}</view>
                <view>{{item.done?'已完成':'未完成'}}</view>
            </view>
        </view>
    </view>
    复制代码

    components/todos/index.js

    import store from '../../store/index';
    
    // 创建组件
    Component(store.createComponent({
        // 依赖的全局状态属性
        globalData: ['todos'],
        // 这里可以定义需要监听的状态,做一些额外的操作,this指向了当前组件实例
        watch: {
            todos(val) {
                console.log('todos更新了', val, this);
                // 做其他事。。。
            }
        }
        // 其他...
    }))
    复制代码

    点击"增加todo"之前

    点击"增加todo"之后

    实现原理

    是不是很神奇,全是代码中那个store的功劳。
    原理其实非常简单,实现起来也就100+行代码。
    主要就是使用观察者模式(或者说是订阅/分发模式),通过dispatch方法更新数据时,执行回调,内部调用this.setData方法更新视图。 不多说,直接贴一下源码。

    code

    store/observer.js

    const events = Symbol('events');
    class Observer {
        constructor() {
            this[events] = {};
        }
        on(eventName, callback) {
            this[events][eventName] = this[events][eventName] || [];
            this[events][eventName].push(callback);
        }
        emit(eventName, param) {
            if (this[events][eventName]) {
                this[events][eventName].forEach((value, index) => {
                    value(param);
                })
            }
        }
    
        clear(eventName) {
            this[events][eventName] = [];
        }
    
        off(eventName, callback) {
            this[events][eventName] = this[events][eventName] || [];
            this[events][eventName].forEach((item, index) => {
                if (item === callback) {
                    this[events][eventName].splice(index, 1);
                }
            })
        }
    
        one(eventName, callback) {
            this[events][eventName] = [callback];
        }
    }
    
    const observer = new Observer();
    
    export {
        Observer,
        observer
    }
    复制代码

    store/index.js

    import {
        Observer
    } from './observer'
    
    const bindWatcher = Symbol('bindWatcher');
    const unbindWatcher = Symbol('unbindWatcher');
    
    class Store extends Observer {
        constructor() {
            super();
            this.app = null;
        }
        // 创建app
        createApp(options) {
            const {
                onLaunch
            } = options;
            const store = this;
            options.onLaunch = function (...params) {
                store.app = this;
                if (typeof onLaunch === 'function') {
                    onLaunch.apply(this, params);
                }
            }
            return options;
        }
        // 创建页面
        createPage(options) {
            const {
                globalData = [],
                    watch = {},
                    onLoad,
                    onUnload
            } = options;
            const store = this;
            // 保存globalData更新回调的引用
            const globalDataWatcher = {};
            // 保存watch监听回调的引用
            const watcher = {};
            // 劫持onLoad
            options.onLoad = function (...params) {
                store[bindWatcher](globalData, watch, globalDataWatcher, watcher, this);
                if (typeof onLoad === 'function') {
                    onLoad.apply(this, params);
                }
            }
            // 劫持onUnload
            options.onUnload = function () {
                store[unbindWatcher](watcher, globalDataWatcher);
                if (typeof onUnload === 'function') {
                    onUnload.apply(this);
                }
            }
            delete options.globalData;
            delete options.watch;
            return options;
        }
        // 创建组件
        createComponent(options) {
            const {
                globalData = [],
                    watch = {},
                    attached,
                    detached
            } = options;
            const store = this;
            // 保存globalData更新回调的引用
            const globalDataWatcher = {};
            // 保存watch监听回调的引用
            const watcher = {};
            // 劫持attached
            options.attached = function (...params) {
                store[bindWatcher](globalData, watch, globalDataWatcher, watcher, this);
                if (typeof attached === 'function') {
                    attached.apply(this, params);
                }
            }
            // 劫持detached
            options.detached = function () {
                store[unbindWatcher](watcher, globalDataWatcher);
                if (typeof detached === 'function') {
                    detached.apply(this);
                }
            }
            delete options.globalData;
            delete options.watch;
            return options;
        }
        // 派发一个action更新状态
        dispatch(action, payload) {
            this.app.globalData[action] = payload;
            this.emit(action, payload);
        }
        /**
         * 1. 初始化页面关联的globalData并且监听更新
         * 2. 绑定watcher
         * @param {Array} globalData
         * @param {Object} watch
         * @param {Object} globalDataWatcher
         * @param {Object} watcher
         * @param {Object} instance 页面实例
         */
        [bindWatcher](globalData, watch, globalDataWatcher, watcher, instance) {
            const instanceData = {};
            for (let prop of globalData) {
                instanceData[prop] = this.app.globalData[prop];
                globalDataWatcher[prop] = payload => {
                    instance.setData({
                        [prop]: payload
                    })
                }
                this.on(prop, globalDataWatcher[prop]);
            }
            for (let prop in watch) {
                watcher[prop] = payload => {
                    watch[prop].call(instance, payload);
                }
                this.on(prop, watcher[prop])
            }
            instance.setData(instanceData);
        }
        /**
         * 解绑watcher与globalDataWatcher
         * @param {Object} watcher
         * @param {Object} globalDataWatcher 
         */
        [unbindWatcher](watcher, globalDataWatcher) {
            // 页面卸载前 解绑对应的回调 释放内存
            for (let prop in watcher) {
                this.off(prop, watcher[prop]);
            }
            for (let prop in globalDataWatcher) {
                this.off(prop, globalDataWatcher[prop])
            }
        }
    }
    
    export default new Store()
    复制代码

    具体的代码就不解释了,源码里也有基本的注释。
    目前实现的功能不算多,基本上能用了,如果业务上需求更高了,再进行拓展。
    以上,
    希望能给一些朋友一点点启发,顺便点个赞哦,嘻嘻!

    展开全文
  • 原生小程序之仿美团外卖

    千次阅读 2020-06-12 23:40:52
    根据美团外卖仿写小程序,请求运用封装的微信小程序原生方法wx.request,后台数据自己在本地搭建的mock模拟数据,封装store代替状态管理工具,即app.js中的globalData.毕竟是大厂的项目不可能一模一样,只写了部分功能,...
  • 微信小程序状态管理和跨页通讯

    千次阅读 2020-05-19 16:43:43
    小程序开发原生框架是足够好的,唯一缺点就是没有一个全局状态管理和跨页通讯的工具。 针对微信小程序状态管理,接触使用github上如下的插件库 Westore —— 1KB javascript覆盖状态管理、跨页通讯、插件开发和...
  • 原生小程序转Taro开发

    千次阅读 2019-07-01 15:45:41
    原生开发小程序的时候初期文件目录是这样的: pages/demo1/demo1 pages/demo2/demo2 .... page/demoN/demoN 这样在一个Pages目录下放置全部文件的方式在前期文件页面和代码比较少的情况下勉强能够接受,一旦项目...
  • 原生开发小程序 和 wepy 、 mpvue 对比

    千次阅读 2018-04-25 16:56:44
    原生开发小程序 和 wepy 、 mpvue 对比本文横向对比、探讨了下原生开发小程序,和目前比较热门的 wepy 、mpvue 开发小程序三种方式的优势和劣势;由于三者的篇幅都比较多,本文只是简单介绍。如有错误,请大神们指正...
  • 小程序原生开发有不少槽点: 原生wxml开发对Node、预编译器、webpack支持不好,影响开发效率和工程构建流程。所以大公司都会用框架开发 微信定义的这套语法,wxml、wxs,以及wx:if等语法,私有化太强。不如正经学...
  • 原生小程序跨页面动态改变状态,动态改变app.js全局定义的数据globalData,并实现实时监听的方法!!!!!!**这是我的第一条博客,写的有什么不对的,请多各位担待!!或者有什么不懂得地方可以随时问我!!!**==...
  • 原生开发小程序文档:点此进入 wepy 开发文档:点此进入 mpVue 开发文档:点此进入 三者的简单对比 小程序缺点 个人认为小程序开发中,最大的缺点,也是很蛋疼的问题 不支持vue的过滤器filters(因此数据都得...
  • 一款小型程序原生代码编写的背景音乐播放管理器,统一了资源管理,按需记录播放进度,通过事件累积同步整个界面的播放状态 小程序sdk2.4.0测试通过 截屏 微信分享代码段/微信小程序代码片段分享链接 注意:从1.02....
  • 与之相随,小程序的开发生态也在蓬勃发展,从最初的微信原生开发,到wepy、mpvue、taro、uni-app等框架依次出现,从刀耕火种演进为现代化开发,生态越来越丰富。 选择多了,问题也就来了,开发小程序,该用原生还是...
  • 小程序原生开发有不少槽点: 原生wxml开发对Node、预编译器、webpack支持不好,影响开发效率和工程构建流程。所以大公司都会用框架开发 微信定义的这套语法,wxml、wxs,以及wx:if等语法,私有化太强。不如正经学vue...
  • 在集成mqtt到小程序的开发过程中,确实走了不少弯路,下了许许多多的示例,一步步踩坑到现在终于完美解决了小程序引入mqtt的方法。该方法原生和uniapp均适用。 1. 小程序网页配置 先登录微信公众平台,找到开发》...
  • 支付界面: 在wx.requestPayment支付完成后,支付用户如果不点击支付界面中的完成按钮,wx.requestPayment不会触发回调函数success,如需获取支付状态,需使用云支付函数中的回调函数,教程链接: 微信小程序云开发...
  • 小程序开发:用原生还是选框架(wepy/mpvue/uni-app/taro)? 自 2017-1-9微信小程序诞生以来,历经2年多的迭代升级,已有数百万小程序上线,成为继Web、iOS、Android之后,第四大主流开发技术。 与之相随,小程序...
  • 微信小程序是当下最为普及、方便以及前沿的一种移动服务方式,微信小程序的开发运用了微信开发者工具,设计并开发图书馆管理小程序,并实现了图书借阅管理、留言管理、图书馆占座管理、预约占座等功能,帮助学生...
  • 小程序 与 App 与 H5 之间的区别

    万次阅读 多人点赞 2018-11-21 11:35:18
    小程序的实现原理 根据微信官方的说明,微信小程序的运行环境有 3 个平台,iOS 的 WebKit(苹果开源的浏览器内核),Android 的 X5 (QQ 浏览器内核),开发时用的 nw.js(C++ 实现的 web 转桌面应用)。 平台 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 55,638
精华内容 22,255
关键字:

原生小程序状态管理