Skip to content

Vite创建项目,准备vuex仓库

https://www.yuque.com/u158868/ng9lzm/gw0v9foop1we9gdm?singleDoc# 《day 05-21 Vuex》 Gitee地址:https://gitee.com/vrfe/vue-28.git

javascript
// 1. 导入Vue / Vuex
import Vue from 'vue'
import Vuex from 'vuex'

// 2. Vue.use注册vuex
Vue.use(Vuex)

// 3. 创建一个仓库store
const store = new Vuex.Store()

// 4. 导出store,挂载到vue根实例上
export default store

main.js

javascript
import Vue from 'vue'
import App from './App.vue'
// 1. 导入
import store from '@/store' // 可以省略index.js

Vue.config.productionTip = false

new Vue({
    render: (h) => h(App),
    store // 所有组件都能访问到store这个仓库
}).$mount('#app')

核心-state

通过$store访问
javascript
// 1. 导入Vue / Vuex
import Vue from 'vue'
import Vuex from 'vuex'

// 2. Vue.use注册vuex
Vue.use(Vuex)

// 3. 创建一个仓库store
const store = new Vuex.Store({
    // state状态 => 数据, 所有组件都可以访问的公共数据
    state: {
        title: 'vuex so easy',
        count: 100
    }
})

// 4. 导出store,挂载到vue根实例上
export default store

app.vue

javascript
<template>
    <div id="app">
        <h1>根组件</h1>
          // template中访问
        <h2>state - {{ $store.state.title }}</h2>
        <h2>state - {{ $store.state.count }}</h2>
        <input type="text" />
        <Son1></Son1>
        <hr />
        <Son2></Son2>
    </div>
</template>

<script>
import Son1 from '@/components/Son1.vue'
import Son2 from '@/components/Son2.vue'

export default {
    name: 'app',
    data: function () {
          return {}
    },
    created() {
        console.log(this.$store.state.title)
    },
    components: {
        Son1,
        Son2
    }
}
</script>

son1 / son2

javascript
从vuex中获取的值:<label>{{ $store.state.count }}</label>
mapState-辅助函数
javascript
<template>
    <div id="app">
        <h1>根组件</h1>
        <h2>state - {{ title }}</h2>
        <h2>state - {{ count }}</h2>
        <input type="text" />
        <Son1></Son1>
        <hr />
        <Son2></Son2>
    </div>
</template>

<script>
import Son1 from '@/components/Son1.vue'
import Son2 from '@/components/Son2.vue'
// 把store中的数据,自动映射到computed中
import { mapState } from 'vuex'

export default {
    name: 'app',
    data: function () {
        return {}
    },
    // computed: mapState(['count', 'title']),
    computed: {
        // count() {
        //     return this.$store.state.count
        // }
        // ...扩展运算符,展开对象
        ...mapState(['count', 'title'])
        // 还可以加一些自己的计算属性
    },
    created() {
        console.log(mapState(['count', 'title']))
         // mapState(['count', 'title'])方法,返回一个对象
        // console.log(this.$store.state.title)
    },
    components: {
        Son1,
        Son2
    }
}
</script>

核心-mutations

  1. 修改store中的数据,唯一的方式就是提交mutation!!
  2. mutations的方法最多接收两个参数
  3. mutation必须是同步的,方法中不能有异步的操作(不能写定时器,不能发请求) ,保证数据的可跟踪性。
Vuex单向数据流
javascript
<template>
    <div class="box">
        <h2>Son1 子组件</h2>
        从vuex中获取的值: <label>{{ count }}</label>
        

        <button @click="handleAdd">值 + 1</button>
    </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
    name: 'Son1Com',
    computed: {
        // count() {
        //     return this.$store.state.count
        // }
        ...mapState(['count'])
    },
    methods: {
        handleAdd() {
            // mapState, 这种形式修改也不行
            this.count++
            // 这句话,不符合单向数据流的规范
            // 子组件不应该操作父组件中的数据,同理,子组件中也不应该操作store中的数据
            // this.$store.state.count++
        }
    }
}
</script>

store/index.js

javascript
const store = new Vuex.Store({
    // 严格模式,如果操作了state数据,报错Error
    strict: true,
    // state状态 => 数据, 所有组件都可以访问的公共数据
    state: {
        title: 'vuex so easy',
        count: 100
    }
})
mutations基本
  1. store/index.jx 中 定义mutation方法
javascript
const store = new Vuex.Store({
    // 严格模式,如果操作了state数据,报错Error
    strict: true,
    //1. state状态 => 数据, 所有组件都可以访问的公共数据
    state: {
        title: 'vuex so easy',
        count: 100
    },
    // 2. mutations : 存放操作数据的方法
    // => 更改Vuex store状态的唯一方法是 提交mutation!!!
    mutations: {
        // 所有的mutation函数,第一个参数都是当前store的state状态(数据)
        addCount(state) {
            state.count += 1
        }
    }
})
  1. 组件中提交mutation
javascript
handleAdd() {
    // 要提交一个mutation方法
    this.$store.commit('addCount')
}

练习

javascript
<button @click="handleAdd">值 + 1</button>
<button @click="handleAddFive">值 + 5</button>
<button @click="changeTitle">改标题</button>
mutation-传参
javascript
handleAdd(n) {
    // 要提交一个mutation方法  => 参数如果要传,这里只能写一个  => str, num, [] {}等
    // this.$store.commit('addCount', n)
    // 多个参数,可以封装成对象传给store的mutation
    this.$store.commit('addCount', {
        count: n,
        msg: 'hello'
    })
},
changeTitle() {
    this.$store.commit('changeTitle', 'vuex-mutation')
}

store/index.js => mutations

javascript
mutations: {
    // 所有的mutation函数, 只有两个参数,多了接收不了
    // 1. 第一个参数都是当前store的state状态(数据)
    // 2. 第二个参数,payload 载荷, payload可以是任意类型
    // addCount(state, n) {
    //     state.count += n
    // },
    addCount(state, payload) {
        console.log(payload)
        state.count += payload.count
    },
    changeTitle(state, newTitle) {
        state.title = newTitle
    }
}
mapMutations-辅助函数
javascript
<template>
    <div class="box">
        <h2>Son2 子组件</h2>
        从vuex中获取的值:<label>{{ $store.state.count }}</label>
        

        <button @click="subCount(1)">值 - 1</button>
        <button @click="subCount(5)">值 - 5</button>
        <button @click="subCount(10)">值 - 10</button>
    </div>
</template>

<script>
import { mapMutations } from 'vuex'
export default {
    name: 'Son2Com',
    created() {
        // {subCount: ƒ, changeTitle: ƒ} => 调用mapMutations,返回一个对象
        console.log(mapMutations(['subCount', 'changeTitle']))
    },
    methods: {
        ...mapMutations(['subCount', 'changeTitle']),
        // subCount(n){
        //     this.$store.commit('subCount', n)
        // },
        // changeTitle(title){
        //     this.$store.commit('changeTitle', title)
        // },
        // 可以直接在模板上调用方法
        handleSub(n) {
            // this.$store.commit('subCount', n)
            this.subCount(n)
        }
    }
}
</script>

核心-actions

基本使用dispatch

mutations必须是同步的,便于检测数据变化,保证数据的可追踪性,方便vue-devtool记录调试。 异步操作,放到actions中处理!actions处理异步逻辑,处理完了,提交mutation,mutation修改数据。

javascript
// 3. actions 如果是异步操作,放到actions中处理
//  1. action可以包含任意异步操作
//  2. action中不能直接修改state数据,还是得提交mutation commit
actions: {
    // 1. context 上下文对象,这里指的是store, 一般可以简写 ctx
    // 2. payload 传来的参数
    awaitToAdd(ctx, payload) {
        setTimeout(() => {
            ctx.commit('changeCount', payload)
        }, 1000)
    }
}

son1.vue

vue
<template>
    <div class="box">
        ... 
        <!--  异步操作 -->
        <button @click="handleWait">一秒后修改为666</button>
				<!-- end -->
        <button @click="changeTitle">改标题</button>
    </div>
</template>

<script>
import { mapState, mapMutations } from 'vuex'
export default {
    name: 'Son1Com',
    computed: {
        ...mapState(['count'])
    },
    methods: {
        handleWait() {
            // action中的方法,需要用dispatch调用
            this.$store.dispatch('awaitToAdd', 666)
        },
    }
}
</script>
mapActions
vue
<template>
    <div class="box">
        <h2>Son2 子组件</h2>
        从vuex中获取的值:<label>{{ $store.state.count }}</label>
        

        <button @click="subCount(1)">值 - 1</button>
        <button @click="subCount(5)">值 - 5</button>
        <button @click="subCount(10)">值 - 10</button>
        <button @click="awaitToAdd(888)">一秒后修改为888</button>
        <button @click="changeTitle('前端666')">改标题</button>
    </div>
</template>

<script>
import { mapMutations, mapActions } from 'vuex'
export default {
    name: 'Son2Com',
    created() {
        console.log(mapMutations(['subCount', 'changeTitle']))
    },
    methods: {
        ...mapMutations(['subCount', 'changeTitle']),
        ...mapActions(['awaitToAdd']),
        // subCount(n){
        //     this.$store.commit('subCount', n)
        // },
        // changeTitle(title){
        //     this.$store.commit('changeTitle', title)
        // },
        // 可以直接在模板上调用方法
        handleSub(n) {
            // this.$store.commit('subCount', n)
            this.subCount(n)
        },
        // 也可以直接在模板上使用
        handleWait() {
            this.awaitToAdd(888)
        }
    }
}
</script>

核心-getters

$store+mapGetters

相当于是store的computed

javascript
<template>
    <div class="box">
        <h2>Son2 子组件</h2>
        从vuex中获取的值:<label>{{ $store.state.count }}</label>
        

        <button @click="subCount(1)">值 - 1</button>
        <button @click="subCount(5)">值 - 5</button>
        <button @click="subCount(10)">值 - 10</button>
        <button @click="awaitToAdd(888)">一秒后修改为888</button>
        <button @click="changeTitle('前端666')">改标题</button>
        <hr />
          
        <div>{{ $store.getters.filterList }}</div>
        <div>{{ filterList }}</div>
    </div>
</template>

<script>
import { mapMutations, mapActions, mapGetters } from 'vuex'
export default {
    name: 'Son2Com',
    created() {
        console.log(mapMutations(['subCount', 'changeTitle']))
    },
    computed: {
        ...mapGetters(['filterList'])
    },
}
</script>
javascript
// getters类似于计算属性,
// 1. getters函数第一个参数state
// 2. 必须要有返回值,return
getters: {
    filterList(state) {
        return state.list.filter((el) => el > 5)
    },
    // 箭头函数的形式
    filterList: state => state.list.filter((el) => el > 5)
},

回顾

mutation同步操作

为什么mutation是同步的?Vuex官网上有一个解释,大概是说保证数据的可跟踪性,可以vue-devtool监听变化。

image.png

actions异步操作

image.png

module模块

image.png

需要新建modules文件夹,然后导入到index.js中

javascript
export default {
    // user用户模块
    state: () => ({
        username: '练练',
        age: 18,
        hobby: '老二次元了'
    }),
    getters: {},
    mutations: {},
    actions: {}
}
javascript
export default {
    // settings用户模块
    state: () => ({
        theme: 'black',
        desc: '练练的blog'
    }),
    getters: {},
    mutations: {},
    actions: {}
}

store/index.js

javascript
import user from './modules/user'
import settings from './modules/settings'

const store = new Vuex.Store({
    // xxx
    // 5. modules 模块
    modules: {
        user,
        settings
    }
})
// 导入模块之后,查看vue-devtool  Vuex 中 state是否有模块中的数据
module-state

image.pngimage.png

javascript
export default {
    // user用户模块
    namespaced: true, // 需要开启命名空间
    state: () => ({
        username: '练练',
        age: 18,
        hobby: '老二次元了'
    }),
    getters: {},
    mutations: {},
    actions: {}
}

son1.vue $store.state.模块名.xxx

html
<!-- 我们让Son1 通过store方式访问state -->


<hr />
<h3>通过store方式 访问模块中的state</h3>
<div>{{ $store.state.user.username }}</div>
<div>{{ $store.state.user.age }}</div>
<div>{{ $store.state.user.hobby }}</div>

Son2.vue

  1. ...mapState(['xxx'])
  2. ...mapState('模块名', ['username', 'age', 'hobby'])
vue


<hr />
<h3>通过mapState的方式 访问模块中的state</h3>
<div>{{ user.username }}</div>
<div>{{ user.age }}</div>
<div>{{ user.hobby }}</div>
<hr />
<div>{{ username }}</div>
<div>{{ hobby }}</div>
  
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
computed: {
    // 1. 默认根级别的映射  ...mapState(['xxx'])
    ...mapState(['user']),
    // 2. 直接映射到子模块  这种形式 需要开启命名空间 namespaced:true , 建议开启!
    ...mapState('user', ['username', 'age', 'hobby'])
},
module-getters

image.pngimage.png

user.js

javascript
export default {
    // user用户模块
    namespaced: true, // 需要开启命名空间
    state: () => ({
        username: 'lianlian',
        age: 18,
        hobby: '老二次元了'
    }),
    getters: {
        upperCaseName(state) {
            // state指的是当前模块中的state
            return state.username.toUpperCase()
        }
    },
    mutations: {},
    actions: {}
}

Son1.vue

html


<hr />
<h3>通过store方式 访问模块中的getters</h3>
<!-- Error 不能这么访问 -->
<!-- <div>{{ $store.getters.user.upperCaseName }}</div> -->
<div>{{ $store.getters['user/upperCaseName'] }}</div>
javascript
import { mapGetters } from 'vuex'

computed: {
    // 1. 默认根级别的映射  ...mapState(['xxx'])
    ...mapState(['user']),
    // 2. 子模块的映射  ...mapState('模块名', ['xxx'])
    ...mapState('user', ['username', 'age', 'hobby']),

    // getters
    // 1. 默认根级别的getters,找的是index.js中getters
    ...mapGetters(['filterList']),
    // 2. 子模块的映射,mapGetters('模块名', ['xxx'])
    ...mapGetters('user', ['upperCaseName'])
},
module-mutations

image.png

javascript
export default {
    // user用户模块
    namespaced: true, // 需要开启命名空间
    state: () => ({
        username: 'lianlian',
        age: 18,
        hobby: '老二次元了'
    }),
    getters: {
        upperCaseName(state) {
            // state指的是当前模块中的state
            return state.username.toUpperCase()
        }
    },
    mutations: {
        // updateXXX()
        updateUser(state, payload) {
            state.username = payload
        }
    },
    actions: {}
}

Son1.vue

javascript
<!-- mutation 修改用户名 -->
<button @click="$store.commit('user/updateUser', '果果')">
    修改用户名
</button>

Son2.vue

javascript
<!-- mutation 修改用户名 -->
<button @click="updateUser('周杰伦')">修改用户名</button>

import { mapMutations } from 'vuex'
methods: {
  ...mapMutations('user', ['updateUser']),
}
module-actions

image.png

javascript
export default {
    // settings用户模块
    namespaced: true, // 需要开启命名空间
    state: () => ({
        theme: 'black',
        desc: '练练的blog'
    }),
    getters: {},
    mutations: {
        updateTheme(state, payload) {
            state.theme = payload
        }
    },
    actions: {
        // ctx ==> 模块中,代表的是当前模块的store
        updateTheme(ctx, payload) {
            console.log(ctx)
            setTimeout(() => {
                // 要提交mutation
                ctx.commit('updateTheme', payload)
            }, 1000)
        }
        // 可以做解构
        // updateTheme({commit}, payload) {
        //     setTimeout(() => {
        //         // 要提交mutation
        //         commit('updateTheme', payload)
        //     }, 1000)
        // }
    }
}

Son1.vue

vue
<!-- actions 一秒之后,修改settings,theme主题 -->
<div>{{ theme }}</div>
<button @click="$store.dispatch('settings/updateTheme', 'skyblue')">
    一秒后修改Theme
</button>

Son2.vue

vue
<!-- actions 一秒之后,修改settings,theme主题 -->
<button @click="updateTheme('orange')">一秒后修改Theme</button>

import { mapActions } from 'vuex'
methods: {
  ...mapActions('settings', ['updateTheme']),
}