大家好,我是Fine。
今天为大家分享一篇Vue专题,面试前你需要知道的Vue相关的知识点,全在这里,一网打尽。
以下是正文:
从我个人的开发经验来说,Vue2 和 Vue3 其实生命周期钩子,在选项式api模式下大差不差对比表格:
Vue2选项式 | Vue3选项式 | Vue3组合式 | 作用 |
---|---|---|---|
beforeCreate | beforeCreate | setup() | 在实例初始化之后,数据观测和事件配置之前被调用 |
created | created | onBeforeMount | 在实例创建完成后被立即调用,可在此执行数据初始化 |
beforeMount | beforeMount | onBeforeMount | 在挂载开始之前被调用,相关render函数首次被调用 |
mounted | mounted | onMounted | 组件挂载到DOM后调用,可获取DOM节点 |
beforeUpdate | beforeUpdate | onBeforeUpdate | 数据更新时,虚拟DOM重新渲染和打补丁之前调用 |
updated | updated | onUpdated | 组件DOM更新后调用,可执行依赖于DOM的操作 |
activated | activated | onActivated | keep-alive组件激活时调用 |
deactivated | deactivated | onDeactivated | keep-alive组件停用时调用 |
beforeDestroy | beforeUnmount | onBeforeUnmount | 实例销毁之前调用,可执行清理操作 |
destroyed | unmounted | onUnmounted | 实例销毁后调用,调用后实例指向销毁,所有东西可回收 |
errorCaptured | errorCaptured | onErrorCaptured | 捕获子孙组件错误时被调用 |
beforeCreate
在实例被创建之后,data
和 methods
还未初始化之前调用setup
在组件创建之后, data
和 methods
初始化之前被调用所以 setup
对应于 beforeCreate
钩子。
created
在组件实例被创建之后调用,这个时候还没有开始 DOM
的挂载,data
数据对象就已经被初始化好了。onBeforeMount
会在组件挂载到 DOM
之前调用,这个时候数据已经初始化完成,但是还没有开始 DOM
渲染。所以其功能与 created
类似,都是表示实例初始化完成,但还未开始 DOM
渲染。
这个算是很容易被问到的,但是又不怎么问的!
通信方式 | 说明 | 优点 | 缺点 |
---|---|---|---|
事件总线 | 利用空Vue实例作为消息总线 | 简单,低耦合 | 难维护,调试难度大 |
provide/inject | 依赖注入,可跨多层级 | 低耦合,方便访问父级数据 | 无法响应式,只适用于父子孙组件间 |
本地存储 | localStorage、sessionStorage | 通用简单 | 没有响应式,需要手动同步 |
状态管理工具 | Vuex、Pinia等 | 集中状态管理,高效调试 | 学习和构建成本较高 |
父子组件通信 | props down, events up | 天然的Vue组件通信方式 | 只能单向,父子组件间才有效 |
通过使用<component>
并动态绑定is属性,可以实现动态切换多个组件的功能。
// 组件对象
const Foo = { /* ... */ }
const Bar = { /* ... */ }
// 动态组件
<component :is="currentComponent"/>
data() {
return {
currentComponent: 'Foo'
}
}
异步组件通过定义一个返回Promise的工厂函数,实现组件的异步加载。
const AsyncComponent = () => ({
// 组件加载中
component: import('./MyComponent.vue'),
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间
delay: 200,
// 加载组件时的提示
loading: LoadingComponent,
})
然后在组件中使用:
<async-component></async-component>
当异步组件加载成功后,将显示该组件,否则展示fallback组件。
异步组件常用于路由按需加载和代码分割。
keep-alive是Vue提供的一个内置组件,可以使被包含的组件保留状态,避免反复重渲染,使用 keep-alive 进行缓存的组件会多两个生命周期钩子函数:activated、deactivated
<!-- 使用keep-alive包裹动态组件 -->
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
<!-- 动态切换组件 -->
<button @click="currentComponent = 'A'">Show A</button>
<button @click="currentComponent = 'B'">Show B</button>
slot 是我们在自定义组件,或者使用组件时候最喜欢用到的一个语法了
base-layout:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name
的 <slot>
出口会带有隐含的名字“default”
使用:
<base-layout>
<template v-slot:header>
<h1>header</h1>
</template>
<p>paragraph</p>
<template v-slot:footer>
<p>footer</p>
</template>
</base-layout>
有时让插槽内容能够访问子组件中才有的数据是很有用的
current-user:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
使用:
<current-user>
<template v-slot:default="{ user }">
{{ user.firstName }}
</template>
</current-user>
我们可以动态配置 slotName 来进行插槽配置
<base-layout>
<template v-slot:[slotName]>
...
</template>
</base-layout>
编译阶段
<slot>
会生成一个Slot AST节点渲染阶段
核心流程
这里引用官方的一句话:
可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
假设一个场景
html
<div>{{ title }}</div>
js
test() {
for(let i = 0; i < 100; i++){
this.title = `第${i}个标题`
}
}
...
mounted(){
test()
}
这里我们在 test
中使用修改了 title
,假设一下,如果没有异步更新这个dom
,那么就要操作100次,为了避免这种无意义的性能消耗,Vue再侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
如果在同一事件循环中多次更新DOM,会导致不必要的计算和DOM操作。将它们 defer 到下一个事件循环执行,可以有效减少开销。
如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作
nextTick 相信大家都在项目中或多或少的用过几次吧!
nextTick: 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
可以简述如下:
let callbacks = [] // 异步回调队列
function nextTick(cb) {
callbacks.push(cb) // 推入回调队列
// 微任务执行callbacks
Promise.resolve().then(flushCallbacks)
}
function flushCallbacks() {
callbacks.forEach(cb => cb()) // 执行队列回调
callbacks = [] // 重置队列
}
ref
和 reactive
区别,如何选择? 个人认为 ref
和 reactive
其实 没必要这个都抛出来给我们用,容易造成一些使用困扰,ref
我觉就好,不过存在即合理,还是细说一下区别
ref() | reactive() |
---|---|
✅支持基本数据类型+引用数据类型 | ❌只支持对象和数组(引用数据类型) |
❌在 <script> 和 <template> 使用方式不同(script中要.value ) | ✅在 <script> 和 <template> 中无差别使用 |
✅重新分配一个新对象不会失去响应 | ❌重新分配一个新对象会丢失响应性 |
需要使用 .value 访问属性 | 能直接访问属性 |
✅传入函数时,不会失去响应 | ❌将对象传入函数时,失去响应 |
✅解构对象时会丢失响应性,需使用toRefs | ❌解构时会丢失响应性,需使用toRefs |
响应式是 Vue中很重要的一环,但是模板编译也是很重要的一环,从面试的角度来说,Vue的模板编译主要是这几个步骤:
解析:解析器将模板解析为抽象语树 AST,只有将模板解析成 AST 后,才能基于它做优化或者生成代码字符串
优化:优化抽象语法树
生成:将渲染函数打包生成新函数,返回函数的字符串形式
框架 | 模板语法 | 编译方式 | 学习曲线 |
---|---|---|---|
Vue.js | 简单 HTML-like | 运行时和构建时编译 | 低 |
易于理解 | |||
React | JSX (JavaScript XML) | 编译为 JavaScript | 中等 |
嵌入 JavaScript 中 | |||
Angular | 复杂,基于 HTML | 预编译 (Ahead of Time) | 较高 |
双向数据绑定 |
关于Vue2 的 diff 算法个人的理解上是:
深度优先(同层比较):
双指针:
这样一直递归的遍历下去,直到整棵树完成对比。
0
里面的数字记录老节点的索引 ,数组索引就是新节点的索引在实际项目中,Vue 的性能优化需要根据具体的场景和需求来选择合适的策略。以下是一些常见的 Vue 性能优化方式,结合项目场景进行总结:
<keep-alive>
缓存组件实例,以减少组件的销毁和重新创建。Vuex有几个很重要的概念:
特点 | Mutation | Action |
---|---|---|
同步/异步 | 同步 | 异步 |
直接/间接 | 直接修改状态 | 通过提交 Mutation 间接修改状态 |
这个比较简单,我们需要记住几个东西:
hash | history | |
---|---|---|
表现形式 | http://aaa/#/user/id | http://aaa/user/id |
基于api | onhashchange | 配合 history.pushState + window.addEventListener("popstate", ()=> {}) |
配置方式 | 不需要后端 | 需要后端协助 |
这里是手写了一个简单的实现机制
原文地址: https://juejin.cn/post/7306018817687109684
原文作者: 是洋柿子啊