Chuyển đến nội dung chính

第 8 課:表單、驗證和樣式

表單處理、v 模型修飾符。 VeeValidate,Zod 模式驗證。 Tailwind CSS,UnoCSS。元件庫(PrimeVue、Vuetify、Naive UI)。 CSS 範圍、CSS 模組。

💻 程式設計 — 第 8 課 第 8 課:表單、驗證和樣式

Vue.js 和 Nuxt:從基礎到高級

第 2 部分:Vue 生態系統

亞洲開發網

1. 表單處理

<script setup lang="ts">
import { reactive } from 'vue'

const form = reactive({
  name: '',
  email: '',
  role: 'user',
  notifications: true,
  tags: [] as string[],
})

function handleSubmit() {
  console.log(form)
}
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <input v-model.trim="form.name" placeholder="Tên" />
    <input v-model.lazy="form.email" type="email" placeholder="Email" />

    <select v-model="form.role">
      <option value="user">User</option>
      <option value="admin">Admin</option>
    </select>

    <label>
      <input type="checkbox" v-model="form.notifications" />
      Nhận thông báo
    </label>

    <div>
      <label v-for="tag in ['vue', 'nuxt', 'ts']" :key="tag">
        <input type="checkbox" v-model="form.tags" :value="tag" />
        {{ tag }}
      </label>
    </div>

    <button type="submit">Submit</button>
  </form>
</template>

2.VeeValidate + Zod

npm install vee-validate @vee-validate/zod zod
<script setup lang="ts">
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import { z } from 'zod'

const schema = toTypedSchema(
  z.object({
    name: z.string().min(2, 'Tên ít nhất 2 ký tự'),
    email: z.string().email('Email không hợp lệ'),
    password: z
      .string()
      .min(8, 'Mật khẩu ít nhất 8 ký tự')
      .regex(/[A-Z]/, 'Cần ít nhất 1 chữ hoa'),
  })
)

const { defineField, handleSubmit, errors, isSubmitting } = useForm({
  validationSchema: schema,
})

const [name, nameAttrs] = defineField('name')
const [email, emailAttrs] = defineField('email')
const [password, passwordAttrs] = defineField('password')

const onSubmit = handleSubmit(async (values) => {
  await fetch('/api/register', {
    method: 'POST',
    body: JSON.stringify(values),
  })
})
</script>

<template>
  <form @submit="onSubmit">
    <div>
      <input v-model="name" v-bind="nameAttrs" placeholder="Tên" />
      <span v-if="errors.name" class="error">{{ errors.name }}</span>
    </div>

    <div>
      <input v-model="email" v-bind="emailAttrs" type="email" placeholder="Email" />
      <span v-if="errors.email" class="error">{{ errors.email }}</span>
    </div>

    <div>
      <input v-model="password" v-bind="passwordAttrs" type="password" placeholder="Mật khẩu" />
      <span v-if="errors.password" class="error">{{ errors.password }}</span>
    </div>

    <button type="submit" :disabled="isSubmitting">Đăng ký</button>
  </form>
</template>

3.順風CSS

<template>
  <div class="max-w-md mx-auto p-6 bg-white dark:bg-gray-800 rounded-lg shadow-lg">
    <h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
      {{ title }}
    </h2>
    <p class="text-gray-600 dark:text-gray-300 leading-relaxed">
      {{ description }}
    </p>
    <button class="mt-4 px-4 py-2 bg-blue-600 text-white rounded-md 
                   hover:bg-blue-700 transition-colors duration-200
                   focus:outline-none focus:ring-2 focus:ring-blue-500">
      Xem thêm
    </button>
  </div>
</template>

4. CSS 範圍和模組

<!-- Scoped CSS -->
<style scoped>
.card { padding: 1rem; }
.card :deep(.child-class) { color: red; } /* Affect child components */
.card :slotted(p) { margin: 0; } /* Affect slot content */
:global(.app-title) { font-size: 2rem; } /* Global style */
</style>

<!-- CSS Modules -->
<style module>
.red { color: red; }
.bold { font-weight: bold; }
</style>

<template>
  <p :class="$style.red">Red text</p>
  <p :class="[$style.red, $style.bold]">Red bold</p>
</template>

5. 元件庫

圖書館風格組件捆綁包
PrimeVue無風格/主題90+可搖樹
維特化材料設計80+大號
樸素的使用者介面自訂主題90+可搖樹
基數Vue無頭40+最小
shadcn-vue順風40+複製貼上

下一篇: Nuxt 3 和基於檔案的路由 — 開始使用 Nuxt 3 框架。