Vuex知识点总结

内容来自尚硅谷教学课件

6.1. vuex 理解

6.1.1. vuex 是什么

  1. github 站点: https://github.com/vuejs/vuex
  2. 在线文档: https://vuex.vuejs.org/zh-cn/
  3. 简单来说: 对vue 应用中多个组件的共享状态进行集中式的管理(读/写)

6.1.2. 状态自管理应用

  1. state: 驱动应用的数据源
  2. view: 以声明方式将state 映射到视图
  3. actions: 响应在view 上的用户输入导致的状态变化(包含n 个更新状态的方法)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

6.1.3. 多组件共享状态的问题

  1. 多个视图依赖于同一状态
  2. 来自不同视图的行为需要变更同一状态
  3. 以前的解决办法
    a. 将数据以及操作数据的行为都定义在父组件
    b. 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)
  4. vuex 就是用来解决这个问题的

6.2. vuex 核心概念和API

6.2.1. state

  1. vuex 管理的状态对象
  2. 它应该是唯一的
const state = {
	xxx: initValue
}

6.2.2. mutations

  1. 包含多个直接更新state 的方法(回调函数)的对象
  2. 谁来触发: action 中的commit(‘mutation 名称’)
  3. 只能包含同步的代码, 不能写异步代码
const mutations = {
	yyy (state, {data1}) {
		// 更新state 的某个属性
	}
}

6.2.3. actions

  1. 包含多个事件回调函数的对象
  2. 通过执行: commit()来触发mutation 的调用, 间接更新state
  3. 谁来触发: 组件中: $store.dispatch(‘action 名称’, data1) // ‘zzz’
  4. 可以包含异步代码(定时器, ajax)
const actions = {
	zzz ({commit, state}, data1) {
		commit('yyy', {data1})
	}
}

6.2.4. getters

  1. 包含多个计算属性(get)的对象
  2. 谁来读取: 组件中: $store.getters.xxx
const getters = {
	mmm (state) {
		return ...
	}
}

6.2.5. modules

  1. 包含多个module
  2. 一个module 是一个store 的配置对象
  3. 与一个组件(包含有共享数据)对应

6.2.6. 向外暴露store 对象

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

6.2.7. 组件中

import {mapState, mapGetters, mapActions} from 'vuex'
export default {
	computed: {
		...mapState(['xxx']),
		...mapGetters(['mmm']),
	}
	methods: mapActions(['zzz'])
}
{{xxx}} {{mmm}} @click="zzz(data)"

6.2.8. 映射store

import store from './store'
	new Vue({
	store
})

6.2.9. store 对象

  1. 所有用vuex 管理的组件中都多了一个属性$store, 它就是一个store 对象
  2. 属性:
    state: 注册的state 对象
    getters: 注册的getters 对象
  3. 方法:
    dispatch(actionName, data): 分发调用action

6.3. demo1: 计数器

6.3.1. store.js

/**
 * vuex 的store 对象模块
 */
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
/*
state 对象
类似于data
*/
const state = {
  count: 0 // 初始化状态数据
}
/*
mutations 对象
包含个方法: 能直接更新state
一个方法就是一个mutation
mutation 只能包含更新state 的同步代码, 也不会有逻辑
mutation 由action 触发调用: commit('mutationName')
*/
const mutations = {
  //增加的mutation
  INCREMENT(state) {
    state.count++
  },

  //减少的mutation
  DECREMENT(state) { // ctrl + shift + x
    state.count--
  }
}
/*
actions 对象
包含个方法: 触发mutation 调用, 间接更新state
一个方法就是一个action
action 中可以有逻辑代码和异步代码
action 由组件来触发调用: this.$store.dispatch('actionName')
*/
const actions = {

  //增加的action
  increment({
    commit
  }) {
    //提交增加的mutation
    commit('INCREMENT')
  },

  //减少的action
  decrement({
    commit
  }) {
    //提交减少的mutation
    commit('DECREMENT')
  },

  //带条件的action
  incrementIfOdd({
    commit,
    state
  }) {
    if (state.count % 2 === 1) {
      commit('INCREMENT')
    }
  },

  //异步的action
  incrementAsync({
    commit
  }) {
      //在action中直接就可以执行异步代码
    setTimeout(() => {
      commit('INCREMENT')
    }, 1000)
  }
}
/*
getters 对象
包含多个get 计算计算属性方法
*/
const getters = {
  oddOrEven(state) { //不需要调用,只需要读取属性值
    return state.count % 2 === 0 ? '偶数' : '奇数'
  },
  count(state) {
    return state.count
  }
}
// 向外暴露store 实例对象
export default new Vuex.Store({
  //构造函数
  state, //状态
  mutations, //包含多个更新state函数的对象
  actions, //包含多个对应事件回调函数的对象
  getters //包含多个getter计算属性函数的对象
})

6.3.2. main.js

import  Vue from 'vue'
import App from './App.vue'
import store from './store'

new Vue({
    el: '#app',
    components: {App},
    template: '<App/>',
    store // 所有组件都多个一个属性: $store
})

6.3.3. app.vue(未优化前)

<template>
  <div>
    <p>clicked: {{ $store.state.count }} times, count is {{ oddOrEven }}</p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementIfOdd">increment if odd</button>
    <button @click="incrementAsync">increment async</button>
  </div>
</template>

<script>
export default {
  computed: {
    oddOrEven() {
      return this.$store.getters.oddOrEven;
    }
  },
  methods: {
    increment() {
        //通知vuex去增加
      this.$store.dispatch("increment");  //触发store中对应的action调用
    },
    decrement() {
        //通知vuex去减少
      this.$store.dispatch("decrement");   //触发store中对应的action调用
    },
    incrementIfOdd() {
      this.$store.dispatch("incrementIfOdd");
    },
    incrementAsync() {
      this.$store.dispatch("incrementAsync");
    }
  }
};
</script>


<style>
</style>

6.3.4. app2.vue(优化后)

<template>
  <div>
    <p>clicked: {{ count }} times, count is {{ oddOrEven2 }}</p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementIfOdd">increment if odd</button>
    <button @click="incrementAsync">increment async</button>
  </div>
</template>
<script>
// import { mapState, mapGetters, mapActions } from "vuex";
import { mapGetters, mapActions } from "vuex";

export default {
  //使用映射减少重复代码
  // computed: {
  //     ...mapState(['count']),
  //     ...mapGetters(['evenOrOdd'])
  // }

  computed: mapGetters({
    // 名称不一样
    oddOrEven2: "oddOrEven",
    count: "count"
  }),
  methods: mapActions([
    "increment",
    "decrement",
    "incrementIfOdd",
    "incrementAsync"
  ]) // 名称一样
};
</script>
<style>
</style>

在这里插入图片描述

6.4. demo2: todo list

6.4.1. store/types.js

/**
* 包含多个mutation name
*/
export const RECEIVE_TODOS = 'receive_todos'
export const ADD_TODO = 'add_todo'      //添加todo
export const REMOVE_TODO = 'remove_todo'
export const DELETE_DONE = 'delete_done'
export const UPDATE_ALL_TODOS = 'update_all_todos'

6.4.2. store/mutations.js

//包含多个由action触发去直接更新状态的方法对象
import {
  RECEIVE_TODOS,
  ADD_TODO,
  REMOVE_TODO,
  DELETE_DONE,
  UPDATE_ALL_TODOS
} from './types'
export default {
  [RECEIVE_TODOS](state, {
    todos
  }) {
    state.todos = todos
  },
  [ADD_TODO](state, {
    todo
  }) {
    state.todos.unshift(todo)
  },
  [REMOVE_TODO](state, {
    index
  }) {
    state.todos.splice(index, 1)
  },
  [DELETE_DONE](state) {
    state.todos = state.todos.filter(todo => !todo.complete)
  },
  [UPDATE_ALL_TODOS](state, {
    isCheck
  }) {
    state.todos.forEach(todo => todo.complete = isCheck)
  }
}

6.4.3. store/actions.js

// 包含多个接受组件通知触发mutation调用,间接更新状态的方法对象
import storageUtil from '../util/storageUtil'
import {
  RECEIVE_TODOS,
  ADD_TODO,
  REMOVE_TODO,
  DELETE_DONE,
  UPDATE_ALL_TODOS
} from './types'
export default {
  readTodo({
    commit
  }) {
    setTimeout(() => {
      const todos = storageUtil.fetch()
      // 提交commit 触发mutation 调用
      commit(RECEIVE_TODOS, {
        todos
      })
    }, 1000)
  },
  addTodo({
    commit
  }, todo) {
    commit(ADD_TODO, {
      todo
    })
  },
  removeTodo({
    commit
  }, index) {
    commit(REMOVE_TODO, {
      index
    })
  },
  deleteDone({
    commit
  }) {
    commit(DELETE_DONE)
  },
  updateAllTodos({
    commit
  }, isCheck) {
    commit(UPDATE_ALL_TODOS, {
      isCheck
    })
  }
}

6.4.4. store/getters.js

// 含有多个计算属性对象
export default {
  todos(state) {
    return state.todos
  },
  totalSize(state) {
    return state.todos.length
  },
  completeSize(state) {
    return state.todos.reduce((preTotal, todo) => {
      return preTotal + (todo.complete ? 1 : 0)
    }, 0)
  },
  isAllComplete(state, getters) {
    return getters.totalSize === getters.completeSize && getters.totalSize > 0
  }
}

6.4.5. store/index.js

/*
vuex最核心的管理对象store
*/

import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
Vue.use(Vuex)
const state = {
  todos: []
}
export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters
})

6.4.6. components/app.vue

<template>
  <div class="todo-container">
    <div class="todo-wrap">
      <todo-header></todo-header>
      <todo-main></todo-main>
      <todo-footer></todo-footer>
    </div>
  </div>
</template>
<script>
import todoHeader from "./todoHeader.vue";
import todoMain from "./todoMain.vue";
import todoFooter from "./todoFooter.vue";
import storageUtil from "../util/storageUtil";
export default {
  created() {
    // 模拟异步读取数据
    this.$store.dispatch("readTodo");
  },
  components: {
    todoHeader,
    todoMain,
    todoFooter
  }
};
</script>
<style>
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

6.4.7. components/todoHeader.vue

<template>
  <div class="todo-header">
    <input
      type="text"
      placeholder="请输入你的任务名称,按回车键确认"
      v-model="title"
      @keyup.enter="addItem"
    />
  </div>
</template>
<script type="text/ecmascript-6">
export default {
  data() {
    return {
      title: null   //组件内部使用的状态,不存在共享的问题-->不需要使用vuex管理它
    };
  },
  methods: {
    addItem() {
      const title = this.title && this.title.trim();
      if (title) {
        const todo = {
          title,
          complete: false
        };
        this.$store.dispatch("addTodo", todo);
        this.title = null;
      }
    }
  }
};
</script>
<style>
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}
.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
    0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

6.4.8. components/todoMain.vue

<template>
  <ul class="todo-main">
    <todo-item
      v-for="(todo, index) in todos"
      :todo="todo"
      :key="index"
      :index="index"
    ></todo-item>
  </ul>
</template>
<script type="text/ecmascript-6">
import todoItem from "./todoItem";
import storageUtil from "../util/storageUtil";
export default {
  components: {
    todoItem
  },
  computed: {
    todos() {
      return this.$store.getters.todos;
    }
  },
  watch: {
    todos: {
      // 深度监视todos, 一旦有变化立即保存
      handler: storageUtil.save,
      deep: true
    }
  }
};
</script>
<style>
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}
.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>

6.4.9. components/todoItem.vue

<template>
  <li
    :style="{ background: libg }"
    @mouseenter="handleStyle(true)"
    @mouseleave="handleStyle(false)"
  >
    <label>
      <input type="checkbox" v-model="todo.complete" />
      <span>{{ todo.title }}</span>
    </label>
    <button class="btn btn-danger" v-show="isShown" @click="deleteItem">
      删除
    </button>
  </li>
</template>
<script type="text/ecmascript-6">
export default {
  props: ["todo", "index"],
  data() {
    return {
      isShown: false,
      libg: "#fff"
    };
  },
  methods: {
    handleStyle(isEnter) {
      if (isEnter) {
        this.isShown = true;
        this.libg = "#ddd";
      } else {
        this.isShown = false;
        this.libg = "#fff";
      }
    },
    deleteItem() {
      const { todo, deleteTodo, index } = this;
      if (window.confirm(`确定删除${todo.title}的评论吗?`)) {
        // deleteTodo(index)
        this.$store.dispatch("removeTodo", index);
      }
    }
  }
};
</script>
<style>
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}
li label {
  float: left;
  cursor: pointer;
}
li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}
li button {
  float: right;
  display: none;
  margin-top: 3px;
}
li:before {
  content: initial;
}
li:last-child {
  border-bottom: none;
}
</style>

6.4.10. components/todoFooter.vue

<template>
  <div class="todo-footer">
    <label>
      <input type="checkbox" v-model="isAllDone" />
    </label>
    <span>
      <span>已完成{{ completeSize }}</span> / 全部{{ totalSize }}
    </span>
    <button
      class="btn btn-danger"
      @click="deleteDone"
      v-show="completeSize > 0"
    >
      清除已 完成任务
    </button>
  </div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
  methods: mapActions(["deleteDone"]),
  computed: {
    isAllDone: {
      get() {
        return this.$store.getters.isAllComplete;
      },
      set(value) {
        //this.$emit('updateTodos', value)
        this.$store.dispatch("updateAllTodos", value);
      }
    },
    ...mapGetters(["completeSize", "totalSize"])
  }
};
/*
const arr1 = [1, 3, 5]
const arr2 = [4, ...arr1, 7]
const obj = {
a: 1,
b () {
}
}
const obj2 = {
c: 3,
...obj
}*/
</script>
<style>
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}
.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}
.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}
.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>

6.4.11. util/storageUtil.js

// 使用localStorage存储数据的工具模块
// 1.函数       :1个功能
// 2.对象       :2个功能
// 需要向外暴露1个功能还是多个功能

var STORAGE_KEY = 'todos'
export default {
  fetch() {
    return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
  },
  save(todos) {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
  }
}

6.4.12. base.css

/*base*/
body {
    background: #fff;
  }
  
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    text-align: center;
    vertical-align: middle;
    cursor: pointer;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    border-radius: 4px;
  }
  
  .btn-danger {
    color: #fff;
    background-color: #da4f49;
    border: 1px solid #bd362f;
  }
  
  .btn-danger:hover {
    color: #fff;
    background-color: #bd362f;
  }
  
  .btn:focus {
    outline: none;
  }

6.4.13. main.js

import  Vue from 'vue'
import App from './components/App.vue'
import store from './store'
import './base.css'

new Vue({
    el: '#app',
    components: {App},
    // template: '<App/>',
    render: h => h(App),
    store
})

6.5. vuex 结构分析

在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值