UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • WebPack

  • 浏览器相关

  • 工程化相关

  • 工作中遇到的问题以及解决方案

  • Git

  • Vite

  • 一些小工具

    • 二次封装axios
    • 妙用svg实现dom转image
    • 小程序实现全局状态管理
      • 前言
      • App.globalData
      • behaviors
      • behaviorsStore
      • 实现
      • Tips
      • 结语
  • 算法

  • 服务器

  • HTTP

  • 技术
  • 一些小工具
hanhanbuku
2025-07-09
目录

小程序实现全局状态管理

# 前言

全局状态管理一直是前端项目中一个比较重要的模块,传统的前端框架基本都提供了全局状态管理的方案,如:vuex,redux等。那我们在开发原生小程序的时候该如何实现全局状态管理呢?下面将以微信小程序为例,实现一个全局状态管理功能

# App.globalData

一说到小程序的全局状态,大家肯定会想到App.globalData,这是官方提供的一个全局状态,我们在使用的过程中只需要调用对应的api拿到app对象就可以获取

const app = getApp()
console.log(app.globalData,'全局状态') // 输出 {}
app.globalData.name = 'xxx'
console.log(app.globalData.name,'更新全局状态') // 输出xxx
1
2
3
4

但是这个状态有一个弊端,我们都知道小程序必须是调用了setData才会触发页面更新,那么意味着挂载在app上的全局状态我们不能直接在页面中渲染,因为他并不会触发页面的更新 要想实现一个可以在任意地方使用,并且值发生变化后还会触发页面更新的全局状态,我们还是得另辟蹊径

# behaviors

不得不说,小程序的文档确实藏得够深,官方其实还是提供了很多好用的东西的,只不过文档太晦涩了。Behaviors (opens new window) 是官方提供的混入器,其用法类似于vue2中的mixins。可以用于将一些属性,方法,生命周期混入到使用者上,以此来实现逻辑,状态的抽离和复用。 而我们的全局状态刚好就可以借助这个工具来完成,将全局状态混入到页面自身,然后在更新值得时候调用setData,就可以完美的触发页面更新。

# behaviorsStore

首先我们创建一个behaviors,然后添加上最基本的属性


export default Behavior({
    data: {
        globalData: {}
    },
    lifetimes:{
        
    },
    pageLifetimes:{
        
    },
    methods:{
        
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

可以看到,他的语法和组件的js文件简直一模一样。在组件中直接引入这个Behavior就可以把Behavior上的属性都混入进组件自身了

import behaviorsStore from '../../../../storePro/behaviorsStore'

Component({
    behaviors:[behaviorsStore],
    ...
})
1
2
3
4
5
6

怎么样,是不是使用起来非常的简单。下面我们去完善一下Store的功能

# 实现

首先实现一下store的setData功能:其实简单来说,我们只需要直接调用setData然后传入globalData的新值就可以更新状态了,像下面这样

this.setData({
    globalData:{
        ...
    }
})
1
2
3
4
5

但是这样调用显然不太合理,因为globalData上我们会挂载很多属性,这样直接设置值的话会覆盖掉其他的值,除非每次设置新状态时我们都把不需要设置的状态都给他加上,例如globalData里有name和age两个属性,如果我只想设置name属性,那么我就需要把age的旧属性也带上,如下

this.setData({
    globalData:{
        name:'新值',
        age:this.data.globalData.name
    }
})
1
2
3
4
5
6

如果有更多的值,那将会非常麻烦,所以我们需要封装一个函数,来专门更新全局状态的值

   // setGlobalData 实现,主要内容为将 globalDataStore 的内容设置进页面的 data 的 globalData 属性中。
    setGlobalData(obj) {
      obj = obj || {};
      let outObj = Object.keys(obj).reduce((sum, key) => {
        let _key = "globalData." + key;
        sum[_key] = obj[key];
        return sum;
      }, {});
      this.setData(outObj);
    }
1
2
3
4
5
6
7
8
9
10

在这个函数中,我们将旧值混合新值一起重新赋值给globalData,使用方式如下

// 设置单个值
this.setGlobalData({
    name:'新值'
})
// 同时设置多个值
this.setGlobalData({
    name:'新值',
    age:'18'
})
1
2
3
4
5
6
7
8
9

ok,接下来我们需要实现的就是跨页面使用的时候如何让状态同步。简单来说就是目前我们的behaviors初始化时data里是一个空的globalData对象,当我们在A页面对这个globalData进行了某些操作后,在B页面引入behaviors其实拿到的并不是被A页面处理过的,而是一个全新的。也就是说这个时候globalData还是一个空对象。 所以我们需要在对globalData进行操作的同时,把他的值保存一份出来,然后在初始化behaviors的时候又给赋值上去

// globalDataStore 用来全局记录 globalData,为了跨页面同步 globalData 用
export let globalDataStore = {};

export default Behavior({
    data: {
        globalData: {}
    },
    lifetimes:{
        attached() {
            // 同步 globalDataStore 的内容
            this.setData({
                globalData: Object.assign(
                    {},
                    this.data.globalData || {},
                    globalDataStore
                )
            });
        }
    },
    pageLifetimes:{
        
    },
    methods:{
        setGlobalData(obj) {
            obj = obj || {};
            let outObj = Object.keys(obj).reduce((sum, key) => {
                let _key = "globalData." + key;
                sum[_key] = obj[key];
                return sum;
            }, {});
            this.setData(outObj, () => {
                globalDataStore = this.data.globalData;
            });
        },
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

上述代码中主要做了以下几点:

  1. 创建一个全局变量globalDataStore用来同步globalData
  2. 在setData的时候更新globalDataStore
  3. 在组件的生命周期attached中将globalDataStore合并进globalData

解决了状态同步的问题,那么接下来还需要考虑一个问题,attached生命周期是组件被创建时执行的钩子,如果我在B页面设置了新状态,然后回到A页面,这时A页面的组件是不会再触发attached的,那此时该如何去同步最新状态到A页面呢?

细心的小伙伴肯定已经想到了,既然attached只会执行一次,那我把同步状态的代码放在一个每次切换页面都会执行的钩子里不就行了嘛。

// globalDataStore 用来全局记录 globalData,为了跨页面同步 globalData 用
export let globalDataStore = {};

export default Behavior({
    data: {
        globalData: {}
    },
    lifetimes:{
        attached() {
            // 同步 globalDataStore 的内容
            this.setData({
                globalData: Object.assign(
                    {},
                    this.data.globalData || {},
                    globalDataStore
                )
            });
        }
    },
    pageLifetimes:{
        show() {
                // 同步 globalData
              this.setGlobalData(Object.assign({}, globalDataStore));
        }
    },
    methods:{
      ...
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

对此我们还可以再进行一次性能上的优化,如果B页面并没有对全局状态做变动,那么A页面就不需要去同步状态了,毕竟多调用一次setData就会多造成一次性能开销。所以这里我们引入了一个新的变量,来标识setData调用了多少次,再onshow里判断如果次数发生了变化,那么就需要重新设置装填

// globalDataStore 用来全局记录 globalData,为了跨页面同步 globalData 用
export let globalDataStore = {};

// setGlobalCount 用来全局记录 setGlobalData 的调用次数,为了在 B 页面回到 A 页面的时候,
// 检查页面 __setGlobalDataCount 和 setGlobalCount 是否一致来判断在 B 页面是否有 setGlobalData,
// 以此来同步 globalData
let setGlobalCount = 0;

export default Behavior({
    data: {
        globalData: {}
    },
    lifetimes:{
        attached() {
            // 页面 onLoad 的时候同步一下 setGlobalCount
            this.__setGlobalDataCount = setGlobalCount;
            // 同步 globalDataStore 的内容
            this.setData({
                globalData: Object.assign(
                    {},
                    this.data.globalData || {},
                    globalDataStore
                )
            });
        }
    },
    pageLifetimes:{
        show() {
            // 为了在 B 页面回到 A 页面的时候,检查页面 __setGlobalDataCount 和 setGlobalCount 是否一致来判断在 B 页面是否有 setGlobalData
            if (this.__setGlobalDataCount != setGlobalCount) {
                // 同步 globalData
                this.__setGlobalDataCount = setGlobalCount;
                this.setGlobalData(Object.assign({}, globalDataStore));
            }
        }
    },
    methods:{
        setGlobalData(obj){
            setGlobalCount = setGlobalCount + 1;
            this.__setGlobalDataCount = this.__setGlobalDataCount + 1;
            ...
        }
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

至此我们的全局状态功能就已经非常完善了,在页面中只需要直接渲染```globalData.xxx就可以了。vuex``提供了持久化的功能,原理就是把状态同步存储在本地缓存中,下次初始化的时候判断缓存中是否存在,存在的话则直接赋值上去。这个功能还是比较实用的,下面就一起来实现以下

// 获取本地的 gloabalData 缓存
try {
  const gloabalData = wx.getStorageSync("gloabalData");
  // 有缓存的时候加上
  if (gloabalData) {
    globalDataStore = { ...gloabalData };
  }
} catch (error) {
  console.error("gloabalData getStorageSync error", "e =", error);
}

methods:{
    // setGlobalDataAndStorage 实现,先调用 setGlobalData,然后存到 storage 里
    setGlobalDataAndStorage(obj,cb=null) {
        this.setGlobalData(obj,cb);
        try {
            let gloabalData = wx.getStorageSync("gloabalData");
            // 有缓存的时候加上
            if (gloabalData) {
                gloabalData = { ...gloabalData, ...obj };
            } else {
                gloabalData = { ...obj };
            }
            wx.setStorageSync("gloabalData", gloabalData);
        } catch (e) {
            console.error("gloabalData setStorageSync error", "e =", e);
        }
    },
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

这样一来,每次初始化的时候都会带上缓存里的值了,现在我们的全局状态使用方法如下

<!--模板中使用-->
<view>{{gloabalData.name}}</view>
1
2
// 更新状态
this.setGlobalData({
    name:'新名字'
})

// 更新状态并持久化
this.setGlobalDataAndStorage({
    name:'新名字'
})
1
2
3
4
5
6
7
8
9

虽然这样用起来已经没什么毛病了,但是并不太好区分哪些是全局状态里的哪些是组件自身的,渲染状态的时候还可以从globalData看出来,但是在调用全局方法的时候就根本分不清了,所以我们还可以进行一点小优化,将方法的调用也区分开来。 在全局状态里新增一个函数用来派发全局状态的事件

    const fun_name_map = {
    'setData':'setGlobalData',
    'setDataStorage':'setGlobalDataAndStorage'
}

    // 调用全局事件
    $dispatch(fun_name = '',payload,cb){
      if(!fun_name){
        console.error("gloabalData dispatch error", "fun_name is undefined", );
        return
      }
      const f = fun_name_map[fun_name]||fun_name
      if(!f){
        console.error("gloabalData dispatch error", `${fun_name} is not defined`, );
        return
      }
      this[f](payload,cb)
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

使用

this.$dispatch(
    'setData',
    {name:'新名字'},
    ()=>{
        console.log('setData的回调')
    })
1
2
3
4
5
6

这样用起来就非常方便了。下面是完整的代码

// globalDataStore 用来全局记录 globalData,为了跨页面同步 globalData 用
export let globalDataStore = {};
// 获取本地的 gloabalData 缓存
try {
  const gloabalData = wx.getStorageSync("gloabalData");
  // 有缓存的时候加上
  if (gloabalData) {
    globalDataStore = { ...gloabalData };
  }
} catch (error) {
  console.error("gloabalData getStorageSync error", "e =", error);
}

// setGlobalCount 用来全局记录 setGlobalData 的调用次数,为了在 B 页面回到 A 页面的时候,
// 检查页面 __setGlobalDataCount 和 setGlobalCount 是否一致来判断在 B 页面是否有 setGlobalData,
// 以此来同步 globalData
let setGlobalCount = 0;

const fun_name_map = {
  'setData':'setGlobalData',
  'setDataStorage':'setGlobalDataAndStorage'
}

const baseState = {
  name:'全局name'
}

export default Behavior({
  data: {
    globalData: Object.assign({
      ...baseState
    }, globalDataStore)
  },
  lifetimes: {
    attached() {
      // 页面 onLoad 的时候同步一下 setGlobalCount
      this.__setGlobalDataCount = setGlobalCount;
      // 同步 globalDataStore 的内容
      this.setData({
        globalData: Object.assign(
          {},
          this.data.globalData || {},
          globalDataStore
        )
      });
    }
  },
  pageLifetimes: {
    show() {
      // 为了在 B 页面回到 A 页面的时候,检查页面 __setGlobalDataCount 和 setGlobalCount 是否一致来判断在 B 页面是否有 setGlobalData
      if (this.__setGlobalDataCount != setGlobalCount) {
        // 同步 globalData
        this.__setGlobalDataCount = setGlobalCount;
        this.setGlobalData(Object.assign({}, globalDataStore));
      }
    }
  },
  methods: {
    // setGlobalData 实现,主要内容为将 globalDataStore 的内容设置进页面的 data 的 globalData 属性中。
    setGlobalData(obj,cb=null) {
      setGlobalCount = setGlobalCount + 1;
      this.__setGlobalDataCount = this.__setGlobalDataCount + 1;
      obj = obj || {};
      let outObj = Object.keys(obj).reduce((sum, key) => {
        let _key = "globalData." + key;
        sum[_key] = obj[key];
        return sum;
      }, {});
      this.setData(outObj, () => {
        globalDataStore = this.data.globalData;
        cb&&cb()
      });
    },
    // setGlobalDataAndStorage 实现,先调用 setGlobalData,然后存到 storage 里
    setGlobalDataAndStorage(obj,cb=null) {
      this.setGlobalData(obj,cb);
      try {
        let gloabalData = wx.getStorageSync("gloabalData");
        // 有缓存的时候加上
        if (gloabalData) {
          gloabalData = { ...gloabalData, ...obj };
        } else {
          gloabalData = { ...obj };
        }
        wx.setStorageSync("gloabalData", gloabalData);
      } catch (e) {
        console.error("gloabalData setStorageSync error", "e =", e);
      }
    },
    // 调用全局事件
    $dispatch(fun_name = '',payload,cb){
      if(!fun_name){
        console.error("gloabalData dispatch error", "fun_name is undefined", );
        return
      }
      const f = fun_name_map[fun_name]||fun_name
      if(!f){
        console.error("gloabalData dispatch error", `${fun_name} is not defined`, );
        return
      }
      this[f](payload,cb)
    }
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

# Tips

页面如何使用 ``Behavior``

Component是Page的超集,因此可以使用Component构造器构造页面。

看看官方文档 (opens new window) :事实上,小程序的页面也可以视为自定义组件。因而,页面也可以使用Component构造器构造,拥有与普通组件一样的定义段与实例方法。 但此时要求对应json文件中包含usingComponents定义段。

# 结语

本文我们通过混入器实现了一个全局状态管理,Behavior非常的强大,还可以通过它来实现很多很实用的功能,例如自动打日志,计算属性,监听等等。大家可以自行探索

编辑 (opens new window)
上次更新: 2025/07/10, 09:21:18
妙用svg实现dom转image
动态规划

← 妙用svg实现dom转image 动态规划→

最近更新
01
前端检测更新,自动刷新网页
06-09
02
swiper渲染大量数据的优化方案
06-06
03
仿抖音短视频组件实现方案
02-28
更多文章>
Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式