前言
最近复盘了一下vue相关知识,对vue有了进一步的认识了解(数据绑定、响应式原理、diff算法、一些工作中常用的属性方法是具体怎么实现等),空余时间整理了一下分享给大家,希望对你有所帮助~~~
vue 的优点
- 低耦合,视图(View)可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同的"View"上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变
- 可重用性,你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 view 重用这段视图逻辑独立开发、可测试
- 独立性,开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计可测试,界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写
vue 的双向数据绑定的原理
在vue2.0单中,实现数据双向绑定时使用了Object.defineProperty() 这个方法重新定义了对象获取 get 和设置属性 set 的操作来实现
vue3.0 中用 proxy 替换 Obiject.defineProperty()
vue响应式原理
当对象传入vue实例作为data对象时,首先被vue遍历所有属性,调用Object.defineProperty设置为 getter 和setter,每个组件都有一个watcher对象,在组件渲染的过程中,把相关的数据都注册成依赖,当数据发生setter变化时,会通知触发watcehr,从而更新相关联的组件达到数据变化驱动试图更新。
1)对象内部通过defineReactive方法,使用Object.defineProperty() 监听数据属性的 get 来进行数据依赖收集,再通过 set 来完成数据更新的派发;数组则通过重写数组方法来实现的。扩展它的 7 个变更⽅法,通过监听这些方法可以做到依赖收集和派发更新;( push/pop/shift/unshift/splice/reverse/sort )多层对象是通过递归来实现劫持,vue3中是使用 proxy来实现响应式数据内部依赖收集是怎么做到的,每个属性都拥有自己的dep属性,存放他所依赖的 watcher,当属性变化后会通知自己对应的 watcher去更新。
响应式流程:
1、defineReactive 把数据定义成响应式的;
2、给属性增加一个 dep,用来收集对应的那些watcher;
3、等数据变化进行更新dep.depend() // get 取值:
进行依赖收集dep.notify() // set 设置时:
通知视图更新这里可以引出性能优化相关的内容:
1)对象层级过深,性能就会差。
2)不需要响应数据的内容不要放在data中。
3)object.freeze() 可以冻结数据。
vue2.0、3.0 Diff 算法
diff 算法过程:
- 同级比较,再比较子节点
- 先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除)
比较都有子节点的情况(核心diff) - 比较都有子节点的情况(核心diff)
- 递归比较子节点
- 正常Diff两个树的时间复杂度是O(n^3) ,但实际情况下我们很少会进行跨层级的移动DOM,所以Vue将Diff进行了优化,从O(n^3) -> O(n),只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。
6.vue2 的核心Diff算法采用了双端比较的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。相比React的Diff算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅
Vue3 借鉴了 ivi算法和 inferno算法
在创建VNode时就确定其类型,以及在mount/patch的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升
proxy 相比于 defineProperty 有哪些优势 / 为什么要在vue3 中替换 Obiject.defineProperty ?
Obiject.defineProperty 是无法监控到数组下标的变化,导致我们需要直接通过数组下标给数组设置值,不会实时响应。Obiject.defineProperty 它只会去劫持对象的属性,因此我们需要对每个对象的每个属性都去遍历。在2.0当中,它是通过 递归 + 遍历data对象来实现对数据的监控,若我们的属性值也是对象的,那么就需要深度遍历,很显然,能劫持一个完整的的对象才是更好的选择。proxy 可以去劫持整个对象 并且返回一个新的对象。
什么是 Vue.nextTick() ?
下次 DOM 更新循环结束之后执行延迟回调;再修改数据之后立即使用这个方法,获取更新后的 DOM;由上所述 所以就衍生出了这个获取更新后 DOM 的 vue方法,所以放在 Vue.nextTick() 回调函数中所执行的是会对 DOM 进行操作的 js 代码,Vue.nextTick() 时将函数延迟到下次dom 更新后数据后调用 ,简单来说就是当我们的数据更新,dom渲染后,激动执行该函数
什么场景下我们需要去使用Vue.nextTick()?
- vue生命周期 created 中进行 Dom 操作 一定要 放在 Vue.nextTick() 的回调函数中,因为在created() 钩子函数执行时 Dom 并未进行任何渲染,在此我们对Dom进行操作无异于徒劳,所此在此钩子函数中 我们一定要将我们 DOM 操作的 js 代码放进 Vue.nextTick()
- 当在我们项目中想在改变 DOM 元素的数据后基于新的 dom 做点什么,对新 DOM 一系列的 js 操作都需要放进 Vue.nextTick()的回调函数中;通俗的理解就是:更改数据后,你想立即使用 js 操作新的视图的时候需要使用它
- 3.当我们在使用某个第三方插件时 ,希望在 vue 生成的某些 dom 动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法
计算属性、方法、watch的区别?
- 计算属性是基于数据的依赖缓存,数据发生变化,缓存才会发生变化,如果数据没有发生变化,调用计算属性直接调用的是存储的缓存值;
- 方法每次调用都会重新计算;所以可以根据实际需要选择使用,如果需要计算大量数据,性能开销比较大,可以选用计算属性,如果不能使用缓存可以使用方法;
- watch是用来监测数据的变化,和计算属性相比,是watch没有缓存,但是一般想要在数据变化时响应时,或者执行异步操作时,可以选择watch
vuex 工作原理详解
- vuex 整体思想诞生于 flux, 可其的实现方式完全使用了 vue 自身的响应式设计,依赖监听、依赖收集都属于 vue 对 对象 Property set get 方法的代理劫持。vuex 中的 store 本质就是没有 template 的隐藏着的 vue 组件;
- vuex生成了一个 store 实例,并且把这个实例挂在了所有的组件上,所有的组件引用的都是同一个store实例。 store实例上有数据、有方法,方法改变的都是store实例上的数据。由于其他组件引用的是同样的实例,所以一个组件改变了store上的数据, 导致另一个组件上的数据也会改变,就像是一个对象的引用。
new Vue() 发生了什么?
new Vue()是创建Vue实例,它内部执行了根实例的初始化过程。
c
h
i
l
d
r
e
n
,
children,
children,refs,
s
l
o
t
s
,
slots,
slots,createElement等实例属性的方法初始化
自定义事件处理
数据响应式处理
生命周期钩子调用
可能的挂载new Vue()创建了根实例并准备好数据和方法,未来执行挂载时,此过程还会递归的应用于它的子组件上,最终形成一个有紧密关系的组件实例树。
vue3 相对于 vue2 新特性、优点
- performance: 性能比vue2.x块1.2~2倍;
- Tree shaking support: 按需编译,体积比vue2.x更小;
- Composition API: 组合API(类似React Hooks);
- Better TypeScript support: 更好的 ts 支持;
- Custom Render API: 暴露了自定义渲染的API ;
- Fragment, Teleport(Protal): 更先进的组件 ;