Vue 基础(实战模板与命名指南)

Vue 基础(实战模板与命名指南)

聚焦三件事:模板化写法、ref/reactive 使用选择、变量与函数命名规范。配合可复制的代码模板,快速搭建可靠页面。

1. 组件标准模板

vue 复制代码
<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-pagination
      class="mt-12"
      background
      layout="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 = true
  try {
    const total = 42
    const start = (pagination.value.page - 1) * pagination.value.pageSize
    const end = start + pagination.value.pageSize
    const 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 = 1
  fetchUsers()
}

function onPageChange(p: number) {
  pagination.value.page = p
  fetchUsers()
}

function onPageSizeChange(s: number) {
  pagination.value.pageSize = s
  pagination.value.page = 1
  fetchUsers()
}

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. 命名规范(变量、函数、计算属性、事件)

  • 状态布尔:isLoadingisVisiblehasErrorisDisabled
  • 数据集合:usersrowsitems;单项用 userrowitem
  • 查询与表单:queryfiltersform
  • 计算属性:用派生语义,如 filteredUsersselectedCount
  • 事件处理:统一前缀 onhandle,如 onSubmithandleDelete
  • 接口函数:动作 + 资源,如 fetchUserscreateUserupdateUser
  • 组件 props:语义清晰,避免缩写;双向绑定使用 modelValue

4. 模板语法常用模式

vue 复制代码
<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. 计算属性与侦听器模板

ts 复制代码
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)

vue 复制代码
<!-- 子组件 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)模板

分页:

ts 复制代码
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 }
}

数据获取:

ts 复制代码
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 = true
    try {
      const res = await http.get('/users', { params })
      rows.value = res.items
    } finally {
      isLoading.value = false
    }
  }
  return { rows, isLoading, fetch }
}

8. 表单状态与校验(简单版)

vue 复制代码
<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)
  • 动作统一:fetchXxxresetXxxonXxxChange 保持一致命名
  • 组件通信明确:props 提数据,emits 发事件,双向绑定用 v-model
  • 抽象通用:将分页/获取/表单校验封装为 composable 或通用组件
相关推荐
ding_zhikai3 小时前
SD:在一个 Ubuntu 系统安装 stable diffusion Web UI
前端·ubuntu·stable diffusion
gerrgwg5 小时前
Vue-library-start,一个基于Vite的vue组件库开发模板
前端·javascript·vue.js
你的人类朋友6 小时前
【Node】单线程的Node.js为什么可以实现多线程?
前端·后端·node.js
iナナ7 小时前
Spring Web MVC入门
java·前端·网络·后端·spring·mvc
驱动探索者7 小时前
find 命令使用介绍
java·linux·运维·服务器·前端·学习·microsoft
开心不就得了7 小时前
自定义脚手架
前端·javascript
星晨雪海8 小时前
怎么格式化idea中的vue文件
前端·vue.js·intellij-idea
没事多睡觉6668 小时前
Vue 虚拟列表实现方案详解:三种方法的完整对比与实践
前端·javascript·vue.js