前端九九八十一难(三)
Vue2中v-model和.sync的区别
在日常开发的过程中,v-model指令是经常用到的,一般来说 v-model 指令在表单及元素上创建双向数据绑定,但 v-model 本质是语法糖。但提到语法糖,这里就不得不提另一个与v-model有相似功能的双向绑定语法糖了,这就是 .sync修饰符。
一、v-model
1. v-model 的作用
相信使用过vue框架的朋友对这个指令不会感到陌生,v-model是用来进行<input>
、<textarea>
及 <select>
元素上数据的双向绑定的,例如:
<template>
<div>
<input v-model="value" type="text"> //这里的v-model里面的value可以直接获取到用户的输入值
</div>
</template>
<script>
import son from '@/yanshi/son.vue'
data() {
return {
value: '' //这里定义的value变量可以直接将上面获取到的值进行操作
}
}
}
</script>
当我们在input
输入框里面输入了某个在值的时候,下面就可以直接获取到我们的输入值,而不需要操作dom元素进行获取。
2. v-model 的本质
v-model
的本质上来说,是一个语法糖
咱们习惯性的写法是这样的:
<input v-model="val" type="text">
其实完整的写法应该是这样:
<input :value="val" @input="val=$event.target.value" />
也可以将@input
后面写成一个函数,然后在methods
中进行赋值操作。
要理解这行代码,首先你要知道 input
元素本身有个input
事件,这是 HTML5
新增加的,类似 onchange
,每当输入框内容发生变化,就会触发 input
事件,把最新的value
值传给传递给val
,完成双向数据绑定的效果 。
我们仔细观察语法糖和原始语法那两行代码,可以得出一个结论:
在给 <input />
元素添加 v-model
属性时,默认会把 val
作为元素的属性,然后把input
事件作为实时传递 value 的触发事件。
注意: 不是所有能进行双向绑定的元素都是input事件。
3. v-model 的特殊使用
一般情况,咱们使用 v-model
主要是用于数据的双向绑定,可以十分方便的获取到用户的输入值。但在某些特殊情况下,我们也可以将 v-model
用于父子组件之间数据的双向绑定。
father组件
<template>
<div>
<son v-model="num"/> //使用子组件
</div>
</template>
<script>
import son from '@/yanshi/son.vue' //引入子组件
export default {
components: {
son //注册子组件
},
data() {
return {
num: 100
}
}
}
</script>
son组件
<template>
<div>
我是son组件里面接收到的值: {{ value }}
</div>
</template>
<script>
export default {
props: {
value: {
type: Number,
required: true
}
}
}
</script>
注意:这里
props
里接受的数据名字必须叫value
,写成其他的名字会无法调用
当我们需要将修改这个值时,就需要再将其传入父组件进行修改。
这就需要在父组件的子组件标签上定义一个自定义的事件,通过在子组件中使用$emit('自定义事件名',值)
的方法将值传入父组件
父组件中定义一个@input
方法,再设置一个形参val
接收子组件的传值
<template>
<div>
{{ num }}
<son v-model="num" @input="val=>num=val" />
</div>
</template>
子组件中使用$emit()
方法.调用父组件中的事件,并且进行传值
<template>
<div>
我是son组件里面接收到的值: {{ value }}
<button @click="$emit('input',value+1)">点我value+1</button>
</div>
</template>
这样的话,就可以完成父子组件之间的数据双向绑定效果,并且不会出现报错
二、 .sync修饰符
1. .sync修饰符的作用
相比较与v-model
来说,sync
修饰符就简单很多了:
.sync修饰符可以实现子组件与父组件的双向绑定,并且可以实现子组件同步修改父组件的值。
2. .sync修饰符的本质
// 正常父传子:
<son :a="num" :b="num2"></son>
// 加上sync之后父传子:
<son :a.sync="num" .b.sync="num2"></son>
// 它等价于
<son
:a="num" @update:a="val=>num=val"
:b="num2" @update:b="val=>num2=val"></son>
// 相当于多了一个事件监听,事件名是update:a,回调函数中,会把接收到的值赋值给属性绑定的数据项中。
这里面的传值与接收与正常的父向子传值没有区别,唯一的区别在于往回传值的时候$emit
所调用的事件名必须是update:属性名
,事件名写错不会报错,但是也不会有任何的改变,这点需要多注意。
总结
.sync与v-model区别是
相同点:都是语法糖,都可以实现父子组件中的数据的双向通信。
区别点:格式不同: v-model=“num”, :num.sync=“num”
v-model: @input + value :num.sync: @update:num
另外需要特别注意的是: v-model
只能用一次;.sync
可以有多个。
v-if和v-show
在讲解两者的不同前,我先来说明一下两者的相同点
1. v-if和v-show的相同点
我们都知道在 vue
中 v-show
与 v-if
的作用效果是相同的(不含v-else),都能控制元素在页面是否显示
在用法上也是相同的
<Model v-show="isShow" />
<Model v-if="isShow" />
2. v-if和v-show的区别
OK,接下来我们来了解一下它们两者之间的区别,我将它们分为了三个大点,分别是:
- 控制手段不同
- 编译过程不同
- 编译条件不同
我们来解释一下这三个大点
控制手段:v-show
隐藏则是为该元素添加css--display:none
,dom
元素依旧还在。v-if
显示隐藏是将dom
元素整个添加或删除。
编译过程:v-if
切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show
只是简单的基于css切换。
编译条件:v-if
是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染。
v-show
由false
变为true
的时候不会触发组件的生命周期v-if
由false
变为true
的时候,触发组件的beforeCreate
、create
、beforeMount
、mounted
钩子,由true
变为false
的时候触发组件的beforeDestory
、destoryed
方法
性能消耗:v-if
有更高的切换消耗;v-show
有更高的初始渲染消耗。
3. v-if和v-show的原理
具体解析流程这里不展开讲,大致流程如下
- 将模板
template
转为ast
结构的JS
对象 - 用
ast
得到的JS
对象拼装render
和staticRenderFns
函数 render
和staticRenderFns
函数被调用后生成虚拟VNODE
节点,该节点包含创建DOM
节点所需信息vm.patch
函数通过虚拟DOM
算法利用VNODE
节点创建真实DOM
节点
4. v-if和v-show 的使用场景
v-if
与 v-show
都能控制dom
元素在页面的显示
v-if
相比 v-show
开销更大的(直接操作dom
节点增加与删除)
如果需要非常频繁地切换,则使用 v-show 较好
如果在运行时条件很少改变,则使用 v-if 较好
$nextTick
1. 什么是 $nextTick
在说其原理之前我们先来看看官方是怎么定义的
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
什么意思呢?
我们可以理解成,Vue
在更新 DOM
时是异步执行的。当数据发生变化,Vue
将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新。
2. $nextTick的原理
$nextTick
的核心是利用了如 Promise
、MutationObserver
、setImmediate
、setTimeout
的原生 JavaScript
方法来模拟对应的微/宏任务的实现,本质是为了利用 JavaScript
的这些异步回调任务队列来实现 Vue
框架中自己的异步回调队列。
3. $nextTick使用场景
- 在Vue生命周期的
created()
钩子函数进行的DOM操作一定要放在nextTick
的回调函数中。
这是因为
created()
钩子函数执行时 DOM 并未进行渲染。
- 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作应该放在
nextTick()
的回调函数中。
Vue异步执行DOM更新,只要观察到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变, 如果同一个watcher被多次触发,只会被推入到队列中一次。
总结:
使用nextTick()
是为了可以获取更新后的DOM 。
触发时机:在同一事件循环中的数据变化后,DOM完成更新后会立即执行nextTick()
的回调。
同一事件循环中的代码执行完毕 -> DOM 更新 -> nextTick callback( )触发。
$set
1. 什么是 $set
和 $nextTick
的一样,在了解其原理之前我们先来看看官方文档是怎么定义的
如图:
我个人的理解就是:
数据变化视图不更新问题, 当在项目中直接设置数组的某一项的值,或者直接设置对象的某个属性值,这个时候,你会发现页面并没有更新。这是因为 Object.defineProperty()
限制,监听不到变化。
解决方式:this.$set
(你要改变的数组/对象,你要改变的位置的 key
,你要改成什么 value
)
2. $set 实现原理
- 如果目标是数组,直接使用数组的
splice
方法触发相应式; - 如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用
defineReactive
方法进行响应式处理(defineReactive
方法就是Vue
在初始化对象时,给对象属性采用Object.defineProperty
动态添加getter
和setter
的功能所调用的方法)
3. 补充延伸
vue 源码里缓存了 array 的原型链,然后重写了这几个方法,触发这几个方法的时候会 observer 数据,意思是使用这些方法不用再进行额外的操作,视图自动进行更新。 推荐使用 splice 方法会比较好自定义,因为 splice 可以在数组的任何位置进行删除/添加操作, 总共提供了 7 个 方法都可实现响应式:splice()、 push()、pop()、shift()、unshift()、sort()、reverse()
转载自:https://juejin.cn/post/7256250499278946360