vuex指公共数据管理模块。如果应用中数据量比较大 传参不方便 可以用vuex,不是很大的建议可以用缓存。

使用准备在项目中新建store目录,在该目录下新建index.js文件

1.引入

uniapp中内置了vuex,所以只需要规范引入即可:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// 页面路径:store/index.js

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex);//vue的插件机制

//Vuex.Store 构造器选项

const store = new Vuex.Store({ 

 state: {

                username:"luo",

        

        //公共的变量,存储数据,这里的变量不能随便修改,只能通过触发mutations的方法才能改变

    },

    mutations: {

        //相当于同步的操作

    },

    actions: {

        //相当于异步的操作,不能直接改变state的值,只能通过触发mutations的方法才能改变

    }

})

export default store

1

2

3

4

5

6

7

8

9

10

11

12

13

// 页面路径:main.js

import Vue from 'vue'

import App from './App'

import store from './store'

Vue.prototype.$store = store

App.mpType = 'app'

// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件

const app = new Vue({

    store,

    ...App

})

app.$mount()

2.state属性,主要功能为存储数据

第一种方法:通过属性访问,需要在根节点注入 store 。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <text>用户名:{{username}}</text>

    </view>

</template>

<script>

    import store from '@/store/index.js';//需要引入store

    export default {

        data() {

            return {}

        },

        computed: {

            username() {

                return store.state.username

            }

        }

    }

</script>

第二种方法:在组件中使用,通过 this.$store 访问到 state 里的数据。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <text>用户名:{{username}}</text>

    </view>

</template>

<script>

    export default {

        data() {

            return {}

        },

        computed: {

            username() {

                return this.$store.state.username

            }

        }

    }

</script>

进阶方法:通过 mapState 辅助函数获取。当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。 为了解决这个问题,我们可以使用 mapState 辅助函数 帮助我们生成计算属性,不用一个一个去声明

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <view>用户名:{{username}}</view>

        <view>年龄:{{age}}</view>

    </view>

</template>

<script>

    import { mapState } from 'vuex'//引入mapState

    export default {

        data() {

            return {}

        },

 // 从state中拿到数据 箭头函数可使代码更简练

        computed: mapState({

            username: state => state.username,

            age: state => state.age,

        })

    }

</script>

3. Getter属性,主要功能为计算筛选数据

可以认为是 store 的计算属性, 就像 computed 计算属性一样,getter 返回的值会根据它的依赖被缓存起来,当它的依赖值发生改变才会被重新计算。(响应式数据的应用)

可以在多组件中共享 getter 函数

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

// 页面路径:store/index.js

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({

    state: {

        todos: [{

                id: 1,

                text: '我是内容一',

                done: true

            },

            {

                id: 2,

                text: '我是内容二',

                done: false

            }

        ]

    },

    getters: {

        doneTodos: state => {

            return state.todos.filter(todo => todo.done)

        }

    }

})

export default store

Getter属性接收传递参数,主要为:state, 如果在模块中定义则为模块的局部状态。getters, 等同于 store.getters。

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

// 页面路径:store/index.js

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({

    state: {

        todos: [{

                id: 1,

                text: '我是内容一',

                done: true

            },

            {

                id: 2,

                text: '我是内容二',

                done: false

            }

        ]

    },

    getters: {

        doneTodos: state => {

            return state.todos.filter(todo => todo.done)

        },

        doneTodosCount: (state, getters) => {

            //state :可以访问数据

            //getters:访问其他函数,等同于 store.getters

            return getters.doneTodos.length

        },

        getTodoById: (state) => (id) => {

            return state.todos.find(todo => todo.id === id)

        }

    }

})

export default store

应用Getter属性方法一:通过属性访问,Getter 会暴露为 store.getters 对象,可以以属性的形式访问这些值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>   

        <view v-for="(item,index) in todos">

            <view>{{item.id}}</view>

            <view>{{item.text}}</view>

            <view>{{item.done}}</view>

        </view>

    </view>

</template>

<script>

    import store from '@/store/index.js'//需要引入store

    export default {

        computed: {

            todos() {

                return store.getters.doneTodos

            }

        }

    }

</script>

应用Getter属性方法二:通过 this.$store 访问。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>   

        <view v-for="(item,index) in todos">

            <view>{{item.id}}</view>

            <view>{{item.text}}</view>

            <view>{{item.done}}</view>

        </view>

    </view>

</template>

<script>

    export default {

        computed: {

            todos() {

                return this.$store.getters.doneTodos

            }

        }

    }

</script>

应用Getter属性方法三:可以通过让 getter 返回一个函数,来实现给 getter 传参。在对 store 里的数组进行查询时非常有用。getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。(一次调用,一次请求,没有依赖关系)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <view v-for="(item,index) in todos">

            <view>{{item}}</view>

        </view>

    </view>

</template>

<script>

    export default {

        computed: {

            todos() {

                return this.$store.getters.getTodoById(2)

            }

        }

    }

</script>

应用Getter属性方法(简写):通过 mapGetters 辅助函数访问。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <view>{{doneTodosCount}}</view>

    </view>

</template>

<script>

    import {mapGetters} from 'vuex' //引入mapGetters

    export default {

        computed: {

            // 使用对象展开运算符将 getter 混入 computed 对象中

            ...mapGetters([

                'doneTodos',

                'doneTodosCount',

                // ...

            ])

        }

    }

</script>

4. Mutation属性,Vuex中store数据改变的唯一地方(数据必须同步)

通俗的理解,mutations 里面装着改变数据的方法集合,处理数据逻辑的方法全部放在 mutations 里,使数据和视图分离。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

// 页面路径:store/index.js

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({

    state: {

        count: 1

    },

    mutations: {

        add(state) {

            // 变更状态

            state.count += 2

        }

    }

})

export default store

不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 add 的 mutation 时,调用此函数”,要唤醒一个 mutation handler,需要以相应的 type 调用 store.commit 方法。
注意:store.commit 调用 mutation(需要在根节点注入 store)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <view>数量:{{count}}</view>

        <button @click="addCount">增加</button>

    </view>

</template>

<script>

import store from '@/store/index.js'   

export default {

    computed: {

        count() {

            return this.$store.state.count

        }

    },

    methods: {

        addCount() {

            store.commit('add')

        }

    }

}

</script>

mutation中的函数接收参数:你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

// 页面路径:store/index.js

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({

    state: {

        count: 1

    },

    mutations: {

       //传递数值

        add(state, n) {

            state.count += n

        }

        //传递对象类型

        add(state, payload) {

            state.count += payload.amount

        }

    }

})

export default store

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

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <view>数量:{{count }}</view>

        <button @click="addCount">增加</button>

    </view>

</template>

<script>

    import store from '@/store/index.js'

    export default {

        computed: {

            count() {

                return this.$store.state.count

            }

        },

        methods: {

           //传递数值

            addCount() {

                store.commit('add', 5)//每次累加 5

            }

            //传递对象

            addCount () {//把载荷和type分开提交

                store.commit('add', { amount: 10 })

            }

        }

    }

</script>

应用Getter属性方法二:通过 this.$store 访问。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>   

        <view v-for="(item,index) in todos">

            <view>{{item.id}}</view>

            <view>{{item.text}}</view>

            <view>{{item.done}}</view>

        </view>

    </view>

</template>

<script>

    export default {

        computed: {

            todos() {

                return this.$store.getters.doneTodos

            }

        }

    }

</script>

应用mutation属性方法进阶(简写):通过 mapMutations辅助函数访问。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <view>数量:{{count}}</view>

        <button @click="add">增加</button>

    </view>

</template>

<script>

    import { mapMutations } from 'vuex'//引入mapMutations

    export default {

        computed: {

            count() {

                return this.$store.state.count

            }

        },

        methods: {

            ...mapMutations(['add'])//对象展开运算符直接拿到add

        }

    }

</script>

5. Action属性:

action 提交的是 mutation,通过 mutation 来改变 state,而不是直接变更状态,action 可以包含任意异步操作。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

// 页面路径:store/index.js

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({

    state: {

        count: 1

    },

    mutations:{

        add(state) {

            // 变更状态

            state.count += 2

        }

    },

    actions:{

        addCountAction (context) {

            context.commit('add')

        }

    }

})

export default store

action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。

actions 通过 store.dispatch 方法触发:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <view>数量:{{count}}</view>

        <button @click="add">增加</button>

    </view>

</template>

<script>

    import store from '@/store/index.js';

    export default {

        computed: {

            count() {

                return this.$store.state.count

            }

        },

        methods: {

            add () {

                store.dispatch('addCountAction')

            }

        }

    }

</script>

actions 支持以载荷形式分发:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

// 页面路径:store/index.js

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({

    state: {

        count: 1

    },

    mutations:{

        add(state, payload) {

            state.count += payload.amount

        }

    },

    actions:{

        addCountAction (context , payload) {

            context.commit('add',payload)

        }

    }

})

export default store

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

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <view>数量:{{count }}</view>

        <button @click="add">增加</button>

    </view>

</template>

<script>

    import store from '@/store/index.js';

    export default {

        computed: {

            count() {

                return this.$store.state.count

            }

        },

        methods: {

            add () {

                // 以载荷形式分发

                store.dispatch('addCountAction', {amount: 10})

            }

            add () {

            // 以对象形式分发

            store.dispatch({

                type: 'addCountAction',

                amount: 5

            })

        }

        }

    }

</script>

actions的异步:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

// 页面路径:store/index.js

//调用mutations的方法来改变数据(同步、异步)

    actions:{

            //异步调用

            actionA({ commit }) {

                        return new Promise((resolve, reject) => {

                            setTimeout(() => {

                                commit('add',5)

                                resolve()

                            }, 2000)

                        })

                    }

            actionB ({ dispatch, commit }) {

                  return dispatch('actionA').then(() => {commit('someOtherMutation')

            })

            async actionA ({ commit }) {commit('gotData', await getData())

        },

        async actionB ({ dispatch, commit }) {

            await dispatch('actionA') // 等待 actionA 完成

            commit('gotOtherData', await getOtherData())

        }

        }

    }

应用Actions属性方法进阶(简写):通过 mapActions辅助函数访问。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view>

        <view>数量:{{count }}</view>

        <button @click="addCountAction">增加</button>

    </view>

</template>

<script>

    import { mapActions } from 'vuex'

    export default {

        computed: {

            count() {

                return this.$store.state.count

            }

        },

        methods: {

            ...mapActions([

                'addCountAction',

                // 将 `this.addCountAction()` 映射为 `this.$store.dispatch('addCountAction')`

            ])

        }

    }

</script>

6. Module结构。

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

1.在 store 文件夹下新建 modules 文件夹,并在下面新建 moduleA.js 和 moduleB.js 文件用来存放 vuex 的 modules 模块。

2.在 main.js 文件中引入 store。

1

2

3

4

5

6

7

8

9

10

11

12

13

// 页面路径:main.js

    import Vue from 'vue'

    import App from './App'

    import store from './store'

    Vue.prototype.$store = store

    // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件

    const app = new Vue({

        store,

        ...App

    })

    app.$mount()

3.在项目根目录下,新建 store 文件夹,并在下面新建 index.js 文件,作为模块入口,引入各子模块。

1

2

3

4

5

6

7

8

9

10

11

12

13

//  页面路径:store/index.js

    import Vue from 'vue'

    import Vuex from 'vuex'

    import moduleA from '@/store/modules/moduleA'

    import moduleB from '@/store/modules/moduleB'

    Vue.use(Vuex)

    export default new Vuex.Store({

        modules:{

            moduleA,moduleB

        }

    })

4.子模块 moduleA 页面内容。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// 子模块moduleA路径:store/modules/moduleA.js

export default {

    state: {

        text:"我是moduleA模块下state.text的值"

    },

    getters: {

         

    },

    mutations: {

         

    },

    actions: {

         

    }

}

5.子模块 moduleB 页面内容。

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

// 子模块moduleB路径:store/modules/moduleB.js

export default {

    state: {

        timestamp: 1608820295//初始时间戳

    },

    getters: {

        timeString(state) {//时间戳转换后的时间

            var date = new Date(state.timestamp);

            var year = date.getFullYear();

            var mon  = date.getMonth()+1;

            var day  = date.getDate();

            var hours = date.getHours();

            var minu = date.getMinutes();

            var sec = date.getSeconds();

            var trMon = mon<10 ? '0'+mon : mon

            var trDay = day<10 ? '0'+day : day

            return year+'-'+trMon+'-'+trDay+" "+hours+":"+minu+":"+sec;

        }

    },

    mutations: {

        updateTime(state){//更新当前时间戳

            state.timestamp = Date.now()

        }

    },

    actions: {

    }

}

6.在页面中引用组件 myButton ,并通过 mapState 读取 state 中的初始数据。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

<!-- 页面路径:pages/index/index.vue -->

<template>

    <view class="content">

        <view>{{text}}</view>

        <view>时间戳:{{timestamp}}</view>

        <view>当前时间:{{timeString}}</view>

        <myButton></myButton>

    </view>

</template>

<script>

    import {mapState,mapGetters} from 'vuex'

    export default {

        computed: {

            ...mapState({

                text: state => state.moduleA.text,

                timestamp: state => state.moduleB.timestamp

            }),

            ...mapGetters([

                'timeString'

            ])

        }

    }

</script>

7.在组件 myButton中,通过 mutations 操作刷新当前时间。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<!-- 组件路径:components/myButton/myButton.vue -->

<template>

    <view>

        <button type="default" @click="updateTime">刷新当前时间</button>

    </view>

</template>

<script>

    import {mapMutations} from 'vuex'

    export default {

        data() {

            return {}

        },

        methods: {

            ...mapMutations(['updateTime'])

        }

    }

</script>

总结

vue是单向数据流,子组件不能直接修改父组件的数据,而通过vuex状态管理实现:把组件的共享状态抽取出来,以一个全局单例模式管理。在这种模式下,组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐