Vue如何使用Vuex进行结构化管理以及处理组件特定数据。

3

我看到很多Vue.js项目都使用这种结构:

├── 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
        └── ...

这种结构的一个例子是'Jackblog'。

例如,Cart.vue 想要在 Vuex 中更新 inCart 数据。为此,Cart 导入 actions.js

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

actions.js 导入 api 的 index.js,以便可以连接到 api。然后,它会更新 Vuex store 中的值。

好的,对我来说很清楚了。但现在,我想要处理 Messages.vue 模块。该模块也应该连接到 api 以获取所有消息,但不必将结果存储在 Vuex 中。唯一需要数据的组件是 Messages.vue 本身,因此它只应存储在组件的 data() 中。

问题: 我不能在 Messages.vue 中导入 actions.js,因为该操作不应该更新 Vuex。但我也不能将 actions.js 移动到 api 目录中,因为这会破坏将所有添加数据到 store 的文件放在 store-directory 中的逻辑。除此之外,逻辑应该放在 Messages.vue 中。例如,当 api 返回错误时,应设置一个本地的 error 常量。因此,它不能由单独的文件处理。

如何建议的应用程序结构来进行 API 调用并在 Vuex 或本地 data() 中存储它们?actions 文件、api 文件等应该放在哪里?当查看 Jackblog 示例时,仅支持 Vuex 数据。如何重构以支持两者?


一个深思熟虑的问题。感谢你的提问,Jordy。 - aero
2个回答

7

我正在使用axios作为HTTP客户端来进行API调用,我在src文件夹中创建了一个gateways文件夹,并为每个后端创建了文件,创建axios实例,如下所示:

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'
  }
})

这些实例既可以用于组件,也可以用于vuex操作以获取数据,以下是两种方式的详细信息。

填充组件数据

如果数据仅在组件中使用,例如您的Messages.vue案例,您可以编写一个方法从api中获取数据,如下所示:

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

填充 Vuex 数据

如果您在一个专用的 Vuex 模块 中维护产品相关数据, 您可以从组件中的方法中调度一个操作,该操作将在内部调用后端 API 并将数据填充到 store 中, 代码将类似于以下内容:

组件中的代码:

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

Vuex store 中的代码:

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
}

2

简短回答:考虑到Jackblog示例 - 您只需从组件中导入“api”,并直接使用API。不要导入actions。在Messages.vue中,忘记store。您不需要与store相关的actions层。您只需要API。


长篇回答:在项目中我们有以下内容

  1. 一个Ajax库包装器提供了一个名为remote的函数,该函数接受两个参数:一个字符串和一个对象。字符串告诉我们我们正在尝试实现什么(例如,“saveProductComment”),而对象是有效载荷(要发送到服务器的参数名称和值)。

  2. 每个应用程序模块都可以包含一个“routes.js”文件,该文件将上述“字符串”与路由配置进行映射。例如:saveProductComment: 'POST api/v1/products/{product_id}/comment'

注意:我使用术语“应用程序模块”来表示NodeJS或Webpack将作为“模块”处理的单个.js.vue文件。我将“应用程序模块”称为包含特定领域相关的应用程序代码的完整文件夹(示例:“cart”模块,“account”模块,“comments”模块,等等)。

  1. 从任何地方,我们都可以调用remote('saveProductComment',{product_id:108,comment:'Interesting!'}),它返回一个Promise。包装器使用路由配置来构建适当的请求,并解析响应并处理错误。在任何情况下,remote函数始终返回Promise

  2. 每个应用程序模块还可以提供自己的存储模块,其中定义了与模块相关的初始状态、变异、操作和getter。我们使用术语“Manager”来表示状态管理代码。例如,我们可以有一个名为“commentsManager.js”的文件,提供存储模块以处理“评论”。

  3. 在Manager中,我们使用remote函数在Vuex actions中进行API调用。我们返回来自远程的Promise,但我们还将处理结果的回调附加到它上面。在回调中,我们调用变异函数来提交结果:


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

现在,如果我们想在 Vuex 上下文之外直接在组件中使用相同的 API 调用,我们只需要在 Vue 组件方法中使用类似的代码即可。例如:

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
            })
        }
    }
}

在应用程序结构方面,对我们来说真正重要的是:

  • 将应用程序正确地分成不同的关注点; 我们称之为“应用模块”; 每个特定事物一个模块

  • 我们有一个“modules”文件夹,其中包含每个“app module”的文件夹

  • 在特定的“app module文件夹”中,我们在routes.js中配置了路由,将远程函数的第一个参数映射到路由配置;我们的自定义代码选择HTTP方法,插入URL,做各种花哨的东西,以适应我们的需要;但在应用程序代码的剩余部分中,我们只需以这种简单的方式使用它:remote('stuffNeededToBeAccomplished', { dataToAccomplishTheNeed })

  • 换句话说,繁重的工作在于映射和Ajax库包装器(您可以使用任何Ajax库进行实际请求);请注意,这完全独立于使用Vue / Vuex

  • 我们还将Vuex存储区分为模块;通常,在应用程序模块中,我们使用那里定义的路由来使用相应的存储模块

  • 在主入口点中,我们导入应用程序模块;每个模块的index.js负责在Ajax包装器中注册路由和在Vuex中注册存储模块(因此,我们只需要导入,而不需要采取进一步的操作,即可在Ajax中使用路由并在Vuex中使用存储模块)


谢谢。如果我理解正确的话:如果您想使用Vuex全局存储数据,只需从组件内部调用Vuex actions函数即可。如果您想将其存储在本地,则运行远程函数?那么如果我两者都想要呢?例如:当获取用户评论时,我想将评论存储在本地,但是将“LAST_TIME_ONLINE”操作全局化。 - Jordy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接