相关概念和原理
响应式原理
vue.js采用发布-订阅模式,使用object.definedProperty()
劫持对象的getter
,setter
属性,在数据发生变化时,发布消息给订阅者,触发响应回调
Object.defineProperty
的问题
- 检测数组
Object.defineProperty
具有一定检测数组数据变化的能力,但是框架作者出于性能的考虑,放弃了这项特性- 数组只能使用经过hack过的方法触发数据的响应
- 该方法只能劫持数据的属性,需要通过递归,遍历等方式劫持每一个对象属性
- 相对于劫持每一个属性,劫持整个对象为更好的办法:
Proxy
虚拟DOM
用对象来描述dom元素,修改数据属性时,使用虚拟dom对比,只修改有差异的部分,提高效率
computed
与watch
的区别
特点
computed
依赖属性,且会缓存,只有依赖的变量变化时才会重新计算watch
观察属性,属性每次变化都会触发执行
使用场景
- 在依赖数据变化来进行计算时使用
computed
,利用computed
的缓存特性避免多次计算 - 在需要异步操作和持续观察数据时使用
watch
key
的作用
- 不使用key的话,vue会就地复用元素,简单列表页可能更快,但是复用会带来 状态错位 ,不能触发过渡bug
- 使用key
- 更精确:使用key来判断渲染元素,保证了元素的准确性
- 更快速:key作为唯一标识能让框架更快速的查找
data
为什么必须是函数
- 组件是可以复用的,因为引用类型的关系,所有的组件将将会指向同一个
data
,数据将会污染 - 使用函数可以为每一个组件返回一个全新的
data
数据
nextTick
的原理
- vue中将数据中的变化进行统一,使用微任务到宏任务的降级方式在下一次执行栈执行之前进行渲染
Promise => MutationObserver => setTimeout
microtask
因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕
keep-alive
的原理
- 获取所包裹的组件对象和组件名
- 根据
include/exclude
决定是否缓存,不缓存则直接返回子组件 - 根据生成的key查找
keys
数组中是否已缓存组件,如已缓存则取出并更新该key在keys
数组中的位置 - 在
this.cache
中缓存该组件并设置key值,根据LRU
规则,超过max
的组件将会被删除 - 然后将属性
keepAlive
设置为true,子组件的钩子将会用到
缓存算法:LRU
经常被访问到的元素,会有较大概率保存在队列中
- 将元素推入队列,并限制最大长度
- 当新元素存在于队列中时,提升到第一位
- 当超过最大长度时,移除最后一位
$set
有什么用
- data数据在初始化时会递归循环设置劫持,但是新增的属性直接赋值不会被劫持到
- 使用该方式会判断目标属性是否需要响应式然后进行响应式赋值处理
scoped
的作用
使样式只作用于当前组件
原理
postcss给每个dom元素添加唯一id,给css选择器绑定此id与元素对应起来
兼容:想要影响子组件的样式
/deep/
关键字样式穿透- 使用两个
style
标签,一个添加scoped
,一个不添加
父子组件的生命周期顺序
加载渲染过程
- 父
beforeCreate
- 父
created
- 父
beforeMount
- 子
beforeCreate
- 子
created
- 子
beforeMount
- 子
mounted
- 子
- 父
mounted
- 父
子组件更新过程
- 父
beforeUpdate
- 子
beforeUpdate
- 子
updated
- 子
- 父
updated
- 父
父组件更新过程
- 父
beforeUpdate
- 父
updated
- 父
销毁过程
- 父
beforeDestroy
- 子
beforeDestroy
- 子
destroyed
- 子
- 父
destroyed
- 父
绑定事件做事件代理吗
首先我们需要知道事件代理主要有什么作用?
- 事件代理能够避免我们逐个的去给元素新增和删除事件
- 事件代理比每一个元素都绑定一个事件性能要更好
从vue的角度上来看上面两点
- 在
v-for
中,我们直接用一个for
循环就能在模板中将每个元素都绑定上事件,并且当组件销毁时,vue也会自动给我们将所有的事件处理器都移除掉。所以事件代理能做到的第一点vue已经给我们做到了 - 在
v-for
中,给元素绑定的都是相同的事件,所以除非上千行的元素需要加上事件,其实和使用事件代理的性能差别不大,所以也没必要用事件代理
详细的生命周期过程
- 初始化
beforeCreated
:创建实例,初始化并注入data
响应式created
:实例创建完成,但是未生成真实dom
beforeMount
:将template
解析成render
函数,调用此钩子后生成真实dom
插入到文档mounted
:组件已经完成挂载
- 更新
beforeUpdate
:对比数据,判断是否需要更新UI,通知所有依赖项更新UIupdated
:完成更新视图
- 销毁
beforeDestroy
:开始销毁元素destroyed
:完成销毁
diff
算法原理
遍历新旧虚拟dom树,检查节点,标签或者属性有更改则进行局部更新
组件之间的通信的4种方法
父组件访问子组件实例
- 通过
$refs
获取子组件 - 在
Composition API
中,setup
定义一个ref
属性并返回,在子组件标签上定义同名ref
即可获取
- 通过
子组件访问父组件
- 子组件可以通过特殊属性
$parent
获取到父组件(不推荐使用) - 通过触发事件,父组件捕获完成交互
- 子组件可以通过特殊属性
父子组件跨级传递
- 使用
Provide
/Inject
传递数据
- 使用
同级组件之间的通信
- 通过
eventEmitter
事件监听,vue2
推荐new Vue
实例,vue3
推荐mitt
- 通过
vuex
- 为什么要用
vuex
?
随着应用的不断扩展,变得复杂,需要管理一些全局的的状态。
可以不用,在状态管理复杂到一定程度时可以考虑
小型状态管理可以使用
new Vue()
实例作为eventBus
,使用$emit
和$on
触发和监听事件mutation
和action
的区别思维的不同
mutation
专门用于修改state
且必须为同步(mutation
期望执行后立刻获得一个新状态,使用异步结果不可预料)action
用于处理业务代码和异步请求,可调用多个mutation
使代码结构和逻辑更清晰
state
和全局变量的区别state
可以做到响应触发组件更新全局变量可随处更改,
state
需要特定方法进行修改不规范,全局变量容易管理混乱,
state
可进行跟踪