浅学前端:Vue篇(四)

2023-11-14 11:13:39 浏览数 (1)

3) Vuex

1. 入门

学习前先看这个需求,我先在有两个组件,一个是外面的主页,一个是里面的p1,p1里有个文本框,需求是在文本框里修改用户姓名,修改完后希望修改后的姓名显示到主页那里,欢迎xxx:

这个需求如何实现呢,用以前的办法就不好实现了,以前的办法是你这个文本框是属于p1组件的,只能和p1组件里的data()返回的数据对象进行绑定,没办法直接被主页这个组件访问到。

根据这个需求,我们就需要学习一个新的技术了——vuex。

这个问题本质上是一个数据共享的问题,我们可以给这两个组件找一个可以数据共享的地方,一个组件往里存数据,另一个从里面取数据。

有人可能说了,我们可以用上面刚刚学到的localStoragesessionStorage,p1组件点击确定之后,可以将用户姓名存到比如说sessionStorage里,再让主页从里面取出修改后的用户名不就可以啦?

vuex 可以在多个组件之间共享数据,并且共享的数据是【响应式】的,即数据的变更能及时渲染到模板。

  • 与之对比 localStorage 与 sessionStorage 也能共享数据,但缺点是数据并非【响应式】

与vuex相关的代码在src/store/index.js里:

代码语言:javascript复制
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

首先需要定义 state 与 mutations 他们一个用来读取共享数据,一个用来修改共享数据

src/store/index.js

代码语言:javascript复制
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

// 这里我们就可与来定义共享数据

/*
  读取数据,走 state, getters
  修改数据,走 mutations, actions
*/
// 从state读取共享数据,在mutations修改共享数据
export default new Vuex.Store({
  // 共享数据的位置
  state: {
    name: '',
  },
  getters: {
  },
  // 如果需要修改共享位置,不可以直接修改state里的内容,需要在mutations里添加方法
  mutations: {
    updateName(state, newName) {
      state.name = newName;
    },
  },
  actions: {
  },
  modules: {
  }
})

修改共享数据

代码语言:javascript复制
<template>
    <div class="p">
        p1
        <el-input placeholder="请修改用户名" v-model="name"></el-input>
        <el-button type="primary" v-on:click="updateName()">修改</el-button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            name: ''
        }
    },
    methods: {
        updateName() {
            // 修改共享数据
            // mutations 方法不能直接调用,只能通过 `store.commit(mutation方法名, 参数)` 来间接调用
            this.$store.commit('updateName', this.name);
        },
    }
}
</script>
<style scoped>
...
</style>
  • mutations 方法不能直接调用,只能通过 store.commit(mutation方法名, 参数) 来间接调用

读取共享数据

代码语言:javascript复制
<template>
    <div class="container">
        主页
        <el-container>
            <el-header>
                <div class="t">欢迎您:{{this.$store.state.name}}</div>
            </el-header>
            <el-container>
                <el-aside width="200px"></el-aside>
                <el-main>
                    <!-- 占位 -->
                    <router-view></router-view>
                </el-main>
            </el-container>
        </el-container>
    </div>
</template>
<script>
export default {
    data() {
        return {}
    },
}
</script>
<style scoped>
    ...
</style>
2. mapState

作用:根据state里的属性,自动生成计算属性 name(){ return this.$store.state.name }

每次去写 $store.state.name 这样的代码显得非常繁琐,可以使用计算属性来简化代码:

代码语言:javascript复制
<template>
。。。
                <div class="t">欢迎您:{{name}}</div>
。。。
</template>
<script>
export default {
    data() {
        return {}
    },
    computed: {
        name() {
            return this.$store.state.name
        },
    }
}
</script>
<style scoped>
...
</style>

如果我们还有一个age数据需要共享,那么还需要在computed里写一个age()...

可以用 vuex 帮我们生成计算属性:

src/views/example16/ContainerView.vue

代码语言:javascript复制
...
<script>
import { mapState } from "vuex"
console.log(mapState(['name', 'age']))
....

可以看到mapState为我们已经生成了对应计算属性了,那么就不需要在computed里写计算属性了:

代码语言:javascript复制
<template>
    <div class="container">
        主页
。。。
                <div class="t">欢迎您:{{name}}{{age}}</div>
。。。
</template>
<script>
import { mapState } from "vuex"
export default {
    data() {
        return {}
    },
    // computed: {
    //     name() {
    //         return this.$store.state.name;
    //     },
    //     age() {
    //         return this.$store.state.age;
    //     }
    // }
    
    computed: mapState(['name', 'age']),
    // 等价的写法:... 展开运算符,相当于展开后又塞入了一个空对象里
    // computed: {
    //     ...mapState(['name', 'age'])
    // }
}
  • mapState方法 返回的是一个对象,对象内包含了 name() 和 age() 的这两个方法作为计算属性
3. mapMutations

作用: 根据mutatinos里的属性方法,生成类似于这种形式的代码: updateName() { this.$store.commit('updateName', this.name); },

刚才我们使用了vuex给我们提供的mapState函数帮我们生成了计算属性,算是对我们读取共享数据的一种优化。

那我们来看修改共享数据,原本是我们自己写了个updataName方法来修改共享数据,那么可以可以也有优化呢?

答案是可以的,也可以让vuex帮我们生成好:

代码语言:javascript复制
<script>
import { mapMutations } from 'vuex'
// mapMutations()
console.log(mapMutations(['updateName']))

代码语言:javascript复制
<template>
    <div class="p">
        p1
        <el-input placeholder="请修改用户名" v-model="name"></el-input>
        <!-- 注意,这里的事件要绑定mapMutations生成的方法名,且需要传入一个参数(作为修改的值) -->
        <el-button type="primary" v-on:click="updateName(name)">修改</el-button>
    </div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
    data() {
        return {
            name: ''
        }
    },
    mounted: function () {
        this.name = this.$store.state.name;
    },
    methods: {
        // updateName() {
        //     // 修改共享数据
        //     // mutations 方法不能直接调用,只能通过 `store.commit(mutation方法名, 参数)` 来间接调用
        //     this.$store.commit('updateName', this.name);
        // },

        ...mapMutations(['updateName'])
    }
}
</script>
<style scoped>
.p {
    background-color: rgb(165, 150, 242);
    width: 80%;
    height: auto;
    margin: 50px;
}
</style>
  • mapMutations返回的对象中包含的方法,就会调用 store.commit()来执行 mutation 方法
  • 注意参数传递略有不同
4. actions

https://www.bilibili.com/video/BV1Tt4y1772f/?p=94&spm_id_from=pageDriver&vd_source=75d41aa86db8ae1e26da90b1ac6488db&t=38.2

mutations 方法内不能包括修改不能立刻生效(

例如:

代码语言:javascript复制
 mutations:{
     async updateServerUser(state){
         const resp = await axios.get('/api/user');
         const {name,age} = resp.data.data;
         state.name= name;
         state.age = age;
     }
 }

错误的用法,如果在mutations方法包含了异步操作,会造成开发工具的不准确

)的代码,否则会造成 Vuex 调试工具工作不准确,必须把这些代码写在 actions 方法中

代码语言:javascript复制
 import Vue from 'vue'
 import Vuex from 'vuex'
 ​
 Vue.use(Vuex)
 ​
 /*
   读取数据,走 state, getters
   修改数据,走 mutations, actions
 */
 import axios from '@/util/myaxios'
 export default new Vuex.Store({
     state: {
         name: '',
         age: 18
     },
     getters: {
     },
     mutations: {
         updateName(state, name) {
             state.name = name;
         },
         // 错误的用法,如果在mutations方法中包含了异步操作,会造成开发工具不准确
         /* async updateServerName(state) {
       const resp = await axios.get('/api/user');
       const {name, age} = resp.data.data;
       state.name = name;
       state.age = age;
     } */
         updateServerName(state, user) {
             const { name, age } = user;
             state.name = name;
             state.age = age;
         }
     },
     // 拿到响应之后,再间接
     actions: {
         async updateServerName(context) {
             const resp = await axios.get('/api/user');
             // 通过commit来定执行那个mutaition
             // 参数1:要执行的mutation的名字
             // 参数2:数据
             context.commit('updateServerName', resp.data.data)
         }
     },
     modules: {
     }
 })
  • 首先应当调用 actions 的 updateServerName 获取数据
  • 然后再由它间接调用 mutations 的 updateServerName 更新共享数据

页面使用 actions 的方法可以这么写

代码语言:javascript复制
<template>
    <div class="p">
        <el-button type="primary" size="mini"
            @click="updateServerName()">从服务器获取数据,存入store</el-button>
    </div>
</template>
<script>
import { mapActions } from 'vuex'
const options = {
    methods: {
        ...mapActions(['updateServerName'])
    }
}
export default options;
</script>
  • mapActions 会生成调用 actions 中方法的代码
  • 调用 actions 的代码内部等价于,它返回的是 Promise 对象,可以用同步或异步方式接收结果 // 不使用mapActions的写法: export default { methods:{ updateServerName(){ // this.$store.dispatch("action名称"[,参数]); this.$store.dispatch("updateServerName"); }, }, }

总结: 立刻生效的数据才可以使用mutation修改,不是立刻能获取到的数据,就需要使用actions中转一下,间接调用mutations。

5. 小结

vuex说白了就是在组件之间共享数据的,他共享的数据有一个特点,是响应式的;

  • 读取数据:
    • 访问store的state属性,可以使用mapState帮我们生成一些计算属性 import {mapState} from 'vuex'
    • 访问store的getter属性,和上面这个差不多。
  • 修改数据:
    • 使用mutations: 可以使用mapMutations替我们生成一些方法,通过调用这些方法,去修改state里的数据 import {mapMutations} from 'vuex'
    • 使用actions: 可以使用mapActions替我们生成一些方法,通过调用这些方法,间接的调用mutations里的方法,从而修改state里的数据。 import {mapActions} from 'vuex'
    • 具体使用那种,就看你这个数据是否包含了异步操作,没有包括就使用mutation,包括就使用actions。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞