
四、Vue基本语法2
一、复杂 data 处理方式
在模板中可以直接通过插值语法显示一些 data 中的数据
但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
比如我们需要对多个 data 数据进行运算、三元运算符来决定结果、数据进行某种转化后显示
- 在模板中使用表达式,可以非常方便的实现,但是设计它们的初衷是用于简单的运算
- 在模板中放入太多的逻辑会让模板过重和难以维护
- 并且如果多个地方都使用到,那么会有大量重复的代码
我们有没有什么方法可以将逻辑抽离出去呢?
- 其中一种方式就是将逻辑抽取到一个 method 中,放到 methods 的 options 中
- 但是,这种做法有一个直观的弊端,就是所有的 data 使用过程都会变成了一个方法的调用
- 另外一种方式就是使用计算属性 computed
二、计算属性 computed
什么是计算属性呢? 官方并没有给出直接的概念解释
- 而是说:对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性
- 计算属性将被混入到组件实例中。所有 getter 和 setter 的 this 上下文自动地绑定为组件实例
计算属性的用法:
- 选项:computed
- 类型:{ [key: string]: Function | { get: Function, set: Function } }
一个案例
对名字的拼接
1 | data: function () { return { message: 'Hello World', firstName: 'x', lastName: |
1、放到模板语法中 使用表达式
1 | <h2>{{firstName + lastName}}</h2> |
- 缺点一:模板中存在大量的复杂逻辑,不便于维护(模板中表达式的初衷是用于简单的计算)
- 缺点二:当有多次一样的逻辑时,存在重复的代码
- 缺点三:多次使用的时候,很多运算也需要多次执行,没有缓存
2、使用 methods
1 | <h2>{{fullName()}}</h2> |
- 缺点一:我们事实上想显示的是一个结果,但是都变成了一种方法的调用
- 缺点二:多次使用方法的时候,没有缓存,也需要多次计算
3、使用 computed
1 | <h2>{{fullName1}}</h2> |
- 计算属性看起来像是一个函数,但是我们在使用的时候不需要加()
- 我们会发现无论是直观上,还是效果上计算属性都是更好的选择
- 并且计算属性是有缓存的
computed vs methods
1 | <h2>{{fullName()}}</h2> |
这是因为计算属性会基于它们的依赖关系进行缓存
- 在数据不发生变化时,计算属性是不需要重新计算的
- 但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算
计算属性的 getter setter
计算属性在大多数情况下,只需要一个 getter 方法即可,所以我们会将计算属性直接写成一个函数
1 | computed: { fullName1: { get() { console.log('computed中的方法执行'); return |
但是,如果我们确实想设置计算属性的值呢? 这个时候我们也可以给计算属性设置一个 setter 的方法
1 | computed: { fullName1: { get() { console.log('computed中的方法执行'); return |
三、侦听器
什么是侦听器呢?
- 开发中我们在 data 返回的对象中定义了数据,这个数据通过插值语法等方式绑定到 template 中
- 当数据变化时,template 会自动进行更新来显示最新的数据
- 但是在某些情况下,我们希望在代码逻辑中监听某个数据的变化,这个时候就需要用侦听器 watch 来完成了
侦听器的用法如下:
- 选项:watch
- 类型:{ [key: string]: string | Function | Object | Array}
举个例子
比如现在我们希望用户在 input 中输入一个问题
每当用户输入了最新的内容,我们就获取到最新的内容,并且使用该问题去服务器查询答案
那么,我们就需要实时的去获取最新的数据变化
1 | <body> |
配置选项
当点击按钮的时候会修改 info.name 的值; 这个时候我们使用 watch 来侦听 info,可以侦听到吗?答案是不可以
这是因为默认情况下,watch 只是在侦听 info 的引用变化,对于内部属性的变化是不会做出响应的
这个时候我们可以使用一个选项 deep 进行更深层的侦听
注意前面我们说过 watch 里面侦听的属性对应的也可以是一个 Object
还有另外一个属性,是希望一开始的就会立即执行一次:
这个时候我们使用 immediate 选项; 这个时候无论后面数据是否有变化,侦听的函数都会有限执行一次
1 | <template id="my-app"> |
侦听器 watch 的其他方式
1、字符串方法别名
2、传入回调数组
另外一个是 Vue3 文档中没有提到的,但是 Vue2 文档中有提到的是侦听对象的属性:
1 | watch:{ "info.name":function(newValue,oldValue) { console.log(newValue,oldValue) |
只会监听到 info.name 的变化
还有另外一种方式就是使用 $watch 的 API
我们可以在 created 的生命周期(后续会讲到)中,使用 this.$watchs 来侦听
- 第一个参数是要侦听的源
- 第二个参数是侦听的回调函数 callback
- 第三个参数是额外的其他选项,比如 deep、immediate
1 | create() { this.$watch('info',(newValue,oldValue) => { |
四、v-model
表单提交是开发中非常常见的功能,也是和用户交互的重要手段:
比如用户在登录、注册时需要提交账号密码
比如用户在检索、创建、更新信息时,需要提交一些数据
这些都要求我们可以在代码逻辑中获取到用户提交的数据
我们通常会使用 v-model 指令来完成
- v-model 指令可以在表单 input、textarea 以及 select 元素上创建双向数据绑定
- 它会根据控件类型自动选取正确的方法来更新元素
- 尽管有些神奇,但 v-model 本质上不过是语法糖,它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理
v-model 原理
官方有说到,v-model 的原理其实是背后有两个操作:
v-bind 绑定 value 属性的值
v-on 绑定 input 事件监听到函数中,函数会获取最新的值赋值到绑定的属性中
1 | <input type="text" :value="message" @input="message = $event.target.value"> |
v-model 绑定 textarea
1 | <!-- 1.绑定textarea --> |
v-model 绑定 checkbox
单个勾选框:
- v-model 即为布尔值。 p 此时 input 的 value 并不影响 v-model 的值
多个复选框:
- 当是多个复选框时,因为可以选中多个,所以对应的 data 中属性是一个数组。 当选中某一个时,就会将 input 的 value 添加到数组中
1 | <!-- 2.checkbox --> |
v-model 绑定 redio
1 | <!-- 3.radio --> |
v-model 绑定 select
和 checkbox 一样,select 也分单选和多选两种情况
单选:
- 只能选中一个值
- v-model 绑定的是一个值
- 当我们选中 option 中的一个时,会将它对应的 value 赋值到 fruit 中
多选:
- 可以选中多个值
- v-model 绑定的是一个数组
- 当选中多个值时,就会将选中的 option 对应的 value 添加到数组 fruit 中
1 | <!-- 4.select --> |
v-model 的值绑定
目前前面的案例中大部分的值都是在 template 中固定好的
比如 gender 的两个输入框值 male、female
比如 hobbies 的三个输入框值 basketball、football、tennis
在真实开发中,我们的数据可能是来自服务器的,那么我们就可以先将值请求下来,绑定到 data 返回的对象中, 再通过 v-bind 来进行值的绑定,这个过程就是值绑定
v-model 修饰符
lazy
默认情况下,v-model 在进行双向绑定时,绑定的是 input 事件,那么会在每次内容输入后就将最新的值和绑定的属性进行同步
如果我们在 v-model 后跟上 lazy 修饰符,那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车) 才会触发
number
输入框的值绑定到 message 中永远是 string 类型的
如果我们希望转换为数字类型,那么可以使用 .number 修饰符
另外,js 中进行逻辑判断时,如果是一个 string 类型,在可以转化的情况下会进行隐式转换的
trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符