Vue3学习 --- v-model和组件基础

928 阅读5分钟

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的原理其实是背后有两个操作:

  1. v-bind绑定value属性的值
  2. 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对象

I8XXNu.png

/*
  在调用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文件进行解析