基础:
v-if和v-show 区别:
v-show:通过display来控制显示和隐藏;
v-if是组件真正的渲染和销毁,而不是现实和吟唱;
频繁切换场景使用v-show;
keep-alive:
缓存组件,需要频繁切换但不需要频繁渲染常用;例如tab切换;
也可以作为vue性能优化手段之一;
mixin:
优点:适用于 多个组件有相同的逻辑,可以使用mixin,比较方便;
缺点:多mixin可能造成命名冲突;变量来源不明确,不利于阅读;
mixin和组件可能出现多对多关系,复杂度比较高;
computed 和watch区别
**computed优点:缓存,data不变不会重新计算;提升性能;
**
watch: 观察作用,数据变化时异步回调操作;
- computed:是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
- watch:没有缓存性,更多的是「观察」的作用,当监听的数据变化时都会执行回调进行后续操作;当需要深度监听对象中的属性时,可以打开deep:true选项,方便对象中的每一项进行监听
- 运用场景:
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
- 当我们需要在数据变化时执行异步或开销较大的操作时,Vue 通过
watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的,限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
$nextTick:
$nextTick在Dom更新完成之后,触发回调;
实现原理:
nextTick 方法主要是使用了宏任务和微任务,定义了一个异步方法.多次调用 nextTick 会将方法存入 队列中,通过这个异步方法清空当前队列。 所以这个 nextTick 方法就是异步方法;
在下次 DOM 更新循环结束之后执行延迟回调。nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用
- Promise
- MutationObserver
- setImmediate
- 如果以上都不行则采用setTimeout
创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。
setImmediate()方法用于中断长时间运行的操作,并在浏览器完成其他操作(如事件和显示更新)后立即运行回调函数。
定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。
v-for中用key:
1)必须是key,不能是index;
2)diff算法中通过tag和key来判断,是不是sameNode;
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速
- 更准确:因为带 key 就不是就地复用了,在 sameNode 函数a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。
- 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快
3)减少渲染次数,提升性能;
vue中常用的组件通讯;
常用的:
父子组件: props和 this.$emit;
自定义事件: event.on,event.on,event.on,event.off 和 event.no(需要在destory生命周期中,销毁自定义事件evenet.no (需要在destory 生命周期中,销毁自定义事件evenet.no(需要在destory生命周期中,销毁自定义事件evenet.off)
vuex
总共大致有这几种:
父子组件通信
- 事件机制(**父->子props,子->父 on、on、on、emit)
- 获取父子组件实例 parent、parent、parent、children
- Ref 获取实例的方式调用组件的属性或者方法
- Provide、inject (不推荐使用,组件库时很常用)
兄弟组件通信
- eventBus 这种方法通过一个空的 Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件;
Vue.prototype.$bus = new Vue
- Vuex
跨级组件通信
-
Vuex
-
attrs、attrs、attrs、listeners
beforeDetory 使用场景:
解除自定义事件evenet.$off
清楚定时器
解除自定义DOM 事件,例如window scroll等等;
模板编译
模板编译通过一种方式生成render函数,执行render函数返回VNode,基于vnode再执行path和diff;模板一定是转化为js代码过程,叫做编译模板;
模板不是html,html是标签语言;
例如列表模板:
// 列表const template = ` <ul>
<li v-for="item in list" :key="item.id">{{iitem.title}}</li>
</ul>`
模板编译成 render函数,用的with语法
给一个数组list遍历数组或者对象,给一个function函数, return返回li标签
//with(this) {return _c('ul',_l((list),function(item){
return _c('li',{key:item.id}[_v(_s(item.title))])}),0)};
其中_c代表 createElemet
_l(列表)代表 target._l = renderList
复制代码
vue组件中可以用render代替template;
vue组件如何渲染和更新的?
大致可以分为 初次渲染,更新过程,异步渲染 这三步骤;
初次渲染:
1)解析模板为render函数(或在开发环境已完成,vue-loader)
2)触发响应式,监听data属性getter setter;
3)执行render函数,生成vnode,patch(elem,vnode)
更新过程话:
1)修改data,触发setter(getter之前已经被监听到;若是模板中没有用到data中某个变量,则不会触发get)
2)重新执行render函数,生成newVnode
3)然后再进行patch(vnode,newVnode) (patch中的diff算法会计算出最小差异更新到dom上)
异步渲染话:
组件的异步渲染非常重要,减少DOM操作次数,可以缓解浏览器压力,主要是提升性能;
主要是$nextTick异步回调,DOM渲染完,再回调;多次data修改,只会渲染一次;(nextTick原理要会)
以下内容都是自己理解(小白啊,若有不当敬请指出):
vue如何监听数据变化?Object.defineProperty响应式原理
核心就是Object.defineProperty来实现响应式(当然vue3.0使用的是Proxy)
大致分为 监听对象情况, 需要深度监听复杂对象(observer(value))情况,监听数组这几种情况;
监听对象核心:
function defineReactive(target,key,value) {
// 核心 api
//深度监听
observer(value)
Object.defineProperty(target,key, {
get() {
return value
},
set(newValue) {
if(newValue !== value) {
//深度监听新值
observer(newValue)
// 设置新值
value = newValue
// 触发更新视图
updateView()
}
}
})}
复制代码
需要深度监听复杂对象(observer(value))情况
//如果是复杂对象,需要调用obser方法// 监听对象
function oberver(target) {
if (typeof target !== 'object' || target === null) {
// 不是对象 或数组
return target }
// 污染全局的Array 原型
if(Array.isArray(target)) {
target._proto_ = arrProto }
// 重新定义各个属性
for (let key in target) {
defineReactive(target,key,target[key])
}}
复制代码
监听数组变化:(无法原生监听到数组,需要此处特殊处理;)
// 重新定义数组原型
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
arrProto[methodName] = function () {
updateView() // 触发视图更新
oldArrayProperty[methodName].call(this, ...arguments)
// Array.prototype.push.call(this, ...arguments)
}
})
复制代码
object.defineProperty 缺点:
1)深度监听,需要递归到底(源数据对象有多深,就要一次性递循环归到最深处),一次性计算量比较大
2)无法监听新增属性/删除属性(可以通过Vue.set Vue.delete增删)
3)无法原生监听到数组,需要特殊处理;
diff 算法过程(未完):
这个我是看博客看了几篇,还是没明白;后来花了快一天时间才明白;建议自己手本子上画diff比对过程;
diff即对比,是一个广泛的概念。**diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom。**换句话说:diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁;
比较过程
-
同级比较,再比较子节点
-
先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除)
-
比较都有子节点的情况(核心diff)
-
递归比较子节点;
Vue2的核心Diff算法采用了_**双端比较**
_的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。相比React的Diff算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。(双端比较,就是同时从新旧 children
的两端开始进行比较的一种方式)
diff算法中的 双端比较大致步骤,再具体每个比对步骤建议另看专门文章,更好理解双端比较逻辑。
双端比较了,在一次比较过程中,最多需要进行四次比较:
- 1、使用旧
children
的头一个VNode
与新children
的头一个VNode
比对,即oldStartVNode
和newStartVNode
比较对。 - 2、使用旧
children
的最后一个VNode
与新children
的最后一个VNode
比对,即oldEndVNode
和newEndVNode
比对。 - 3、使用旧
children
的头一个VNode
与新children
的最后一个VNode
比对,即oldStartVNode
和newEndVNode
比对。 - 4、使用旧
children
的最后一个VNode
与新children
的头一个VNode
比对,即oldEndVNode
和newStartVNode
比对。
在如上四步比对过程中,试图去寻找可复用的节点,即拥有相同 key
值的节点。这四步比对中,在任何一步中寻找到了可复用节点,则会停止后续的步骤,可以用下图来描述在一次比对过程中的四个步骤:
diff算法时间复杂度
O(n)
正常Diff两个树的时间复杂度是O(n^3),但实际情况下我们很少会进行跨层级的移动DOM,所以Vue将Diff进行了优化,从O(n^3) -> O(n),只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。
树diff O(n3) :
包含了三部 第一: 遍历tree1;第二遍历tree2;第三,排序
vue中diff优化到O(n):
只比较同一层级,不跨级比较
tag不相同,则直接删除重建,不再深度比较
tag和key,两者都相同,则认为是相同节点,不再深度比较;
V3跟V2对比优势
性能更好
更好的ts支持
更好的代码组织
更好的逻辑抽离
体积更小
更多新功能
V3.0 proxy响应式原理
vue3使用proxy代替Ojject.defineProperty
proxy实现响应式优点:
可以实现深度监听,性能更好;
可监听 新增/删除 属性;
可直接监听数组变化;
proxy如何实现响应式监听的呢?(可参考下面代码,看代码比较好理解)
-
判断当前的返回值是否为Object或者数组,如果是,则再通过做代理方法进行逻辑处理, 实现深度监听是在代理中的get方法调用了递归,实现了深度监听;这也是v3中proxy响应式比v2响应式性能好的原因,v3是在set方法中调用递归实现深度监听,对于data中多层嵌套结构,set获取到哪一层才递归到具体哪一层数据结构(用不到的就不做处理);而在v2 object.defineProperty响应式处理中,是一开始就递归响应式的,默认一次性全部递归了;
-
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?
-
我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。也就是为了防止数据多次触发,需要做个判断旧值与新值是否相等,相等直接返回true;
怎么判断是不是新增的数据?
- 需要在Reflect.ownKeys(target)中看是否已经有这个key了;
Vite是什么?
vite是一个前端打包工具,vue作者发起项目
发展比较快,和webpack竞争
优势:开发环境下无需打包,启动快
vite为何启动快?
开发环境使用es6 module,无需打包---就非常快
生产环境使用rollup,并不会快很多;
vue 和 react 对比;
同:
- 都支持组件化;
- 都支持数据驱动视图;
- 都是用vdome操作DOM;
异:
- react使用jsx倾向于一切都js;vue使用模板,倾向于html;
- react函数式编程;vue声明式编程;
- react更多要自理更生(创建了一个更分散的生态系统,所以有更丰富的生态系统),vue生态配套比较全,Vue 的路由库和状态管理库都是由官方维护支持且与核心库同步更新的;