methods中this的指向
不能使用箭头函数
我们在 methods 中要使用 data 返回对象中的数据:
- 首先,需要明确一点,vue中this必须是有值的,我们通过{{ this.message }} 的方式读取vue中的变量,让我们能够在模板语法和方法中可以使用data中的变量,所以this必须是有值的,并且可以通过this获取到data返回对象中的数据。
- 那么,this值可以是window吗?
- 不可以,因为如果等于window,就无法读取data对象中的值
- 但如果我们使用箭头函数定义方法,那么this的值就是window
- 为什么箭头函数中的this是window?
methods中定义的普通函数,它们的内部this指向是vue单独处理的,vue2与vue3的实现差不多,有一个专门的bind函数用来给methds中的函数绑定this,this是一个叫publicThis的对象,里面是vue自己实现的,需要绑定到this上的一些变量。
伪代码如下:
methods:{
btnClick: function(){
console.log(this)
}
}
// vue内部实现
ctx[btnClick] = btnClick.bind(publicThis)
所以我们模板中写的 onCLick=”this.btnClick” 实际上是从 ctx 这个数组中找到 btnClick 这个方法
模板语法
React的开发模式:
- React使用的jsx,所以对应的代码都是编写的类似于js的一种语法
- 之后通过Babel将jsx编译成React.createElement函数调用
Vue也支持jsx的开发模式:
v-once用于指定元素或者组件只渲染一次:
v-html
默认情况下,如果我们展示的内容本身是html,vue并不会对其做特殊处理,而是作为字符串处理,如果想作为真正的html解析,需要使用v-html。
v-pre
v-pre用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签:
- 跳过不需要编译的节点,加快编译速度
- 就是template中写的什么,页面就展示什么
- 基本用不上
v-bind属性直接绑定一个对象
给div绑定属性,可以使用这个语法
这个方法在自定义组件时非常有用。<template>
<div v-bind="info"></div>
</template>
<script>
data() {
return {
info: {
name: "jujuul",
age: 18,
height: 180
}
}
}
</script>
v-on绑定多个事件
给一个div绑定多个事件,可以使用以下的语法
可以使用语法糖,不过不够直观<div v-on="{click: btnClick, mousemove: mouseMove}"></div>
<div @="{click: btnClick, mousemove: mouseMove}"></div>
条件渲染
条件渲染的三个指令
在某些情况下,我们需要根据当前的条件决定某些元素或组件是否需要渲染,这时就要用到条件渲染v-if。
v-if、v-else-if、v-else用于条件渲染:
- 只有当条件为true时,才会被渲染
- 这三个指令与if、else if、else类型
v-if的渲染原理:
- v-if是惰性的
- 当条件为false时,其判断的内容不会被渲染或者会被销毁掉
-
条件渲染结合template
因为v-if是一个指令,必须要添加到一个元素上:
但是如果我们希望切换的是多个元素呢?
- 此时我们渲染div,但是我们并不希望div这种元素被渲染
- 这个时候我们可以使用template
示例如下:
// 使用div
<div v-if="isShow">
<div>1</div>
<div>2</div>
</div>
// 使用template
<template v-if="isShow">
<div>1</div>
<div>2</div>
</template>
上面两段代码的区别是,使用div会多出一层DOM层级,使用template则不会。
使用template能够减少DOM层级,稍微提升性能。
v-show和v-if的区别
v-show和v-if用法看起来是一致的,也是根据一个条件决定是否显示元素或者组件。
首先,在用法上的区别:
- v-show不支持template
- v-show不能和v-else一起使用
其次,本质的区别:
- v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有渲染的,只是通过CSS的diplay属性来进行切换
- v-if当条件为false时,其对应的元素根本不会被渲染到DOM中
开发中的选择:
- 如果元素需要频繁的切换显示和隐藏的状态,使用v-show
-
列表渲染
在真实开发中,我们往往会从服务器拿到一些数据,并且对其进行渲染
这个时候我们可以使用v-for来完成
v-for类似于JavaScript的for循环,可以用于遍历一组数据
v-for基本使用
v-for的基本格式是“item in 数组”
数组通常是来自data或者prop,也可以是其他方式
-
v-for结合tmplate
和v-if结合template类似,目的也是为了少渲染div元素,减少DOM层级,提高性能。
数组更新检测
Vue将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新,这些方法包括:
push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
上面的方法会直接修改原来的数组,但是某些方法不会替换原来的数组,而是会生成新的数组,比如filter()、concat()和slice()。
v-for中的key的作用
在使用v-for进行列表渲染时,我们通常会给元素或者组件绑定一个key属性。
这个key属性有什么作用呢?官方的解释是:
- key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes
- 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法
而使用key时,他会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素
认识VNode
我们先来解释一下VNode的概念:
VNode全称是Virtual Node,也就是虚拟节点
- 事实上无论是组件还是元素,它们最终在Vue中表现出来的都是一个个VNode
- VNode的本质是一个JavaScript对象
虚拟DOM
如果我们不是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode tree:
没有key的diff执行过程(源码)
如果没有绑定key,那么在vue更新的过程中,会调用patchUnkeyChildren方法,这个方法最主要的是接收两个参数(还有其它参数,不过不是很重要),一个是c1,旧的VNodes集合,一个是c2,新的VNodes集合。patchUnkeyCHildren方法会先获取这新旧两个集合的长度,然后取最小值,循环遍历。这里取最小值循环是因为如果取大的那个,会因为小的那个长度不够最终发生越界(下标错误)。
比如我们的一个数组arr=[‘a’,’b’,’c’,’d’],我们通过v-for在页面中遍历显示,这时我们插入f到数组中第三位,这时旧的VNodes就是[‘a’,’b’,’c’,’d’],而新的VNodes则是[‘a’,’b’,’f’,’c’,’d’]。长度分别是旧VNodes为4,新VNodes为5,这时会有一个长度为4的循环,然后循环比较新旧VNodes,也就是oldVNodes[0]和newVNodes[0]比较,如果这两个VNode类型相同,内容相同,那么就不会做更新,直到oldVNodes[2]和newVNodes[2]比较,虽然类型可能都是div,但是innerHTML的值不同,所以会更新innerHTML的值,后面的oldVNodes[3]和newVNodes[3]也要更新,然后就遍历完了。遍历完后,vue会做一个判断,如果旧的VNodes长度大于新的VNodes,那么嗲用unmountChildren方法,把旧VNodes不在刚才循环中的元素全部删除;如果新VNodes长度大于旧VNodes,那么调用mountChildren方法将多出来的部分生成新的VNode,并且挂载到新的VNodes上,最后生成真实DOM就实现了DOM节点的更新。
有key的diff执行过程(源码)
如果有key,会调用patchKeyedChildren,这个方法和patchUnkeyChildren方法接收的参数类似,主要的也是c1旧VNodes和c2新VNodes集合。也是先获取长度,不过这里不用比较,而是先存下来留着后面用到的时候再使用。patchKeyedChildren和patchUnkeyChild两个方法内部执行也是有很大不同的。
- 首先,从头部遍历,并且是使用while而不是for循环
通过变量n1获取c1中的参数,n2获取c2中的参数,比较n1[i]和n2[i]的值,判断节点类型和key值是否相同,相同就不需要更新,i++继续遍历,不同就直接跳出循环。
- 然后vue会从尾部开始向前进行遍历
也是通过n1获取c1中最后的参数,n2获取c2中最后的参数,比较n1[e1]和n2[e2]的值,这里的e1和e2分别是oldVNodes的长度和newVNodes的长度,比较也是比较节点类型和key值,相同就不需要更新,e1—,e2—继续遍历,不同就直接跳出循环。
- 上面两步从前后两边遍历完了全部节点,这里做新节点的添加
找到需要挂载的新节点,给patch方法传入两个参数,第一个为null,第二个为需要挂载的节点,在patch方法中,第一个参为null就是进行挂载
- 这一步是如果newVNodes长度小于oldVNodes,那么做卸载处理
把新节点中不存在,旧节点中存在的节点卸载掉
- 前面两步都是有序的,而这里用来处理乱序的
这种不知道具体顺序的,最简单粗暴的方法是删除全部旧节点,按序生成全部新节点,但是这种效率非常低。vue的实现是,尽可能在就节点中查找和新节点匹配的节点,如果找到,会创建一个新数组,把旧节点放入新数组,就是极可能的使用旧的节点(必须要确保节点类型和key值相同),都比较完以后,如果旧节点没有出现在新结点中,那么就移除掉,如果新结点中有旧节点中不存在的,就新增节点。