Vue3
Vue3:Options API 与 Composition API 的区别
1. Options API
Options API 是 Vue 的传统编程模式。在这种模式下,我们通过定义 data、computed、methods、watch 等选项来描述组件的逻辑。
在大多数情况下,使用组件的选项组织逻辑是非常有效的。但是,当组件变得复杂时,导致这些选项的数量增加,可能会使组件难以阅读和理解。
2. Composition API
在 Composition API 中,我们不再基于选项组织逻辑,而是根据逻辑功能进行组织。这意味着与某个功能相关的所有逻辑都放在一起,从而实现高内聚和低耦合。
3. Composition API 相对于 Options API 的优点
逻辑组织
- Options API:在处理大型组件时,相关的逻辑点容易分散在
methods、computed、watch等选项中。这种碎片化使得代码阅读和维护变得困难。 - Composition API:允许将某个逻辑关注点的所有代码放在一起。这样,当需要修改某个功能时,我们不再需要在文件中不断地跳转。
- Options API:在处理大型组件时,相关的逻辑点容易分散在
逻辑复用
- Options API:在 Vue 2 中,混入(mixin)是复用逻辑的主要方式,但存在命名冲突和数据来源不明确的问题。
- Composition API:提供了更灵活的逻辑复用机制,例如通过创建自定义 hooks 函数。
总结
Composition API 在逻辑组织和复用方面明显优于 Options API。
Composition API 更加函数式,从而提供更好的类型推断支持。
Composition API 更加友好于 tree-shaking 和代码压缩。
Composition API 不使用
this,从而避免了this指向不明的问题。对于简单的小型组件,Options API 仍然是一个很好的选择。
Vue3.0 性能提升主要是通过哪几方面体现的?
1. 编译阶段优化
在 Vue2 中,每个组件实例都对应一个 watcher 实例。它会在组件渲染的过程中记录用到的数据属性作为依赖。当依赖变更时,会通知 watcher 使关联的组件重新渲染。对此,Vue3 在编译阶段做了以下优化:
Diff 算法优化:Vue3 在 diff 算法中,相比 Vue2,增加了静态标记。其作用是对可能发生变化的部分添加标记,从而在后续变化时直接进行比较。
静态提升:Vue3 会对不参与更新的元素做静态提升。这些元素只会被创建一次,每次渲染时都会复用,避免了不必要的创建操作。
事件监听缓存:在默认情况下,绑定的事件行为被视为动态绑定,所以每次都会追踪其变化。开启事件监听器缓存后,下次 diff 时可以直接复用。
SSR 优化:当静态内容达到一定量级时,会在客户端生成一个 static node,避免了创建对象并根据对象渲染。
2. 源码体积
相较于 Vue2,Vue3 的整体体积减小了。除了去除了一些不常用的 API 外,其主要利益来自于 Tree shaking。例如,只有在用到 ref、reactive、computed 等函数时,它们才会被打包。
3. 响应式系统
Vue2 使用 defineProperty 来劫持并监听对象的属性。相反,Vue3 则采用了 Proxy 重写了响应式系统,它能:
- 监听动态属性的添加。
- 监听数组的索引和
length属性的变化。 - 监听属性的删除。
Vue3.0 为什么使用 Proxy API 替代 defineProperty API?
1.Vue2 的局限性:虽然 Vue2 使用 defineProperty 来实现响应式,但它:
- 无法检测对象属性的添加和删除。
- 无法监听数组的特定方法。
- 需要遍历每个属性进行监听,如果对象深度嵌套,则需要递归监听,可能造成性能问题。
2.Proxy 的优势:
- 可以直接劫持整个对象,并返回一个新对象。
- 可以直接监听数组的变化。
- Proxy 有多达 13 种拦截方法,例如
apply、ownKeys、deleteProperty等。
Vue3.0 响应式原理
Vue3 使用 ES6 的 Proxy 和 Reflect 为数据实现响应式,解决了 Vue2 中的某些限制。Proxy 为深度监听,所以它能监听对象和数组内的任意元素。
响应式系统大致分为三个阶段:
- 初始化阶段:组件初始化时创建对应的
Proxy对象,并生成一个渲染的effect。 get依赖收集阶段:通过解析 template 来触发get,并收集依赖。set派发更新阶段:当属性变化时,触发更新。
对于深度监听,Vue3 判断当前的返回值是否为对象。如果是,它会递归地使用 reactive 方法进行代理。此外,为了避免频繁的更新,Vue3 提供了策略来判断是否真的需要执行更新。
说说Vue 3.0中Treeshaking特性?
1、是什么?
Tree shaking 是一种通过清除多余代码的方式来优化项目打包体积的技术,也称为 "Dead code elimination"。简言之,在保持代码运行结果不变的前提下,去除无用的代码。
在 Vue 2 中,无论我们使用什么功能,它们最终都会出现在生产代码中。这主要是因为 Vue 实例在项目中是单例的,打包工具无法检测到该对象的哪些属性在代码中被使用到。但 Vue 3 源码引入了 tree shaking 特性,将全局 API 进行了分块处理。如果您不使用某些功能,它们将不会包含在您的打包结果中。
2、如何做?
Tree shaking 基于 ES6 模块语法(import 和 exports),主要利用 ES6 模块的静态结构特性。在编译时,就可以确定模块的依赖关系,以及输入和输出的变量。Tree shaking 主要完成两件事:
- 利用 ES6 Module 在编译阶段判断哪些模块已经加载。
- 判断哪些模块和变量未被使用或引用,并删除对应的代码。
3、作用(好处)?
通过 Tree shaking,Vue 3 为我们带来以下好处:
- 减少程序体积,使其更小。
- 减少程序执行时间,使其运行更快。
- 为未来程序架构优化提供了更多的可能性,使其更加友好。
Vue3 新特性有哪些?
1、性能提升
- 由原来的
Object.defineProperty改为基于 ES6 的Proxy,提高响应式性能。 - 重写了 Vdom,包括 diff 算法优化和增加的静态标志。
- 进行了模板编译优化,如静态提升,使不参与更新的元素仅被创建一次。
- 更加高效的组件初始化。
2、更好的支持 TypeScript
- Vue.js 2.x 使用 Flow 进行类型检查,以避免因类型问题导致的错误,但 Flow 在一些复杂场景的类型检查上存在不足。
- Vue.js 3.0 抛弃了 Flow,选用 TypeScript 重新构建了整个项目,从而得到更加强大和精确的类型支持。
3、新增 Composition API
- Composition API 是 Vue 3 的一个新特性,与 mixin 相比,它更为强大。它可以将功能模块独立出来,提高代码逻辑的可复用性,同时增强代码压缩性。
- 在 Vue 3 中,
methods、watch、computed和data等都被放在了setup()函数中。 setup()函数会在created()生命周期之前执行,执行顺序为:beforeCreate->setup->created。
4、新增组件
- Fragment: Vue 3 不再限制 template 必须只有一个根节点。
- Teleport: 允许我们将组件的内容传送到页面上的任何位置。
- Suspense: 在等待异步组件加载过程中,允许渲染一些占位内容,提升用户体验。
5、Tree-shaking
- 支持 Tree shaking 优化,优化后的项目体积可能只有原来的一半,从而加快了页面加载速度。
6、Custom Renderer API
- 允许开发者不仅仅局限于 DOM 渲染,还可以使用例如 WebGL 这样的技术进行渲染。
vue3 组合式API生命周期钩子函数有变化吗?
在 Vue3 中,引入了组合式 API 作为一种新的方式来组织和复用组件逻辑。由于 setup 函数是这个新 API 的入口点,因此 Vue3 的组合式 API 的生命周期钩子与传统的选项式 API 有所不同。
setup 函数确实是在 beforeCreate 和 created 生命周期钩子之间执行的,因此在使用组合式 API 时,通常不需要明确地定义这两个钩子。
为了在组合式 API 中使用生命周期钩子,Vue3 引入了一系列新的函数,它们的名称以 on 开头,如 onMounted、onUpdated 等。这些函数可以在 setup 函数中使用,它们的作用与传统生命周期钩子相似,但是它们是函数式的,并且需要在 setup 函数中明确调用。
值得注意的是,组合式 API 的生命周期钩子只能在 setup 函数中使用,并且必须在 setup 期间同步使用,因为它们依赖于内部的全局状态来定位当前的组件实例。
以下是选项式 API 与组合式 API 之间生命周期钩子的对比:
| 选项式 API | 组合式 API | 生命周期说明 |
|---|---|---|
| beforeCreate | (setup) | 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。 |
| created | (setup) | 实例已经创建完成之后被调用。 |
| beforeMount | onBeforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用。 |
| mounted | onMounted | el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。 |
| beforeUpdate | onBeforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前。 |
| updated | onUpdated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。 |
| beforeDestroy | onBeforeUnmount | 实例销毁之前调用。 |
| destroyed | onUnmounted | Vue 实例销毁后调用。 |
| errorCaptured | onErrorCaptured | 当捕获一个来自子孙组件的错误时被调用。 |
| activated | onActivated | keep-alive 组件激活时调用。 |
| deactivated | onDeactivated | keep-alive 组件停用时调用。 |
watch 和 watchEffect 的区别?
watch 和 watchEffect 都是 Vue 3 中的监听器函数,它们之间存在以下区别:
1. 指明监视的数据
watch: 需要指明要监视的数据源和相应的回调函数。watchEffect: 自动识别回调中使用的数据作为依赖,不需显式指明。你使用了哪个响应式数据,它就监听哪个。
2. 访问数据的能力
watch: 可以访问数据改变之前和之后的值。watchEffect: 只能获取到数据改变后的值。
3. 执行时机
watch: 默认情况下,初始化时不执行,只有在数据变化后才执行。但可以通过配置项immediate设为true来立即执行。watchEffect: 初始化时立即执行,之后的执行则是在其依赖数据发生变化时。
4. 与 computed 的相似性
watchEffect与computed都自动收集依赖,但:computed: 注重计算出的结果(回调函数的返回值),因此必须返回一个值。watchEffect: 注重副作用过程(回调函数执行的内容),因此不需要返回值。
5. 注意事项
- 当使用
watch监视reactive定义的响应式数据时,oldValue无法正确获取,并且强制开启了深度监视,即deep配置项失效。 - 当监视
reactive定义的响应式数据中的某个属性时,deep配置项仍然有效。
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
//情况1:监视ref定义的响应式数据
watch(sum,(newValue, oldValue)=>{
console.log("sum变化了", newValue, oldValue),(immediate:true)
})
//情况2:监视多个ref定义的响应式数据
watch([sum, msg],(newValue, oldValue)=>{
console.log("sum或msg变化了", newValue, oldValue),(immediate:true)
})
//情况3:监视reactive定义的响应式数据
//若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue,且强制开启了深度监视。
watch(person,(newValue, oldValue)=>{
console.log("person变化了", newValue, oldValue),(immediate:true,deep:false) //此处的deep配置不再生效。
})
//情况4:监视reactive所定义的一个响应式数据中的某个属性
watch(()=>person.name,(newValue, oldValue)=>{
console.log("person.name变化了", newValue, oldValue)
})
//情况5:监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.name, ()=>person.age],(newValue, oldValue)=>{
console.log("person.name或person.age变化了", newValue, oldValue)
})
//特殊情况:
watch(()=>person.job,(newValue, oldValue)=>{
console.log("person.job变化了", newValue, oldValue)
}, {deep:true})let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
//情况1:监视ref定义的响应式数据
watch(sum,(newValue, oldValue)=>{
console.log("sum变化了", newValue, oldValue),(immediate:true)
})
//情况2:监视多个ref定义的响应式数据
watch([sum, msg],(newValue, oldValue)=>{
console.log("sum或msg变化了", newValue, oldValue),(immediate:true)
})
//情况3:监视reactive定义的响应式数据
//若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue,且强制开启了深度监视。
watch(person,(newValue, oldValue)=>{
console.log("person变化了", newValue, oldValue),(immediate:true,deep:false) //此处的deep配置不再生效。
})
//情况4:监视reactive所定义的一个响应式数据中的某个属性
watch(()=>person.name,(newValue, oldValue)=>{
console.log("person.name变化了", newValue, oldValue)
})
//情况5:监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.name, ()=>person.age],(newValue, oldValue)=>{
console.log("person.name或person.age变化了", newValue, oldValue)
})
//特殊情况:
watch(()=>person.job,(newValue, oldValue)=>{
console.log("person.job变化了", newValue, oldValue)
}, {deep:true})v-if 和 v-for 的优先级哪个高?
在 Vue 2 中,v-for 的优先级高于 v-if。但在 Vue 3 中,这一优先级已经改变,现在 v-if 的优先级更高。
script setup 是干啥的?
<script setup> 是 Vue3 提供的一种新的语法糖,它的目的是为了简化组合式 API 的使用,并提供一个更加精简和高效的方式来写 Vue3 组件。以下是使用 <script setup> 的一些主要特点:
- 没有需要返回的属性或方法,因为它们在组件模板中都是自动可用的。
- 引入的组件会自动进行注册,无需手动在
components选项中进行注册。 - 使用
defineProps函数来定义并接收父组件传递的 props。 - 使用
useAttrs获取非 prop 的属性,useSlots获取插槽内容,而defineEmits则用于定义组件可以触发的事件。 - 默认情况下,组件不会暴露任何属性或方法。如果需要,可以使用
defineExpose函数来指定哪些属性或方法应该被暴露。
Vue2/Vue3组件通信方式?
Vue 作为一个前端框架,组件间的通信是其核心特性之一。Vue2 和 Vue3 都提供了多种组件间通信的方式。
Vue3 组件通信方式
props: 用于父组件向子组件传递数据。$emit: 子组件向父组件发送消息或触发事件。ref/expose: 使用 ref 获取子组件的实例,并通过 expose 暴露子组件的属性或方法。$attrs: 包含了传递给组件的所有 attribute。v-model: 创建双向数据绑定。provide/inject: 主要用于避免 props 的深层嵌套传递,实现祖先组件和后代组件之间的通信。Vuex/pinia: 状态管理工具,用于在应用的任何部分之间共享状态。mitt: 一个小型的事件总线库,用于非父子组件之间的通信。
Vue2.x 组件通信方式
props: 用于父组件向子组件传递数据。$emit/v-on: 子组件向父组件发送消息或触发事件。.sync: 用于创建双向数据绑定。v-model: 用于在组件上创建双向数据绑定。ref: 获取子组件的实例。children/parent: 通过$children和$parent访问子组件和父组件。attrs/listeners:$attrs包含了传递给组件的所有 attribute,而$listeners包含了组件上的所有事件监听器。provide/inject: 主要用于避免 props 的深层嵌套传递,实现祖先组件和后代组件之间的通信。EventBus: 创建一个事件总线,用于非父子组件之间的通信。Vuex: 状态管理工具,用于在应用的任何部分之间共享状态。$root: 通过根实例进行组件间通信。slot: 使用插槽进行内容分发。
ref 与 reactive 的区别?
ref 和 reactive 是 Vue3 新推出的重要 API 用于创建响应式数据。
1. 使用方式
- 在
template中,通过setup函数return的数据和方法才可以被使用。 ref创建的响应式数据在模板中可以直接使用,而在 JS 中需通过.value的形式使用。
2. 数据类型支持
ref可以接收原始数据类型及引用数据类型。reactive只能接收引用数据类型。
3. 实现原理
ref底层实际上使用reactive实现,并对其进行了进一步封装,使其能够支持原始数据类型。
4. 能力比较
- 在 Vue3 中,
reactive能做的事情,ref都能做;而reactive不能做的事情,ref也都能做。
EventBus 与 mitt 的区别?
在 Vue2 中,我们使用 EventBus 实现跨组件通信,依赖于 Vue 自带的 $on/$emit/$off。虽然它简单方便,但如果使用不当可能导致维护困难。
而在 Vue3,相关方法被移除,意味着无法使用 EventBus。Vue3 推荐使用 props/emits、provide/inject、vuex 替代。但如果这些无法满足需求,官方推荐使用库如 mitt。
1. mitt 优点
- 超轻量,压缩后只有 200 bytes。
- 完整的 TS 支持。
- 跨框架,除 Vue 外,还可以用于 React、jQuery 等。
- API 简单,主要包括
on、emit、off。
谈谈 pinia?
Pinia 是 Vue 官方团队开发的新状态管理库,取代了 Vuex,并且被视为 Vuex 5 的替代品,意味着不再发布 Vuex 5。
1. Pinia 优点
- 轻量级,压缩后只有 1.6kb。
- 完整的 TS 支持。
- 无
mutations,仅保留state、actions、getters。 - 去除了 Vuex 的模块化结构,主要概念为
store。每个store是独立的,可以手动导入其他模块实现嵌套。 - 自动注册
store,无需手动添加。 - 支持服务端渲染(SSR)。
- 支持 Vue DevTools。
- 更友好的代码分割。
2. 数据持久化
Pinia 配套有 pinia-plugin-persist 插件来进行数据持久化,避免刷新导致的数据丢失。