关于vue中表单和组件的笔记
今天内容
- 表单
- 组件
表单
文本
<input type="text" v-model="message">
多行文本
<textarea v-model="message"></textarea>
单选框
<input type="radio" name="gender" v-model="gender" value="男"> 男
<input type="radio" name="gender" v-model="gender" value="女"> 女
多选框
<input type="checkbox" name="hobby" v-model="hobby" value="游戏">游戏
<input type="checkbox" name="hobby" v-model="hobby" value="睡觉">睡觉
<input type="checkbox" name="hobby" v-model="hobby" checked value="打码">打码
下拉选择框
<p>
<select v-model="area">
<option disabled>请选择</option>
<option value="重庆">重庆</option>
<option value="西安">西安</option>
<option value="北京">北京</option>
</select>
<span>{{ area }}</span>
</p>
<p>
<select v-model="area" size="2">
<option disabled>请选择</option>
<option value="重庆">重庆</option>
<option value="西安">西安</option>
<option value="北京">北京</option>
</select>
<span>{{ area }}</span>
</p>
<p>
<select v-model="area" size="3" multiple>
<option disabled>请选择</option>
<option value="重庆">重庆</option>
<option value="西安">西安</option>
<option value="北京">北京</option>
</select>
<span>{{ area }}</span>
</p>
修饰符
.lazy
<p><input type="text" v-model.lazy="username"></p>
<p>{{ username }}</p>
它是在执行 change 事件或者 blur 事件时才会触发
.number
<p><input type="text" v-model.number="age"></p>
<p>{{ age }}</p>
把输入的值定义为数字
.trim
<p><input type="text" v-model.trim="password"></p>
<p>{{ password }}</p>
注意:修饰符是可以组合使用的。
案例:用户注册
搭建界面
<template>
<form action="" method="post">
<table>
<thead>
<tr>
<th colspan="2">用户注册</th>
</tr>
</thead>
<tbody>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td>性别:</td>
<td><input type="radio" name="gender" value="男">男 <input type="radio" name="gender" value="女">女</td>
</tr>
<tr>
<td>邮箱:</td>
<td><input type="email" name="email"></td>
</tr>
<tr>
<td>密码问题:</td>
<td><select name="question">
<option disabled>请选择</option>
<option value="你喜欢的游戏">你喜欢的游戏</option>
<option value="你最爱人">你最爱人</option>
</select></td>
</tr>
<tr>
<td>密码答案:</td>
<td><select name="question">
<option disabled>请选择</option>
<option value="LoL">LoL</option>
<option value="我自己">我自己</option>
</select></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="注册">
<input type="reset" value="取消">
</td>
</tr>
</tbody>
</table>
</form>
</template>
定义数据
// 定义类型
type User = {
username: string,
password: string,
gender: string,
email: string,
question: string,
answer: string,
}
const user: User = {
username: '',
password: '',
gender: '男',
email: '',
question: '',
answer: ''
}
绑定数据
<template>
<form action="" method="post">
<table>
<thead>
<tr>
<th colspan="2">用户注册</th>
</tr>
</thead>
<tbody>
<tr>
<td>用户名:</td>
<td><input type="text" name="username" v-model="user.username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password" v-model="user.password"></td>
</tr>
<tr>
<td>性别:</td>
<td><input type="radio" name="gender" value="男" v-model="user.gender">男 <input type="radio" name="gender" value="女" v-model="user.gender">女</td>
</tr>
<tr>
<td>邮箱:</td>
<td><input type="email" name="email" v-model="user.email"></td>
</tr>
<tr>
<td>密码问题:</td>
<td><select name="question" v-model="user.question">
<option disabled>请选择</option>
<option value="你喜欢的游戏">你喜欢的游戏</option>
<option value="你最爱人">你最爱人</option>
</select></td>
</tr>
<tr>
<td>密码答案:</td>
<td><select name="answer" v-model="user.answer">
<option disabled>请选择</option>
<option value="LoL">LoL</option>
<option value="我自己">我自己</option>
</select></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="注册" @click.prevent="register">
<input type="reset" value="取消">
</td>
</tr>
</tbody>
</table>
</form>
</template>
通过 v-model
指令来把表单元素和数据进行绑定,使用 user.xxx
方式。
绑定事件
<tr>
<td colspan="2">
<input type="submit" value="注册" @click.prevent="register">
<input type="reset" value="取消">
</td>
</tr>
使用了事件修饰符 .prevent
来阻止表单的默认形为。
const register = () => {
console.log(user)
}
组件
组件是 Vue 框架的核心,我们前面所学的内容都会在组件中去使用。
一个组件包含三个部分:
template
: 用于定义视图script
:用于定义 TS 脚本,来让组件可用style
:编写视图的样式
这三个部分不是完全必须的,可以根据实际情况来选择基本一到三个
组件基础
基本使用
实际上,在我们通过 vue cli 或者 vite 来创建项目时,会自动给我们添加两个组件:
- App.vue:它是根组件,我们所有的组件都要通过根组件来使用。
- HelloWorld.vue:它是一个 hello world 程序,这个组件我们是可以删除
在项目中有一个 components
的目录,这个目录一般用于一些小组件的编写,我们还可以自己定义目录来存放我们组件的组件。
组件是可以复用的。
- 在
components
目录下创建一个Menu.vue
组件
<template>
<div class="container">
<ul>
<li>首页</li>
<li>新闻</li>
<li>关于我们</li>
<li>联系我们</li>
</ul>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
- 要在根组件 App.vue 中引入
首先在 App.vue 组件的script
标签中,通过import
来把 Menu.vue 组件引入进行
import Menu from './components/Menu.vue'
然后在 App.vue 组件中的 template
标签中使用
<template>
<Menu></Menu>
</template>
最后就可以启动项目来测试了。
美化组件
我们可以在组件的 style
标签中使用 CSS 来编写样式,也可以使用 SCSS 或者 Less 来编写。
CSS 大家比熟悉,它是不能使用变量,表达式、语句的。而 SCSS 或者 Less 是支持变量、表达式、数组、语句。
所以我们使用 Less 来给大家讲解。
要想使用 Less,我们先安装 Less 依赖和它的加载器依赖
执行如下命令来安装。
npm install less less-loader -D
安装完后,还需要在 style
标签中指定 lang
属性的值为 less
@color: #ffffff;
@bg: #0a53be;
.container {
width: 100%;
height: 35px;
background-color: @bg;
ul {
padding: 0;
margin: 0;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
li {
width: 80px;
height: 35px;
list-style: none;
color: @color;
}
}
}
修改根组件
我们需要把项目的入口文件 main.ts
中引入即可。
import { createApp } from 'vue'
import Menu from './components/Menu.vue'
createApp(Menu).mount('#app')
一般来说,我们都不会去修改根组件,而是在根组件中使用别的组件。
组件生命周期
在 Vue3 中有以下几个生命周期方法(钩子)
- beforeCreate:在组件实例化之后,数据观察和事件/监听设置之前被调用
- created:在实例创建完成后调用
- beforeMount:在组件 DOM 实际渲染之前调用。在这一步中,根元素不存在。
- mounted:在组件第一次渲染之后调用,该元素现在可用,允许直接 DOM 访问
- beforeUpdate:数据更新之前调用
- updated:DOM 更新后调用
- beforeUnmount:在卸载组件实例之前调用,在这个阶段,实例依然可以正常的使用
- unmounted:卸载组件完成后调用,在这个阶段,所有的事件、计算属性、监听器等都会被移除
演示组件生命周期的使用
import {onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated, ref} from 'vue'
const count = ref<number>(0)
// beforeMount,它是一个钩子函数
onBeforeMount(() => {
console.log('onBeforeMount....')
})
// onMounted
onMounted(() => {
console.log('onMounted......');
})
// onBeforeUpdate
onBeforeUpdate(() => {
console.log('onBeforeUpdate......')
})
// onUpdated
onUpdated(() => {
console.log('onUpdated......')
})
// onBeforeUnmount
onBeforeUnmount(() => {
console.log('onBeforeUnmount......')
})
// onUnmounted
onUnmounted(() => {
console.log('onUnmounted........')
})
组件通信
组件通信就是指组件与组件之间的数据传递。
父传子
- 首选一个父组件,我们以 App.vue 来作为父组件。
<template>
<!--父组件-->
</template>
<script setup lang="ts">
</script>
- 然后定义一个子组件 Son.vue
<template>
<h3>子组件</h3>
</template>
<script setup lang="ts">
</script>
- 在父组件中引入子组件,并在
template
中使用子组件
<template>
<!--父组件-->
<son></son>
</template>
<script setup lang="ts">
import Son from './layout/Son.vue'
</script>
- 在子组件的标签上添加一个 title 属性,这个属性就是我们向子组件传递的参数
<template>
<!--父组件-->
<son title="hello"></son>
</template>
<script setup lang="ts">
import Son from './layout/Son.vue'
</script>
到目前为止,我们已经完成了父传子的操作。接下来是子组件去接收父组件传递的数据。
5. 在子组件中通过 defineProps
API 来接收传递过来的参数。
<template>
<div>
<h3>子组件</h3>
<div>{{ title }}</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
title: string
}>()
</script>
默认值
我们希望子组件中接收到的值可以有默认值,如果父组件没有传递参数过来我们就使用默认值,如果有参数传递过来我们就使用传递过来的参数。
也就是说,子组件中的属性是可选的。
要想使用默认值,我们还需要使用别一个 API,它叫 withDefults()
,它也是直接使用而无须引入。
type Props = {
title?: string,
num?: number,
}
withDefaults(defineProps<Props>(), {
title: '顶部组件',
num: 100,
})
withDefaults() 函数有两个参数:第一个参数为接收的属性对象,第二个参数为这些属性的默认值,它也是一个对象。
子传父
子组件定义事件
对于子组件向父组件传递数据,需要做以下几步:
1)在子组件中定义一个事件
<template>
<div class="menu">
<button @click="clickTip">派发给父组件</button>
</div>
</template>
2) 定义要传递的数据
const data = reactive<Array<number>>([1, 2, 3, 4, 5, 6])
3)在子组件中定义派发事件
const emit = defineEmits(['aaaaa'])
定义派发事件需要使用 defineEmits()
API,它也是一个可以不用引入就可以直接使用的 API,和 defineProps()
一样。
defineEmits()
API,它是一个函数,可以接收一个数组对象,对象中是一些自定义的事件名称。这个函数执行后有一个变更来接收,
通过这个变量(它其实是一个函数)就可以向父组件传值了。
4)在我们指定的事件中来调用 defineEmits()
执行后产生的函数。
const clickTip = () => {
emit('aaaaa', data)
}
在 emit
(它是一个自己定义的变量),它可以接收两个参数:第一个参数为在派发中指定的事件名称;第二个参数为要传递的数据。
父组件接收数据
父组件要接收子组件传递过来的的数据,需要做以下几步:
1)在要父组件中处定义事件,这个事件的名称必须与子组件派发的事件名称相同
<Menu @aaaaa="getData"></Menu>
2)然后实现事件,来处理获取子组件传递的数据并根据业务需求来处理数据
const dd = reactive<Array<number>>([])
const getData = (d: Array<number>) => {
dd.push(...d)
}
祖孙传参
在祖组件中使用 provide(key, value)
API 来向子组件提供数据
provide('name', '黄飞鸿')
在子组件中使用 const value = inject(key)
API 来接收数据
const val = inject('name')
注意:这两个API在使用之前必须先引入。
兄弟传参
实现方式有两种:
- 兄弟传参需要借助于父组件
- 使用 Event Bus
借助父组件
首先定义 A.vue 和 B.vue 这两个子组件,它们是平级的,就构成了兄弟组件。
然后在 App.vue 组件中引用。
<template>
<A></A>
<B></B>
</template>
<script setup lang="ts">
import A from './borther/A.vue'
import B from './borther/B.vue'
</script>
现在 A.vue 组件要向 B.vue 组件传第一个 name 变量的值。
1)首先在 A.vue 组件中向 App.vue 组件派发一上事件(子传父)
<template>
<div>A组件</div>
<button @click="sendDataToApp">向父组件派发事件来传递数据</button>
</template>
<script setup lang="ts">
const name: string = '张三丰'
const emit = defineEmits(['sendData'])
const sendDataToApp = () => {
emit('sendData', name)
}
</script>
2)然后在 App.vue 组件中通过自定义事件来接收 A.vue 组件会传递的数据
<template>
<A @sendData="getData"></A>
<B></B>
</template>
<script setup lang="ts">
import A from './borther/A.vue'
import B from './borther/B.vue'
import {ref} from "vue"
const data = ref<string>('')
const getData = (name: string) => {
data.value = name
}
</script>
首先通过自定义事件来接收子组件派发的事件,并通过事件来接收数据。
3)父组件 App.vue 向子组件 B.vue 传递参数
<B :name="data"></B>
4)在子组件 B.vue 中接收父组件 App.vue 传递过来的数据
<template>
<div>B组件 --- {{ name }}</div>
</template>
<script setup lang="ts">
defineProps<{
name: string
}>()
</script>
使用 Event Bus(了解)
1)定义一个容器,用于收集和执行容器中的事件(在 src 下新建一个 Bus.ts 文件)
type BusClass<T> = {
emit: (name: T) => void
on: (name: T, callback: Function) => void
}
// 定义注册中心 Key 的类型
type BusParams = string | number | symbol
// 定义调度中心类型
type List = {
// 动态属性,使用对象签名的方式来定义
[key: BusParams]: Array<Function>
}
class Bus<T extends BusParams> implements BusClass<T> {
// 注册中心
list: List
// 调度中心
constructor() {
this.list = {}
}
// 执行方法
emit(name: T, ...args: Array<any>) {
let eventName: Array<Function> = this.list[name]
eventName.forEach(ev => {
ev.apply(this, args)
})
}
// 收集方法
on(name: T, callback: Function) {
let fn: Array<Function> = this.list[name] || []
fn.push(callback)
this.list[name] = fn
}
}
// 导出
export default new Bus<number>()
2)把这个文件挂载到 Vue 的全局中