Vue3 script setup结合VueX实现表单:用v-model?VueX是否过时?
问题解答
一、VueX是不是已经过时,完全不能用了?
Pinia是Vue官方现在主推的状态管理库,API更简洁、类型支持更友好,但VueX并没有被官方废弃,目前仍在维护。
- 新项目优先选Pinia,开发体验更好;
- 已有VueX的老项目,没必要强行迁移,VueX依然能正常胜任状态管理工作;
- 你当前的小场景用VueX完全没问题,先把功能跑通再考虑是否切换到Pinia。
二、表单与VueX结合的实现方法
关键问题说明
- v-model 和 dispatch 怎么选?
两种方式都可行:- 想实时更新状态,就用v-model配合带setter的computed属性,在setter里dispatch action;
- 想点击“保存”时统一提交,就在提交函数里收集表单数据,再批量dispatch actions,更符合“保存”的操作逻辑。
- dispatch函数放哪?
可以放在表单的submit事件处理函数里(就是你写的onSubmit),也可以放在输入框的change/input事件里。 - 表单的value和name属性有啥用?
value是表单元素的实际值,提交时会被收集;name用来标识字段,方便一次性收集表单数据时,对应到store里的状态字段。
- 和提交服务器的流程一样吗?
流程逻辑类似:收集表单数据 → 触发操作。但区别是:- 更新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
相关产品推荐
相关产品推荐

