VUE 和 VUEX 中的数据流

本文讲的是VUE 和 VUEX 中的数据流,

看起来在 Vue 里面困扰开发者的事情之一是如何在组件之间共享状态。对于刚刚接触响应式编程的开发者来说,像Vuex这种库,有着繁多的新名词及其关注点分离的方式,往往令人望而生畏。特别是当你只希望分享一两个数据片段时,(这一套逻辑的复杂性)就显得有点过分了。

考虑到这一点的话,我想我应该把两个简短的演示放到一起展示出来。第一个通过使用一个简单的 JavaScript 对象,在每个新组件当中引用来实现共享状态。第二个做了和 Vuex 一样的事情,当它运行成功的时候,也是一个你绝对不应该做的事情的示例(我们将在最后看看为什么)。

你可以通过查看下面这些演示来开始:

或者获取这个仓库并在本地运行试试看!代码里很多地方是2.0版本的特性,但我接下来想讲的数据流概念在任何版本里都是相关的,并且它可以通过一些改变很轻易地向下兼容到1.0。

这些演示都是一样的功能,只是实现的方法不同。应用程序由两个独立的聊天组件实例组成。当用户在一个实例里提交一个消息的时候,它应该在两个聊天窗口都出现,因为消息状态是共享的,下面是一个截图:

用一个对象共享状态

开始前,让我们先来看看数据是如何在示例的应用程序当中流转的。

在这个演示里,我们将使用一个简单的 JavaScript 对象:var store = {...},在Client.vue组件的实例之间共享状态。下面是关键文件的重要代码部分:

index.html

1

2

3

4

5

6

7

8

9

10

11


<div id="app"></div>

<script>

var store = {

state: {

messages: []

},

newMessage (msg) {

this.state.messages.push(msg)

}

}

</script>

这里有两个关键的地方:

  1. 我们通过把这个对象直接添加到index.html里来让其对整个应用程序可用,也可以将它注入到应用程序里更下一层的作用链,但目前直接添加显然更快捷简单。
  2. 我们在这里保存状态,但同时也提供了一个函数来调用它。相比起分散在组件各处的函数,我们更倾向于让它们保持在一个地方(便于维护),并在任何需要它们的地方简单使用。
App.vue

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22


<template>

<div id="app">

<div class="row">

<div class="col">

<client clientid="Client A"></client>

</div>

<div class="col">

<client clientid="Client B"></client>

</div>

</div>

</div>

</template>

<script>

import Client from './components/Client.vue'

export default {

components: {

Client

}

}

</script>

这里我们引入了 Client 组件,并创建了两个它的实例,使用一个属性:clientid,来对每个实例进行区分。事实上,你应该更动态地去实现这些,但别忘了,目前快捷简单更重要。

注意一点,到这里我们还完全没有同步任何状态。

Client.vue

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39


<template>

<div>

<h1>{{ clientid }}</h1>

<div class="client">

<ul>

<li v-for="message in messages">

<label>{{ message.sender }}:</label> {{ message.text }}

</li>

</ul>

<div class="msgbox">

<input v-model="msg" placeholder="Enter a message, then hit [enter]" @keyup.enter="trySendMessage">

</div>

</div>

</div>

</template>

<script>

export default {

data() {

return {

msg: '',

messages: store.state.messages

}

},

props: ['clientid'],

methods: {

trySendMessage() {

store.newMessage({

text: this.msg,

sender: this.clientid

})

this.resetMessage()

},

resetMessage() {

this.msg = ''

}

}

}

</script>

下面是应用程序的主要内容:

  1. 在该模板里,设置一个v-for循环去遍历messages集合。
  2. 绑定在文本输入框上的v-model简单地存储了组件的本地数据对象msg
  3. 同样在数据对象里,我们创建了一个store.state.messages的引用,它将触发组件的更新。
  4. 最后,将 enter 键绑定到trySendMessage函数,这个函数包含了以下几个功能:
    1. 准备好需要存储的数据(发送者和消息的一个字典对象)。
    2. 调用定义在共享存储里的newMessage函数。
    3. 调用一个清理函数:resetMessage,重置输入框。通常你更应该在一个promise完成之后再调用它。

这就是使用对象的方法,来试一试

用 Vuex 共享状态

好了,现在来试试看用 Vuex 实现。同样的,先上图,也便于我们将 Vuex 的术语(actions,mutations等等)对应到我们刚刚完成的示例中。

正如你所看到的,Vuex 简单地形式化了我们刚刚完成的过程。使用它的时候,所做的事情其实和我们上面做过的非常像:

  1. 创建一个用来共享的存储,在这个例子中它将通过 vue/vuex 注入到组件当中。
  2. 定义组件可以调用的 actions,它们仍然是集中定义的。
  3. 定义实际接触 store 中 state 的 mutations。我们这么做,actions 内就可以将不止一个 mutation 组合在一起,或者执行逻辑去决定调用哪一个 mutation。这意味着你再也不用担心组件当中的业务逻辑了,成功!
  4. 当状态更新时,任何拥有 getter,计算属性和映射到 store 的组件都会被立即更新。

同样再来看看代码:

main.js

1

2

3

4

5

6

7


import store from './vuex/store'

new Vue({ // eslint-disable-line no-new

el: '#app',

render: (h) => h(App),

store: store

})

这次,我们用 Vuex 创建了一个存储并将其直接传入应用程序当中,替代掉了之前index.html中的 store 对象。在继续之前,先来看一下这个存储:

store.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21


export default new Vuex.Store({

state: {

messages: []

},

actions: {

newMessage ({commit}, msg) {

commit('NEW_MESSAGE', msg)

}

},

mutations: {

NEW_MESSAGE (state, msg) {

state.messages.push(msg)

}

},

strict: debug

})

和我们自己创建的对象非常相似,但是多了一个mutations对象。

Client.vue

1

2

3

4

5

6

7

8


<div class="row">

<div class="col">

<client clientid="Client A"></client>

</div>

<div class="col">

<client clientid="Client B"></client>

</div>

</div>

和上次一样的配方。(惊人的相似,对吧?)

Client.vue

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30


<script>

import { mapState, mapActions } from 'vuex'

export default {

data() {

return {

msg: ''

}

},

props: ['clientid'],

computed: {

...mapState({

messages: state => state.messages

})

},

methods: {

trySendMessage() {

this.newMessage({

text: this.msg,

sender: this.clientid

})

this.resetMessage()

},

resetMessage() {

this.msg = ''

},

...mapActions(['newMessage'])

}

}

</script>

模板仍然刚好一样,所以我甚至不需要费心怎么去引入它。最大的不同在于:

  1. 使用mapState来生成对共享消息集合的引用。
  2. 使用mapActions来生成创建一个新消息的动作(action)。

(注意:这些都是 Vuex 2.0特性。)

好的,做完啦!也来看一下这个演示吧。

结论

所以,正如你所希望看到的,自己进行简单的状态共享和使用 Vuex 进行共享并没有多大区别。而 Vuex 最大的优点在于它为你形式化了集中处理数据存储的过程,并提供了所有功能方法去处理那些数据。

最初,当你阅读 Vuex 的文档和示例的时候,它那些针对 mutations,actions 和 modules 的单独文档很容易让人感觉困扰。但是如果你敢于跨出那一步,简单地在store.js文件里写一些关于它们的代码来开始学习。随着这个文件的大小增加,你就将找到正确的时间移步到actions.js里,或者是把它们更进一步地分离开来。

不要着急,慢慢来,一步一个台阶。当然也可以使用vue-cli从创建一个模板开始,我使用browserify模板,并把下面的代码添加进我的package.json文件。


1

2

3

4


"dependencies": {

"vue": "^2.0.0-rc.6",

"vuex": "^2.0.0-rc.5"

}

还在看吗?

我知道我还说过要再讲一个“不好的”方式。再次,这个演示恰好也是一样的。不好的地方在于我利用了 Vue 2.0 里单向绑定的特性来注入回调函数,从而允许了父子模板之间顺序的双向绑定。首先,来看一下2.0文档中的这个部分,然后再来看看我这个不好的方法。

App.vue

1

2

3

4

5

6

7

8


<div class="row">

<div class="col">

<client clientid="Client A" :messages="messages" :callback="newMessage"></client>

</div>

<div class="col">

<client clientid="Client B" :messages="messages" :callback="newMessage"></client>

</div>

</div>

这里,我在组件上使用了一个属性将一个动态绑定传递到messages集合里。但是,我同时还传递了一个动作函数,所以可以在子组件里调用它。

Client.vue

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22


<script>

export default {

data() {

return {

msg: ''

}

},

props: ['clientid', 'messages', 'callback'],

methods: {

trySendMessage() {

this.callback({

text: this.msg,

sender: this.clientid

})

this.resetMessage()

},

resetMessage() {

this.msg = ''

}

}

}

</script>

这里就是不好的做法。

要问为什么有这么不好吗?

  1. 我们正在破坏之前图中所展示的单向循环。
  2. 我们创建了一个在组件及其父组件之间的紧密耦合。
  3. 这将变得不可维护。如果你在组件里需要20个函数,你就将添加20个属性,管理它们的命名等等,然后,如果任何东西发生改变,呃!

所以为什么还要再展示这段?因为我和其他人一样很懒。有时我就会做这样的事情,仅仅想知道再继续做下去会有多么糟糕,然后我就会咒骂自己的懒惰,因为我可能要花上一小时或者一天的时间去清理它们。鉴于这种情况,我希望我可以帮助你尽早避免无谓的决定和错误,千万不要传递任何你不需要的东西。99%的情况下,一个单独的共享状态已经足够完美。(不久再详细讲讲那1%的情况)





原文发布时间为:2016年10月14日


本文来自合作伙伴掘金,了解相关信息可以关注掘金网站。

时间: 2025-01-21 07:31:15

VUE 和 VUEX 中的数据流的相关文章

weex中使用数据流工具Vuex实践

背景 weex刚开源不久,作为一名前端,当然是抑制不住自己的好奇心想要尝尝鲜.虽然weex的最大亮点在于对于电商类应用场景能够提供快速动态部署的功能,但是用js就能写跑在native端的页面更加吸引我.于是在空余时间就开始捣腾着weex,想做一个native app看看weex有什么"能耐". 在开发过程中,在体会到weex周边工具带来的效率提升的同时,也发现了不少问题.除了weex本身刚开源肯定会存在各种问题之外,还有一些开发体验的问题.weex相关的问题都在GitHub上提了iss

巧用Vue.js+Vuex制作专门收藏微信公众号的app_javascript技巧

本文一步一步教大家如何利用Vue.js + Vuex制作专门收藏微信公众号的app 项目地址: https://github.com/jrainlau/wechat-subscriptor   下载&运行 git clone git@github.com:jrainlau/wechat-subscriptor.git cd wechat-subscriptor && npm install npm run dev   // run in dev mode cd backend-se

用v-html解决Vue.js渲染中html标签不被解析的问题_javascript技巧

前言 最近在工作中遇到一个问题,在网页中后台传来的json数据中包含html标签,将该json数据绑定到Vue.js中对象中,对该对象进行for循环,发现数据中的html标签不能被解析,而是当作字符显示出来. 问题如下所示: 解决方法: Vue.js中提供了v-html这个指令来解决这个问题,或者对数据对象使用{{{vm.data}}}三个大括号来包裹对象,就可以正常解析了. 代码改动如下: 总结 以上就是这篇文章的全部内容了,希望本文的内容对大家都 学习或者工作能带来一定的帮助,如果有疑问大家

怎么用v-html解决Vue.js渲染中html标签不被解析的教程

前言 最近在工作中遇到一个问题,在网页中后台传来的json数据中包含html标签,将该json数据绑定到Vue.js中对象中,对该对象进行for循环,发现数据中的html标签不能被解析,而是当作字符显示出来. 问题如下所示: 解决方法: Vue.js中提供了v-html这个指令来解决这个问题,或者对数据对象使用{{{vm.data}}}三个大括号来包裹对象,就可以正常解析了. 代码改动如下: 总结 以上就是这篇文章的全部内容了,希望本文的内容对大家都 学习或者工作能带来一定的帮助,如果有疑问大家

Linux系统中的数据流重定向

Linux命令执行过程的数据传输情况如下所示: 1)标准输入(stdin):代码为0,使用<或<<: 2)标准输出(stdout):代码为1,使用>或>>: 3)标准错误输出(stderr):代码为2,使用2>或2>>: 其中>表示以覆盖的方式将数据写入文件或设备,>>表示以累加的方式将数据写入文件或设 备. 其中<表示将原本需要由键盘输入的数据改由文件内容来替代,而<<表示结束输入的意思. 举例来说,我要用cat直

向项目的文件夹中写入数据流

public void GetChargStationFileCount(HttpContext context) { var chargstationfilebll = new ChargStationFileBll(); var cPictureMessage = new Message<Picture>(); var fileid = context.Request.Params["id"]; string ret = null; if (string.IsNullO

使用Vue.js和Vuex实现购物车场景

什么是Vuex? Flux-inspired Application Architecture for Vue.js Vuex实际上是类Flux的数据管理架构.它主要帮我们更好的组织代码,让Vue中的状态通过状态管理维护起来.在实际项目运用中我们需要对组件的 组件本地状态(component local state) 和 应用层级状态(application level state) 进行区分. Vuex的作用就是汇集应用层级的状态到一处,方便管理. 说状态其实有些同学可能不太理解,那么在刚上手

09Vue.js快速入门-Vue入门之Vuex实战

9.1. 引言 Vue组件化做的确实非常彻底,它独有的vue单文件组件也是做的非常有特色.组件化的同时带来的是:组件之间的数据共享和通信的难题. 尤其Vue组件设计的就是,父组件通过子组件的prop进行传递数据,而且数据传递是单向的.也就是说:父组件可以把数据传递给子组件,但是 反之则不同.如下图所示:   vue父子传递 9.2. 单向数据流动 单方向的数据流动带来了非常简洁和清晰的数据流,纯展示性或者独立性较强的模块的开发确实非常方便和省事. 但是复杂的页面逻辑,组件之间的数据共享处理就会需

vue中的组件

什么是组件 组件(Component)是Vue.js最强大的功能之一.组件可以扩展HTML元素,封装可重用的代码.在较高层面上,组件是自定义元素,Vue.js的编译器为它添加特殊功能.在有些情况下,组件也可以是原生HTML元素的形式,以is特性扩展 使用组件 注册 我们可以通过以下方式创建一个Vue实例: new Vue({ el: '#some-element', // 选项 }) 要注册一个全局组件,可以使用Vue.component(tagName, options).例如: Vue.co