2019独角兽企业重金招聘Python工程师标准>>>

随着应用复杂度的增加,我们需要考虑如何进行应用的状态管理,将业务逻辑与界面交互相剥离,详细讨论参考笔者的2016-我的前端之路:工具化与工程化。Vue 为我们提供了方便的组件内状态管理的机制,下面这个例子就是常见的获取列表数据然后渲染到界面中:

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>

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

何谓 Vuex?

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

Vuex 中 Store 的模板化定义如下:

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()来调用更改 Store 中的状态。可以在组件中使用dispatch来发出 Actions。

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

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 对象允许将单一的 Store 拆分为多个 Store 的同时保存在单一的状态树中。随着应用复杂度的增加,这种拆分能够更好地组织代码,更多细节参考这里。

Example

在理解了 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 文件中我们需要将该 Store 实例添加到构造的 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

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

// /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>

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

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

mapState 是 Vuex 提供的简化数据访问的辅助函数。我们视线回到 project.vue 容器组件,在该组件中调用this.$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 Action 来使服务端进行相对应的更新与相应:

// 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].completedres.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

再谈引入状态管理的意义

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

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

转载于:https://my.oschina.net/u/2456768/blog/858673

详解 Vue Vuex 实践相关推荐

  1. js定义全局变量 vue页面_详解Vue.js 定义全局变量的几种实现方式

    详解Vue.js 定义全局变量的几种实现方式 发布于 2020-8-11| 复制链接 本篇文章主要介绍了VUE 全局变量的几种实现方式,小妖觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小妖 ...

  2. vue输入框输入触发事件_详解.vue文件中监听input输入事件(oninput)

    详解.vue文件中监听input输入事件(oninput) .vue文件其实是一个组件,关于它的说明我之前也写过一篇文章,地址:.vue文件,今天这篇文章要讲的是.vue文件中监听input的输入值变 ...

  3. Python数据科学-技术详解与商业实践视频教程

    Python数据科学-技术详解与商业实践(八大案例) 网盘地址:https://pan.baidu.com/s/13QrR_5Er6LgWCWzSb7qOrQ 提取码:s7vw 备用地址(腾讯微云): ...

  4. vue动态设置文字布局方式_详解Vue动态添加模板的几种方法

    动态添加模板需要收集原始数据的页面,这个时候我们需要很多原始数据收集模板,下面给大家详解Vue 动态添加模板的几种方法,希望对你学习这方面知识有所帮助. 通常我们会在组件里的 template 属性定 ...

  5. layui日期与vue_详解Vue.js和layui日期控件冲突问题解决办法

    详解Vue.js和layui日期控件冲突问题解决办法 发布于 2020-8-10| 复制链接 摘记: 事故还原: 今天在用layui的日期控件的时候发现一个问题,就是form表单中的日期选择之后,如果 ...

  6. 爬虫解析利器PyQuery详解及使用实践

    作者:叶庭云 整理:Lemon 爬虫解析利器 PyQuery详解及使用实践 之前跟大家分享了 selenium.Scrapy.Pyppeteer 等工具的使用. 今天来分享另一个好用的爬虫解析工具 P ...

  7. 详解Vue中watch的高级用法

    转载自  详解Vue中watch的高级用法 我们通过实例代码给大家分享了Vue中watch的高级用法,对此知识点有需要的朋友可以跟着学习下. 假设有如下代码: 1 2 3 4 5 6 7 8 9 10 ...

  8. vue调用手机相机相册_详解Vue调用手机相机和相册以及上传

    组件 选中{{imgList.length}}张文件,共{{bytesToSize(this.size)}} javaScript代码 export default { name: "cam ...

  9. render注册一个链接组件_详解vue 动态加载并注册组件且通过 render动态创建该组件...

    基于 iview Tabs 组件实现 功能:为每个 tab 动态创建不同的.特定的组件内容,而不需要大量的 import 组件并进行 component 注册 Index.vue import loa ...

最新文章

  1. 什么是OKR?目标管理如何做?
  2. visio 模具_小研爱科研 || 那些Visio里的小技巧你Get了吗?
  3. 硅谷理发460!奥巴马前手下发明AI理发机器人,10分钟理发,只收15%费用
  4. 2008-2013年写的10个小软件
  5. synchronized 原理知多少
  6. 攻防世界 web(二)
  7. 加载(WebView)页面,调JS刷新数据
  8. 版本控制工具--svn和git的使用(一) -----版本控制的好处以及分类
  9. oracle 能被2整除_2021辽宁公务员考试:好用的“整除”法
  10. 灰度斜坡intensity ramp和灰度台阶intensity step的区别
  11. 高校企业双向赋能,首届飞桨启航菁英计划圆满结束
  12. DMM6500+Keithley6517B/6514纳米发电机测试软件
  13. cufflinks修改使用
  14. 牛顿法迭代法 | matlab实现
  15. 解决tomcat启动时的45秒时间限制
  16. 什么高大填空四个字动人_照样子填空填四字成语什么什么什么地想
  17. nasmc++ 读取FAT12文件系统
  18. MDN Web Docs
  19. 无线pda是快递员随身携带的设备
  20. Mybatis(见资源)

热门文章

  1. gsoap 学习 1-由wsdl文件生成h头文件
  2. Hibernate的使用梳理
  3. 黄聪:BackGroundWorker解决“线程间操作无效: 从不是创建控件的线程访问它” (C# VS2008)...
  4. 建立YUM服务器CENTOS
  5. 使用C# 3.0编译器编译 Asp.Net 项目代码
  6. 基于jwt的用户登录认证
  7. 用模板实现单例模式(线程安全)、模板方式实现动态创建对象
  8. CES上百度无人车队炫技,陆奇要用“China speed”改变世界
  9. 驼峰命名法模态对话框
  10. nginx如何解决超长请求串