VUEX

为什么要用vuex?

组件通信的类型

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


通信解决方案

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

vuex 是什么?

Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式,类似 redux


这种状态管理模式包含:

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

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

vuex 的工作流

  • State : 存储应用状态数据(React 中的 State)
  • Vue Component : 显示 <u>State</u>
  • Actions : 提交修改 <u>State</u> 的动作(包括异步行为)(React 中的 action)
  • Mutations : 唯一更改 <u>State</u> 的位置(React 中的 Reducer)

安装 vuex

npm i vuex
// or
yarn add vuex

引入 vuex

通过 <script> 引入

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

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

通过 import 引入

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

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

从 Store 开始

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

创建一个 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

核心概念

  • state
  • getters
  • mutations
  • actions

state

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 一样是被追踪的

在组件中使用 store

// stores/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'

Vue.use(Vuex)

const store = new Vuex.Store({
    state
})

export default store;
// stores/state.js
export default () => ({
    title: '开课吧',
    content: 'javascript 高级工程师'
})
<template>
  <div class="home">
    <h2>{{title}}</h2>
    <div>{{content}}</div>
  </div>
</template>

<script>
import store from '@/stores'
export default {
  name: 'home',
  data() {
    return {
      title: store.state.title,
      content: store.state.content
    }
  }
}
</script>

问题:

state 的更新并不会更新视图

解决

使用 computed

<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 辅助函数帮助我们生成计算属性,让你少按几次键,通常我们把 storestate 通过 mapState 函数映射到组件的 computed

<template>
  <div class="home">
    <h2>{{title}}</h2>
    <div>{{content}}</div>
  </div>
</template>

<script>
import {mapState} from 'vuex'

export default {
  name: 'home',
  computed: mapState([
    'title','content'
  ])
}
</script>

通过对象方式进行映射

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

<template>
  <div class="home">
    <h1>{{title}}</h1>
    <h2>{{subTitle}}</h2>
    <div>{{content}}</div>
  </div>
</template>

<script>
import {mapState} from 'vuex'

export default {
  name: 'home',
  data() {
    return {title: 'Vuex'}
  },
  computed: mapState({
      subTitle: 'title',
      content: ({content}) => content.length <= 12 ? content : content.substring(0, 12) + '......'
  })
}
</script>

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

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

<script>
import {mapState} from 'vuex'

export default {
  computed: {
      computed() {/.../},
      ...mapState({
          // ...
      })
  }
}
</script>

9、getters

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

9-1、getters 函数

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

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

9通过属性访问

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

通过方法访问

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

使用辅助函数 mapGetters

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

mutations

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation(类似 redux 中的 action + reducer),Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)

mutation 中的函数不要直接调用

提交

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

type

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

payload

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

mutation 函数

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

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

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

使用辅助函数 mapMutations

mapMutations 函数的使用与 mapStatemapGetters 类似,但是其一般是把组件的 methods 映射为 storemutationscommit 调用

mutation 函数必须是同步的

commit 方法没有返回值

actions

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

注意:action 中的函数不要直接修改 state

提交

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

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

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

action 函数

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

  • 第一个参数:store 对象
  • 第二个参数: dispatch 提交的 payload

使用辅助函数 mapActions

mapMutations 函数类似,把组件的 methods 映射为 storeactionsdispatch 调用

Module

这个更多的是基于一种代码组织结构上的辅助

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355