Structuring Vue with Vuex and Component-Specific Data

I see many Vue.js projects using this structure:

β”œβ”€β”€ main.js β”œβ”€β”€ api β”‚ └── index.js β”‚ └── services #containing files with api-calls β”‚ β”œβ”€β”€ global.js β”‚ β”œβ”€β”€ cart.js β”‚ └── messages.js β”œβ”€β”€ components β”‚ β”œβ”€β”€ Home.vue β”‚ β”œβ”€β”€ Cart.vue β”‚ β”œβ”€β”€ Messages.vue β”‚ └── ... └── store β”œβ”€β”€ store.js β”œβ”€β”€ actions.js #actions to update vuex stores β”œβ”€β”€ types.js └── modules β”œβ”€β”€ global.js β”œβ”€β”€ cart.js └── ... 

(An example with this structure is: Jackblog '.)

So, for example, Cart.vue wants to update inCart data in Vuex. To do this, Cart imports actions.js :

import { inCart } from '../../store/actions'

actions.js imports the api index.js so that it can connect to the api. It then updates the values ​​in the Vuex repository.

Okay, so this is clear to me. But now I want to work with the Messages.vue module. This module must connect to the api to receive all messages, but there is no need to store the results in Vuex. The only component that needs data is Message.vue itself, so it should only be stored in the data() component.

Question: I cannot import actions.js inside Messages.vue , because the action should not update Vuex. But I can not move actions.js to the api directory, because it violates the logic of placing all the files that add data to the store in the store directory. In addition, the logic should be placed inside Messages.vue . For example, when api returns an error, the local constant error must be set. Therefore, it cannot be processed as a separate file.

What is the recommended application structure for making api calls and storing them in vuex or local data() ? Where to put the action file, api files, etc.? When looking at the Jackblog example, it only supports Vuex data. How to restructure this to support both?

+2
source share
2 answers

I use axios as an HTTP client to make API calls, I created a gateways folder in my src folder and I have files for each backend, creating axios instances , like the following

myApi.js

 import axios from 'axios' export default axios.create({ baseURL: 'http://localhost:3000/api/v1', timeout: 5000, headers: { 'X-Auth-Token': 'f2b6637ddf355a476918940289c0be016a4fe99e3b69c83d', 'Content-Type': 'application/json' } }) 

The same instances are used both for component actions and for vuex to receive data, the following details of both methods.

Component data

If the data is used only in the component, for example, in your case Messages.vue , you can get a method that will extract data from the api as follows:

 export default { name: 'myComponent', data: () => ({ contents: '', product: [] }), props: ['abc'], methods: { getProducts (prodId) { myApi.get('products?id=' + prodId).then(response => this.product = response.data) }, error => { console.log('Inside error, fetching products failed') //set error variable here }) } ..... 

Filling Vuex Data

If you support product-related data in a dedicated vuex module , you can send an action from a method in the component that will call the backend API internally and populate the data in the repository, the code will look something like this:

Code in component:

 methods: { getProducts (prodId) { this.$store.dispatch('FETCH_PRODUCTS', prodId) } } 

Code in vuex repository:

 import myApi from '../../gateways/my-api' const state = { products: [] } const actions = { FETCH_PRODUCTS: (state, prodId) => { myApi.get('products?id=' + prodId).then(response => state.commit('SET_PRODUCTS', response)) } } // mutations const mutations = { SET_PRODUCTS: (state, data) => { state.products = Object.assign({}, response.data) } } const getters = { } export default { state, mutations, actions, getters } 
+6
source

Short answer: given the Jackblog example, you just need to import the β€œapi” from the component and use the API directly. Do not import actions. In Messages.vue forget about the store. You do not need an action layer that is attached to the repository. You just need an API.


Long answer: in the project we have the following

  • An Ajax library wrapper that provides a function called remote that takes two parameters: a string and an object. The line tells us what we're trying to achieve (for example, "saveProductComment"), and the object is the payload (the names and values ​​of the parameters that should be sent to the server).

  • Each application module may contain a routes.js file that displays a β€œline” above with the route configuration. For example: saveProductComment: 'POST api/v1/products/{product_id}/comment'

Note. I am not using the term β€œapplication module” for a single .js or .vue , which is treated by NodeJS or Webpack as a β€œmodule”. I call the β€œapplication module” a complete folder containing the application code for a specific domain (examples: β€œcart”, module β€œaccount”, module β€œcomments”, etc.).

  1. From anywhere, you can call remote('saveProductComment', { product_id: 108, comment: 'Interesting!' }) , And it returns Promise . The shell uses the route configuration to create the correct request, and also analyzes the response and processes errors. In any case, the remote function always returns Promise .

  2. Each application module can also provide its own storage module , where we define the initial state, mutations, actions and getters associated with the module. We use the term "manager" for the government control code. For example, we can have a commentsManager.js file, letting the repository module keep track of β€œcomments”.

  3. In Manager, we use the remote function to call the API inside Vuex actions. We return Promise from the remote, but we also attach a callback to it that processes the results. In the callback, we call mutation functions to commit the results:


 newProductComment ({ commit }, { product, contents }) { return remote('saveProductComment', { product_id: product.id, comment: contents }) .then(result => { commit('SOME_MUTATION', result.someProperty) }) } 

Now, if we want to use the same API call outside the Vuex context, but directly inside the component, we just need to use the same code in the Vue component. For instance:

 export default { name: 'myComponent', data: () => ({ contents: '', someData: null }), props: ['product'], methods: { saveComment () { remote('saveProductComment', { product_id: this.product.id, comment: this.contents }) .then(result => { this.someData = result.someProperty }) } } } 

As for the structure of the application, it is really important for us:

  • app properly divided into individual issues; what we call "application modules"; one module for each specific thing

  • we have a β€œmodules” folder containing a folder for each β€œapplication module”

  • inside a specific β€œapplication module folder”, we have config config on routes.js , mapping the first parameter of the remote function to the route configuration; our custom code selects the HTTP method, interpolates the URL, does all kinds of fancy things that suit our needs properly; but anywhere in the rest of the application code, we just use it in such a simple way: remote('stuffNeededToBeAccomplished', { dataToAccomplishTheNeed })

  • In other words, the hard work is in matching and in the shell of the Ajax library (you can use any Ajax library for actual requests); that it absolutely does not depend on the use of Vue / Vuex

  • we have a Vuex repository, also divided into modules; usually, in the application module, we have the corresponding storage module using the routes defined there

  • at the main entry point we import application modules; index.js of each module takes care of registering both routes in the Ajax wrapper and storage modules in Vuex (so we just need to import and not take any further steps to have accessible routes for Ajax and available storage modules in Vuex)

+2
source

Source: https://habr.com/ru/post/1261867/


All Articles