在Web應用開發中,列表頁的編輯和新增功能是非常常見的交互場景。通常我們會通過點擊"新增"或"編輯"按鈕,打開一個彈窗(Modal/Dialog)來展示表單信息,用户填寫或修改數據後,點擊"確定"按鈕提交表單,最後關閉彈窗並刷新列表。雖然這個流程看似簡單,但在實際開發中,不同的項目團隊可能會採用不同的實現方案。本文將對這些常見的實現方式進行系統整理和分析,幫助開發者理解各種方案的優缺點,以便在實際項目中做出合適的選擇。
一、引言
在B端業務中,列表頁與編輯/新增彈窗的組合是企業級應用中最經典的CRUD模式實現。一個典型場景是:列表頁包含新增/編輯按鈕,點擊後彈出表單模態框,用户填寫表單後點擊確定提交數據到後端,成功後關閉彈窗並刷新列表。
二、核心實現方案詳解
方案一:父組件完全控制模式
這是最基礎、最直接的實現方式,所有邏輯都由父組件控制。
父組件 ParentComponent.vue
<template>
<div class="list-container">
<el-button @click="handleAdd">新增</el-button>
<el-table :data="tableData">
<el-table-column prop="name" label="名稱"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button @click="handleEdit(scope.row)">編輯</el-button>
</template>
</el-table-column>
</el-table>
<!-- 彈窗組件 -->
<el-dialog
title="編輯/新增"
:model-value="dialogVisible"
@update:model-value="dialogVisible = $event"
@close="handleDialogClose"
>
<el-form ref="formRef" :model="formData" :rules="rules">
<el-form-item label="名稱" prop="name">
<el-input v-model="formData.name"></el-input>
</el-form-item>
<!-- 其他表單項 -->
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">確定</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
dialogVisible: false,
tableData: [],
formData: {},
isEdit: false,
rules: {
name: [{ required: true, message: '請輸入名稱', trigger: 'blur' }],
},
}
},
methods: {
// 加載列表數據
loadData() {
this.$api.getDataList().then((res) => {
this.tableData = res.data
})
},
// 新增操作
handleAdd() {
this.isEdit = false
this.formData = {}
this.dialogVisible = true
},
// 編輯操作
handleEdit(row) {
this.isEdit = true
// 深拷貝避免直接修改表格數據
this.formData = JSON.parse(JSON.stringify(row))
this.dialogVisible = true
},
// 表單提交
handleSubmit() {
this.$refs.formRef.validate((valid) => {
if (valid) {
const apiMethod = this.isEdit ? 'updateData' : 'createData'
this.$api[apiMethod](this.formData)
.then(() => {
this.$message.success('操作成功')
this.dialogVisible = false
// 關鍵:成功後刷新列表
this.loadData()
})
.catch((error) => {
this.$message.error(`操作失敗:${error.message}`)
})
}
})
},
// 彈窗關閉時重置表單
handleDialogClose() {
this.$refs.formRef?.resetFields()
},
},
mounted() {
this.loadData()
},
}
</script>
方案二:子組件封裝模式(Props + Events)
將彈窗封裝成獨立組件,通過props接收數據和控制顯隱,通過events回調結果。
父組件 ParentComponent.vue
<template>
<div>
<el-button @click="handleAdd">新增</el-button>
<el-table :data="tableData">
<!-- 表格列定義 -->
<el-table-column label="操作">
<template #default="scope">
<el-button @click="handleEdit(scope.row)">編輯</el-button>
</template>
</el-table-column>
</el-table>
<!-- 引入子組件 -->
<EditFormModal
:visible="dialogVisible"
:form-data="formData"
:is-edit="isEdit"
@close="handleClose"
@success="handleSuccess"
/>
</div>
</template>
<script>
import EditFormModal from './components/EditFormModal.vue'
export default {
components: { EditFormModal },
data() {
return {
dialogVisible: false,
formData: {},
isEdit: false,
tableData: [],
}
},
methods: {
handleAdd() {
this.isEdit = false
this.formData = {}
this.dialogVisible = true
},
handleEdit(row) {
this.isEdit = true
this.formData = { ...row }
this.dialogVisible = true
},
handleClose() {
this.dialogVisible = false
},
handleSuccess() {
this.dialogVisible = false
this.loadData() // 刷新列表
},
loadData() {
// 加載數據
},
},
}
</script>
子組件 EditFormModal.vue
<template>
<el-dialog
title="編輯/新增"
:model-value="localVisible"
@update:model-value="localVisible = $event"
@close="$emit('close')"
>
<el-form ref="formRef" :model="localFormData" :rules="rules">
<!-- 表單項 -->
</el-form>
<template #footer>
<el-button @click="$emit('close')">取消</el-button>
<el-button type="primary" @click="handleSubmit">確定</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
props: {
visible: {
type: Boolean,
default: false,
},
formData: {
type: Object,
default: () => ({}),
},
isEdit: {
type: Boolean,
default: false,
},
},
data() {
return {
localVisible: this.visible,
localFormData: { ...this.formData },
rules: {},
}
},
watch: {
visible(newVal) {
this.localVisible = newVal
// 當彈窗打開時,同步數據
if (newVal) {
this.localFormData = { ...this.formData }
this.$nextTick(() => {
this.$refs.formRef?.resetFields()
})
}
},
formData: {
handler(newVal) {
if (this.visible) {
this.localFormData = { ...newVal }
}
},
deep: true,
},
},
methods: {
handleSubmit() {
this.$refs.formRef.validate((valid) => {
if (valid) {
const apiMethod = this.isEdit ? 'updateData' : 'createData'
this.$api[apiMethod](this.localFormData).then(() => {
this.$emit('success')
})
}
})
},
},
}
</script>
方案三:子組件控制模式(子組件管理所有狀態)
子組件完全管理自身的顯示/隱藏狀態,通過回調函數與父組件通信。
父組件 ParentComponent.vue
<template>
<div>
<el-button @click="showAddModal">新增</el-button>
<el-table :data="tableData">
<!-- 表格列定義 -->
<el-table-column label="操作">
<template #default="scope">
<el-button @click="showEditModal(scope.row)">編輯</el-button>
</template>
</el-table-column>
</el-table>
<!-- 子組件不需要props控制顯隱 -->
<EditFormModal ref="editModalRef" @success="handleSuccess" />
</div>
</template>
<script>
import EditFormModal from './components/EditFormModal.vue'
export default {
components: { EditFormModal },
data() {
return {
tableData: [],
}
},
methods: {
showAddModal() {
this.$refs.editModalRef.showModal(null)
},
showEditModal(row) {
this.$refs.editModalRef.showModal({ ...row })
},
handleSuccess() {
this.loadData() // 刷新列表
},
},
}
</script>
子組件 EditFormModal.vue
<template>
<el-dialog title="編輯/新增" :model-value="visible" @update:model-value="visible = $event">
<!-- 表單內容 -->
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="1.primary" @click="handleSubmit">確定</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
data() {
return {
visible: false,
formData: {},
isEdit: false,
}
},
methods: {
// 對外暴露的方法
showModal(data) {
this.isEdit = !!data
this.formData = data ? { ...data } : {}
this.visible = true
},
handleSubmit() {
// 表單提交邏輯
// ...
this.visible = false
this.$emit('success')
},
},
}
</script>
方案四:Promise模式(異步回調)
使用Promise處理彈窗的確認/取消操作,讓代碼更具可讀性。
父組件 ParentComponent.vue
<template>
<div>
<el-button @click="handleAdd">新增</el-button>
<EditFormModal ref="editModalRef" />
</div>
</template>
<script>
import EditFormModal from './components/EditFormModal.vue'
export default {
components: { EditFormModal },
methods: {
async handleAdd() {
try {
// 使用await等待彈窗的Promise結果
const result = await this.$refs.editModalRef.showModal()
// 處理成功結果
await this.$api.createData(result)
this.$message.success('新增成功')
this.loadData()
} catch (error) {
// 用户取消或發生錯誤
if (error !== 'cancel') {
this.$message.error('操作失敗')
}
}
},
},
}
</script>
子組件 EditFormModal.vue
<template>
<el-dialog title="新增" :model-value="visible" @update:model-value="visible = $event" @close="handleClose">
<!-- 表單內容 -->
<template #footer>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm">確定</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
data() {
return {
visible: false,
resolve: null,
reject: null,
formData: {},
}
},
methods: {
showModal() {
this.visible = true
// 返回Promise
return new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
},
handleConfirm() {
this.$refs.formRef.validate((valid) => {
if (valid) {
this.visible = false
// 解析Promise,返回表單數據
this.resolve(this.formData)
}
})
},
handleCancel() {
this.visible = false
// 拒絕Promise
this.reject('cancel')
},
handleClose() {
// 彈窗被強制關閉時
if (this.reject) {
this.reject('cancel')
}
},
},
}
</script>
方案五:事件總線模式
使用全局事件總線在組件間通信,適用於多層級組件。
// 父組件中
export default {
mounted() {
// 監聽成功事件
this.$bus.$on('formSubmitSuccess', this.handleSuccess)
},
beforeDestroy() {
// 移除事件監聽,避免內存泄漏
this.$bus.$off('formSubmitSuccess', this.handleSuccess)
},
methods: {
handleSuccess() {
this.loadData()
},
},
}
// 子組件 EditFormModal.vue
export default {
methods: {
handleSubmit() {
// 提交成功後
this.$bus.$emit('formSubmitSuccess')
},
},
}
方案六:狀態管理模式(Vuex/Pinia)
使用狀態管理庫統一管理表單和列表數據。
// store/modules/data.js
const state = {
list: [],
formData: {},
dialogVisible: false,
isEdit: false,
}
const mutations = {
SET_LIST(state, list) {
state.list = list
},
SET_FORM_DATA(state, data) {
state.formData = data
},
SET_DIALOG_VISIBLE(state, visible) {
state.dialogVisible = visible
},
SET_IS_EDIT(state, isEdit) {
state.isEdit = isEdit
},
}
const actions = {
async loadList({ commit }) {
const res = await api.getDataList()
commit('SET_LIST', res.data)
},
async submitForm({ commit, state }, data) {
const apiMethod = state.isEdit ? 'updateData' : 'createData'
await api[apiMethod](data)
// 提交成功後關閉彈窗並刷新列表
commit('SET_DIALOG_VISIBLE', false)
await this.dispatch('loadList')
},
}
// 父組件 ParentComponent.vue
export default {
computed: {
...mapState('data', ['list', 'dialogVisible']),
},
methods: {
...mapActions('data', ['loadList']),
showAddModal() {
this.$store.commit('data/SET_IS_EDIT', false)
this.$store.commit('data/SET_FORM_DATA', {})
this.$store.commit('data/SET_DIALOG_VISIBLE', true)
},
},
}
// 子組件 EditFormModal.vue
export default {
computed: {
...mapState('data', ['dialogVisible', 'formData', 'isEdit']),
},
methods: {
...mapActions('data', ['submitForm']),
handleSubmit() {
this.submitForm(this.formData)
},
},
}
方案七:作用域插槽模式
通過作用域插槽傳遞表單數據和方法。
父組件 ParentComponent.vue
<template>
<div>
<el-button @click="dialogVisible = true">新增</el-button>
<el-dialog :model-value="dialogVisible" @update:model-value="dialogVisible = $event">
<template #default="{ save, cancel, form }">
<el-form :model="form" label-width="80px">
<el-form-item label="名稱">
<el-input v-model="form.name"></el-input>
</el-form-item>
<!-- 其他表單項 -->
</el-form>
<template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="save">確定</el-button>
</template>
</template>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
dialogVisible: false,
formData: {},
}
},
methods: {
handleSave() {
// 保存邏輯
this.dialogVisible = false
this.loadData()
},
},
}
</script>
通用彈窗組件 DialogWrapper.vue
<template>
<el-dialog :model-value="visible" @update:model-value="visible = $event" :title="title">
<slot :form="form" :save="handleSave" :cancel="handleCancel"></slot>
</el-dialog>
</template>
<script>
export default {
props: {
visible: Boolean,
title: String,
initialForm: {
type: Object,
default: () => ({}),
},
},
data() {
return {
form: { ...this.initialForm },
}
},
watch: {
initialForm: {
handler(newVal) {
this.form = { ...newVal }
},
deep: true,
},
},
methods: {
handleSave() {
this.$emit('save', this.form)
},
handleCancel() {
this.$emit('cancel')
},
},
}
</script>
方案八:組合式API模式(Vue 3)
使用Vue 3的組合式API實現邏輯複用。
父組件 ParentComponent.vue
<script setup>
import { ref, onMounted } from 'vue'
import EditFormModal from './components/EditFormModal.vue'
import { useDataService } from './services/dataService.js'
const { tableData, loadData } = useDataService()
const dialogVisible = ref(false)
const formData = ref({})
const isEdit = ref(false)
const handleAdd = () => {
isEdit.value = false
formData.value = {}
dialogVisible.value = true
}
const handleEdit = (row) => {
isEdit.value = true
formData.value = { ...row }
dialogVisible.value = true
}
const handleSuccess = () => {
dialogVisible.value = false
loadData()
}
onMounted(() => {
loadData()
})
</script>
<template>
<div>
<el-button @click="handleAdd">新增</el-button>
<el-table :data="tableData">
<!-- 表格列定義 -->
</el-table>
<EditFormModal
:visible="dialogVisible"
:form-data="formData"
:is-edit="isEdit"
@success="handleSuccess"
@close="dialogVisible = false"
/>
</div>
</template>
子組件 EditFormModal.vue
<script setup>
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { api } from './api.js'
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
formData: {
type: Object,
default: () => ({}),
},
isEdit: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['success', 'close'])
const formRef = ref(null)
const localFormData = ref({ ...props.formData })
// 監聽外部數據變化
watch(
() => props.formData,
(newVal) => {
localFormData.value = { ...newVal }
},
{ deep: true }
)
const handleSubmit = async () => {
const valid = await formRef.value.validate()
if (valid) {
try {
const method = props.isEdit ? 'updateData' : 'createData'
await api[method](localFormData.value)
ElMessage.success('操作成功')
emit('success')
} catch (error) {
ElMessage.error(`操作失敗:${error.message}`)
}
}
}
</script>
<template>
<el-dialog title="編輯/新增" v-model="visible" @close="emit('close')">
<el-form ref="formRef" v-model="localFormData">
<!-- 表單項 -->
</el-form>
<template #footer>
<el-button @click="emit('close')">取消</el-button>
<el-button type="primary" @click="handleSubmit">確定</el-button>
</template>
</el-dialog>
</template>
方案九:高階組件(HOC)模式
通過高階組件增強彈窗功能。
// withFormModal.js
import EditFormModal from './EditFormModal.vue'
export function withFormModal(BaseComponent) {
return {
name: 'WithFormModal',
components: {
BaseComponent,
EditFormModal
},
data() {
return {
dialogVisible: false,
formData: {},
isEdit: false
}
},
methods: {
showAddModal() {
this.isEdit = false
this.formData = {}
this.dialogVisible = true
},
showEditModal(row) {
this.isEdit = true
this.formData = { ...row }
this.dialogVisible = true
},
handleSuccess() {
this.dialogVisible = false
this.$emit('refresh-list')
}
},
render(h) {
return h('div', [
h(BaseComponent, {
on: {
'show-add-modal': this.showAddModal,
'show-edit-modal': this.showEditModal
},
attrs: this.$attrs
}),
h(EditFormModal, {
props: {
visible: this.dialogVisible,
formData: this.formData,
isEdit: this.isEdit
},
on: {
success: this.handleSuccess,
close: () => {
this.dialogVisible = false
}
}
})
])
}
}
}
// 使用示例
import { withFormModal } from './withFormModal.js'
import ListComponent from './ListComponent.vue'
const EnhancedListComponent = withFormModal(ListComponent)
export default EnhancedListComponent
方案十:Mixins模式
通過混入複用彈窗邏輯。
// formModalMixin.js
export default {
data() {
return {
dialogVisible: false,
formData: {},
isEdit: false,
loading: false,
}
},
methods: {
showAddModal() {
this.isEdit = false
this.formData = this.getEmptyForm ? this.getEmptyForm() : {}
this.dialogVisible = true
},
showEditModal(row) {
this.isEdit = true
this.formData = { ...row }
this.dialogVisible = true
},
async handleSubmit() {
if (this.beforeSubmit && typeof this.beforeSubmit === 'function') {
const shouldContinue = this.beforeSubmit()
if (!shouldContinue) return
}
this.loading = true
try {
const apiMethod = this.isEdit ? 'updateData' : 'createData'
await this.api[apiMethod](this.formData)
this.$message.success('操作成功')
this.dialogVisible = false
this.onSuccess && this.onSuccess()
} catch (error) {
this.$message.error(`操作失敗:${error.message}`)
} finally {
this.loading = false
}
},
},
}
// 使用示例
import formModalMixin from './mixins/formModalMixin.js'
export default {
mixins: [formModalMixin],
methods: {
getEmptyForm() {
return { name: '', description: '' }
},
onSuccess() {
this.loadData()
},
},
}
方案十一:Teleport模式(Vue 3)
使用Vue 3的Teleport特性將彈窗渲染到指定DOM節點。
<!-- 彈窗組件 -->
<template>
<teleport to="body">
<el-dialog title="編輯/新增" v-model="visible">
<!-- 表單內容 -->
</el-dialog>
</teleport>
</template>
<script setup>
import { ref } from 'vue'
const visible = ref(false)
const formData = ref({})
// 其他邏輯
</script>
方案十二:自定義指令模式
通過自定義指令控制彈窗的顯示和隱藏。
// directives/dialog.js
export default {
bind(el, binding) {
const dialog = el
let callback = binding.value
dialog.show = function (data) {
dialog.visible = true
dialog.$emit('show', data)
}
dialog.hide = function () {
dialog.visible = false
}
dialog.$on('success', () => {
if (typeof callback === 'function') {
callback()
}
dialog.hide()
})
},
}
// 註冊指令
Vue.directive('dialog', dialogDirective)
<!-- 使用示例 -->
<template>
<div>
<el-button @click="showDialog">新增</el-button>
<el-dialog v-dialog="handleSuccess" ref="dialogRef">
<!-- 表單內容 -->
</el-dialog>
</div>
</template>
<script>
export default {
methods: {
showDialog() {
this.$refs.dialogRef.show({})
},
handleSuccess() {
this.loadData()
},
},
}
</script>
方案十三:Composition Function模式(Vue 3)
使用可組合函數封裝彈窗邏輯。
// useFormDialog.js
import { ref, reactive, watch } from 'vue'
export function useFormDialog(initialForm = {}, options = {}) {
const visible = ref(false)
const formData = reactive({ ...initialForm })
const isEdit = ref(false)
const loading = ref(false)
const { onSuccess, onCancel, api } = options
const showAdd = () => {
isEdit.value = false
// 重置表單
Object.keys(formData).forEach((key) => {
formData[key] = initialForm[key] || ''
})
visible.value = true
}
const showEdit = (data) => {
isEdit.value = true
// 填充表單數據
Object.assign(formData, data)
visible.value = true
}
const handleSubmit = async () => {
loading.value = true
try {
const method = isEdit.value ? 'updateData' : 'createData'
await api[method]({ ...formData })
visible.value = false
if (typeof onSuccess === 'function') {
onSuccess()
}
} catch (error) {
console.error('提交失敗', error)
} finally {
loading.value = false
}
}
const handleCancel = () => {
visible.value = false
if (typeof onCancel === 'function') {
onCancel()
}
}
return {
visible,
formData,
isEdit,
loading,
showAdd,
showEdit,
handleSubmit,
handleCancel,
}
}
// 使用示例
<script setup>
import { useFormDialog } from './useFormDialog.js'
import { api } from './api.js'
const { visible, formData, showAdd, showEdit, handleSubmit } = useFormDialog(
{
name: '',
description: '',
},
{
onSuccess: () => {
loadData()
},
api,
}
)
</script>
方案十四:Pub/Sub模式(發佈-訂閲模式)
使用發佈-訂閲模式實現組件間通信。
// 簡單的PubSub實現
const PubSub = {
events: {},
subscribe(event, callback) {
if (!this.events[event]) {
this.events[event] = []
}
this.events[event].push(callback)
},
unsubscribe(event, callback) {
if (!this.events[event]) return
this.events[event] = this.events[event].filter((cb) => cb !== callback)
},
publish(event, data) {
if (!this.events[event]) return
this.events[event].forEach((callback) => callback(data))
},
}
// 全局註冊
Vue.prototype.$pubsub = PubSub
// 父組件中訂閲事件
mounted() {
this.$pubsub.subscribe('formSubmitted', this.handleSuccess)
},
beforeDestroy() {
this.$pubsub.unsubscribe('formSubmitted', this.handleSuccess)
}
// 子組件中發佈事件
handleSubmit() {
// 提交邏輯
this.$pubsub.publish('formSubmitted')
}
方案十五:provide/inject模式
使用Vue的依賴注入機制傳遞數據和方法。
<!-- 父組件 -->
export default {
provide() {
return {
showDialog: this.showDialog,
refreshList: this.refreshList,
}
},
methods: {
showDialog(data, isEdit) {
this.dialogData = data
this.isEdit = isEdit
this.dialogVisible = true
},
refreshList() {
this.loadData()
},
},
}
export default {
inject: ['showDialog', 'refreshList'],
methods: {
handleEdit(row) {
this.showDialog(row, true)
},
handleSubmit() {
// 提交邏輯
this.refreshList()
},
},
}
三、實現方案對比與總結
|
方案名稱
|
優點
|
缺點
|
適用場景
|
|
父組件完全控制模式
|
實現簡單,邏輯集中
|
組件耦合度高,不易複用
|
簡單頁面,快速實現
|
|
子組件封裝模式
|
組件解耦,易於複用
|
需要處理props和events傳遞
|
中大型項目,組件複用需求高
|
|
子組件控制模式
|
子組件自主性強
|
父組件對子組件控制弱
|
獨立功能彈窗,無需父組件過多幹預
|
|
Promise模式
|
異步流程清晰,代碼簡潔
|
需處理Promise異常
|
複雜業務流程,多步驟操作
|
|
事件總線模式
|
組件解耦,通信靈活
|
複雜應用中事件管理困難
|
多層級組件通信
|
|
狀態管理模式
|
狀態集中管理,追蹤變化
|
中小型項目過於重量級
|
複雜應用,多組件共享數據
|
|
作用域插槽模式
|
父組件可自定義表單內容
|
組件結構複雜
|
需要高度定製表單內容的場景
|
|
組合式API模式
|
邏輯複用性強,TypeScript支持好
|
需要Vue 3
|
Vue 3項目,邏輯複用需求高
|
|
高階組件模式
|
不修改原組件,功能增強
|
不易調試,理解成本高
|
第三方組件增強,功能擴展
|
|
Mixins模式
|
邏輯複用,易於理解
|
可能導致命名衝突,來源不明
|
多個組件共享相似邏輯
|
|
Teleport模式
|
DOM結構更靈活,樣式隔離好
|
需要Vue 3
|
彈窗、通知等需要特殊定位的組件
|
|
自定義指令模式
|
使用簡潔,侵入性低
|
功能有限,複雜邏輯實現困難
|
簡單交互增強
|
|
Composition Function
|
邏輯封裝性好,複用性高
|
需要Vue 3
|
Vue 3項目,邏輯抽象需求高
|
|
Pub/Sub模式
|
組件完全解耦,通信靈活
|
事件管理複雜,可能導致內存泄漏
|
大型應用,複雜組件網絡
|
|
provide/inject模式
|
跨層級組件通信,無需props逐層傳遞
|
組件依賴關係不明確
|
深層嵌套組件通信
|
四、推薦實現方案
綜合考慮各種因素,推薦以下實現方案:
中小型Vue 2項目:優先選擇子組件封裝模式或子組件控制模式
- 代碼組織清晰,組件複用性好
- 易於理解和維護
複雜Vue 2項目:結合狀態管理模式和事件總線模式
- 統一管理複雜狀態
- 靈活處理組件間通信
Vue 3項目:推薦使用組合式API模式或Composition Function
- 邏輯複用性強
- TypeScript支持更好
- 代碼組織更靈活
最佳實踐建議
- 保持組件單一職責:彈窗組件只負責表單展示和提交,不處理業務邏輯
- 數據解耦:使用深拷貝避免父子組件數據相互影響
- 表單驗證:合理使用同步/異步表單驗證
- 加載狀態:添加加載指示器,防止重複提交
- 錯誤處理:統一的錯誤處理機制,提供友好的錯誤提示
- 內存管理:及時清理事件監聽器,避免內存泄漏
- 用户體驗:添加操作成功/失敗提示,優化交互流程
- 性能優化:大型表單考慮虛擬滾動、異步加載等優化手段
選擇合適的實現方案需要根據項目規模、技術棧、團隊熟悉度等多方面因素綜合考慮。無論選擇哪種方案,保持代碼的可維護性和可擴展性都是最重要的原則。