Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

面试官:vue用三年多了?来说说你是如何理解vue的... #63

Open
wuyunqiang opened this issue Jun 18, 2024 · 0 comments
Open

Comments

@wuyunqiang
Copy link
Owner

先有问题再有答案

  1. 如何理解vue框架
  2. vue架构可以分为哪些部分?
  3. 如何理解响应式数据系统?
  4. 为什么vue不需要开发者手动处理 性能就很好?

作为前端领域的主流框架之一, Vue.js 的核心任务是将声明式的模板转化为真实的 DOM 并渲染到浏览器上。这一过程由三大部分协作完成: 模板编译处理、数据响应系统和渲染流程

模板编译处理将开发者编写的声明式模板转换为高效的渲染函数,这些函数会被传递给渲染流程部分。

渲染流程负责根据这些函数生成虚拟 DOM 树,并最终渲染到浏览器中的真实 DOM 上。

数据响应系统则担当着监测数据变化的重任。一旦数据发生变化,它会通知渲染流程进行必要的更新,确保视图与数据保持同步。

这三个部分环环相扣, 共同构成了 Vue.js 的高性能和灵活性。

一图胜千言

截屏2024-06-07 下午3.10.01.png

具体步骤如下:

  1. 静态模板编译

    • 这一步通常发生在构建阶段,Vue模板被编译为渲染函数:即用来返回虚拟 DOM 树的函数
  2. 响应式系统数据转换

    • Vue 会遍历组件的 data 对象,用 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3)将它们转换成响应式属性。
  3. 响应式系统的依赖收集

    • 每个响应式属性拥有一个 dep 对象,它维护一份 watcher 列表,该列表包括所有依赖于此属性的 watcher。
  4. 响应式数据变更

    • 当响应式数据发生变化时,对应的 dep 对象通知所有绑定的 watcher 进行更新。
    • 变更的每个属性可能对应多个 watcher(包括渲染 watcher 和用户定义的watcher)。
  5. 更新队列

    • Reactivity system 把准备更新的 watcher 添加到一个异步执行队列中。如果一个 watcher 在同一个事件循环中被多次触发,它只会被加入队列一次(去重)。
  6. 微任务(microtask)的异步更新

    • Vue 通常将更新队列的处理推到下一个 JavaScript 事件循环的微任务(microtask)中。
    • 在微任务队列中,Vue 执行完所有同步代码后,清空更新队列,按照优先级顺序同步地执行队列中的 watcher 更新。
  7. 组件重新渲染(DOM 更新)

    • 队列中的渲染 watcher 被触发,导致组件进行重新渲染,生成新的虚拟 DOM。
    • Vue 使用 "diff" 算法,应用keys机制等,比较新旧虚拟 DOM,计算出最小的更新,做patch操作来应用于实际的 DOM 树上。
  8. nextTick

    • 所有 DOM 更新后,Vue 调用在 nextTick 注册的任何回调。
    • 这确保了调用 nextTick可以在DOM 更新完成后执行一些额外的操作。

响应式数据系统:

一句话说明本质:当数据变化后运行对应的函数。

如何知道数据变化了

通过proxy代理拦截属性赋值setter操作. 所以我们操作的并不是普通的js对象,是经过响应式api处理的添加了setter拦截器的响应式对象。

要运行哪些函数

vue需要知道当前变化的属性,都被哪些函数wacth了。这就需要依赖dep对象在初始化时做依赖收集。
每个响应式对象的属性都对应一个dep实例,dep存储了对应的watcher列表。

什么时间运行函数合适

当找到对应的watcher列表Vue并不会立即运行,而是会将这些wacther加入到队列中 等待这次事件变化结束 所有响应式数据都改动完成,在异步执行这些任务队列。

当执行watcher过程中 又收集到新的响应式数据发生变化 同样将对应的watcher列表加入队列,依次执行 直到任务队列为空,本次批处理结束 触发渲染流程。

<script setup>
import { ref, watch, computed } from 'vue'
const msg = ref('Hello World!')

watch(()=>msg.value,()=>{
  console.log('test watch msg', msg)
})

const msgTo = computed(()=>{
  return  'me: ' + msg.value;
})
</script>

<template>
  <h1>{{ msg }}</h1>
  <input v-model="msg" />
   <h1>{{msgTo}}</h1>
</template>

截屏2024-06-07 下午4.34.04.png

为什么vue不需要开发者手动处理 性能就很好?

编译器对模板的静态优化

因为模板支持的语法结构是固定的,编译器可以静态分析模板并在生成的代码中留下标记,使得运行时尽可能地走捷径。

  1. 静态提升
  2. 更新类型标记
  3. 树结构打平

以上三点源自vue官网

响应式自动追踪

通过响应式api可以自动做依赖收集,获取到哪些组件发生了变化 精准渲染对应的组件。对比react不存在过度渲染的问题。

渲染流程的高效diff

通过渲染流程的diff算法后 计算出需要变更的dom节点被进一步缩小。

总结

通过静态优化大大减少了js的运行时时间。通过响应式自动追踪减少diff范围,通过高效diff算法减少真实操作dom数量。这一切都是vue自动帮我们做的 并不需要开发者手动处理。

其他文章

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant