vuex总结笔记

组件之间共享数据的方式:

1.父向子: v-bind 属性绑定

2.子向父: v-on 事件绑定

3.兄弟之间共享数据: EventBus

  • $on 接收数据的那个组件 数据接收方
  • $emit 发送数据的那个组件 数据发送方

上面的三种方式 只适合在小范围的数据之间传递。如果要大范围进行数据的传递或者说频繁的进行数据传递,使用这三种方式就会力不从心

vuex概念

vuex是实现组件传去状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享

如果不适用vuex 实现数据状态的管理的话,一个数据要想传递给另外一个不相干的组件的时候,就必须一层一层的进行传递,或者往上或者往下。形成链条的形式。给他的父组件,父组件再传递给他的父组件。我只是想传递给其中的一个组件,但是这样的方式,就会造成,链条上的很多不相干的组件都会被影响到

vuex就相当于在所有的组件旁边,定义了一个数据共享对象。这个对象就叫store,store里面就存储着我们班要共享的数据。这样的话,组件a只需要将数据共享到store中,其他想要使用a组件中的数据的话,就可以从store对象中直接拿到数据,不需要进行传递

使用vuex统一管理状态的好处

  • 能够在vuex中集中管理共享的数据,易于开发和后期维护
  • 能够高效的实现组件之间的数据共享,提高开发效率
  • 存储在vuex中的数据都是响应式的,能够实时保持数据与页面的同步。不需要程序员手动进行刷新操作。

一般情况下,只有组件与组件之间共享的 数据,才有必要存储到vuex中

但是在实际开发中,如果想把数据都存储到vuex中,也是可以的

vuex基本使用

npm install vuex --save
import Vuex from 'vuex'
Vuex.use(Vuex)
  • 创建store对象
 const store = new Vuex.Store({
    	//state 中存放的就是全局共享的数据
    	state:{ count:0 }
    })
  • 将Store对象挂载到vue实例中
  new Vue({
    	el:'#app',
    	render:h => h(app),// 渲染app根组件
    	router,
    	// 将创建的共享数据对象,挂载到Vue 实例中
    	// 所有的组件 就可以直接从store中获取全局的数据了
    	store
    })

store.js 就是用来初始化vuex的。在state中定义我们需要在全局共享的数据

如何使用store中的数据??

打开main.js文件,导入 import store from './store' 导入store 并且在main.js文件中new 一个vue实例,将store挂载到vue实例中去

new Vue({
	store,
	render:h => h(App)
}).$mount('#app')

这样的话,每一个组件都可以放到到在全局挂载的store中的数据了

新建两个组件,在App.vue文件中导入

import Addition from "./components/Addition.vue";
import Subtraction from "./components/Subtraction.vue";
  • 将导入的两个组件 变成当前组件的私有组件

新起一个名字,一般都设置成- 形式的名字

components:{
	"my-addition":Addition;
	"my-subtraction":Subtraction
}
  • 变成了当前组件的私有组件之后,就可以在当前页面进行时使用了
    
    

在项目根目录新建文件 .prettierrc 来格式化我们的代码

{
	"semi":false,
	"singleQuote":true
}

格式化文件 使用Prettier-Code formatter 来格式化

vuex中的核心概念

1.State

state提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储

  • 组件访问State中数据的第一种方式:
this.$store.state.全局数据名称// 如:this.$store.state.count
    在组件中 template结构中 this 是可以省略的
    
  • 组件访问State中数据的第二种方式:
// 1.先从vuex中按需导入mapState函数
 import { mapState } from 'vuex'
 // 2.然后通过刚才导入的mapState函数,将当前组件需要的全局数据,映射为当前组件的computed计算属性
 computed:{ ...mapState(['全局数据名称']) }
//比如说我们在某个组件中需要用到全局的 count数据了。可以在当前组件中华先定义一个computed属性,在计算属性中,调用mapState函数,需要用到哪些全局的数据了,就在mapState函数中做一下声明
//...展开运算符,将全局的数据映射为当前组件的一个计算属性。可以认为使用了...展开运算符,当前的count属性就是我当前组件的一个计算属性。
//然后再html结构中。直接把 count数据用 {{count}} 就可以了

2.Mutation

现在的要求是,在加法或者减法组件中分别有一个按钮,点击,就将当前的count(vuex中全数据count加一或者减一)。最平常的思路,就是在加法组件或者减法组件中直接访问到全局对象store中的count中。通过this.$srore.state.count让这个全局属性加一 或者减一就可以。当然这种方式是可以实现我们现在的需求的。但其实这种方法是完全不正确的。

因为我们在一个组件中直接在修改了全局中的数据,在vuex中,是不允许一个组件直接去修改全局的数据的。vuex推荐我们使用 Mutation 。Mutation用于变更Store中的数据

原因是:如果我们的项目很大,代码越写越多。如果全局中的数据发生了变化,如果我们想知道到底是谁把全局中的数据改变了。这时候就会比较麻烦,就会一个一个组件的去翻,到底在那个组件中发生了改变。这种方式不利于后期的维护。使用Mutation 中的函数来修改,后期发生改变了,直接来store.js中文件中的mutation来查找什么时候发生改变就会很方便了

1.只能通过mutation变更Store数据,不可以直接操作Store中的数据

2.通过这种方式虽然看起来麻烦,但是可以集中监控所有数据的变化

在state平级定义
mutations:{
	//里面可以定义很多的额方法函数,比如实现自增加一,可以定义 add(){}函数
	add(state){
	//上面的state就是当前的state对象。既然可以拿到state对象,自然就可以拿到state对象中的任何属性
	state.count++
	}
}

如何在组件中调用mutation 中的函数??

  • 在组件中触发mutation的第一种方式
this.$store.commit('add')// 通过commit函数可以调用mutation中的任何函数 

如何在调用Mutation的时候传递参数??

mutations:{
	addN(state,step){//第一个永远都是state,第二个就是外界传递过来的值
		state.count += step
	}
}

如何在组件中调用 mutation中的函数并传递参数呢?

this.$store.commit('addN',3)// commit的作用就是调用某个mutation 函数
  • 在组件中触发mutation的第二种方式
 从vuex中按需导入mapMutation函数
    import { mapMutations } from 'vuex'

通过刚才导入的mapMutations函数,将需要的mutations函数,映射为当前组件的methods方法

在组件中写::
methods:{
	...mapMutations(['add','addN'])//想要调用vuex全局中的那个函数,就在组件这里进行声明。加上...展开云算符,就可以像,调用当前组件的方法一样调用全局函数了
}

具体实例代码:
store.js文件

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

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        count: 4
    },
    mutations: {
        add(state, step) {
            // 第一个形参永远都是state也就是$state对象
            // 第二个形参是调用add时传递的参数
            // 只要调用add,就让值加一
            state.count += 1
        },
        addN(state, step) {
            state.count += step
        },
        sub(state) {
            state.count -= 1
        },
        subN(state, step) {
            state.count -= step
        }
    },
    actions: {

    }
})

substraction.vue文件




如下:

//按需导入函数名
import { mapState,mapMutations } from 'vuex'

export default {
  data() {
    return {}
  },
  //映射为当前组件的methods方法
  methods:{
      //获得mapMutations映射的sub函数
      ...mapMutations(['sub']),
      //当点击按钮时触发Sub函数
      Sub(){
          //调用sub函数完成对数据的操作
          this.sub(10);
      }
  },
  computed:{
      ...mapState(['count'])
      
  }
}

3.Action

mutation中不能写异步的代码vue的调试器不能正常使用了。异步的代码在Action中写如果通过异步操作变更数据,必须通过Action,而不能使用Mution,但是在action中还是要通过触发Mutation的方式间接变更数据

action中只能通过commit函数去触发mutation中的函数

  • 触发action的第一种方式

dispath就是用来触发action中定义的函数 (commit的作用 就是调用某个mutation函数)

在store.js中定义

  // 定义 Action
     const store = new Vuex.Store({
     		// ...省略其他代码
    		 mutations: {
     			add(state) {
     				state.count++
    			 }
		     },
     actions: {
    	 addAsync(context) {
     		setTimeout(() => {
    			 context.commit('add')
    			 }, 1000)
     		}
     	}
}) 
  • 在组件中触发(使用)
 // 触发 Action
     methods: {
    	 handle() {
    		 // 触发 actions 的第一种方式     dispatch就是用来触发某个函数
     		this.$store.dispatch('addAsync')
     		}
     } 

我们知道,只有mutations中定义的函数,才有权利修改state中的数据
1.commit的作用 用来调用(触发)某个mutation函数
2.dispatch函数,用来触发actions中的函数
操作步骤如下:
打开store.js文件,修改Action,如下:

actions: {
  addAsync(context,step){
    setTimeout(()=>{
      context.commit('add',step);
    },2000)
  }
}

然后在Addition.vue中给按钮添加事件代码如下:



methods:{
  AddAsync(){
    this.$store.dispatch('addAsync',5)
  }
}

触发actions异步任务时携带参数

异步任务的方法函数的第一个参数永远都是context

// 定义 Action
 const store = new Vuex.Store({
 	// ...省略其他代码
 	mutations: {
 		addN(state, step) {
			 state.count += step
		 }
 	},
 actions: {
 // 所有的额异步任务方法都需要在actions中定义,才能在组件中使用
        addNAsync(context, step) {
            setTimeout(() => {
                context.commit('addN', step)
            }, 1000)
        }
 }) 

// 触发 Action
 methods: {
 btnHandler4(){
            this.$store.dispatch('addNAsync',3)
        }
 } 
  • 触发Action的第二种方式:
  // 1. 从 vuex 中按需导入 mapActions 函数
    import { mapActions } from 'vuex'

通过刚才导入的 mapActions 函数,将需要的 actions 函数,映射为当前组件的 methods 方法:

// 2. 将指定的 actions 函数,映射为当前组件的 methods 函数
methods: {
 ...mapActions(['addASync', 'addNASync'])
} 

如下:substraction.vue组件代码




上面的使用异步方法二,还需要每次使用的时候通过点击事件绑定函数的方式来调用。也可以不需要绑定函数,就可以直接使用映射过来的subAsync函数,方法是直接复制映射过来的函数名,给点击事件直接绑定映射过来的函数名(不需要自己再定义一个函数)

删除掉btnHandler3函数

直接调用:


原因是:mapActions函数就是把全局的某个函数映射为自己的某个函数,既然subAsync是自己的额函数了,就相当于在组件中在methods中定义了函数subAsync,所以就可以直接把subAsync作为函数直接调用

4.Getter

Getter用于对Store中的数据进行加工处理形成新的数据
① Getter 可以对 Store 中已有的数据加工处理之后形成新的数据,类似 Vue 的计算属性。
② Store 中数据发生变化,Getter 的数据也会跟着变化。
打开store.js文件,添加getters,如下:

export default new Vuex.Store({
  .......
  getters:{
    //添加了一个showNum的属性 接收的第一个参数就是state对象
    showNum : state =>{
      return '最新的count值为:'+state.count;
    }
  }
})

然后打开Addition.vue中,添加插值表达式使用getters

  • 使用getter的第一种方式
  

{{$store.getters.showNum}}

  • 使用getter的第二种方式(在Addition.vue中,导入mapGetters,并将之映射为计算属性)
或者也可以在Addition.vue中,导入mapGetters,并将之映射为计算属性
import { mapGetters } from 'vuex'
computed:{
  ...mapGetters(['showNum'])
}
方式一:

方式二:

{{showNum}}

vuex案例

A.初始化案例

首先使用vue ui初始化一个使用vuex的案例

项目中的配置有(4个 vuex,link/formatter/使用配置文件/router);创建好之后安装依赖 axios;安装ant-design-vue依赖(都安装到运行依赖)

创建好之后,自动生成一个store.js (使用vuex,store被挂载到vue实例中,这样就可以在项目的全局使用store) main.js文件

初始化项目:赋值写好的代码,打开控制台看有没有出错,然后再催可视化面板中看有没有警告信息,有的话,去掉警告。(新建格式化工具eslink配置文件 .prettierrc文件,去除分号,可以使用单引号),然后在.eslinktrc配置文件中去除那些不必要的空格警告(函数名与小括号之间需要有空格等警告,在rules数组中,赋值警告信息到这里来,给他的值设置为0 就不会再报错了)、

将store.js组件导入到main.js中,并将其挂载到vue全局中

将list数组中的静态内容放到list.json文件中

然后打开public文件夹,创建一个list.json文件,文件代码如下:

[
    {
        "id": 0,
        "info": "Racing car sprays burning fuel into crowd.",
        "done": false
    },
    {
        "id": 1,
        "info": "Japanese princess to wed commoner.",
        "done": false
    },
    {
        "id": 2,
        "info": "Australian walks 100km after outback crash.",
        "done": false
    },
    {
        "id": 3,
        "info": "Man charged over missing wedding girl.",
        "done": false
    },
    {
        "id": 4,
        "info": "Los Angeles battles huge wildfires.",
        "done": false
    }
]

再接着,打开main.js,添加store.js的引入,如下:

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

// 1. 导入 ant-design-vue 组件库
import Antd from 'ant-design-vue'
// 2. 导入组件库的样式表
import 'ant-design-vue/dist/antd.css'

Vue.config.productionTip = false
// 3. 安装组件库
Vue.use(Antd)

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')

(因为想要通过异步请求的方式模拟发送真正ajax调取后台数据的过程,需要导入axios发起异步请求)再接着打开store.js,添加axios请求json文件获取数据的代码,如下:

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

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    //所有任务列表
    list: [],
    //文本输入框中的值
    inputValue: 'AAA'
  },
  mutations: {
    initList(state, list) {
      state.list = list
    },
    setInputValue(state,value){
      state.inputValue = value
    }
  },
  actions: {
    getList(context) {
    // // 将data从对象中解构赋值出来
      axios.get('/list.json').then(({ data }) => {
        console.log(data);
        context.commit('initList', data)
      })
    }
  }
})

最后,打开App.vue文件,将store中的数据获取并展示:

  • 实现页面的文本框跟着文本框中的值变化(文本框的change事件)用的属性绑定 …mapState([‘inputValue’])

      // 监听文本框内容的变化
      handleInputChange(e){
        // 拿到文本框最新的值 文本框的change事件
        console.log(e.target.value)
        //调用mutation函数
        this.$store.commit('setInputValue',e.target.value)
      }
    

store.js文件中

    mutations: {
        // 只能在这里操作state中的数据
        initList(state, list) {
            state.list = list
        },
        // 为store中的inputValue赋值
        setInputValue(state, val) {
            state.inputValue = val
        }
    },
  • 点击添加事项完成添加事项操作

设置好要传递的数据对象参数,然后将对象添加到list列表中去

store.js中

        // 添加事项操作
        addItem(state) {
            // 根据json数据(后台数据库 接口要求的数据类型添加对象参数到list.json列表中)
            const obj = {
                    id: state.nextId,
                    info: state.inputValue.trim(),
                    // 默认为没有完成的数据
                    done: false
                }
            // 将数据对象追加到list数组中
            state.list.push(obj)
            // 追加完之后就及时的让nextId 加一
            state.nextId++
            // 将input框中的值清空
            state.inputValue = ''

        }

app.vue文件中

    // 为添加事项按钮绑定时间函数
    addItemToList(){
      // 判断文本框的内容是否为空
      if(this.inputValue.trim().length <= 0){
        return this.$message.warning('文本框中的内容不能为空!')
      }
      // 用户输入的数据不为空
      this.$store.commit('addItem')
    }
  • 删除事项功能

为每个删除按钮绑定点击事件,删除的时候,拿到当前数据列表的id,根据id号删除相应json文件中的数据。删除数组中的意向

store.js文件中

        // 根据id删除事项
        removeItem(state, id) {
            // 根据id查找到对象想的索引
            const i = state.list.findIndex(x => x.id === id)
                // 根据索引,删除对应的元素
            if (i !== -1) {
                // 数组索引存在 就删除这条数据
                state.list.splice(i, 1)
            }
        }

app.vue文件中

    // 删除该条数据   在当前组件不能之家删除 操作 state中的数据 需要使用到mutation
    removeItemById(id){
      // console.log(id)
      this.$store.commit('removeItem',id)

    }
  • 将每条数据中的完成状态参数与复选框的选中状态做绑定

复选框的选中状态是由 :checked 属性决定的额

 
        {{item.info}}
  • 修改文本框的状态

点击复选框,拿到复选框的id,根据id将状态赋值到list对应的item项

监听复选框状态改变的事件(change)

app.vue中

   //复选框的状态发生改变 的事件  e就是事件对象
   cbStatusChanged(e,id){
    //  console.log(e)
    // 拿到当前最新的文本框选中状态
    //  console.log(e.target.checked)
    //  获取到当前点击的文本框的id
    // console.log(id)
    // 定义参数对象值
    const param ={
      id:id,
      status:e.target.checked
    }
    this.$store.commit('changeStatus',param)
   } 

store.js

        // 复选框状态发生改变函数  修改列表项的选中状态
        changeStatus(state, param) {
            // 查找当前的id
            const i = state.list.findIndex(x => x.id === param.id)
            if (i !== -1) {
                state.list[i].done = param.status
            }
        }
  • 同级未完成的任务的条数 使用getters函数,在顶部按需导入getters函数(getters函数需要在computed函数中使用)

store.js文件中

// 
    getters: {
        // 统计未完成的任务的条数
        unDoneLength(state) {
            return state.list.filter(x => x.done === false).length
        }
    }

app.vue文件中,先按需导入mapGetters函数然后再computed属性中映射一下,最后再在template结构中使用(差值表达式)

  import { mapState,mapGetters} from 'vuex'
***********************************************
  computed:{
    // 全局中的state对象中的list数组  就变成当前组件中的一个list计算属性了
    // 接下来就可以将list数据渲染到页面上了
    ...mapState(['list','inputValue']),
    ...mapGetters(['unDoneLength'])
    
  },
  • 清除已完成功能实现

为清除已完成按钮绑定事件

store.js文件中

        // 清除已完成事项
        cleanDone(state) {
            // 将属性done是true的过滤出来,重新计算一下条数
            // 将未完成的数据拿出来重新赋值给当前事项的数组,就相当于删除了属性值是true的事项
            state.list = state.list.filter(x => x.done === false)
                // 过滤出来的是一个新的数组,里面全部都是属性done是false的值
        }

app.vue中 定义点击事件的函数

  //  清除已完成函数
   clean(){
    //  还是不能再当前组件中直接操作store中的数据,需要使用mutation还操作数据
    this.$store.commit('cleanDone')
   } 
  • 列表底部高亮按钮的切换

  • app.vue文件

    //  修改页面上展示的列表数据
    changeList(key){
      console.log(key)
      this.$store.commit('changeViewKey',key)
    }
    
    
          
            全部
            未完成
            已完成
    

store.js文件中

先把store.js文件中的数据映射到app.vue组件中

  computed:{
    // 全局中的state对象中的list数组  就变成当前组件中的一个list计算属性了
    // 接下来就可以将list数据渲染到页面上了
    ...mapState(['list','inputValue','viewKey']),
    ...mapGetters(['unDoneLength'])
    
  },




        // 修改视图的关键字
        changeViewKey(state, key) {
            state.viewKey = key
        }
  • 列表数据的按需切换

使用getters对数据进行包装 实现按需切换。在getters节点中新增infolist节点

store.js文件

        // 过滤数据,对数据进行包装,但会相应的数据
        infolist(state) {
            if (state.viewKey === 'all') {
                return state.list
            }
            if (state.viewKey === 'undone') {
                return state.list.filter(x => !x.done)
            }
            if (state.viewKey === 'done') {
                return state.list.filter(x => x.done)
            }
            //防止报错
            return state.list
        }

computed中按需导入函数

    ...mapGetters(['unDoneLength','infolist'])

将list的数据源绑定为 infolist

//-----------------------------------------------------------------------------------------------------------------------------------------------------






B.完成添加事项

首先,打开App.vue文件,给“添加事项”按钮绑定点击事件,编写处理函数

//绑定事件
添加事项

//编写事件处理函数
methods:{
    ......
    addItemToList(){
      //向列表中新增事项
      if(this.inputValue.trim().length <= 0){
        return this.$message.warning('文本框内容不能为空')
      }

      this.$store.commit('addItem')
    }
  }

然后打开store.js编写addItem

export default new Vuex.Store({
  state: {
    //所有任务列表
    list: [],
    //文本输入框中的值
    inputValue: 'AAA',
    //下一个id
    nextId:5
  },
  mutations: {
    ........
    //添加列表项
    addItem(state){
      const obj = {
        id :state.nextId,
        info: state.inputValue.trim(),
        done:false
      }
      //将创建好的事项添加到数组list中
      state.list.push(obj)
      //将nextId值自增
      state.nextId++
      state.inputValue = ''
    }
  }
  ......
})

C.完成删除事项

首先,打开App.vue文件,给“删除”按钮绑定点击事件,编写处理函数

//绑定事件
删除

//编写事件处理函数
methods:{
    ......
    removeItemById(id){
      //根据id删除事项
      this.$store.commit('removeItem',id)
    }
  }

然后打开store.js编写addItem

export default new Vuex.Store({
  ......
  mutations: {
    ........
    removeItem(state,id){
      //根据id删除事项数据
      const index = state.list.findIndex( x => x.id === id )
      // console.log(index);
      if(index != -1) state.list.splice(index,1);
    }
  }
  ......
})

D.完成选中状态的改变

首先,打开App.vue文件,给“复选”按钮绑定点击事件,编写处理函数

//绑定事件
{{item.info}}

//编写事件处理函数
methods:{
    ......
    cbStateChanged(id,e){
      //复选框状态改变时触发
      const param = {
        id:id,
        status:e.target.checked
      }

      //根据id更改事项状态
      this.$store.commit('changeStatus',param)
    }
  }

然后打开store.js编写addItem

export default new Vuex.Store({
  ......
  mutations: {
    ........
    changeStatus(state,param){
      //根据id改变对应事项的状态
      const index = state.list.findIndex( x => x.id === param.id )
      if(index != -1) state.list[index].done = param.status
    }
  }
  ......
})

E.剩余项统计

打开store.js,添加getters完成剩余项统计

getters:{
  unDoneLength(state){
    const temp = state.list.filter( x => x.done === false )
    console.log(temp)
    return temp.length
  }
}

打开App.vue,使用getters展示剩余项

//使用映射好的计算属性展示剩余项

{{unDoneLength}}条剩余

//导入getters
import { mapState,mapGetters } from 'vuex'
//映射
computed:{
  ...mapState(['list','inputValue']),
  ...mapGetters(['unDoneLength'])
}

F.清除完成事项

首先,打开App.vue文件,给“清除已完成”按钮绑定点击事件,编写处理函数


清除已完成

//编写事件处理函数
methods:{
  ......
  clean(){
    //清除已经完成的事项
    this.$store.commit('cleanDone')
  }
}

然后打开store.js编写addItem

export default new Vuex.Store({
  ......
  mutations: {
    ........
    cleanDone(state){
      state.list = state.list.filter( x => x.done === false )
    }
  }
  ......
})

G.点击选项卡切换事项

打开App.vue,给“全部”,“未完成”,“已完成”三个选项卡绑定点击事件,编写处理函数
并将列表数据来源更改为一个getters。


  ......
  
  
    全部
    未完成
    已完成
  
  ......


//编写事件处理函数以及映射计算属性
methods:{
  ......
  changeList( key ){
    //点击“全部”,“已完成”,“未完成”时触发
    this.$store.commit('changeKey',key)
  }
},
computed:{
  ...mapState(['list','inputValue','viewKey']),
  ...mapGetters(['unDoneLength','infoList'])
}

打开store.js,添加getters,mutations,state

export default new Vuex.Store({
  state: {
    ......
    //保存默认的选项卡值
    viewKey:'all'
  },
  mutations: {
    ......
    changeKey(state,key){
      //当用户点击“全部”,“已完成”,“未完成”选项卡时触发
      state.viewKey = key
    }
  },
  ......
  getters:{
    .......
    infoList(state){
      if(state.viewKey === 'all'){
        return state.list
      }
      if(state.viewKey === 'undone'){
        return state.list.filter( x => x.done === false )
      }
      if(state.viewKey === 'done'){
        return state.list.filter( x => x.done === true )
      }
    }
  }
})

你可能感兴趣的:(vue)