vuex的使用

VUE + vuex的使用总结如下(查阅资料):

写在前面

本文旨在通过一个简单的例子,练习vuex的几个常用方法,使初学者以最快的速度跑起来一个vue + vuex的示例。

学习vuex需要你知道VUE的一些基础知识和用法。相信点开本文的同学都具备这个基础。

另外对vuex已经比较熟悉的大佬可以忽略本文。

生成基于VUE的项目

基于vue-cli脚手架生成一个vue项目常用npm命令:

npm i vue-cli -g
vue --version
vue init webpack 项目名


进入项目目录,使用npm run dev先试着跑一下。

一般不会出现问题,试跑成功后,就可以写我们的vuex程序了。

使用VUE完成的示例

使用vuex首先得安装vuex,命令:

npm i vuex --save

介绍一下我们的超简单演示,一个父组件,一个子组件,父组件有一个数据,子组件有一个数据,想要将这两个数据都放置到vuex的状态中,然后父组件可以修改自己的和子组件的数据。子组件可以修改父组件和自己的数据。

先放效果图,初始化效果如下:

扫描二维码关注公众号,回复: 1100368 查看本文章

如果想通过父组件触发子组件的数据,就点“改变子组件文本”按钮,点击后效果如下:

如果想通过子组件修改父组件的数据,就在子组件点击“修改父组件文本”按钮,点击后效果如下:

代码文件介绍

首先是Parent.vue组件

<template>
    <div class="parent">
        <h3>这里是父组件</h3>
        <button type="button" @click="clickHandler">修改自己文本</button>
        <button type="button" @click="clickHandler2">修改子组件文本</button>
        <div>Test: {{msg}}</div>
        <child></child>
    </div>
</template>

<script>
    import store from '../vuex'
    import Child from './Child.vue'

    export default {

        computed: {
            msg(){
                return store.state.testMsg;
            }
        },
        methods:{
            clickHandler(){
                store.commit('changeTestMsg', '父组件修改自己后的文本')
            },
            clickHandler2(){
                store.commit('changeChildText', '父组件修改子组件后的文本')
            }
        },
        components:{
            'child': Child
        },
        store,
    }
</script>

<style scoped>
    .parent{
        background-color: #00BBFF;
        height: 400px;
    }
</style>

下面是Child.vue子组件

<template>
    <div class="child">
        <h3>这里是子组件</h3>
        <div>childText: {{msg}}</div>
        <button type="button" @click="clickHandler">修改父组件文本</button>
        <button type="button" @click="clickHandler2">修改自己文本</button>
    </div>
</template>

<script>
    import store from '../vuex'
    export default {
        name: "Child",
        computed:{
            msg(){
                return store.state.childText;
            }
        },
        methods: {
            clickHandler(){
                store.commit("changeTestMsg", "子组件修改父组件后的文本");
            },
            clickHandler2(){
                store.commit("changeChildText", "子组件修改自己后的文本");
            }
        },
        store
    }
</script>

<style scoped>
    .child{
        background-color: palegreen;
        border:1px solid black;
        height:200px;
        margin:10px;
    }
</style>


最后是vuex的配置文件

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

Vue.use(Vuex)

const state = {
    testMsg: '原始文本',
    childText:"子组件原始文本"
}

const mutations = {
    changeTestMsg(state, str){
        state.testMsg = str;
    },
    changeChildText(state, str){
        state.childText = str;
    }

}

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

export default store;

这个例子就是常见的获取列表数据然后渲染到界面中:

import axios from 'axios'
export default {
  name: 'projects',
  data: function () {
    return {
      projects: []
    }
  },
  methods: {
    loadProjects: function () {
      axios.get('/secured/projects').then((response) => {
        this.projects = response.data
      }, (err) => {
        console.log(err)
      })
    }
  },
  mounted: function () {
    this.loadProjects()
  }
}
</script>

在模板中我们可以方便的访问项目列表并且进行过滤,排序等操作,不过如果我们在另一个列表中也需要来展示相同的数据信息,继续按照这种方式实现的话我们不得不重新加载一遍数据。更麻烦的是如果用户在本地修改了某个列表数据,那么如何同步两个组件中的列表信息会是个头疼的问题.Vue官方推荐使用Vuex,类似于Redux的集中式状态管理工具来辅助解决这个问题。

何谓Vuex?

根据Vuex文档中的描述,Vuex是适用于Vue.js应用程序的状态管理库,为应用程序的所有组件提供集中式的状态存储与操作,保证了所有状态以可预测的方式进行修改。

Vuex中商店的模板化定义如下:

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

上述代码中包含了定义Vuex Store时关键的5个属性:

  • state:state定义了应用状态的数据结构,同样可以在这里设置默认的初始状态。

state: {
  projects: [],
  userProfile: {}
}
  • actions:Actions即是定义提交触发更改信息的描述,常见的例子有从服务端获取数据,在数据获取完成后会调用store.commit()来调用更改存储中的状态。可以在组件中使用dispatch来发出行动。

actions: {
    LOAD_PROJECT_LIST: function ({ commit }) {
      axios.get('/secured/projects').then((response) => {
        commit('SET_PROJECT_LIST', { list: response.data })
      }, (err) => {
        console.log(err)
      })
    }
  }
  • 突变:调用突变是唯一允许更新应用状态的地方。

mutations: {
    SET_PROJECT_LIST: (state, { list }) => {
      state.projects = list
    }
  }
  • getters:Getters允许组件从Store中获取数据,譬如我们可以从Store中的projectList中筛选出已完成的项目列表:

getters: {
 completedProjects: state => {
  return state.projects.filter(project => project.completed).length
 }
}
  • modules:modules对象允许将单一的存储拆分为多个存储的同时保存在单一的状态树中。随着应用复杂度的增加,这种拆分能够更好地组织代码

在理解了Vuex的基础概念之后,我们会创建一个真正的应用来熟悉整个使用流程。在准备好基础项目之后,我们需要将vuex引入项目中:

$ yarn add vuex

该步骤完成之后,我们需要在src目录下创建名为store的目录来存放状态管理相关代码,首先创建index.js:

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {

  },
  actions: {

  },
  mutations: {

  },
  getters: {

  }
})
export default store

然后在main.js文件中我们需要将此实例添加到构造的Vue实例中:

import store from './store'
/* eslint-disable no-new */
new Vue({
  template: `
  <div>
    <navbar />
    <section class="section">
      <div class="container is-fluid">
        <router-view></router-view>
      </div>
    </section>
  </div>
  `,
  router,
  store,
  components: {
    navbar
  }
}).$mount('#app')

然后,我们需要去完善Store定义:

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    projects: []
  },
  actions: {
    LOAD_PROJECT_LIST: function ({ commit }) {
      axios.get('/secured/projects').then((response) => {
        commit('SET_PROJECT_LIST', { list: response.data })
      }, (err) => {
        console.log(err)
      })
    }
  },
  mutations: {
    SET_PROJECT_LIST: (state, { list }) => {
      state.projects = list
    }
  },
  getters: {
    openProjects: state => {
      return state.projects.filter(project => !project.completed)
    }
  }
})
export default store

在本项目中,我们将原本存放在组件内的项目数据组移动到存储中,并且将所有关于状态的改变都通过动作进行而不是直接修改:

// /src/components/projectList.vue
<template lang="html">
  <div class="">
    <table class="table">
      <thead>
        <tr>
          <th>Project Name</th>
          <th>Assigned To</th>
          <th>Priority</th>
          <th>Completed</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="item in projects">
          <td>{{item.name}}</td>
          <td>{{item.assignedTo}}</td>
          <td>{{item.priority}}</td>
          <td><i v-if="item.completed" class="fa fa-check"></i></td>
        </tr>
      </tbody>
    </table>
  </div>
</template>
<script>
import { mapState } from 'vuex'
export default {
  name: 'projectList',
  computed: mapState([
    'projects'
  ])
}
</script>
<style lang="css">
</style>

这个模板还是十分直观,我们通过计算对象来访问Store中的状态信息。值得一提的是这里的mapState函数,这里用的是简写,完整的话可以直接访问Store对象:

computed: {
  projects () {
    return this.$store.state.projects
  }
}

mapState是Vuex提供的简化数据访问的辅助函数。我们视线回到project.vue容器组件,在该组件中调用这个$ store.dispatch('LOAD_PROJECT_LIST)来触发从服务端中加载项目列表:

<template lang="html">
  <div id="projects">
    <div class="columns">
      <div class="column is-half">
        <div class="notification">
          Project List
        </div>
        <project-list />
      </div>
    </div>
  </div>
</template>
<script>
import projectList from '../components/projectList'
export default {
  name: 'projects',
  components: {
    projectList
  },
  mounted: function () {
    this.$store.dispatch('LOAD_PROJECT_LIST')
  }
}
</script>

当我们启动应用时,Vuex状态管理容器会自动在数据获取之后渲染整个项目列表。现在我们需要添加新的Action与Mutation来创建新的项目:

// under actions:
ADD_NEW_PROJECT: function ({ commit }) {
  axios.post('/secured/projects').then((response) => {
    commit('ADD_PROJECT', { project: response.data })
  }, (err) => {
    console.log(err)
  })
}
// under mutations:
ADD_PROJECT: (state, { project }) => {
  state.projects.push(project)
}

然后我们创建一个简单的用于添加新的项目的组件addProject.vue:

<template lang="html">
  <button type="button" class="button" @click="addProject()">Add New Project</button>
</template>
<script>
export default {
  name: 'addProject',
  methods: {
    addProject () {
      this.$store.dispatch('ADD_NEW_PROJECT')
    }
  }
}
</script>

该组件会派发某个Action来添加组件,我们需要将该组件引入到projects.vue中:

<template lang="html">
  <div id="projects">
    <div class="columns">
      <div class="column is-half">
        <div class="notification">
          Project List
        </div>
        <project-list />
        <add-project />
      </div>
    </div>
  </div>
</template>
<script>
import projectList from '../components/projectList'
import addProject from '../components/addProject'
export default {
  name: 'projects',
  components: {
    projectList,
    addProject
  },
  mounted: function () {
    this.$store.dispatch('LOAD_PROJECT_LIST')
  }
}
</script>

重新运行下该应用会看到服务端返回的创建成功的提示,现在我们添加另一个功能,就是允许用户将某个项目设置为已完成。我们现在添加新的组件completeToggle.vue:

<template lang="html">
  <button type="button" class="button"  @click="toggle(item)">
    <i class="fa fa-undo" v-if="item.completed"></i>
    <i class="fa fa-check-circle" v-else></i>
  </button>
</template>
<script>
export default {
  name: 'completeToggle',
  props: ['item'],
  methods: {
    toggle (item) {
      this.$store.dispatch('TOGGLE_COMPLETED', { item: item })
    }
  }
}
</script>

该组件会展示一个用于切换项目是否完成的按钮,我们通过Props传输具体的项目信息然后通过触发TOGGLE_COMPLETED操作来使服务端进行相对应的更新与相应:

// actions
TOGGLE_COMPLETED: function ({ commit, state }, { item }) {
  axios.put('/secured/projects/' + item.id, item).then((response) => {
    commit('UPDATE_PROJECT', { item: response.data })
  }, (err) => {
    console.log(err)
  })
}
// mutations
UPDATE_PROJECT: (state, { item }) => {
  let idx = state.projects.map(p => p.id).indexOf(item.id)
  state.projects.splice(idx, 1, item)
}

UPDATE_PROJECT会触发项目列表移除对应的项目并且将服务端返回的数据重新添加到数组中:

app.put('/secured/projects/:id', function (req, res) {
  let project = data.filter(function (p) { return p.id == req.params.id })
  if (project.length > 0) {
    project[0].completed = !project[0].completed
    res.status(201).json(project[0])
  } else {
    res.sendStatus(404)
  }
})

最后一步就是将completeToggle组件引入到projectList组件中,然后将其添加到列表中:

// new column in table
<td><complete-toggle :item="item" /></td>
// be sure import and add to the components object

再谈引入状态管理的意义

现在我们的应用已经具备了基本的特性,这里我们再度回顾下文首的讨论,为什么我们需要大费周章的引入外部状态管理,将业务逻辑切分到组件外。譬如这里我们需要另一个组件来展示项目的统计信息,譬如项目的总数或者已完成项目的数目。我们肯定要避免重复地从服务端抓取数据,而是所谓的单一来源的真相。这里我们添加新的projectStatus.vue组件来展示项目的统计信息:

<template lang="html">
  <article class="message">
    <div class="message-header">
      <p>Project Status:</p>
    </div>
    <div class="message-body">
      <div class="control">
        <span class="tag is-info">Number of projects: {{projectCount}}</span>
      </div>
      <div class="control">
        <span class="tag is-success">Completed: {{completedProjects}}</span>
      </div>
    </div>
  </article>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
  name: 'projectStatus',
  computed: {
    ...mapGetters([
      'completedProjects',
      'projectCount'
    ])
  }
}
</script>

该组件会展示项目的总数与已完成项目的总数,上面我们使用了maoGetters辅助函数来减少冗余代码:

getters: {
  completedProjects: state => {
    return state.projects.filter(project =>project.completed).length
  },
  projectCount: state => {
    return state.projects.length
  }
}

最后我们将该统计信息添加到项目列表中,效果图示如下:



猜你喜欢

转载自blog.csdn.net/weixin_41646716/article/details/80451129