博客 / 詳情

返回

從硬編碼到 Schema 推斷:前端表單開發的工程化轉型

一、你的表單,是否正在失控?

想象一個場景,你正在開發一個“企業貸款申請”或“保險理賠”系統。

最初,頁面只有 5 個字段,你寫得優雅從容。隨着業務迭代,表單像吹氣球一樣膨脹到了 50 多個字段:“如果用户選了‘個體工商户’,不僅要隱藏‘企業法人’字段,還得去動態請求‘經營地’的下拉列表,同時‘註冊資本’的校驗規則還要從‘必填’變成‘選填’……”

於是,你的 Vue 文件變成了這樣:

  • <template> 裏塞滿了深層嵌套的 v-ifv-show
  • <script> 裏到處是監聽聯動邏輯的 watch 和冗長的 if-else
  • 最痛苦的是: 當後端決定調整字段名,或者公司要求把這套邏輯複用到小程序時,你發現邏輯和 UI 已經像麻繩一樣死死纏在一起,拆不開了。

“難道寫表單,真的只能靠體力活嗎?”

為了擺脱這種低效率重複,我們嘗試將 中間件思想 引入 Vue 3,把複雜的業務規則從 UI 框架中剝離出來。今天,我就把這套“一次編寫,到處複用”的工程化方案分享給你。


二、 核心思想:讓數據自帶“説明書”

傳統模式下,前端像是一個**“搬運工”:拿到後端數據,手動判斷哪個該顯、哪個該隱。

而工程化模式下,前端更像是一個“組裝廠”**:數據在進入 UI 層之前,先經過一套“中間件流水線”,數據會被自動標註上 UI 描述信息(Schema)。

1. 什麼是 Schema 推斷?

數據不再是冷冰冰的鍵值對,而是變成了一個包含“元數據”的對象。通過 TypeScript 的類型推斷,我們讓數據自己告訴頁面:

  • 我應該用什麼組件渲染(componentType
  • 我是否應該被顯示(visible
  • 我依賴哪些字段(dependencies
  • 我的下拉選項去哪裏拉取(request

2. UI 框架只是“皮膚”

既然邏輯都抽離到了框架無關的中間件裏,那麼 UI 層無論是用 Ant Design 還是 Element Plus,都只是換個“解析器”而已。


三、 實戰:構建 Vue 3 自動化渲染引擎

1. 組件註冊表

首先,我們要定義一個組件映射表,把抽象的字符串類型映射為具體的 Vue 組件。

TypeScript

// src-vue/components/FormRenderer/componentRegistry.ts
import NumberField from '../FieldRenderers/NumberField.vue'
import SelectField from '../FieldRenderers/SelectField.vue'
import TextField from '../FieldRenderers/TextField.vue'
import ModeToggle from '../FieldRenderers/ModeToggle.vue'

export const componentRegistry = {
  number: NumberField,
  select: SelectField,
  text: TextField,
  modeToggle: ModeToggle,
} as const

2. 組裝線:自動渲染器(AutoFormRenderer)

這是我們的核心引擎。它不關心業務,只負責按照加工好的 _fieldOrder_schema 進行遍歷。

<template>
  <a-row :gutter="[16,16]">
    <template v-for="key in orderedKeys" :key="key">
      <component
        v-if="shouldRender(key)"
        :is="resolveComponent(key)"
        :value="data[key]"
        :config="schema[key].fieldConfig"
        :dependencies="collectDeps(schema[key])"
        :request="schema[key].request"
        @update:value="onFieldChange(key, $event)"
      />
    </template>
  </a-row>
</template>

<script setup lang="ts">
const props = defineProps<{ data: any }>();
const schema = computed(() => props.data?._schema || {});
const orderedKeys = computed(() => props.data?._fieldOrder || Object.keys(props.data));

// 根據中間件注入的 visible 函數判斷顯隱
function shouldRender(key: string) {
  const s = schema.value[key];
  if (!s || s.fieldConfig?.hidden) return false;
  return s.visible ? s.visible(props.data) : true;
}

function resolveComponent(key: string) {
  const type = schema.value[key]?.componentType || 'text';
  return componentRegistry[type];
}
</script>

3. 原子化:會“思考”的字段組件

SelectField 為例,它不再是被動等待賦值,而是能感知依賴。當它依賴的字段(如“省份”)變化時,它會自動重新調用 request

<script setup lang="ts">
const props = defineProps(['value', 'dependencies', 'request']);
const options = ref([]);

async function loadOptions() {
  if (props.request) {
    options.value = await props.request(props.dependencies || {});
  }
}

// 深度監聽依賴變化,實現聯動效果
watch(() => props.dependencies, loadOptions, { deep: true, immediate: true });
</script>

四、 方案的“真香”時刻

1. 邏輯與 UI 的徹底解耦

所有的聯動規則、校驗邏輯、接口請求都定義在獨立於框架的 src/core 下。如果你明天想把項目從 Vue 3 遷到 React,你只需要重寫那幾個基礎字段組件,核心業務邏輯 一行都不用動

2. “潔癖型”提交

很多動態表單方案會將 visibleoptions 等 UI 狀態混入業務數據,導致傳給後端的 JSON 極其混亂。我們的方案在提交前會運行一次“清洗中間件”:

const cleanPayload = submitCompileOutputs(formData.compileOutputs);
// 自動剔除所有以 _ 開頭的輔助字段和臨時狀態

後端拿到的永遠是乾淨、純粹的業務模型。

3. 開發體驗的飛躍

現在,當後端新增一個字段時,你的工作流變成了:

  1. 在類型推斷引擎里加一行規則。
  2. 刷新頁面,字段已經按預定的位置和樣式長好了。

    你不再需要去 .vue 文件裏翻找幾百行處的 template 插入 HTML,更不需要擔心漏掉了哪個 v-if。


結語:不要為了用框架而用框架

很多時候,我們覺得 Vue 或 React 難維護,是因為我們將過重的業務決策交給了視圖層

通過引入中間件和 Schema 推斷,我們實際上在 UI 框架之上建立了一個“業務邏輯防火牆”。Vue 只負責監聽交互和渲染結果,而變幻莫測的業務規則被關在了純 TypeScript 編寫的沙盒裏。

這種“工程化”的思維,不僅是為了今天能快速復刻功能,更是為了明天業務變動時,我們能優雅地“配置升級”,而不是“推倒重來”。


你是如何處理複雜表單聯動的?歡迎在評論區分享你的“避坑”指南!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.