vue组件通信传值——Vuex
一、Vuex介绍
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 也集成到 Vue 的官方调试工具devtools extension,提供了诸如零配置的 time-tavel 调试、状态快照导入导出等高级调试功能。
1、状态管理模式
一个简单的 Vue 计数应用范例如下所示:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
状态自管理应用包含如下部分:
- state,驱动应用的数据源;
- view,以声明方式将state映射到视图;
- actions,响应在view上的用户输入导致的状态变化;
2、单向数据流理念
用下图表示“单向数据流”理念的简单示意:
但在应用遇到 多组件共享状态 时,单向数据流的简洁性很容易遭到破坏:
(1)问题一:多个视图依赖于同一状态。
传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
(2)问题二:来自不同视图的行为需要变更同一状态。
经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
(3)解决思路
把组件的共享状态抽取出来,以一个全局单例模式管理!!
在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。
以上也是Vuex背后的基本思想,借鉴了 Flux、Redux 和 The Elm Architecture。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
3、Vuex图解
首先通过vue组件触发,比如执行了一个按钮事件,修改一个页面中的数据。然后通过dispatch方法分发到Vuex(类似一个商店,包含有Actions、Mutations、State),将数据共享给Vuex中的State(一定是关联于某个组件的某个标签上);当要更改数据时,先分发到Actions,再提交给Mutations,随后修改State。最后通过Render方法将数据渲染到vue组件中。
需要特别注意:Dispatch这一步是异步的,而Commit这一步是同步的。
4、项目结构
(1)Vuex必须遵循的规则
Vuex不限制代码结构,但规定了一些需要遵守的规则:
1)应用层级的状态应集中到单个 store 对象中;
2)提交 mutation 是更改状态的唯一方法,且这个过程是同步的;
3)异步逻辑都应该封装在 action 里面。
只要遵循上述规则,可以任意组织代码。如果遇到store文件过大的情况,只需将 action、mutation 和 getter 分割到单独的文件。
(2)项目结构示例
对于大型应用,会希望将 Vuex 相关代码分割到模块中。如下例所示:
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
二、Vuex实践
1、安装Vuex
(1)直接下载/CDN引用方式
CDN地址:https://unpkg.com/vuex,该链接一直指向NPM上发布的最新版本。
还可以使用 https://unpkg.com/vuex@2.0.0 这样的方式指定特定的版本。
当使用全局script标签引用 vuex 时,会进行自动安装:
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>
(2)NPM安装
npm install vuex --save
在模块化的打包系统中,必须显式地通过 Vue.use()来安装Vuex:
import Vue from 'vue'
import Vuex from 'vuex' Vue.use(Vuex)
2、vuex-demo项目创建配置
创建测试项目如下所示:
$ vue init webpack vuex-demo ? Project name vuex-demo
? Project description A Vue.js project
? Author xiugeng <@qq.com>
? Vue build standalone
? Install vue-router? No
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recom
mended) npm vue-cli · Generated "vuex-demo".
安装vuex组件:
$ cd vuex-demo/
$ npm install vuex -S
3、Vuex应用核心——Store(仓库)
“Store”基本上就是一个容器,它包含着应用中大部分的状态(state)。
(1)Vuex和单纯的全局对象的两点不同
1)Vuex的状态存储是响应式的
当Vue组件从 Store 中读取状态的时候,若 Store 中状态发送变化,那么相应的组件也会相应得到高效更新。
2)不能直接改变 Store 中的状态
改变 Store 中的状态的唯一途径就是下显式地提交(commit)mutation。这样使得可以方便地跟踪每个状态的变化,从而能够通过实现一些工具帮助更好地了解自己的应用。
(2)简单 Store样例
安装 Vuex 后,创建一个 store。创建 src/store 目录,再创建 src/store/index.js 文件,用作组装模块并导出store。
import Vue from 'vue'
import Vuex from 'vuex' // 使用插件(如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex))
Vue.use(Vuex); const store = new Vuex.Store({
// 五大将:state mutation action getter module
state:{
count: 1
},
mutations:{ },
actions:{ }
}); export default store;
在 src/main.js 文件中引入Store,将store保存在组件中,共享store状态:
import Vue from 'vue'
import App from './App'
import store from './store/index' Vue.config.productionTip = false; /* eslint-disable no-new */
new Vue({
el: '#app',
store, // store保存在组件中,可共享store状态
components: { App },
template: '<App/>'
})
在修改 src/components/HelloWorld.vue,使用computed实时监听状态对象:
<template>
<div class="hello">
<h2>{{myCount}}</h2>
</div>
</template> <script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
computed: {
myCount(){
// 通过 store.state 来获取状态对象
return this.$store.state.count;
}
}
}
</script>
由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。
显示效果:
(3)子组件共享store信息
创建组件 src/components/Child.vue,编码内容如下所示:
<template>
<div>
<p>{{myCount}}</p>
</div>
</template> <script>
export default {
name: "Child",
data() {
return { };
},
computed: {
myCount(){
return this.$store.state.count;
}
}
};
</script>
在组件 src/components/HelloWorld.vue 中引入子组件,形成父子关系:
<template>
<div class="hello">
<h2>{{myCount}}</h2>
<!-- 渲染子组件 -->
<Child/>
</div>
</template> <script>
// 引入子组件,形成父子关系
import Child from './Child' export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
computed: {
myCount(){
// 通过 store.state 来获取状态对象
return this.$store.state.count;
}
},
components: {
Child
}
}
</script>
显示效果如下所示:
三、Vuex——mutation使用
1、 mutation 状态变更
更改 Vuex 的 store 中的状态(state)的唯一方法是提交(commit) mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
修改 HelloWorld.vue 文件:
<template>
<div class="hello">
<h2>{{myCount}}</h2>
<!-- 渲染子组件 -->
<Child/> <button @click="change">修改</button>
</div>
</template> <script>
// 引入子组件,形成父子关系
import Child from './Child' export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
computed: {
myCount(){
// 通过 store.state 来获取状态对象
return this.$store.state.count;
}
},
components: {
Child
},
methods: {
change(){
// 修改状态
this.$store.commit('addCount',3)
}
}
}
</script>
再在 store/index.js中声明 mutations 方法 addCount:
const store = new Vuex.Store({
// 五大将:state mutation action getter module
state:{
count: 1
},
mutations:{
// 声明方法
// 只能做同步操作,不能直接commit
addCount(state, val) {
state.count += val;
}
},
actions:{ }
});
显示效果:
点击修改按钮,页面上显示的数字从1》4》7》10》13实时修改变换。
通过提交 mutation 的方式,而非直接改变 store.state.count
,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。
2、mutation中不能做异步操作
Mutation 重要的原则就是要记住 mutation 必须是同步函数。
每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。
(1)在HelloWorld.vue中添加异步操作按钮
<template>
<div class="hello">
<h2>{{myCount}}</h2>
<!-- 渲染子组件 -->
<Child/>
<button @click="change">同步修改</button>
<button @click="asyncHandler">异步修改</button>
</div>
</template> <script>
// 引入子组件,形成父子关系
import Child from './Child' export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
computed: {
myCount(){
// 通过 store.state 来获取状态对象
return this.$store.state.count;
}
},
components: {
Child
},
methods: {
change(){
// 修改状态,更改 Vuex的store中的状态state的唯一方法是提交(commit)mutation
this.$store.commit('addCount',3);
},
asyncHandler(){
this.$store.commit('asyncHandler',1);
}
}
}
</script>
(2)mutation处理提交的事件
在src/store/index.js中添加异步操作:
import Vue from 'vue'
import Vuex from 'vuex' // 使用插件(如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex))
Vue.use(Vuex); const store = new Vuex.Store({
// 五大将:state mutation action getter module
state:{
count: 1
},
mutations:{
// 声明方法
// 只能做同步操作,不能直接commit
addCount(state, val) {
state.count += val; // 同步操作
},
asyncHandler(state, val) {
setTimeout(()=>{ // 异步操作
state.count += val;
}, 2000);
},
},
actions:{ }
}); export default store;
(3)现象展示
1.使用Vue Devtool调试系统:
2.点击同步修改:
3.点击异步修改:
注意:异步操作执行后,页面上的值发生了修改,但却没有修改state中的值。其他组件调用state值时会出现数据对不上。
四、vuex——Action 的使用
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态
- Action 可以包含任意异步操作
1、注册Action
注册一个简单action示例:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。
实践中,通常使用 ES2015 的参数解构 来简化代码(尤其是需要多次调用 commit 的场景)。
src/store/index.js 修改如下所示:
import Vue from 'vue'
import Vuex from 'vuex' // 使用插件(如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex))
Vue.use(Vuex); const store = new Vuex.Store({
// 五大将:state mutation action getter module
state:{
count: 1
},
mutations:{
// 声明方法
// 只能做同步操作,不能直接commit
addCount(state, val) {
state.count += val; // 同步操作
},
asyncHandler(state, val) {
// 只做同步操作
state.count += val;
},
},
actions:{
// Action类似mutation,但Action提交的是mutation,不直接变更状态,且可以包含任意异步操作
addCount({commit},val) {
commit('addCount',val);
},
asyncHandler({commit},val) {
setTimeout(()=>{
commit('asyncHandler', val);
},2000)
}
}
}); export default store;
2、分发Action
Action 通过 store..dispatch 方法触发,在组件中使用 this.$store.dispatch('xxx')
分发 action,修改HelloWorld.vue如下所示:
<template>
<div class="hello">
<h2>{{myCount}}</h2>
<!-- 渲染子组件 -->
<Child/>
<button @click="change">同步修改</button>
<button @click="asyncHandler">异步修改</button>
</div>
</template> <script>
// 引入子组件,形成父子关系
import Child from './Child' export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
computed: {
myCount(){
// 通过 store.state 来获取状态对象
return this.$store.state.count;
}
},
components: {
Child
},
methods: {
change(){
// 修改状态,更改 Vuex的store中的状态state的唯一方法是提交(commit)mutation
this.$store.dispatch('addCount',3);
},
asyncHandler(){
this.$store.dispatch('asyncHandler',1);
}
}
}
</script>
异步修改state的值,问题解决,点击两次同步修改和一次异步修改后,显示如下所示:
之所以不直接分发 mutation 而要多这一步操作,主要是因为 mutation必须同步执行这个限制。而Action则没有这个约束,可以在action内部执行异步操作。
vue组件通信传值——Vuex的更多相关文章
- vue 组件通信传值
父子组件通信: 子组件 <template> <div> <h3 @click="alerrt"> 我是子组件一</h3> < ...
- Vue 组件间传值
前言 Vue 作为现在比较火的框架之一,相信您在使用的过程中,也会遇到组件间传值的情况,本文将讲解几种 Vue 组件间传值的几种方法,跟着小编一起来学习一下吧! 实现 注意: 学习本文,需要您对 Vu ...
- VUE 组件通信总结
1.prop 父组件传递给子组件,即通过VUE本身具有的Props属性来传递值 Child组件 <template> <span>{{message}}</span> ...
- vue组件之间传值方式解析
vue组件之间传值方式解析一.父组件传到子组件 1.父组件parent代码如下: <template> <div class="parent"> <h ...
- 在vue组件中使用vuex的state状态对象的5种方式
下面是store文件夹下的state.js和index.js内容 //state.js const state = { headerBgOpacity:0, loginStatus:0, count: ...
- 【Vue组件通信】props、$ref、$emit,组件传值
1.什么是组件通信 组件间如何通信,也就成为了vue中重点知识,组件通信,涉及到组件之间数据的传递.类似NET POST/GET参数传递. Vue基本的三种传递方式** (props.\(ref.\) ...
- vue组件通信的几种方式
最近用vue开发项目,记录一下vue组件间通信几种方式 第一种,父子组件通信 一.父组件向子组件传值 1.创建子组件,在src/components/文件夹下新建一个Child.vue 2.Child ...
- vue组件通信全面总结
写在前面 组件间的通信是是实际开发中非常常用的一环,如何使用对项目整体设计.开发.规范都有很实际的的作用,我在项目开发中对此深有体会,总结下vue组件间通信的几种方式,讨论下各自的使用场景 文章对相关 ...
- vue组件通信新姿势
在vue项目实际开发中我们经常会使用props和emit来进行子父组件的传值通信,父组件向子组件传递数据是通过prop传递的, 子组件传递数据给父组件是通过$emit触发事件来做到的.例如: Vue. ...
随机推荐
- vim 下修改tab键为四个空格
最近在运行python的时候,发现tab键在在运行过程中无法使用,报错:IndentationError: unindent does not match any outer indentation ...
- KiRaiseException函数逆向
KiRaiseException函数是记录异常的最后一步,在这之后紧接着就调用KiDispatchException分发异常. 我们在逆向前,先看一下书中的介绍: 1. 概念认知: KiRaiseEx ...
- wpf 当DataGrid列模版是ComboBox时,显示信息
实际工作中,有时DataGrid控件某一列显示数据是从Enum集合里面选择出来的,那这时候设置列模版为ComboBox就能满足需求.而关于显示的实际内容,直接是Enum的string()返回值可能 ...
- DevExpress的TextEdit控件没法调整高度解决
场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...
- DevExpress的分页Tab控件XtraTabControl控件的使用
场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...
- HTML5新标签与特性---多媒体
多媒体标签 embed:标签定义嵌入的内容 audio:播放音频 video:播放视频 多媒体 embed(会使用) embed可以用来插入各种多媒体,格式可以是 Midi.Wav.AIFF.AU.M ...
- 图片在DIV里边水平垂直居中
图片在一个DIV中要垂直水平居中,首先定义一个DIV .wrap{ width: 600px; height: 400px; border: 1px #000 solid; } 插入图片 <di ...
- MES系统在小批量电子行业生产管理中的应用
小批量电子产品生产管理的主要问题 电子电器制造类企业,既有单件小批量生产,也有批量生产:有按库存生产,也有按订单生产,属于典型的离散制造行业.因产品的不同其生产工艺流程也不尽相同,生产设备的布置不是按 ...
- Android开发利器之pidcat
介绍pidcat: pidcat 是Android届JakeWharton大神开发的一款命令行工具,堪称Android开发利器,它能方便Android程序猿捕获日志,过滤日志,定位程序问题,超级好用. ...
- 9.智能快递柜SDK(串口型锁板)
1.智能快递柜(开篇) 2.智能快递柜(终端篇) 3.智能快递柜(通信篇-HTTP) 4.智能快递柜(通信篇-SOCKET) 5.智能快递柜(通信篇-Server程序) 6.智能快递柜(平台篇) 7. ...