Vue08:vuex

    技术2025-02-05  5

    文章目录

    1、为什么要用 vuex ?2、通信解决方案 - 面试中可能会问到2-1 props/$emit(父子通信)2-2 $refs/ref(父子通信)2-3 children/parent(父子通信)2-4 attrs/listeners(父子通信)2-5 provide/inject(父子通信、跨级通信)方式一:通过`$parent`方式二:provide/inject 跨级通信命名冲突的问题解决 2-6- localStorage/sessionStorage等基于浏览器客户端的存储(父子通信、跨级通信、兄弟通信、路由视图级别通信)2-7 eventBus(父子通信、跨级通信、兄弟通信) 3、vuex 是什么?4、vuex 的工作流5、安装 vuex6、引入 vuex7、从 Store 开始7-1、创建一个 Store 8、核心概念8-1、state8-1-1、state 的创建8-1-2、在组件中使用 store8-1-3、store 配置8-1-4、使用辅助函数 `mapState`8-1-5、使用扩展运算符组合mapState当组件中已有与 `store` 同名的数据名称 9、getters9-1、getters 函数9-2、通过属性访问9-3、通过方法访问9-4、使用辅助函数 `mapGetters` 10、mutations10-1、提交10-2、mutation 函数10-3、使用辅助函数 `mapMutations`10-4、mutation 函数必须是同步的,不支持异步 11、actions11-1、提交11-2、action 函数11-3、使用辅助函数 `mapActions` 再看vuex 的工作流12 Module

    1、为什么要用 vuex ?

    组件通信的类型

    父子通信跨级通信兄弟通信路由视图级别通信

    2、通信解决方案 - 面试中可能会问到

    通信核心:找到数据交互之间的桥梁。如果两者之间能直接沟通,那就直接沟通,如果不能,则找一个两者都能说得上话的人。

    props/$emit(父子通信)$refs/ref(父子通信)children/parent(父子通信)attrs/listeners(父子通信)provide/inject(父子通信、跨级通信)eventBus(父子通信、跨级通信、兄弟通信)vuex(父子通信、跨级通信、兄弟通信、路由视图级别通信)localStorage/sessionStorage等基于浏览器客户端的存储(父子通信、跨级通信、兄弟通信、路由视图级别通信)

    2-1 props/$emit(父子通信)

    父级

    通过给子级标签添加动态属性传值子集通过props参数接收父级数据 <template> <div> <h2>MyParent1</h2> <MyChild1 :data="name" @sendData="getChildData"></MyChild1> <p>我接收到了子级的数据-{{childData}}</p> </div> </template> <script> import MyChild1 from "@/components/MyChild1"; export default { name: "MyParent1", data() { return { name: "MyParent1-Name", childData: "" }; }, components: { MyChild1 }, methods: { getChildData(data) { this.childData = data } } }; </script>

    子集

    子集通过$emit方法提交给父级对应事件名传值父级对应事件名触发自身方法 <template> <div> <h2>MyChild1</h2> <p>我接收来自父级传入的数据:{{ data }}</p> <button @click="sendDataToParent">把数据传递给父级</button> </div> </template> <script> export default { name: "MyChild1", props: ["data"], data() { return { name: "MyChid1-Name" }; }, methods: { sendDataToParent() { this.$emit("sendData", this.name); } } }; </script>

    2-2 $refs/ref(父子通信)

    与props/emit 不同的是ref不需要子集事先向父级提交而是父级直接拿到子组件的实例中的数据

    父组件

    <template> <div> <h2>MyParent1</h2> <MyChild1 :data="name" ref="myChild"></MyChild1> <button @click="getChildData">通过ref获取子集数据</button> <p>我接收到了子级的数据-{{childData}}</p> </div> </template> <script> import MyChild1 from "@/components/MyChild1"; export default { name: "MyParent1", data() { return { name: "MyParent1-Name", childData: "" }; }, components: { MyChild1 }, methods: { getChildData() { console.log(this.$refs.myChild) // 拿到子级的组件实例对象 this.childData = this.$refs.myChild.name; } } }; </script>

    子组件

    <template> <div> <h2>MyChild1</h2> <p>我接收来自父级传入的数据:{{ data }}</p> </div> </template> <script> export default { name: "MyChild1", props: ["data"], data() { return { name: "MyChid1-Name" }; }, }; </script>

    2-3 children/parent(父子通信)

    console.log(this.$children) // 所有子组件实例的数组

    获取子组件数据就更方便了

    methods: { getChildData() { console.log(this.$children) // 所有子组件的数组 this.childData = this.$children[0].name; } }

    同理,子组件可通过this.$parent获取父组件数据

    methods: { getParentData() { console.log(this.$parent); this.parentData = this.$parent.name; } },

    2-4 attrs/listeners(父子通信)

    attrs 获取父级在子组件标签上写的非props属性(非动态属性)

    methods: { getAttrs() { console.log(this.$attrs); // 获取非props属性 } },

    但是不能获取事件,要获取事件可通过$listeners获取

    getListeners() { console.log(this.$listeners); // 获取事件 },

    2-5 provide/inject(父子通信、跨级通信)

    组件与组件之间是隔离的,没有所谓的作用域链 所以如何实现爷爷组件和孙子组件之间的数据传递呢?

    方式一:通过$parent

    拿爷爷传给父组件的数据

    <p>爷爷的名字-{{$parent.data}}</p>

    或者直接链式获取

    <p>爷爷的名字-{{$parent.$parent.name}}</p>

    但是这种方法很麻烦,易出错

    方式二:provide/inject 跨级通信

    提供数据/注入数据 爷爷组件provide选项

    provide() { return { pName: this.name } },

    孙子组件inject选项

    export default { name: "MyChidl1Child2", inject: ["pName"] };

    命名冲突的问题解决

    如果自己本身有同样的命名,就会出现命名冲突的问题

    export default { name: "MyChidl1Child2", inject: { parentPname: { from: "pName" } } };

    2-6- localStorage/sessionStorage等基于浏览器客户端的存储(父子通信、跨级通信、兄弟通信、路由视图级别通信)

    如果采用以上的方式进行兄弟组件数据传递的话,会一层层传递,非常繁琐,所以可以利用第三方来帮助数据传递:例如localstorage

    存储

    methods: { saveToLS(){ localStorage.setItem("pName", this.pName) } },

    获取

    methods: { getLS() { this.pName = localStorage.getItem("pName") } },

    存在问题

    只能直接传字符串,不能是对象函数很难传递

    2-7 eventBus(父子通信、跨级通信、兄弟通信)

    就是一个全局对象,例如挂载到windows下

    通过Vue实例化一个eventBus对象

    import Vue from "vue"; let eventBus = new Vue({}) export default eventBus;

    组件引入eventBus并注册一个事件

    import eventBus from "@/eventBus"; export default { name: "MyChild2", data() { return { pName: "" }; }, created() { eventBus.$on("bus", data => { this.pName = data; }); } };

    另一个组件通过eventBus触发这个事件,实现数据存储到Vue全局,数据传递

    import eventBus from "@/eventBus" export default { name: "MyChidl1Child2", inject: { parentPname: { from: "pName" } }, data() { return { pName: "我是兄弟组件的pName" } }, methods: { saveDataToEventBus(){ eventBus.$emit("bus", this.pName) } }, };

    3、vuex 是什么?

    Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式,类似 redux vuex(父子通信、跨级通信、兄弟通信、路由视图级别通信)

    这种状态管理模式包含:

    State : 状态数据源View : 使用状态数据源的视图Actions : 修改更新数据源的操作

    这种模式遵循的是 单向数据流 模式

    4、vuex 的工作流

    State : 存储应用状态数据(React 中的 State)Vue Component : 消费 StateActions : 提交修改 State 的动作(包括异步行为)(React 中的 action)Mutations : 唯一更改 State 的位置(React 中的 Reducer)

    5、安装 vuex

    npm i vuex // or yarn add vuex

    6、引入 vuex

    通过 <script> 引入

    <script src="vue.js"></script> <script src="vuex.js"></script>

    通过 <script> 方式引入,vuex 会自动安装(也就是主动调用 Vue.use(Vuex))

    通过 import 引入

    import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)

    通过 import 方式引入,需要手动安装(手动调用 Vue.use(Vuex))

    7、从 Store 开始

    **Store ** 就是仓库,我们前面提到的 state 就存储在 store 中,同时提交动作、修改状态的方法也都由 store 提供和管理

    7-1、创建一个 Store

    import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) let store = new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {} })

    必须在 Vue.use(Vuex) 之后创建 store

    8、核心概念

    stategettersmutationsactions

    8-1、state

    8-1-1、state 的创建

    存储应用状态数据的对象,state 的值可以是一个对象,也可以是一个返回对象的函数,类似 vue 中组件的 data ,使用函数的方式返回对象可以避免对象引用导致的副作用问题

    // let state = { // a: 1 // } let state = _=>({a:1}) const store = new Vuex.Store({ state }) const store2 = new Vuex.Store({ state }) console.log(store.state == store2.state) store.state.a = 100; console.log(store.state.a, store2.state.a); 通过 store.state 访问状态数据state 数据与组件 data 一样是被追踪的

    8-1-2、在组件中使用 store

    store文件设置state

    import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); // 一个用于管理维护以及提供数据修改接口的对象 // 数据最好有一些规范,保证数据操作的安全性 const store = new Vuex.Store({ // 存储基础数据的位置,类似组件中的data state: { title: "gaibain", } }); console.log(store) export default store;

    将store引入Vue实例

    import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' Vue.config.productionTip = false new Vue({ router, store, render: function (h) { return h(App) } }).$mount('#app')

    使用state中的数据

    <template> <div class="home"> <h2>Home</h2> 仓库中数据title: {{$store.state.title}} </div> </template> <script> export default { name: "Home" }; </script>

    简化数据

    <template> <div class="home"> <h2>Home</h2> title </div> </template> <script> export default { name: "Home", data() { return { title: this.$store.state.title } }, }; </script>

    简化后出现的 问题: state 的更新并不会更新视图,因为组件中的data和store中的data并无更新关联

    解决

    使用 computed包裹store数据

    <template> <div class="home"> <h2>{{title}}</h2> <div>{{content}}</div> </div> </template> <script> import store from '@/stores' export default { name: 'home', computed: { title() { return store.state.title }, content() { return store.state.content } } } </script>

    8-1-3、store 配置

    如果每个组件在使用 store 的时候都 import 会比较繁琐,这个时候,我们通过 vuex 提供的 store 选项把 store 对象注入到 vue 的原型上

    import Vue from 'vue' import App from './App.vue' import router from './router' import store from '@/stores' Vue.config.productionTip = false new Vue({ router, store, render: h => h(App) }).$mount('#app')

    配置注入后,我们就可以在组件实例中使用 this.$store 来访问 store 对象了

    <template> <div class="home"> <h2>{{title}}</h2> <div>{{content}}</div> </div> </template> <script> // import store from '@/stores' // 可以去掉了 export default { name: 'home', computed: { title() { return this.$store.state.title }, content() { return this.$store.state.content } } } </script>

    8-1-4、使用辅助函数 mapState

    当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键通常我们把 store 的 state 通过 mapState 函数映射到组件的 computed 上 <template> <div class="home"> <h2>Home</h2> {{title}} </div> </template> <script> import { mapState } from "vuex"; export default { name: "Home", computed: mapState(["title", "user"]) }; // function mapState(keys) { // return keys // .map(key => { // return { // [key]() { // return this.$store.state[key]; // } // }; // }) // .reduce((prev, curr) => { // prev[curr.key] = curr.value; // return prev; // }, {}); // } </script>

    通过对象方式进行映射

    8-1-5、使用扩展运算符组合mapState

    通过对象扩展运算符,可以把 mapState 返回的 state 属性与组件已有计算属性进行融合

    <script> import {mapState} from 'vuex' export default { computed: { data() { return "test"}, ...mapState(["title", "user"]) } } </script>

    当组件中已有与 store 同名的数据名称

    使用对象的格式给mapState传参

    <template> <div class="home"> <h2>Home</h2> {{stateTitle}} - {{title}} </div> </template> <script> import { mapState } from "vuex"; export default { name: "Home", computed: { title() { return "myTitle"; }, ...mapState({ stateTitle: "title", stateUser: "user" }) } }; </script>

    或者通过函数返回数据展示的格式

    <template> <div class="home"> <h2>Home</h2> {{user}} </div> </template> <script> import { mapState } from "vuex"; export default { name: "Home", computed: { title() { return "myTitle"; }, ...mapState({ stateTitle: "title", user(state) { return state.user.name + "/" + state.user.age } }) } }; </script>

    9、getters

    有时候我们需要从 store 中的 state 中派生出一些状态,类似组件的 data 与 computed,store 也提供了一个 getters 对象来处理派生数据

    9-1、getters 函数

    与组件属性一样,我们是通过定义一个函数的形式来返回派生数据的,getters 函数接受两个参数

    第一个参数:state 对象第二个参数:getters 对象

    9-2、通过属性访问

    同样的,与组件计算属性一样,默认是通过属性的方式去访问 getters 中的数据的,这种方式与组件的计算属性一样,也是会缓存结果的

    // 类似组件中的computed getters: { test1(state){ return `我是${state.user.name} 通过属性访问` }, } import { mapState } from "vuex"; export default { name: "Home", computed: { title() { return "myTitle"; }, ...mapState({ stateTitle: "title", user(state) { return state.user.name + "/" + state.user.age }, test1() { return this.$store.getters.test1 } }), } };

    9-3、通过方法访问

    我们还可以通过闭包函数的形式返回一个函数,来实现给 getters 函数传参,需要注意的是这种方式不支持结果缓存

    getters: { test1(state){ return `我是${state.user.name} 通过属性访问` }, test2:(state) => (id) =>{ return `我是${state.user.name} 通过方法访问,传入id为:${id}` } } computed: { // ... test2() { return this.$store.getters.test2(1); } }

    9-4、使用辅助函数 mapGetters

    与 mapState 函数类似,通常映射到组件的 computed 上

    10、mutations

    更改 Vuex 的 store 中的状态的唯一方法是提交 mutation(类似 redux 中的 action + reducer)

    Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)

    mutation 中的函数不要直接调用

    10-1、提交

    store.commit(type, payload) // or store.commit({ type: ..., payload: ... })

    type

    要提交的 mutation 回调函数名称,type 为固定的 key

    payload

    载荷:提交的额外数据,任意格式

    methods: { editTitle() { // 直接修改会让数据比较乱 // this.$store.state.title = "修改了title" this.$store.commit("editTitle", "newTitle"); } }

    10-2、mutation 函数

    mutation 中的函数被 commit 执行的时候,接收两个参数

    第一个参数:state 对象第二个参数: commit 提交的 payload

    在 mutation 函数中,我们就可以通过 state 对象进行状态数据的修改

    // 存储修改数据的方法 mutations: { editTitle(state, newTitle) { state.title = newTitle; } }

    10-3、使用辅助函数 mapMutations

    mapMutations 函数的使用与 mapState 和 mapGetters 类似,但是其一般是把组件的 methods 映射为 store 的 mutations 的 commit 调用

    10-4、mutation 函数必须是同步的,不支持异步

    commit 方法没有返回值,直接就是同步执行

    提交了commit以后,组件中可能需要根据提交后的结果去做一些处理,比如显示修改信息(成功了,失败了,这个提示视图有关,那么这个逻辑代码就不方便放在store去做了

    methods: { editTitle() { // 直接修改会让数据比较乱 // this.$store.state.title = "修改了title" this.$store.commit("editTitle", "newTitle"); console.log(this.$store.state.title) } }

    11、actions

    action 中的函数与 mutation 中的函数类似,但是它主要用来进行异步任务的处理,然后通过提交 mutation 来修改 state

    注意:action 中的函数不要直接修改 state,数据的修改还是需要mutation

    11-1、提交

    store.dispatch(type, payload) // or store.dispatch({ type: ..., payload: ... })

    action 任务需要通过 dispatch 方法来提交(派发),与 commit 类似

    dispatch 方法有返回值,且一定返回一个 promise 对象

    11-2、action 函数

    action 中的函数执行的过程中也接受两个参数

    第一个参数:store 对象第二个参数: dispatch 提交的 payload // 存储修改数据的方法 mutations: { editTitle(state, newTitle) { state.title = newTitle; } }, actions: { editTitle(store, payload) { return new Promise((resolve, reject) => { setTimeout(() => { store.commit("editTitle", payload); resolve(); }, 1000) }); } }

    支持异步

    methods: { async editTitle() { await this.$store.dispatch("editTitle", "newTitle"); console.log(this.$store.state.title); },

    11-3、使用辅助函数 mapActions

    与 mapMutations 函数类似,把组件的 methods 映射为 store 的 actions 的 dispatch 调用

    再看vuex 的工作流

    State : 存储应用状态数据(React 中的 State)Vue Component : 消费 StateActions : 提交修改 State 的动作(包括异步行为)(React 中的 action)Mutations : 唯一更改 State 的位置(React 中的 Reducer)

    12 Module

    这个更多的是基于一种代码组织结构上的辅助 数据特别多的时候可以用这个模块化方式管理 modules

    structure

    Processed: 0.009, SQL: 9