v-model
表单提交是开发中非常常见的功能,也是和用户交互的重要手段
但这就要求我们可以在代码逻辑中获取到用户提交的数据,我们通常会使用v-model指令来完成:
- v-model指令可以在表单元素上创建
双向数据绑定 - 会根据
控件类型自动选取正确的方法来更新元素 - v-model 本质上是
语法糖,它负责监听用户的输入事件来更新数据,并在某种极端场景 下进行一些特殊处理;
不使用v-model
<template id="template">
username: <input type="text" :value="username" @input="username = $event.target.value" />
<h3>{{ username }}</h3>
</template>
<script>
Vue.createApp({
template: '#template',
data() {
return {
username: 'Klaus'
}
}
}).mount('#app')
</script>
我们可以使用绑定value的值,和监听input事件的方式实现双向数据绑定。
但是在网站中,表单元素多数情况下并不是一个,如果每一个表单元素都需要双向绑定的时候,代码就会显得比较冗余。
此时就可以使用v-model
使用v-model
<template id="template">
username: <input type="text" v-model="username" />
<h3>{{ username }}</h3>
</template>
<script>
Vue.createApp({
template: '#template',
data() {
return {
username: 'Klaus'
}
}
}).mount('#app')
</script>
v-model的原理其实是背后有两个操作:
- v-bind绑定value属性的值
- v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中
v-model结合表单元素
input
<input type="text" v-model="username" />
textarea
<textarea cols="30" rows="10" v-model="intro"></textarea>
checkbox --- 单选框
<!-- isAgree的值是boolean值 -->
<input type="checkbox" v-model="isAgree"/>同意协议
本质上是下边代码的语法糖:
<!-- 注意:此时值是绑定到checked上,并不是value上 -->
<input type="checkbox" :checked="isAgree" @input="handleInput"/>同意协议
<script>
methods: {
handleInput(e) {
this.isAgree = e.target.checked
}
}
</script>
checkbox --- 多选框
<!--
如果多选框没有设置value值的时候,那么就会使用html默认提供的value值,也就是'on'
此时是没有意义的,所以推荐为每一个多选框都设置自己的value值
v-model值相同的checkbox为一组,值(即fruits)的类型是数组
-->
<input type="checkbox" v-model="fruits" value="apple" />apple
<input type="checkbox" v-model="fruits" value="banana" />banana
radio
<!--
如果没有设置value值的时候默认值依旧是on
这是不合理的,所以需要给每一个checkbox设置value值
v-model值相同的radio为一组,同一组中的value值是互斥的
-->
<input type="radio" v-model="gender" value="male" /> male
<input type="radio" v-model="gender" value="female" /> female
select --- 单值下拉列表
<!-- fruit的类型是字符串 -->
<select v-model="fruit">
<option value="apple">apple</option>
<option value="banana">banana</option>
<option value="strawberry">strawberry</option>
</select>
select --- 多值下拉列表
<!--
fruits的类型是数组
size属性表示的是 一次显示几条数据 默认值是4
-->
<select v-model="fruits" multiple size="2">
<option value="apple">apple</option>
<option value="banana">banana</option>
<option value="strawberry">strawberry</option>
</select>
修饰符
v-lazy
默认情况下,v-model在进行双向绑定时,绑定的是input事件,那么会在每次内容输入后就将最新的值和绑定 的属性进行同步
如果我们在v-model后跟上lazy修饰符,那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车或失去焦点) 才会触发
<input type="text" v-model.lazy="username">
.number
在表单中,获取的值都是字符串类型的,即使input的type设置为了number
<!-- 此时获取的score的值的类型为string -->
<input type="number" v-model="score">
我们希望转换为数字类型,那么可以使用 .number 修饰符
<input type="number" v-model.number="score">
需要注意的时候,我们在进行逻辑判断的时候,字符串也会被隐式转换为数值类型
const score = '80'
if (score > 90) {
console.log('优秀')
} else {
console.log('良好')
}
// => 输出的结果是 良好
如果一个input设置了number修饰符的时候,但是用户输入的值不是number类型的值的时候
vue会尽可能的尝试将输入的值转换为number类型的数值
<input type="text" v-model="score">
<!--
1. 输入 123 | 输出 123 | 类型 number
2. 输入 123aa123 | 输出 123 | 类型 number
3. 输入 12.3aa | 输出 12.3 | 类型 number
4. 输入 a123aaa | 输出 a123aaa | 类型 string
==> 如果开头就是字母,那么vue对值转换会失败,此时number修饰符就会自动失去作用
==> 因此此时获取的是是 'a123aaa' 类型是字符串
-->
.trim
trim修饰符会自动过滤用户输入的值中前后的空白字符
<input type="text" v-model.trim="intro">
组件
组件是按照功能点划分的,拥有自己的模板,逻辑,样式的代码块
组件化提供了一种抽象,让我们可以开发出一个个
独立可复用的小组件来构造我们的应用任何的应用都会被抽象成一颗
组件树
-
一个
完整的页面可以分成很多个组件 -
每个组件都用于实现页面的一个功能块
-
每一个组件又可以进行细分
-
组件本身又可以在多个地方进行复用
-
组件本质上就是一个JS对象
/*
在调用createApp方法的时候 传入的配置对象,就是组件对象
createApp返回的方法就是一个组件实例, app组件是应用程序的根组件实例
*/
const app = Vue.createApp({
template: '#template',
data() {
return {
score: 0
}
}
})
app.mount('#app')
组件可以分为两种:
- 全局组件:在任何其他的组件中都可以使用的组件 --- 全局注册
- 局部组件:只有在注册的组件中才能使用的组件 --- 局部注册
全局注册
<template id="template">
<component-a />
</template>
<script>
const app = Vue.createApp({
template: '#template'
})
// 全局注册 --- 全局组件
// 在dom模板中,组件名应该符合html规范,不要使用驼峰法
// app.component(组件名, 组件的配置对象)
// 在vue2中,组件有且必须有一个根元素
// 在vue3中,组件可以有多个根元素,因为实际在打包的过程中,会在所有的元素外使用fragment进行包裹
app.component('component-a', {
template: `
<h2>Hello Vue</h2>
<h2>Hello React</h2>
`
})
app.mount('#app')
</script>
组件名称
-
使用kebab-case(短横线分割符)
当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case, 例如
<my-component-name> -
使用PascalCase(驼峰标识符)
当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也 就是说
<my-component-name>和<MyComponentName>都是可接受的但是推荐使用
<my-component-name>, 因为<MyComponentName>在DOM模板中无法被正常识别
局部组件
全局组件往往是在应用程序一开始就会全局组件完成,那么就意味着如果某些组件我们并没有用到,也会一起被注册
所以在开发中我们通常使用组件的时候采用的都是局部注册
局部注册是在我们需要使用到的组件中,通过components属性选项来进行注册
该components选项对应的是一个对象,对象中的键值对是 组件的名称: 组件对象
如果一个组件需要使用局部组件,那么就需要在这个组件中注册对应的局部组件,如果没有注册,那么就无法使用对应的局部组件
const app = Vue.createApp({
template: '#template',
components: {
// 局部组件,要注册后才可以使用
'component-a': {
template: `
<h2>Hello Vue</h2>
<h2>Hello React</h2>
`
}
}
})
app.mount('#app')
SFC
随着项目越来越复杂,我们会采用组件化的方式来进行开发,但是把模板相关的文件使用代码抽离的方式进行编写的时候,有以下的问题:
- 每个组件都会有自己的模板、脚本逻辑、样式等,我们可以把它们抽离到单独的js、css文件中,但是它们还是会分离开来
- 我们的script是在一个全局的作用域下,很容易出现命名冲突的问题,而且对引入的先后顺序有着比较高的要求
所以在真实开发中,我们可以通过一个后缀名为 .vue 的single-file components (单文件组件 SFC) 来解决
但SFC并不能被浏览器所直接解析,所以需要使用webpack或rollup或vite这类打包工具,对其进行打包处理,将SFC转换为JS对象的形式再交由vue compiler进行解析
而默认情况下,使用Vue CLI来创建项目的时候,默认使用的就是webpack的插件vue-loader来帮助我们对SFC文件进行解析