You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3 script setup结合VueX实现表单:用v-model?VueX是否过时?

问题解答

一、VueX是不是已经过时,完全不能用了?

Pinia是Vue官方现在主推的状态管理库,API更简洁、类型支持更友好,但VueX并没有被官方废弃,目前仍在维护。

  • 新项目优先选Pinia,开发体验更好;
  • 已有VueX的老项目,没必要强行迁移,VueX依然能正常胜任状态管理工作;
  • 你当前的小场景用VueX完全没问题,先把功能跑通再考虑是否切换到Pinia。

二、表单与VueX结合的实现方法

关键问题说明

  1. v-model 和 dispatch 怎么选?
    两种方式都可行:
    • 想实时更新状态,就用v-model配合带setter的computed属性,在setter里dispatch action;
    • 想点击“保存”时统一提交,就在提交函数里收集表单数据,再批量dispatch actions,更符合“保存”的操作逻辑。
  2. dispatch函数放哪?
    可以放在表单的submit事件处理函数里(就是你写的onSubmit),也可以放在输入框的change/input事件里。
  3. 表单的value和name属性有啥用?
    • value是表单元素的实际值,提交时会被收集;
    • name用来标识字段,方便一次性收集表单数据时,对应到store里的状态字段。
  4. 和提交服务器的流程一样吗?
    流程逻辑类似:收集表单数据 → 触发操作。但区别是:
    • 更新VueX是客户端状态管理,同步操作;
    • 提交服务器是和后端交互,通常在VueX的action里做异步请求,成功后再commit mutation更新状态。

代码修正与完善

1. 先修复store.ts里的错误

你的actions里有两个明显bug:

  • changeGender的commit没传gender参数;
  • changeJob里commit了错误的mutation(写成了CHOOSE_GENDER,应该是CHOOSE_JOB)。

修正后的store.ts:

import { createStore } from 'vuex'

export default createStore({
  state: () => ({
    gender: '',
    name: '',
    job: '',
  }),
  getters: {
    currentGender: (state) => state.gender,
    currentName: (state) => state.name,
    currentJob: (state) => state.job,
  },
  actions: {
    changeGender({ commit }, { gender }) {
      commit('CHOOSE_GENDER', gender)
    },
    changeName({ commit }, { name }) {
      commit('CHOOSE_NAME', name)
    },
    changeJob({ commit }, { job }) {
      commit('CHOOSE_JOB', job)
    },
  },
  mutations: {
    CHOOSE_GENDER(state, gender) {
      state.gender = gender
    },
    CHOOSE_NAME(state, name) {
      state.name = name
    },
    CHOOSE_JOB(state, job) {
      state.job = job
    },
  },
})

2. 完善Component.vue

下面提供两种常用方案,你可以根据需求选:

方案一:点击保存时统一提交表单数据
<template>
  <p>Vue Form Test</p>
  <!-- 绑定submit事件,阻止默认跳转行为 -->
  <form @submit.prevent="onSubmit">
    <div class="nes-field">
      <label for="name_field">Character Name</label>
      <input placeholder="Enter name" type="text" name="name" id="name_field" class="nest-input" />
    </div>
    <section>
      <label>
        <input type="radio" value="Male" class="nes-radio" name="gender" />
        <span>Male</span>
      </label>
      <label>
        <input type="radio" value="Female" class="nes-radio" name="gender" />
        <span>Female</span>
      </label>
      <label>
        <input type="radio" name="gender" value="Other" class="nes-radio" />
        <span>Other</span>
      </label>
    </section>
    <div class="nes-select job">
      <select name="job" id="default_select">
        <option value="" disabled>pick a job</option>
        <option value="mage">Mage</option>
        <option value="fighter">Fighter</option>
        <option value="druid">Druid</option>
        <option value="sorcerer">Sorcerer</option>
      </select>
    </div>
    <button type="submit">Save</button>
  </form>
  <br />
  <h1>Results:</h1>
  <h3>Name: {{name}}</h3>
  <h3>Gender: {{gender}}</h3>
  <h3>Job: {{job}}</h3>
</template>

<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'

const store = useStore()
const name = computed(() => store.getters.currentName)
const gender = computed(() => store.getters.currentGender)
const job = computed(() => store.getters.currentJob)

const onSubmit = () => {
  // 收集表单数据
  const form = document.querySelector('form')
  const formData = new FormData(form)
  const formObj = Object.fromEntries(formData)
  
  // 分发actions更新store状态
  store.dispatch('changeName', { name: formObj.name })
  store.dispatch('changeGender', { gender: formObj.gender })
  store.dispatch('changeJob', { job: formObj.job })
}
</script>
方案二:v-model实时更新状态
<template>
  <p>Vue Form Test</p>
  <form>
    <div class="nes-field">
      <label for="name_field">Character Name</label>
      <input placeholder="Enter name" type="text" id="name_field" class="nest-input" v-model="localName" />
    </div>
    <section>
      <label>
        <input type="radio" value="Male" class="nes-radio" v-model="localGender" />
        <span>Male</span>
      </label>
      <label>
        <input type="radio" value="Female" class="nes-radio" v-model="localGender" />
        <span>Female</span>
      </label>
      <label>
        <input type="radio" value="Other" class="nes-radio" v-model="localGender" />
        <span>Other</span>
      </label>
    </section>
    <div class="nes-select job">
      <select id="default_select" v-model="localJob">
        <option value="" disabled>pick a job</option>
        <option value="mage">Mage</option>
        <option value="fighter">Fighter</option>
        <option value="druid">Druid</option>
        <option value="sorcerer">Sorcerer</option>
      </select>
    </div>
    <button type="button" @click="save">Save</button>
  </form>
  <br />
  <h1>Results:</h1>
  <h3>Name: {{name}}</h3>
  <h3>Gender: {{gender}}</h3>
  <h3>Job: {{job}}</h3>
</template>

<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'

const store = useStore()

// 带setter的computed,实现v-model绑定到store状态
const localName = computed({
  get() {
    return store.getters.currentName
  },
  set(value) {
    store.dispatch('changeName', { name: value })
  }
})

const localGender = computed({
  get() {
    return store.getters.currentGender
  },
  set(value) {
    store.dispatch('changeGender', { gender: value })
  }
})

const localJob = computed({
  get() {
    return store.getters.currentJob
  },
  set(value) {
    store.dispatch('changeJob', { job: value })
  }
})

// 保存按钮可用来触发额外操作(比如提交服务器)
const save = () => {
  console.log('状态已更新,可在此处添加提交服务器逻辑')
}

// 展示用的computed
const name = computed(() => store.getters.currentName)
const gender = computed(() => store.getters.currentGender)
const job = computed(() => store.getters.currentJob)
</script>

内容的提问来源于stack exchange,提问作者Eric Andre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 20:31:02