花了一個月時間重構了一個多步驟表單

將原本混亂的資料流整理得一目瞭然

前言

2026/03,主管把從一個表單功能產生出的 QA 單都交由我修改。

這是一個有 5 個步驟的表單,分成 1 個 main.vue + 5 個 step.vue + 1 個 main.store.js + 5 個 step.store.js。

當我要找出顯示錯誤的參數來源,從對應步驟的 step.vue 開始搜尋,跳到對應的 step.store.js 找到參數宣告,再回到主視窗的 watch 或是 onMounted 看看是什麼時機點取得值,再檢查是不是被 main.store.js 內其他步驟的參數影響到、有沒有 import 其他 step.store.js,還需要判斷是否是另一個也使用到這份表單的功能才有的參數。每次要處理該議題的 QA 單,我都必須花 1 個多小時先定位問題。除了花時間外,還無法確定我改了這個參數是不是會間接影響另一個正常功能。

於是我向主管提出我的擔憂:

  1. 未來對於每位要調整這個表單的同仁來說都需要花大量時間先釐清資料流而不能專注在商業邏輯本身,認知負擔有點太高。
  2. 假設後續想要增加表單步驟或是調整流程,無法保證能夠處理並維持既有功能正常性。 並找其他同事附議我的看法,希望主管給我 1 個月的時間重構這個功能。

怎麼重構?

  • 先保留每步驟 step.vue 的 <template>,畫面一直都沒有太大問題,所以可以大幅度保留邊做邊修就好。
  • 再來把所有 step.store.js + main.store.js 全部砍掉,把資料收回 main.vue。主元件保留整份表單的狀態,再用 v-model 交給目前顯示的步驟。也不需要再使用 watch 監聽 currentStep 把 main.store.js 的值賦予 step.store.js 參數。
<template>
  <Step1
    v-if="currentStep === 'S1'"
    ref="componentRef"
    v-model="step1Data"
    :initial-data="getInitialData('S1')"
  />
  <Step2
    v-if="currentStep === 'S2'"
    ref="componentRef"
    v-model="step2Data"
    :initial-data="getInitialData('S2')"
  />
</template>
  • 各步驟的驗證仍然留在自己的元件裡,並統一暴露 submitForm。主元件不需要認識每份表單的細節,只負責現在是哪一步,以及按下下一步時請當前元件送出。
const componentRef = ref()
const currentStep = ref('S1')
const step1Data = reactive({})
const step2Data = reactive({})

async function nextStep() {
  await componentRef.value?.submitForm()
}
  • 步驟判斷也從原本判斷在 ['step1', 'step2'] 的 index 換成和後端一致的 S1、S2。方便從 API 拿到之前暫存到哪步時,能馬上知道不必再做轉換。
  • 原本不影響功能的圖片上傳功能,我也將他從每次使用者上傳就打 API 儲存改成使用者按下儲存時才打 API,避免儲存不需要圖片。

成果如何?

這次沿著 v-model 回到主元件就結束了。初始值在哪裡整理、目前的表單資料放在哪裡、子元件又能改動哪一份資料,都在同一條看得見的路徑上。 能放心的任意改動步驟順序,也不用擔心意想不到的副作用。

過程中也發現先前程式碼沒有考慮到的情境,花了點時間跟 PM 討論,最後順利在一個月內完成讓 PM 驗收,從原本會產生上百份 QA 單的地雷功能,到只剩下一些小問題、同事們也覺得程式碼很好理解的穩定功能。

我覺得我很滿意這次的成果。