vue2.0组件通信⽅式有哪些?

  • ⽗⼦组件通信: propseventv-model.syncref$parent$children
  • 非⽗⼦组件通信: $attr$listenersprovideinjecteventbus、通过根实例$root访问、 vuexdispatchbrodcast

Vue3组件通信

  • props$emit
  • provideinject

v-model是如何实现双向绑定的?

  • vue 2.0 v-model 是⽤来在表单控件或者组件上创建双向绑定的,他的本质是 v-bindv-on 的语法糖,在 ⼀个组件上使⽤ v-model ,默认会为组件绑定名为 valueprop 和名为 input 的事件。

  • Vue3.0 在 3.x 中,⾃定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件

Vuex和单纯的全局对象有什么区别?

Vuex和全局对象主要有两⼤区别:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发⽣变 化,那么相应的组件也会相应地得到⾼效更新。
  2. 不能直接改变 store 中的状态。改变 store 中的状态的唯⼀途径就是显式地提交 (commit)mutation。这样使得我们可以⽅便地跟踪每⼀个状态的变化,从⽽让我们能够实现⼀些⼯具帮助我们更好地了解我们的应⽤。

Vue 的父子组件生命周期钩子执行顺序

渲染过程: ⽗组件挂载完成⼀定是等⼦组件都挂载完成后,才算是⽗组件挂载完,所以⽗组件的mounted在⼦组件mouted之后

⽗beforeCreate -> ⽗created -> ⽗beforeMount -> ⼦beforeCreate -> ⼦created -> ⼦beforeMount -> ⼦mounted -> ⽗mounted

⼦组件更新过程:

  1. 影响到⽗组件: ⽗beforeUpdate -> ⼦beforeUpdate->⼦updated -> ⽗updted
  2. 不影响⽗组件: ⼦beforeUpdate -> ⼦updated

⽗组件更新过程:

  1. 影响到⼦组件: ⽗beforeUpdate -> ⼦beforeUpdate->⼦updated -> ⽗updted
  2. 不影响⼦组件: ⽗beforeUpdate -> ⽗updated

销毁过程: ⽗beforeDestroy -> ⼦beforeDestroy -> ⼦destroyed -> ⽗destroyed

看起来很多好像很难记忆,其实只要理解了,不管是哪种情况,都⼀定是⽗组件等待⼦组件完成后,才会执⾏⾃⼰对应完成的钩⼦,就可以很容易记住

v-show 和 v-if 有哪些区别?

  • v-if 会在切换过程中对条件块的事件监听器和⼦组件进⾏销毁和重建,如果初始条件是false,则什么都不做,直到条件第⼀次为true时才开始渲染模块。

  • v-show 只是基于css进⾏切换,不管初始条件是什么,都会渲染。

所以, v-if 切换的开销更⼤,⽽ v-show 初始化渲染开销更⼤,在需要频繁切换,或者切换的部分dom很复杂时,使⽤ v-show 更合适。渲染后很少切换的则使⽤ v-if 更合适。

Vue 中 v-html 会导致什么问题

在⽹站上动态渲染任意 HTML,很容易导致 XSS 攻击。所以只能在可信内容上使⽤ v-html,且永远不能⽤于⽤户提交的内容上。

v-for 中 key 的作⽤是什么?

key 是给每个 vnode 指定的唯⼀ id ,在同级的 vnode diff 过程中,可以根据 key 快速的对⽐,来判断是否为相同节点,并且利⽤ key 的唯⼀性可以⽣成 map 来更快的获取相应的节点。

另外指定 key 后,就不再采⽤“就地复⽤”策略了,可以保证渲染的准确性。

为什么 v-for 和 v-if 不建议⽤在⼀起

  • v-forv-if 处于同⼀个节点时, v-for 的优先级⽐ v-if 更⾼,这意味着 v-if 将分别重复运⾏于每个 v-for 循环中。如果要遍历的数组很⼤,⽽真正要展示的数据很少时,这将造成很⼤的性能浪费。
  • 这种场景建议使⽤ computed ,先对数据进⾏过滤。

vue-router hash 模式和 history 模式有什么区别?

vue一共有三种路由:hash、history、abstract

区别:

  1. url 展示上,hash模式有 “#”,history 模式没有
  2. 刷新⻚⾯时,hash 模式可以正常加载到 hash 值对应的⻚⾯,⽽ history 没有处理的话,会返回404,⼀般需要后端将所有⻚⾯都配置重定向到⾸⻚路由。
  3. 兼容性。hash 可以⽀持低版本浏览器和 IE

Vue Router 提供了几种路由模式,用于定义如何在浏览器中同步 URL 与你的 Vue 应用。这些路由模式主要包括:

hash 模式

# 后⾯ hash 值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新⻚⾯。同时通过监听 hashchange 事件可以知道 hash 发⽣了哪些变化,然后根据 hash 变化来实现更新⻚⾯部分内容的操作。

  • 描述: hash 模式是 Vue Router 的默认模式。它使用 URL 的哈希(即 URL 中 # 符号后面的部分)来模拟一个完整的 URL,从而实现不重新加载页面的情况下进行页面跳转。
  • 优点: 不需要服务器特别配置,可以在所有支持前端 JavaScript 的浏览器上运行。
  • 缺点: 哈希变化不会被包括在 HTTP 请求中,因此,哈希模式下的 URL 不会被搜索引擎索引。

history 模式

history 模式的实现,主要是 HTML5 标准发布的两个 API, pushStatereplaceState ,这两个 API 可以在改变 url,但是不会发送请求。这样就可以监听 url 变化来实现更新⻚⾯部分内容的操作。

  • 描述: history 模式利用了 HTML5 History API 来实现 URL 的导航而无需重新加载页面。
  • 优点: URL 看起来更美观,像正常的 URL 那样(没有 #)。这种类型的 URL 有利于 SEO。
  • 缺点: 服务器必须配置好对所有路由都返回同一个 index.html 页面,否则在用户直接访问非首页 URL 或刷新页面时会收到 404 错误。

abstract 模式

  • 描述: abstract 模式并不依赖于浏览器的 History API 或哈希变化。它是用在任何 JavaScript 环境中,如 Node.js 或者 Electron。
  • 优点: 在非浏览器环境中使用 Vue,或者在测试过程中不想涉及浏览器特性时,这种模式非常有用。
  • 缺点: 在常规的浏览器应用中很少使用。

在创建 Vue Router 实例时,可以通过 mode 选项来设置路由模式。例如,要设置为 history 模式,可以这样配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from 'vue';
import VueRouter from 'vue-router';

Vue.use(VueRouter);

const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
});

export default router;

vue3.0 相对于 vue2.x 有哪些变化?

  • 监测机制的改变(Object.defineProperty —> Proxy)

  • 模板

  • 对象式的组件声明⽅式 (class)

  • 使⽤ts

  • 其它⽅⾯的更改:⽀持⾃定义渲染器、 ⽀持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件、基于 treeshaking 优化,提供了更多的内置功能

  • Vue 3.x 使用了一个新的响应式系统,该系统基于 ES6 的 Proxy 特性,来实现数据的响应式变化。这标志着 Vue.js 从 2.x 版本使用的 Object.defineProperty 方法迁移到了 Proxy,以提供更好的性能和更丰富的功能。下面是 Vue 3.x 响应式系统的基本原理概述:

    优势

    与 Vue 2.x 相比,Vue 3.x 的响应式系统使用 Proxy 提供了更好的性能和更丰富的特性,包括:

    • 更好的性能:Proxy 的操作几乎没有开销,且不需要遍历对象属性进行响应式转换。
    • 更好的语言特性支持:Proxy 可以拦截更多类型的操作,包括数组操作、动态属性的添加等。
    • 更好的兼容性:Vue 3.x 设计时考虑了未来的 JavaScript 新特性,使得其更加前瞻性和兼容性强。

    总之,Vue 3.x 的响应式系统通过 Proxy 提供了一个更加强大、灵活且高效的数据响应机制,为开发现代 Web 应用提供了坚实的基础。

vue3的setup

在 Vue 3 中,setup 函数和 <script setup> 是两种用于编写组合式 API(Composition API)的方式。虽然它们在功能上有相似之处,但在使用方式和语法上有一些重要的区别。以下是对它们的详细比较和说明。

setup 函数

setup 函数是 Vue 3 组合式 API 的核心部分,它在组件实例创建之前执行,并且是定义响应式状态、计算属性和方法的地方。

特点

  1. 函数形式

    • setup 函数是组件选项对象中的一个方法,类似于 datamethods 等选项。
  2. 接收参数

    • setup 函数接收两个参数:propscontextprops 是传递给组件的属性,context 包含 attrsslotsemit
  3. 返回值

    • setup 函数返回一个对象,包含需要在模板中使用的响应式状态和方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>{{ count }}</div>
<button @click="increment">Increment</button>
</template>

<script>
import { ref } from 'vue';

export default {
setup(props, context) {
const count = ref(0);

const increment = () => {
count.value++;
};

return {
count,
increment
};
}
};
</script>

<script setup>

<script setup> 是 Vue 3.2 引入的一种语法糖,简化了使用组合式 API 的方式。它将 setup 函数的逻辑直接放在 <script setup> 标签中,无需显式定义和返回。

特点

  1. 语法糖

    • <script setup> 是一种更简洁的语法糖,自动将脚本中的顶层变量和函数暴露给模板。
  2. 自动导入

    • 不需要显式返回对象,顶层的响应式变量和方法会自动暴露给模板。
  3. 更少的样板代码

    • 由于不需要显式定义 setup 函数和返回对象,代码更简洁、更易读。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div>{{ count }}</div>
<button @click="increment">Increment</button>
</template>

<script setup>
import { ref } from 'vue';

const count = ref(0);

const increment = () => {
count.value++;
};
</script>

区别总结

  1. 语法

    • setup 函数:需要显式定义 setup 函数,并返回一个对象。
    • <script setup>:直接在 <script setup> 标签中编写逻辑,顶层变量和函数自动暴露给模板。
  2. 简洁性

    • setup 函数:需要更多的样板代码,包括定义和返回对象。
    • <script setup>:更少的样板代码,更简洁和易读。
  3. 自动导入

    • setup 函数:需要显式返回需要在模板中使用的变量和方法。
    • <script setup>:顶层变量和函数自动暴露给模板,无需显式返回。
  4. 类型支持

    • <script setup> 支持 TypeScript 类型声明,能够更好地与 TypeScript 集成。

选择使用哪一个

  • 简洁和易读:如果你喜欢更简洁的代码和更少的样板代码,<script setup> 是一个更好的选择。
  • 传统方式:如果你更习惯于 Vue 2 的选项式 API,或者需要在组件定义中使用其他选项(如 datamethods 等),setup 函数可能更适合你。

总结

  • setup 函数:传统的组合式 API 方式,需要显式定义和返回对象,适用于需要更多控制和与其他选项式 API 结合使用的场景。
  • **<script setup>**:Vue 3.2 引入的语法糖,更简洁,自动暴露顶层变量和函数,适用于喜欢简洁代码和更少样板代码的开发者。

通过理解这两种方式的区别和特点,可以根据项目需求和个人偏好选择合适的方式来编写 Vue 3 组件。

讲⼀讲MVVM原理

  • MVVM是 Model-View-ViewModel 缩写,也就是把 MVC 中的 Controller 演变成 ViewModel
  • Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并⾃动将数据渲染到⻚⾯中,视图变化的时候会通知viewModel层更新数据

vue 中组件 data 为什么是 return ⼀个对象的函数,⽽不是直接是个对象?

  • 如果将data定义为对象,这就表示所有的组件实例共⽤了⼀份data数据,因此,⽆论在哪个组件实例中修改了data,都会影响到所有的组件实例。
  • 组件中的data写成⼀个函数,数据以函数返回值形式定义,这样每复⽤⼀次组件,就会返回⼀份新的data,类似于给每个组件实例创建⼀个私有的数据空间,让各个组件实例维护各⾃的数据。⽽单纯的写成对象形式,就使得所有组件实例共⽤了⼀份data,就会造成⼀个变了全都会变的结果。

Vue 中的 computed 是如何实现的

流程总结如下:只有当计算属性的依赖发生变化时,计算属性的值才会重新计算。如果依赖没有变化,Vue 会返回计算属性上一次计算的结果,从而避免不必要的计算开销。

  1. 当组件初始化的时候, computeddata 会分别建⽴各⾃的响应系统, Observer 遍历 data中每个属性设置 get/set 数据拦截

  2. 初始化 computed 会调⽤ initComputed 函数

  • 注册⼀个 watcher 实例,并在内实例化⼀个 Dep 消息订阅器⽤作后续收集依赖(⽐如渲染函数的 watcher 或者其他观察该计算属性变化的 watcher

  • 调⽤计算属性时会触发其 Object.definePropertyget 访问器函数

  • 调⽤ watcher.depend() ⽅法向⾃身的消息订阅器 depsubs 中添加其他属性的 watcher

  • 调⽤ watcherevaluate ⽅法(进⽽调⽤ watcherget ⽅法)让⾃身成为其他 watcher 的消息订阅器的订阅者,⾸先将 watcher 赋给 Dep.target ,然后执⾏ getter 求值函数,当访问求值函数⾥⾯的属性(⽐如来⾃ dataprops 或其他 computed )时, 会同样触发它们的 get 访问器函数从⽽将该计算属性的 watcher 添加到求值函数中属性的 watcher 的消息订阅器 dep 中,当这些操作完成,最后关闭 Dep.target 赋为 null 并 返回求值函数结果。

  1. 当某个属性发⽣变化,触发 set 拦截函数,然后调⽤⾃身消息订阅器 depnotify ⽅法,遍 历当前 dep 中保存着所有订阅者 wathcersubs 数组,并逐个调⽤ watcherupdate ⽅ 法,完成响应更新。

Vue 的响应式原理

如果⾯试被问到这个问题,⼜描述不清楚,可以直接画出 Vue 官⽅⽂档的这个图,对着图来解释效果会更好。

image.png

  • Vue 的响应式是通过 Object.defineProperty 对数据进⾏劫持,并结合观察者模式实现。
  • Vue 利⽤Object.defineProperty 创建⼀个 observe 来劫持监听所有的属性,把这些属性全部转为 gettersetter
  • Vue 中每个组件实例都会对应⼀个 watcher 实例,它会在组件渲染的过程中把使⽤过的 数据属性通过 getter 收集为依赖。之后当依赖项的 setter 触发时,会通知 watcher ,从⽽使它关联的组件重新渲染。

Object.defineProperty有哪些缺点?

  1. Object.defineProperty 只能劫持对象的属性,⽽ Proxy 是直接代理对象由于 Object.defineProperty 只能对属性进⾏劫持,需要遍历对象的每个属性。⽽ Proxy 可以直接代理对象。
  2. Object.defineProperty 对新增属性需要⼿动进⾏ Observe , 由于 Object.defineProperty 劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新 增属性再使⽤ Object.defineProperty 进⾏劫持。 也正是因为这个原因,使⽤ Vue 给 data中的数组或对象新增属性时,需要使⽤ vm.$set才能保证新增的属性也是响应式的。
  3. Proxy ⽀持13种拦截操作,这是 defineProperty 所不具有的。
  4. 新标准性能红利Proxy 作为新标准,⻓远来看,JS引擎会继续优化 Proxy ,但 gettersetter 基本不会再有针对性优化。
  5. Proxy 兼容性差 ⽬前并没有⼀个完整⽀持 Proxy 所有拦截⽅法的Polyfill⽅案

为什么 Proxy 比 Object.defineProperty() 更优?

  1. 更丰富的拦截操作: Proxy 对象可以拦截 JavaScript 对象的各种操作,包括属性的读取、赋值、删除等,以及对象的各种操作,如构造函数调用、in 操作符、has 操作符等。相比之下,Object.defineProperty() 只能拦截属性的读取、赋值和删除操作。
  2. 深层嵌套的响应式: 使用 Proxy 可以轻松地实现深层嵌套的响应式数据结构,无需像 Object.defineProperty() 那样需要递归地遍历对象属性并进行响应式处理。
  3. 更简洁的代码: 使用 Proxy 可以编写更简洁、更易于理解的代码,因为 Proxy 提供了一个统一的 API 来处理所有的拦截操作,而不需要像 Object.defineProperty() 那样需要为每个属性单独编写拦截逻辑。
  4. 无需预定义属性: 使用 Proxy 可以在对象创建之后动态地添加和删除属性,并使得这些动态添加的属性也能够响应式地更新。相比之下,Object.defineProperty() 只能处理预定义的属性。
  5. 更好的性能: 由于 Proxy 是原生实现的,因此在一些情况下,Proxy 的性能可能会比 Object.defineProperty() 更好。例如,在处理大型数据结构或频繁更新的情况下,Proxy 可能会比 Object.defineProperty() 更高效。

Vue2.0中如何检测数组变化?

Vue 的 Observer 对数组做了单独的处理,对数组的⽅法进⾏编译,并赋值给数组属性的 __proto__属性上,因为原型链的机制,找到对应的⽅法就不会继续往上找了。编译⽅法中会对⼀些会增加索引的⽅法( pushunshiftsplice )进⾏⼿动 observe。

Vue3重新使用Proxy监听数据的双向绑定。

nextTick是做什么⽤的,其原理是什么?

nextTick 是 Vue 提供的一个全局方法,它用于延迟回调的执行直到下一次 DOM 更新循环之后。在 Vue 的数据绑定机制中,当响应式数据发生变化后,视图不会立即更新,而是异步更新。这意味着 Vue 会将所有数据变更放入一个队列中,并在一个事件循环中批量处理这些变更,以优化性能和避免不必要的 DOM 操作。nextTick 允许你在 DOM 更新完成后,立即执行某些操作,这对于需要在视图完全渲染后执行的 DOM 依赖操作非常有用。

使用场景

  • 验证 DOM 更新的结果,如测试或检查元素位置。
  • 在数据变化后,执行依赖于新 DOM 结构的操作。

原理

Vue 的异步更新队列意味着你在同一个事件循环中对响应式数据进行的多次修改会被合并,从而减少实际的渲染次数。nextTick 则提供了一种方式,确保在所有数据变更导致的视图更新之后再运行指定的回调函数。

Vue 内部会维护一个待处理的回调函数队列。当你调用 nextTick 时,Vue 会将你的回调函数添加到这个队列中。一旦当前事件循环中的所有数据变更都处理完毕,Vue 会在下一个事件循环“tick”开始之前,处理这个队列中的所有回调函数。

实现细节

Vue 2.x 和 Vue 3.x 在实现 nextTick 方面略有不同,但基本原理相似,都是利用了 JavaScript 事件循环和宏任务(MacroTask)或微任务(MicroTask)的概念。

  • 微任务优先: Vue 会首先尝试使用如 Promise.thenMutationObserverObject.observe(已废弃)这样的微任务API,因为微任务会在当前事件循环的末尾执行,保证了执行时机的及时性。
  • 宏任务备选: 如果环境不支持上述微任务API,Vue 会回退到宏任务API,如 setTimeout,尽管它的执行时机比微任务稍晚。

通过这种机制,nextTick 能够确保回调函数在 DOM 更新完成后尽快执行,同时不会阻塞浏览器的 UI 渲染。这对于高性能的响应式数据绑定和用户界面更新至关重要。

  • 在下次 DOM 更新循环结束后执⾏延迟回调,在修改数据之后⽴即使⽤ nextTick 来获取更新后的DOM。
  • nextTick 对于 micro task 的实现,会先检测是否⽀持 Promise ,不⽀持的话,直接指向 macrotask,⽽ macro task 的实现,优先检测是否⽀持 setImmediate (⾼版本IE和Etage⽀持),不⽀持的再去检测是否⽀持 MessageChannel,如果仍不⽀持,最终降级为 setTimeout 0;
  • 默认的情况,会先以 micro task ⽅式执⾏,因为 micro task 可以在⼀次 tick 中全部执⾏完毕,在⼀些有重绘和动画的场景有更好的性能。
  • 但是由于 micro task 优先级较⾼,在某些情况下,可能会在事件冒泡过程中触发,导致⼀些问题,所以有些地⽅会强制使⽤ macro task (如 v-on )。

注意:之所以将 nextTick 的回调函数放⼊到数组中⼀次性执⾏,⽽不是直接在 nextTick 中执⾏回调函数,是为了保证在同⼀个tick内多次执⾏了 nextTcik ,不会开启多个异步任务,⽽是把这些异步任务都压成⼀个同步任务,在下⼀个tick内执⾏完毕。

Vue 的模板编译原理

vue模板的编译过程分为3个阶段:

  • 第⼀步:解析

将模板字符串解析⽣成 AST,⽣成的AST 元素节点总共有 3 种类型,1 为普通元素, 2 为表达式,3为纯⽂本。

  • 第⼆步:优化语法树

Vue 模板中并不是所有数据都是响应式的,有很多数据是⾸次渲染后就永远不会变化的,那么这部分数据⽣成的 DOM 也不会变化,我们可以在 patch 的过程跳过对他们的⽐对。

此阶段会深度遍历⽣成的 AST 树,检测它的每⼀颗⼦树是不是静态节点,如果是静态节点则它们⽣成DOM 永远不需要改变,这对运⾏时对模板的更新起到极⼤的优化作⽤。

  1. ⽣成代码
1
const code = generate(ast, options)

通过 generate ⽅法,将ast⽣成 render 函数。

你知道Vue3.x响应式数据原理吗?

Vue3.x改⽤ Proxy 替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截⽅法。并且作为新标准将受到浏览器⼚商重点持续的性能优化。

Proxy只会代理对象的第⼀层,那么Vue3⼜是怎样处理这个问题的呢?

判断当前Reflect.get的返回值是否为Object,如果是则再通过 reactive ⽅法做代理, 这样就实现了深度观测。

监测数组的时候可能触发多次get/set,那么如何防⽌触发多次呢?

我们可以判断key是否为当前被代理对象target⾃身属性,也可以判断旧值与新值是否相等,只有满⾜以上两个条件之⼀时,才有可能执⾏trigger。

你都做过哪些Vue的性能优化?

在使用 Vue 进行前端开发时,性能优化是一个重要的考虑点,特别是在处理大型应用或复杂界面时。以下是一些常见的 Vue 性能优化策略:

1. 优化初始加载时间

  • 路由懒加载:使用 Vue Router 时,通过动态导入(import())来实现路由级别的代码分割,只有当路由被访问时才加载对应组件,减少初始包大小。
  • Webpack 代码分割:利用 Webpack 的代码分割(SplitChunks)功能,将第三方库、组件等分割成不同的 chunks,按需加载。

2. 减少不必要的重新渲染

  • **合理使用 v-showv-if**:v-show 只是切换元素的 CSS display 属性,而 v-if 是条件渲染,会更消耗性能。根据需要合理选择。
  • **使用 v-memo**(Vue 3.x 新增):用于缓存组件渲染结果,减少不必要的渲染。
  • 避免在模板中使用复杂表达式:复杂表达式会在每次渲染时重新计算,应该使用计算属性代替。

3. 优化列表渲染

  • **为 v-for 使用唯一的 key**:帮助 Vue 更快地识别节点,尤其是在列表数据频繁变化时。
  • 虚拟滚动:当需要渲染大量列表项时,通过虚拟滚动只渲染可视范围内的列表项,减少 DOM 元素的数量。

4. 合理使用事件 & 绑定

  • 事件解绑:避免内存泄漏,组件销毁时清理自定义事件监听器和定时器。
  • 使用事件代理:减少事件监听器的数量,特别是在列表中每项都需要绑定事件时。

5. 使用计算属性和侦听器

  • 合理使用计算属性:对于任何复杂逻辑,特别是那些依赖响应式数据的,使用计算属性可以缓存结果,避免每次都重新计算。
  • 谨慎使用侦听器:过多的侦听器会影响性能,尽量使用计算属性或提供的 Vue 事件处理机制。

6. 优化第三方库的使用

  • 按需引入:对于大型第三方库(如 lodash、element-ui 等),尽量按需引入所需的功能模块,避免引入整个库。

7. 服务端渲染(SSR)或预渲染

  • SSR:对 SEO 和首屏加载性能有显著提升,但增加了服务器的渲染负担。
  • 预渲染:适用于静态网站,可以在构建时生成静态 HTML,提高首屏加载速度。

8. 静态资源优化

  • 图片懒加载:减少初始页面加载时间,提高页面响应速度。
  • 使用 CDN:减少静态资源的加载时间。

9. 性能监控

  • 使用 Vue Devtools 性能标签页:监控和分析组件渲染性能。
  • 使用浏览器性能分析工具:定位渲染瓶颈。

性能优化是一个持续的过程,需要根据应用的实际情况不断调整和优化。通过以上方法,可以有效地提升 Vue 应用的性能,改善用户

体验。

编码阶段

  • 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
  • v-if和v-for不能连⽤
  • 如果需要使⽤v-for给每项元素绑定事件时使⽤事件代理
  • SPA ⻚⾯采⽤keep-alive缓存组件
  • 在更多的情况下,使⽤v-if替代v-show
  • key保证唯⼀
  • 使⽤路由懒加载、异步组件
  • 防抖、节流
  • 第三⽅模块按需导⼊
  • ⻓列表滚动到可视区域动态加载
  • 图⽚懒加载

SEO优化

  • 预渲染
  • 服务端渲染SSR

打包优化

  • 压缩代码
  • Tree Shaking/Scope Hoisting
  • 使⽤cdn加载第三⽅模块
  • 多线程打包happypack
  • splitChunks抽离公共⽂件
  • sourceMap优化

⽤户体验

  • 骨架屏
  • PWA

还可以使⽤缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

通过事件总线实现一个发布订阅

1
2
3
4
5
6
7
8
9
10
// 创建一个事件总线实例
const eventBus = new Vue();

// 订阅事件
eventBus.$on('custom-event', (data) => {
console.log('订阅者收到消息:', data);
});

// 发布事件
eventBus.$emit('custom-event', '这是一条自定义事件消息');

ref和reactive的区别

在 Vue 3 Composition API 中,refreactive 都是用来创建响应式数据的函数,但它们在使用方式和适用场景上有一些区别:

1. 定义

  • **ref**:

    • 用于包装一个基本类型值(如 stringnumberboolean)或对象,使其成为响应式的。
    • ref 返回的是一个包含 value 属性的对象。无论何时访问或修改该值,都需要通过 .value 属性。
    • 对于基本类型数据,ref 是必须的,因为基本类型不能被直接转换为响应式数据。
  • **reactive**:

    • 用于创建一个响应式的对象或数组。
    • 直接返回原始对象的响应式代理,不需要通过 .value 访问内部值。
    • 不能用于基本数据类型。

2. 使用场景

  • ref 适用于:

    • 需要响应式的基本数据类型。
    • 需要一个响应式对象,但希望在模板中直接使用而不是作为对象属性访问时。
  • reactive 适用于:

    • 对象或数组,特别是当你需要深层响应式支持时。
    • 复杂的数据结构,如嵌套对象。

3. 深层响应式

  • reactive 提供深层响应式。这意味着无论对象结构有多复杂,其内部所有层级的属性都是响应式的。
  • ref 对于对象,也能提供深层响应式,但当直接修改对象内部的属性时(不是 ref 对象本身的 .value),需要使用 reactive 来确保深层属性的响应性。

4. 模板引用

  • 在模板中使用时,ref 创建的响应式数据在引用时不需要 .value,Vue 会自动解包。
  • reactive 对象直接作为响应式数据使用,不需要任何额外的解包。

5. TypeScript 支持

  • ref 在 TypeScript 中提供了更好的类型推断。因为你总是通过 .value 访问值,所以 TypeScript 可以更准确地知道值的类型。
  • reactive 对象的类型推断也很好,但在某些复杂的情况下可能需要显式类型声明。

6. 响应式转换注意事项

  • 使用 reactive 对一个对象进行响应式转换时,如果该对象被重新赋值为另一个对象,原对象会失去响应式能力。而 ref 则可以通过 .value 属性来更新其内部值,保持响应式。
  • ref 可以被用来跟踪基本类型值的变化,这是 reactive 无法做到的。

总的来说,refreactive 各有适用场景,在 Vue 3 Composition API 中灵活使用这两者可以更好地管理和组织响应式状态。

vue2和vue3 核心 diff 算法区别?

Vue 2.x使用的是双向指针遍历的算法,也就是通过逐层比对新旧虚拟DOM树节点的方式来计算出更新需要做的最小操作集合。但这种算法的缺点是,由于遍历是从左到右、从上到下进行的,当发生节点删除或移动时,会导致其它节点位置的计算出现错误,因此会造成大量无效的重新渲染。

Vue 3.x使用了经过优化的单向遍历算法,也就是只扫描新虚拟DOM树上的节点,判断是否需要更新,跳过不需要更新的节点,进一步减少了不必要的操作。此外,在虚拟DOM创建后,Vue 3会缓存虚拟DOM节点的描述信息,以便于复用,这也会带来性能上的优势。同时,Vue 3还引入了静态提升技术,在编译时将一些静态的节点及其子节点预先处理成HTML字符串,大大提升了渲染性能。

因此,总体来说,Vue 3相对于Vue 2拥有更高效、更智能的diff算法,能够更好地避免不必要的操作,并提高了渲染性能。

Vue.js 是一个流行的JavaScript框架,用于构建Web界面。Vue 2和Vue 3分别代表了该框架的两个主要版本,它们在设计、性能和功能上有着显著的区别。这些版本之间的一个关键区别是它们的虚拟DOM (VDOM) diff算法的实现。

Vue.js 的响应式系统在数据变化时需要更新 DOM,但是频繁地直接操作 DOM 会导致性能问题。为了高效地处理这些更新,Vue 使用了一个虚拟 DOM 机制,配合 diff 算法和 nextTick 方法来优化 DOM 的更新过程。

Vue 的 Diff 算法

Vue.js 使用虚拟 DOM 来抽象真实的 DOM,当数据变化时,Vue 会生成一个新的虚拟 DOM 树,并将其与旧的虚拟 DOM 树进行比较。这个比较过程就是所谓的 diff 算法,其主要步骤和特点包括:

  1. 同级比较:Vue 的 diff 算法只进行同层级节点的比较,不跨层级比较节点。这种限制大幅降低了复杂性,从而提高了性能。

  2. Patch 过程:当比较两棵树时,Vue 会通过 patch 函数逐节点比较新旧虚拟 DOM 的差异,并按需进行最小量更新。如果一个节点在新旧虚拟 DOM 中完全一样(比如标签名和 key),则 Vue 会复用该节点,仅对节点的属性或子节点进行更新。

  3. Key 的重要性:在处理列表时,为每个节点提供一个唯一的 key 可以帮助 Vue 更快地识别节点。这样,当数据发生变化导致 DOM 需要部分更新时,带有 key 的节点可以被高效地复用或重新排序,而无需重新渲染整个列表。

Vue 的 nextTick

nextTick 是 Vue 的一个重要方法,它允许你在下一个“tick”(即下一个 DOM 更新循环)之后执行某些代码。这通常用于在数据变化之后立即操作更新后的 DOM。因为 Vue 的 DOM 更新是异步的,直接在数据变化后访问变更的 DOM 可能不会得到预期结果。

使用场景

  • 当你更改了一些数据,并需要在 DOM 完全渲染更新后执行操作,nextTick 就非常有用。

示例

假设你有一个元素需要在 Vue 更新 DOM 之后进行操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
data() {
return {
message: 'Hello'
}
},
methods: {
updateMessage() {
this.message = 'Updated';
this.$nextTick(() => {
console.log('DOM updated');
});
}
}
}

在这个例子中,updateMessage 方法改变了数据,并使用 nextTick 来确保在 DOM 更新后执行代码。

总结

Vue 的 diff 算法和 nextTick 方法共同优化了更新性能和操作时机,确保了高效的 DOM 更新和正确的代码执行时序。通过虚拟 DOM 和异步更新策略,Vue 成功地减少了不必要的 DOM 操作,同时提供了强大的编程抽象,使得开发高效且响应式的界面变得简单可行。