Vuex状态管理

文章内容输出来源:大前端高薪训练营

一、组件内的状态管理流程

每个组件都有自己的状态、视图和行为等组成部分。

new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `<div>{{ count }}</div>`,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})
复制代码

状态管理包含以下几部分:

  • state,驱动应用的数据源
  • view,以声明方式将 state 映射到视图
  • actions,响应在 view 上的用户输入导致的状态变化

二、组件间的通信方式

1.父组件给子组件传值

  • 子组件中通过 props 接收数据
<template>
  <div>
    <h2>{{title}}</h2>
  </div>
</template>

<script>
export default {
  // props 有两种类型:数组、对象,需要约定值的类型用对象
  // props: ['title'],
  props: {
    title: String
  }
}
</script>
复制代码
  • 父组件中给子组件通过相应属性传值
<template>
  <div>
    <child title="My journey with Vue"></child>
  </div>
</template>

<script>
import child from './01-Child'
  export default {
    components: {
      child
    }
  }
</script>
复制代码

2.子组件给父组件传值

  • 在子组件中使用 $emit 发布一个自定义事件:
<template>
  <div>
    <h1 :style="{ fontSize: fontSize + 'em' }">Props Down Child</h1>
    <button @click="handler">文字增大</button>
  </div>
</template>

<script>
export default {
  props: {
    fontSize: Number
  },
  methods: {
    handler () {
      this.$emit('enlargeText', 01)
    }
  }
}
</script>
复制代码
  • 在使用这个组件的时候,使用v-on监听这个自定义事件,并且父组件中注册子组件内部触发的事件
<template>
  <div>
    <h1 :style="{ fontSize: hFontSize + 'em' }">Event Up Parent</h1>
    
    这里的文字不需要变化
    
    <child :fontSize="hFontSize" v-on:enlargeText="enlargeText"></child>
    <child :fontSize="hFontSize" v-on:enlargeText="enlargeText"></child>
    <child :fontSize="hFontSize" v-on:enlargeText="hFontSize + $event"></child>
  </div>
</template>

<script>
import child from './02-child'
export default {
  components: {
    child
  },
  data () {
    return {
      hFontSize: 1
    }
  },
  methods: {
    enlargeText (size) {
      this.hFontSize += size
    }
  }
}
</script>

复制代码

3.不相关组件传值

不相关组件的通信也是使用自定义事件的方式,因为没有父子关系,所以不能再由子组件触发自定义事件传值,而是需要使用eventbus创建一个公共实例,这个实例的作用是作为事件组件或者事件中心。

// eventbus.js
// 不需要传递任何选项,为了用了调用实例的 $emit 和 $on来触发和注册事件
import Vue from 'Vue'
export default new Vue()
复制代码

然后在需要通信的两端:
使用$on订阅

import bus from './eventbus'
// 没有参数 
bus.$on('自定义事件名称', () => { 
  // 执行操作 
})
// 有参数 
bus.$on('自定义事件名称', data => { 
  // 执行操作 
})
复制代码

使用$emit发布

import bus from './eventbus'
// 没有自定义传参
bus.$emit('自定义事件名称');

// 有自定义传参
bus.$emit('自定义事件名称',数据);
复制代码

4.其他常见方式(不推荐使用)

只有当项目非常小或者在开发自定义组件的时候才会用到。

  • $root
  • $parent
  • $children
  • $refs

ref 两个作用:

  1. 在普通HTML标签上使用 ref, 获取到的是DOM
  2. 在组件标签上使用 ref,获取到的是组件实例

注意:ref这种方式不到万不得已不要使用,会导致数据的混乱。

创建 base-input 组件

<template>
  <input ref="input">
</template>
<script>
  export default { 
    methods: { 
      // 用来从父级组件聚焦输入框 
      focus: function () {
        this.$refs.input.focus()
      } 
    } 
  }
</script>
复制代码

在使用子组件的时候,添加 ref 属性:

<base-input ref="usernameInput"></base-input>
复制代码

然后在父组件等渲染完毕后使用 $refs 访问:

mounted () {
  this.$refs.usernameInput.focus() 
}
复制代码

$refs 只会在组件渲染完成后生效,并且他们不是响应式的。应该避免在模板或计算属性中访问$refs

三、简易的状态管理方案

如果多个组件之间要共享状态(数据),使用上述方式虽然可以实现,但是比较麻烦,而且多个组件之间互相传值很难跟踪数据的变化,如果出现问题很难定位。
因此,我们可以把多个组件的共享状态抽取出来,以一个全局单例模式管理,在这种模式下我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。你可能已经想到了Vuex。
这里我们先用哪个简单的方式实现:

  • 首先创建一个共享的仓库 store 对象
export default {
  debug: true,
  state: {
    user: {
      name: 'xiaomao',
      age: 18,
      sex: '男'
    }
  },
  setUserNameAction (name) {
    if (this.debug) {
      console.log('setUserNameAction triggered: ', name)
    }
    this.state.user.name = name
  }
}
复制代码
  • 把共享的仓库 store 对象,存储到需要共享状态的组件的data中
<template>
  <div>
    <h1> componentA </h1>
    user name : {{ sharedState.user.name }}
    <button @click="change"> Change Info </button>
  </div>
</template>

<script>
import store from './store'
export default {
  methods: {
    change () {
      store.setUserNameAction('componentA')
    }
  },
  data () {
    return {
      privateState: {},
      sharedState: store.state
    }
  }
}
</script>
复制代码
<template>
  <div>
    <h1> componentB </h1>
    user name : {{ sharedState.user.name }}
    <button @click="change"> Change Info </button>
  </div>
</template>

<script>
import store from './store'
export default {
  methods: {
    change () {
      store.setUserNameAction('componentB')
    }
  },
  data () {
    return {
      privateState: {},
      sharedState: store.state
    }
  }
}
</script>
复制代码

四、Vuex回顾

1. 什么是Vuex

  • Vuex是专门为Vue.js设计的状态管理库
  • Vuex采用集中式的方式存储需要共享的状态
  • Vuex的作用是进行状态管理,解决复杂组件通信,数据共享
  • Vuex集中到了devtools中,提供了time-travel时光旅行历史回滚等功能

2. 什么情况下使用Vuex

  • 非必要情况下不要使用Vuex
  • 大型的单页面应用程序
    • 多个视图依赖同一个状态
    • 来自不同视图的行为需要变更同一状态

    例如:购物车组件

3. Vuex核心概念

  • Store: 是一个容器,包含着应用中的大部分状态,不能直接改变store中的状态,要通过mutation的方式改变状态。
  • State:是状态,保存在Store中,因为Store是唯一的,所以State也是唯一的,也称为单一状态树。这里的状态是响应式的。
  • Getter:是Vuex中的计算属性,方便从一个属性派生出其他的值。它内部会对计算的属性进行缓存,只有当依赖发生改变的时候,才会重新进行计算。
  • Mutation: 状态的变换必须要通过提交Mutation来完成。
  • Action:和Mutation类似,不同的是Action可以进行异步操作,内部改变状态的时候,都需要提交Mutation。
  • Module:当Store太多臃肿时,可以将Store分成多个模块,每个模块里有State、Mutation、Action、Getter,甚至是子模块。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享