使用 Vuex 和特定于组件的数据进行 Vue 结构化

Posted

技术标签:

【中文标题】使用 Vuex 和特定于组件的数据进行 Vue 结构化【英文标题】:Vue structuring with Vuex and component-specific data 【发布时间】:2017-04-11 06:48:48 【问题描述】:

我看到很多 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 数据。为此,购物车导入 actions.js:

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

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

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

问题:我无法在 Messages.vue 中导入 actions.js,因为该操作不应更新 Vuex。但我无法将actions.js 移动到api 目录,因为这破坏了将所有向存储添加数据的文件放在存储目录中的逻辑。除此之外,逻辑应该放在Messages.vue 中。例如,当 api 返回错误时,应设置本地 error-constant。所以不能用单独的文件来处理。

进行 api 调用并将它们存储在 vuex 或本地 data() 中的推荐应用程序结构是什么?将操作文件、api 文件等放在哪里?查看 Jackblog 示例时,它仅支持 Vuex 数据。如何重组它以支持两者?

【问题讨论】:

一个深思熟虑的问题。谢谢你的提问,乔迪。 【参考方案1】:

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

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 module 中维护产品相关数据, 您可以从组件中的方法调度一个操作,该操作将在内部调用后端 API 并在存储中填充数据,代码如下所示:

组件中的代码:

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

vuex 商店中的代码:

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。不要导入操作。在 Messages.vue 中,忘记存储。您不需要绑定到商店的操作层。您只需要 API。


长答案:在一个项目中,我们有以下内容

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

    每个应用模块可能包含一个“routes.js”文件,该文件将上面的“字符串”映射到一个路由配置。例如:saveProductComment: 'POST api/v1/products/product_id/comment'

注意:我对单个 .js.vue 文件使用术语“应用程序模块”,NodeJS 或 Webpack 将其视为“模块”。我将“app 模块”称为包含与特定域相关的应用代码的完整文件夹(例如:“cart”模块、“account”模块、“cmets”模块、等等)。

    我们可以从任何地方调用remote('saveProductComment', product_id: 108, comment: 'Interesting!' ),它会返回一个Promise。包装器使用路由配置来构建正确的请求,它还解析响应并处理错误。在任何情况下,remote 函数总是返回一个Promise

    每个应用程序模块还可以提供自己的存储模块,我们在其中定义与模块相关的初始状态、突变、动作和获取器。我们使用术语“Manager”来表示状态管理代码。例如,我们可以有一个“cmetsManager.js”文件,提供存储模块来处理“cmets”。

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


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

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

我们有一个“模块”文件夹,其中包含每个“应用程序模块”的文件夹

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

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

我们将 Vuex 存储也划分为模块;通常,在一个应用模块中,我们有相应的商店模块使用那里定义的路由

在主入口点,我们导入应用模块;每个模块的 index.js 负责在 Ajax 包装器中注册路由和在 Vuex 中注册存储模块(因此,我们只需要导入,无需采取进一步行动,即可获得可用于 Ajax 的路由和可用的存储模块在 Vuex 中)

【讨论】:

谢谢。因此,如果我理解正确:如果您想使用 Vuex 全局存储数据,您只需从组件内部调用 Vuex 操作函数。如果你想在本地存储它,你运行远程功能?如果我想要他们两个怎么办?例如:获取用户 cmets 时,我想在本地存储 cmets,但在全局范围内执行“LAST_TIME_ONLINE”操作。

以上是关于使用 Vuex 和特定于组件的数据进行 Vue 结构化的主要内容,如果未能解决你的问题,请参考以下文章

Vue-vuex

十Vue:Vuex实现data(){}内数据多个组件间共享

vue路由自动加载、按组件异步载入vuex以及dll优化

vue3 项目添加vuex 进行组件中传递数据

vue中vuex定义的以及使用方法

Vuex的使用