未加星标

[英] VUE 和 VUEX 中的数据流

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二04 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

It seems like one of the things that trips people up in Vue is how to share state across components. For those new to reactive programming, something like vuex can seem daunting with loads of new jargon and the way it separates concerns. It can also seem like overkill when all you want is to share one or two pieces of data.

With that in mind, I thought I’d pull together a couple of quick demos. The first implements shared state by using a simple javascript object that is referenced by each new component. The second does the same with vuex. There’s also an example of something that, while it works, you should never do. (We’ll look at why at the end.)

You can start by checking out the demos:

Using shared object Using vuex Using evil bindings

Or grab the repo and try it out locally! The code is 2.0 specific at a couple points, but the data flow theory is relevant in any version and is easily ported down to 1.0 with a couple changes that I try to note below.

The demos are all the same, just implemented differently. The app consists of two instance of a single chat component. Whena user posts a message in one instance, it should appear in both chat windows because the message state is what is shared. Here’s a screenshot:


[英] VUE 和 VUEX 中的数据流
Sharing State With An Object

To start off, let’s take a look at how the data will flow in our example app.


[英] VUE 和 VUEX 中的数据流

In this demo we’ll use a simple javascript object, var store = {...} , to share our state between the instances of the Client.vue component. Here’s the important bits of the key files.

index.html <div id="app"></div> <script> var store = { state: { messages: [] }, newMessage (msg) { this.state.messages.push(msg) } } </script>

There’s two key things here.

We make the object available to our entire app by just adding it to index.html . We could inject it into the app further down the chain, but this is quick and easy for now. We store our state here, but we also provide a function to act on that data. Rather than scatter unctions all over our components, we want to keep them all in one place, and simple use them whereever we need them. App.vue <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>

Here we import our client componet, and create two instances of it. We use a prop, clientid , to uniquely identify each client. In reality, you’d do this more dynamically, but remember, quick and easy for now.

One thing to note, we don’t even pull in any state here at all.

Client.vue <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>

Here’s the meat of the app.

In the template, we set up our v-for loop to iterate over the messages collection. The v-model binding on our text input simply stores the message the component’s local data object at msg . Also in the data object, we establish a reference to store.state.messages . This is what will trigger the update of our component. Lastly, we bind the enter key to the trySendMessage function. That function is a wrapper that Prepares the data to be stored (a dictionary of sender and message). Invokes the newMessage function we defined on our shared store. Calls a cleanup function, resetMessage , that resets the input box. Typically you’d call that after a promise is fulfilled.

That’s it! Gogive it a try.

Sharing State With Vuex

Okay, so now let’s try it with vuex. Again, a diagram, so we can map vuex’s terminology (actions, mutations, etc) to the example we just went through.


[英] VUE 和 VUEX 中的数据流

As you can see, vuex simply formalizes the process we just went through. When you use it, you do the very same things we did above:

Create a store that is shared, in this case injected into components for you by vue/vuex. Define actions that components can call, so they remain centralized. Define mutations which actually touch the store’s state. We do this so actions can compose more than one mutation, or perform logic todetermine which mutation to call. That means you never have to worry about that business logic in the components. Win! When the state is updated, any component with a getter, computed property, or other mapping to the store will be instantly up-to-date.

Again, the code.

main.js import store from './vuex/store' new Vue({ // eslint-disable-line no-new el: '#app', render: (h) => h(App), store: store })

This time, instead of a store object in the index, we create it with vuex and pass it into our app directly. Let’s take a look at the store, before we continue.

store.js 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 })

Very similar to the object we made ourselves, but with the addition of the mutations object.

Client.vue <div class="row"> <div class="col"> <client clientid="Client A"></client> </div> <div class="col"> <client clientid="Client B"></client> </div> </div>

Same deal as last time. (Amazing how similar it is, right?)

Client.vue <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>

The template remains exactly the same, so I didn’t even bother to include it. The big differences here are:

We use mapState to bring in the reference to our shared messages collection. we use mapActions to bring in the action that will create a new message.

( Note : These are vuex 2.0 features.)

And bingo, we are done! Feel free to go give this one a look too .

Conclusions

So, as you can hopefully see, there is not a huge gulf between simply sharing state on your own, and using vuex. The huge advantage to vuex is that it formalizes the process of centralizing your data store for you, and providing all the machinery to work wirth that data.

At first, when you read the vuex docs or examples, it can seem daunting with the individual files for mutations, actions and modules. But if you are just starting out, simply write all of those in the single store.js file to begin. As your file size grows, you’ll find the right time to move the actions into actions.js or to split them out even further.

Don’t fret, take it slow, and you’ll be up in no time. And definitely start off with a template used with vue-cli . I use the browserify template, and add the following to my package.json .

"dependencies": { "vue": "^2.0.0-rc.6", "vuex": "^2.0.0-rc.5" } Still Here?

I know, I promised the “evil” way. Once again, the demo isexactly the same. What is evil is that I am exploiting the one-way binding nature of Vue 2.0 to inject the callback function, thus allowing a two-way binding of sorts between the parent and child templates. First, check out this portion of the 2.0 docs , then take a look at my evil.

App.vue <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>

Here, I use a dynamic binding to pass in the messages collection using a prop on the component. But , I also pass in the action function so I can call it from the child component.

Client.vue <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>

Here’s the evil in action.

Why so bad you ask?

We are defeting the one-way loop found in the diagrams above. We create a ridculously tight coupling between the component and it’s parent. This would be impossible to maintain. If you needed twenty functions in the component, you’d have to add twenty props, manage their names, etc, etc. Then, if anything ever changed, ugh!

So why bother showing this at all? Because I am lazy just like everyone else. Sometimes I do something like this only to discover how horrible it is down the road. Then I curse my lazy self because I have hours, or days, of cleanup to do. In this case, I hope I can help you avoid costly decisions or mistakes early by advocating for never passing around anything you don’t need to. In 99% of the cases I’ve run into, a single shared state is more than perfect. (More on that 1% case soon).

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: VU数据数据流SAGE
分页:12
转载请注明
本文标题:[英] VUE 和 VUEX 中的数据流
本站链接:http://www.codesec.net/view/480701.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(38)