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面试题 #6

Open
mewcoder opened this issue Mar 16, 2024 · 12 comments
Open

Vue面试题 #6

mewcoder opened this issue Mar 16, 2024 · 12 comments

Comments

@mewcoder
Copy link
Owner

mewcoder commented Mar 16, 2024

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

组件通信方式

Vue:

  • 父子组件通信:props / emit / ref / parent
  • 兄弟组件通信:eventBus / vuex
  • 跨级组件通信:provide/ inject / eventBus / vuex

React:

  • 父子组件通信:props / 回调函数 / ref
  • 兄弟组件通信: eventBus / redux
  • 跨级组件通信: context / redux

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

Vue3 和 Vue2 的区别

1.API 方面:

  • Composition API 更好的逻辑组织和逻辑复用
  • Teleport 传送⻔、Fragments ⽚段、SFC 的 CSS 变量、Suspense 等
  • 自定义渲染器等

2.性能方面:

  • 速度

    • 基于 Proxy 的响应式系统

    • 编译优化:静态标记、静态提升等

    • diff 算法优化

  • 体积:支持 tree-shaking

3.维护性:

  • TypeScript:更好的类型检查和类型推导,也省去了维护 d.ts 的麻烦

  • Monorepo:响应式库可以单独使用

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

说一下 Composition API

函数式的优势
和 Options API 对比,分两个方面

  • 代码组织:Options API 代码长了要跳来跳去
  • 逻辑复用:mixins 来源不明 命名冲突

还有就是函数式的优势

  • 更简洁清晰:没有了 this 的指向不明的问题
  • 函数更好的类型推断 :vue2 使用 TS vue-class-component 提供的 Class API,依赖装饰器的提案不稳定

Composition API
Composition API 包括:

  • 响应式相关(ref、reactive、computed、watch 等)
  • 生命周期钩子(onMounted 和 onUnmounted 等)
  • provide、inject 等

与 React Hooks 对比
灵感来源, 和 hooks 有相同级别的逻辑组织能力,
React Hooks 组件更新时都会重新调用,有一些痛点:

  • hooks 只能写在顶层

  • 变量有闭包陷阱

  • 避免子组件不必要的更新,要使用 useMemo / useCallback

    而 Vue 是基于响应式系统的只执行一次,会自动收集依赖,可避免不必要的更新

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

vue 响应式理解

  • vue 在组件初始化时,对对象的属性进行劫持,在 get 时进行依赖收集,set 时触发响应。

  • vue2:

  • 对象使用Object.defineProperty 对属性进行劫持,嵌套对象是递归实现劫持的;

  • 数组是通过重写数据的 7 个原型方法。

  • 问题

    • 对象无法监听到新增和删除属性,需要使 $set/$delete
    • 数组无法监听索引(出于性能考虑)和长度变化;
    • 嵌套对象需要深层监听,性能问题
    • 不支持Map/Set
  • Vue3 使用 Proxy 对对象进行代理,拦截 set/get 行为

  • 解决 Vue2 的问题,避免初始化时的性能开销,懒劫持

代理:拦截并重新定义对一个对象的基本操作

function reactive(target) {
  const handler = {
    get(target, key, receiver) {
      const res = Reflect.get(target, key, receiver);
      track(target, key); // 收集依赖
      if (isObject(res)) return reactive(res); // 如果是对象,递归懒代理
      return res;
    },
    set(target, key, value, receiver) {
      const oldValue = target[key]; // 获取老的值
      const result = Reflect.set(target, key, value, receiver);
      if (!Object.is(oldValue, value)) {
        trigger(target, key);
      }
      return result;
    },
  };

  return new Proxy(target, handler);
}

// track: 收集依赖 存放targetMap: `WeakMap<object, Map<key, Set<effect>>`

为什么用 Reflect
Reflect.xxx 提供对象的基本操作方法,与 proxy 拦截器 中的方法相对应,确保和对象默认行为一致
参数相同,传递参数,其中 receiver 能被正确的传递,确保调用时的 this 正确性

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

虚拟 DOM 和 Diff

虚拟 DOM:

  • 虚拟 DOM 就是用 JS 对象来描述真实的 DOM,是一种抽象
  • 直接操作 DOM 是有限制,通过 JS 操作对象更方便;频繁的操作 dom 会引发重排和重绘,通过 patch 方法(diff)渲染的页面,可以减少 dom 直接操作的次数。
  • 实现跨平台,通过 vdom 可以渲染到不同的平台。

diff 算法

  • 首先 dom diff 是同层比较,不考虑跨层的情况
  • 先比较是否是相同节点 key tag,相同节点比较属性,并复用老节点,然后比较儿子节点:
  • Vue2 是双端比较,两个列表的头尾相互比较,对比的过程中逐渐向内靠拢,直到某一个列表遍历完成。
  • Vue3 是头和头比,尾和尾比,剩余的基于最长递增子序列进行增/删/移

key 的作用

key 的作用是为了更高效的更新 DOM,diff 算法的目标就是尽可能复用原来对应的节点,减少 dom 操作量。

key 就是 vnode 的唯一 id,在 dom diff 的过程中,通过 key 找到对应原型的节点,来减少 dom 操作量,提升视图更新的性能。

  • 若不设置 key,就是 undefined,diff 算法会认为是相同节点,会就地复用元素,会增加 dom 操作量,而且在元素有状态的情况下会造成渲染错误(隐蔽性 bug)。
  • 使用数组 index 作为 key,如果列表的顺序会发生变化,和不写 key 区别不大。

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

模板编译原理

Vue2 的过程是 parse -> optimize -> generate

optimize:优化原始 AST,标记静态节点。

Vue3 更加标准化,中间过程为 transform

  1. parse:compiler 会对 template 进行解析,得到抽象语法树 AST,
  2. transform:遍历 AST 进行 转换
  3. generate: 生成 render 函数,render 函数是返回虚拟 DOM 的函数

vue3 的编译优化:编译阶段尽可能提取关键信息

  • patchFlag 对节点打上标记,为 runtime patch 提供依据,实现静态提升和靶向更新。
  • 静态提升: 将纯静态节点提升到渲染函数外部,在渲染函数内部保持对静态节点的引用即可
  • Block:dynamicChildren 收集子代所有的动态节点,无需层级遍历,做到靶向更新。
    • Block Tree: 将 v-if 分支作为单独的 Block 节点, 解决如 v-if v-for 导致的 DOM 结构不稳定问题
  • 缓存模板中的内联事件处理函数,避免函数重新创建

https://zhuanlan.zhihu.com/p/150732926

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

双向绑定

  • 解释:
  1. vue 中双向绑定使用指令 v-model,可以绑定数据到视图,对应的视图变化也会改变该数据。
  2. v-model 是语法糖,默认情况下相当于:value@input。v-model 经过模板编译被转换为属性 value 和一个事件监听,事件回调函数中会做相应变量更新操作。
  • 使用
  1. 通常在原生表单项上使⽤ v-model ,也可以在自定义组件上使用
  2. v-model 只能去绑定一个变量,使用 .sync 可以实现多个变量的双向绑定
  3. Vue3 的 v-model 有更新,类似.sync,默认的 v-model 相当于:modelValue@update:modelValue

Vue 中的单向数据流是指数据的流向只能从父组件向子组件,子组件不能直接修改父组件中的数据,而是通过触发父组件中的事件来实现数据的修改

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

nextTick

  • Vue 是异步更新策略,数据变化,vue 不会立即更新 dom,是开启一个队列,同一个事件循环里发生的变化会异步的批量更新,避免频繁的 DOM 更新。
  • 要获取到更新后的 DOM,需要使用 nextTick,用户传入的回调函数被添加到刷新函数(flushSchedulerQueue)的后面
  • 降级处理 微任务(promise->MutationObserver)->宏任务(setImmediate->setTimeout )

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

slot 实现原理

  • 父组件把插槽编译成返回 VNode 的函数,子组件渲染时,找到对应函数调用执行
  • 分为普通插槽(默认插槽、具名插槽)和作用域插槽,作用域插槽就是子组件调用时传入 props

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

keep-alive

  • 开发中缓存组件使⽤ keep-alive 组件,keep-alive 是 vue 内置组件,keep-alive 包裹动态组件 component 时,会缓存不活动的组件实例,⽽不是销毁它们,这样在组件切换过程中将状态保留在内存中,防⽌重复渲染。

  • 结合属性 include 和 exclude 可以明确指定缓存哪些组件或排除缓存指定组件,集合 vue-router 缓存页面

  • activated 和 deactivated 钩子

  • keep-alive 是⼀个通⽤组件,它内部定义了⼀个 map,缓存创建过的组件实例,它返回的渲染函数内部会查找,如果该组件在 map 中存在就直接返回它。

  • 指定了 max 后类似一个 LRU 缓存:如果缓存的实例数量即将超过指定的那个最大数量,则最久没有被访问的缓存实例将被销毁,以便为新的实例腾出空间。

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

路由实现原理

一个 SPA 应用的路由需要解决的问题是页面跳转内容改变同时不刷新

  1. router 实例化的时候保存用户传入的配置项和路由表,运行时要
    • 监听 popstate 事件
    • 回调里根据 path 匹配对应路由
  2. 将 router 定义成一个 Vue 插件,即实现 install 方法,内部做两件事:
    • 实现两个全局组件:router-link 和 router-view,分别实现页面跳转和内容显示
  • history 基于 history的API 去实现
  • hash 模式通过锚点实现,通过 location.hash 改变,通过 hashchange 事件监听路由变化,v4 都是通过 history 实现的,v3会判断是否支持 history 的API

@mewcoder
Copy link
Owner Author

mewcoder commented Apr 2, 2024

pinia 对比 vuex

  • pinia 更轻量,体积更小,更易用,支持 Vue2 和 Vue3
  • 支持组合式 API
  • 不需要区分 mutations 和 actions,只有 actions
  • store 可以定义多个, 是扁平化的
  • 更好的 TS 类型支持

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

No branches or pull requests

1 participant