likes
comments
collection
share

前端九九八十一难(三)

作者站长头像
站长
· 阅读数 16

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:nonedom元素依旧还在。v-if显示隐藏是将dom元素整个添加或删除。

编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换。

编译条件:v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染。

  • v-show 由false变为true的时候不会触发组件的生命周期
  • v-iffalse变为true的时候,触发组件的beforeCreatecreatebeforeMountmounted钩子,由true变为false的时候触发组件的beforeDestorydestoryed方法

性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗。

3. v-if和v-show的原理

具体解析流程这里不展开讲,大致流程如下

  • 将模板template转为ast结构的JS对象
  • ast得到的JS对象拼装renderstaticRenderFns函数
  • renderstaticRenderFns函数被调用后生成虚拟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 的核心是利用了如 PromiseMutationObserversetImmediatesetTimeout 的原生 JavaScript 方法来模拟对应的微/宏任务的实现,本质是为了利用 JavaScript 的这些异步回调任务队列来实现 Vue 框架中自己的异步回调队列。

3. $nextTick使用场景

  1. 在Vue生命周期的created()钩子函数进行的DOM操作一定要放在nextTick的回调函数中。

这是因为 created()钩子函数执行时 DOM 并未进行渲染。

  1. 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的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 动态添加 gettersetter 的功能所调用的方法)

3. 补充延伸

vue 源码里缓存了 array 的原型链,然后重写了这几个方法,触发这几个方法的时候会 observer 数据,意思是使用这些方法不用再进行额外的操作,视图自动进行更新。 推荐使用 splice 方法会比较好自定义,因为 splice 可以在数组的任何位置进行删除/添加操作, 总共提供了 7 个 方法都可实现响应式:splice()、 push()、pop()、shift()、unshift()、sort()、reverse()