Vue 基础(实战模板与命名指南)
Vue 基础(实战模板与命名指南)
聚焦三件事:模板化写法、ref/reactive
使用选择、变量与函数命名规范。配合可复制的代码模板,快速搭建可靠页面。
1. 组件标准模板
<template><section class="user-list-page"><header class="page-header"><h2>用户列表</h2><div class="actions"><el-input v-model="query.keyword" placeholder="关键字" class="mr-8" /><el-button type="primary" :loading="isLoading" @click="fetchUsers">查询</el-button><el-button @click="resetQuery">重置</el-button></div></header><el-table :data="rows" v-loading="isLoading"><el-table-column prop="id" label="ID" width="80" /><el-table-column prop="name" label="姓名" /><el-table-column prop="phone" label="手机" /><el-table-column label="操作" width="160" align="center"><template #default="{ row }"><el-button size="small" @click="onEdit(row)">编辑</el-button></template></el-table-column></el-table><el-paginationclass="mt-12"backgroundlayout="prev, pager, next, sizes, total":current-page="pagination.page":page-size="pagination.pageSize":total="pagination.total"@update:current-page="onPageChange"@update:page-size="onPageSizeChange"/></section></template><script setup lang="ts">
import { ref, reactive, computed } from 'vue'// 查询条件:对象推荐用 reactive(可读性强,字段集中)
const query = reactive({ keyword: '', dateRange: [] as [string, string] | [] })// 列表数据:数组通常用 ref,便于整体替换(分页、筛选场景)
const rows = ref<any[]>([])
const isLoading = ref(false)// 分页:对象一次性传递,使用 ref 包裹以便整体更新
const pagination = ref({ page: 1, pageSize: 10, total: 0 })// 计算属性:派生状态(示例)
const hasKeyword = computed(() => query.keyword.trim().length > 0)// 事件处理:动作动词开头 + 语义对象(handle/on 均可,保持一致)
function fetchUsers() {isLoading.value = truetry {const total = 42const start = (pagination.value.page - 1) * pagination.value.pageSizeconst end = start + pagination.value.pageSizeconst all = Array.from({ length: total }).map((_, i) => ({ id: i + 1, name: `用户${i + 1}`, phone: '13800000000' }))rows.value = hasKeyword.value ? all.filter(u => u.name.includes(query.keyword)) .slice(start, end) : all.slice(start, end)pagination.value.total = hasKeyword.value ? all.filter(u => u.name.includes(query.keyword)).length : total} finally {isLoading.value = false}
}function resetQuery() {query.keyword = ''query.dateRange = []pagination.value.page = 1fetchUsers()
}function onPageChange(p: number) {pagination.value.page = pfetchUsers()
}function onPageSizeChange(s: number) {pagination.value.pageSize = spagination.value.page = 1fetchUsers()
}function onEdit(row: any) {console.log('edit', row)
}// 初始化
fetchUsers()
</script><style scoped>
.page-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.actions { display: flex; align-items: center; }
.mr-8 { margin-right: 8px; }
.mt-12 { margin-top: 12px; }
</style>
2. ref
vs reactive
使用选择
- 基础类型(数值、字符串、布尔)用
ref
- 示例:
const count = ref(0)
、const isLoading = ref(false)
- 示例:
- 复杂对象/表单状态用
reactive
- 示例:
const form = reactive({ name: '', age: 0 })
- 示例:
- 数组在分页/筛选场景常用
ref<any[]>([])
,便于整体替换- 赋值:
rows.value = newRows
- 变更:
rows.value.push(item)
(注意.value
)
- 赋值:
- 嵌套对象:
reactive
自动深层代理;若需要整体替换用ref<{...}>()
常见陷阱:
- 模板中
ref
会自动解包;脚本中需.value
- 对
reactive
使用解构会失去响应式,改用toRefs
或在模板直接访问字段
3. 命名规范(变量、函数、计算属性、事件)
- 状态布尔:
isLoading
、isVisible
、hasError
、isDisabled
- 数据集合:
users
、rows
、items
;单项用user
、row
、item
- 查询与表单:
query
、filters
、form
- 计算属性:用派生语义,如
filteredUsers
、selectedCount
- 事件处理:统一前缀
on
或handle
,如onSubmit
、handleDelete
- 接口函数:动作 + 资源,如
fetchUsers
、createUser
、updateUser
- 组件 props:语义清晰,避免缩写;双向绑定使用
modelValue
4. 模板语法常用模式
<div><!-- 插值 --><div>当前计数:{{ count }}</div><!-- 属性绑定 --><img :src="imgUrl" :alt="altText" /><button :disabled="isDisabled">保存</button><!-- class/style 绑定 --><div :class="{ active: isActive }" :style="{ color: textColor }">样式示例</div><!-- 事件绑定 --><button @click="onClick">点击</button><!-- 条件/列表 --><p v-if="isShow">显示内容</p><ul><li v-for="user in users" :key="user.id">{{ user.name }}</li></ul>
</div>
5. 计算属性与侦听器模板
import { computed, watch } from 'vue'// 计算属性:依赖数据变化自动更新
const isAllChecked = computed(() => items.value.every(i => i.checked))// 侦听:响应变化执行副作用
watch(() => query.keyword, (newVal, oldVal) => {// 节流/防抖可在此接入fetchUsers()
})// 深度与立即选项
watch(form, (val) => { console.log('form changed', val) }, { deep: true })
watch(() => route.path, () => init(), { immediate: true })
6. 组件通信模板(props / emits / v-model)
<!-- 子组件 Child.vue -->
<script setup lang="ts">
import type { PropType } from 'vue'const props = defineProps({modelValue: { type: String, default: '' },maxLength: { type: Number, default: 20 }
})const emit = defineEmits<{(e: 'update:modelValue', v: string): void(e: 'change', v: string): void
}>()function onInput(v: string) {emit('update:modelValue', v)emit('change', v)
}
</script><template><el-input :model-value="props.modelValue" :maxlength="props.maxLength" @input="onInput" />
</template><!-- 父组件使用 -->
<Child v-model="name" @change="handleNameChange" />
补充:
- 子组件事件示例:
const emit = defineEmits(['change']); emit('change', newVal)
- 父组件监听:
<Child @change="handleChildChange" />
7. 可复用逻辑(Composable)模板
分页:
import { ref } from 'vue'export function usePagination(initial = { page: 1, pageSize: 10, total: 0 }) {const pagination = ref(initial)const onPageChange = (p: number) => (pagination.value.page = p)const onPageSizeChange = (s: number) => { pagination.value.pageSize = s; pagination.value.page = 1 }return { pagination, onPageChange, onPageSizeChange }
}
数据获取:
import { ref } from 'vue'
import http from '@/api/http'export function useFetchUsers() {const rows = ref<any[]>([])const isLoading = ref(false)async function fetch(params: { page: number; pageSize: number; keyword?: string }) {isLoading.value = truetry {const res = await http.get('/users', { params })rows.value = res.items} finally {isLoading.value = false}}return { rows, isLoading, fetch }
}
8. 表单状态与校验(简单版)
<script setup lang="ts">
import { reactive, ref } from 'vue'
const formRef = ref()
const form = reactive({ name: '', age: 18 })
const rules = {name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],age: [{ type: 'number', required: true, message: '请输入年龄', trigger: 'blur' }]
}async function onSubmit() {await formRef.value.validate()// 调用接口提交
}
</script><template><el-form ref="formRef" :model="form" :rules="rules" label-width="80px"><el-form-item label="姓名" prop="name"><el-input v-model="form.name" /></el-form-item><el-form-item label="年龄" prop="age"><el-input-number v-model="form.age" :min="0" /></el-form-item><el-form-item><el-button type="primary" @click="onSubmit">提交</el-button></el-form-item></el-form></template>
9. 小结与实践建议
- 页面数据结构优先:
query
(reactive)+rows
(ref)+pagination
(ref) - 动作统一:
fetchXxx
、resetXxx
、onXxxChange
保持一致命名 - 组件通信明确:
props
提数据,emits
发事件,双向绑定用v-model
- 抽象通用:将分页/获取/表单校验封装为 composable 或通用组件