ホームページ  >  記事  >  ウェブフロントエンド  >  2023 年の Vue の高頻度面接質問の共有 (回答分析付き)

2023 年の Vue の高頻度面接質問の共有 (回答分析付き)

青灯夜游
青灯夜游転載
2022-08-01 20:08:164131ブラウズ

この記事では、2023 年に選ばれた、収集する価値のある高頻度の面接の質問 (回答付き) をいくつかまとめています。一定の参考値があるので、困っている友達が参考になれば幸いです。

2023 年の Vue の高頻度面接質問の共有 (回答分析付き)

Vue-router ナビゲーション ガードとは何ですか?

グローバル プレフィックス/フック: beforeEach、beforeResolve、afterEach
  • ルーティング専用ガード: beforeEnter
  • コンポーネント内のガード: beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
  • (学習ビデオ共有:
vue ビデオ チュートリアル

)

Vue3.0 で Proxy が採用され、Object.defineProperty が廃止されたのはなぜですか?

Object.defineProperty 自体には、配列添字の変更を監視する一定の機能がありますが、Vue では、パフォーマンス/エクスペリエンスの費用対効果の観点から、Youda はこの機能を放棄しました (なぜ Vue ではできないのですか)配列の変更を検出します)。この問題を解決するには、Vue の内部処理後、次のメソッドを使用して配列を監視します。

push();
pop();
shift();
unshift();
splice();
sort();
reverse();
上記 7 つのメソッドのみがハッキングされるため、他の配列のプロパティを検出することはできません。特定の制限。

Object.defineProperty はオブジェクトのプロパティのみをハイジャックできるため、各オブジェクトの各プロパティをトラバースする必要があります。 Vue 2.x では、データ モニタリングはデータ オブジェクトを再帰的に走査することによって実現されます。属性値もオブジェクトである場合は、深い走査が必要です。完全なオブジェクトをハイジャックできる場合は、明らかにそれがより良い選択です。

プロキシはオブジェクト全体をハイジャックして、新しいオブジェクトを返すことができます。プロキシはオブジェクトをプロキシするだけでなく、配列をプロキシすることもできます。動的に追加された属性をプロキシすることもできます。

v-for キーを追加する理由

キーを使用しない場合、Vue は動的要素を最小限に抑え、インプレースを試みるメソッドを使用します。可能な限り同じタイプの要素を変更/再利用するアルゴリズム。 Key は Vue の vnode の唯一のマークです。このキーを使用すると、 diff 操作がより正確かつ高速になります

  • より正確になります

    : key を使用すると、 not インプレース再利用は、sameNode 関数 a.key === b.key 比較で回避できます。したがって、より正確になります。

  • 高速

    : キーの一意性を使用してマップ オブジェクトを生成し、対応するノードを取得します。これはトラバーサル メソッドよりも高速です。

実際の DOM から仮想 DOM に移行する方法

##Vue のテンプレート コンパイル原則、メイン プロセスが含まれます:

#テンプレートを

ast
    ツリー、
  • ast

    に変換します。オブジェクトを使用して実際の JS 構文を記述します (実際の DOM を仮想 DOM に変換します) 最適化します。 tree

  • Put

    ast
  • ツリー生成コード
  • Vue が非同期レンダリングを使用するのはなぜですか?

Vue はコンポーネント レベルの更新です。非同期更新が使用されない場合、データが更新されるたびに現在のコンポーネントが再レンダリングされます。パフォーマンス、

Vue

この一連のデータ更新の後、ビューは非同期的に更新されます。コアアイデア 次のティック dep.notify() ウォッチャーに更新を通知します。

subs[i].update

ウォッチャーの update queueWatcher を呼び出します。 in sequence ウォッチャーをキューに入れると、nextTick (lushSchedulerQueue) が次のティックでウォッチャー キューを (非同期的に) 更新します。 vue コンポーネント内のデータはなぜ関数でなければならないのですか?

オブジェクトは参照型です。コンポーネントを再利用する場合、データ オブジェクトはすべて同じデータ オブジェクトを指すため、1 つのコンポーネントでデータが変更されると、他の再利用コンポーネントのデータも同時に変更されます。 . ; オブジェクトを返す関数を使用する場合、新しいオブジェクト (Object のインスタンス) が返されるたびに参照アドレスが異なるため、この問題は発生しません。

MVC と MVVM の違い

MVC

MVC の正式名は Model View Controller、つまりモデルビューです。 (ビュー) - コントローラーの略語、ソフトウェア設計モデル

モデル (モデル): アプリケーション データ ロジックを処理するために使用されるアプリケーションの一部です。通常、モデル オブジェクトはデータベース内のデータへのアクセスを担当します。

View (ビュー): データの表示を処理するアプリケーションの一部です。通常、ビューはモデル データに基づいて作成されます。
  • Controller (コントローラー): ユーザー操作を処理するアプリケーションの一部です。通常、コントローラーは、ビューからのデータの読み取り、ユーザー入力の制御、およびモデルへのデータの送信を担当します。
  • MVC の考え方: 一文で説明すると、コントローラーは、 Viewを使ったModelのデータ、つまりController内のViewにModelのデータを代入することです。
MVVM

MVVM は新しい VM クラスを追加しました

  • ViewModel 层:做了两件事达到了数据的双向绑定 一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。

MVVM 与 MVC 最大的区别就是:它实现了 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不用再自己手动操作 Dom 元素,来改变 View 的显示,而是改变属性后该属性对应 View 层显示会自动改变(对应Vue数据驱动的思想)

整体看来,MVVM 比 MVC 精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作 DOM 元素。因为在 MVVM 中,View 不知道 Model 的存在,Model 和 ViewModel 也观察不到 View,这种低耦合模式提高代码的可重用性

注意:Vue 并没有完全遵循 MVVM 的思想 这一点官网自己也有说明

那么问题来了 为什么官方要说 Vue 没有完全遵循 MVVM 思想呢?

  • 严格的 MVVM 要求 View 不能和 Model 直接通信,而 Vue 提供了$refs 这个属性,让 Model 可以直接操作 View,违反了这一规定,所以说 Vue 没有完全遵循 MVVM。

Vue 为什么要用 vm.$set() 解决对象新增属性不能响应的问题 ?你能说说如下代码的实现原理么?

1)Vue为什么要用vm.$set() 解决对象新增属性不能响应的问题

  • Vue使用了Object.defineProperty实现双向数据绑定

  • 在初始化实例时对属性执行 getter/setter 转化

  • 属性必须在data对象上存在才能让Vue将它转换为响应式的(这也就造成了Vue无法检测到对象属性的添加或删除)

所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)

2)接下来我们看看框架本身是如何实现的呢?

Vue 源码位置:vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any {
  // target 为数组  
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 修改数组的长度, 避免索引>数组长度导致splcie()执行有误
    target.length = Math.max(target.length, key)
    // 利用数组的splice变异方法触发响应式  
    target.splice(key, 1, val)
    return val
  }
  // key 已经存在,直接修改属性值  
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  // target 本身就不是响应式数据, 直接赋值
  if (!ob) {
    target[key] = val
    return val
  }
  // 对属性进行响应式处理
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

我们阅读以上源码可知,vm.$set 的实现原理是:

  • 如果目标是数组,直接使用数组的 splice 方法触发相应式;

  • 如果目标是对象,会先判读属性是否存在、对象是否是响应式,

  • 最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理

defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法

Vue3.0 和 2.0 的响应式原理区别

Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。

相关代码如下

import { mutableHandlers } from "./baseHandlers"; // 代理相关逻辑
import { isObject } from "./util"; // 工具方法

export function reactive(target) {
  // 根据不同参数创建不同响应式对象
  return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {
  if (!isObject(target)) {
    return target;
  }
  const observed = new Proxy(target, baseHandler);
  return observed;
}

const get = createGetter();
const set = createSetter();

function createGetter() {
  return function get(target, key, receiver) {
    // 对获取的值进行放射
    const res = Reflect.get(target, key, receiver);
    console.log("属性获取", key);
    if (isObject(res)) {
      // 如果获取的值是对象类型,则返回当前对象的代理对象
      return reactive(res);
    }
    return res;
  };
}
function createSetter() {
  return function set(target, key, value, receiver) {
    const oldValue = target[key];
    const hadKey = hasOwn(target, key);
    const result = Reflect.set(target, key, value, receiver);
    if (!hadKey) {
      console.log("属性新增", key, value);
    } else if (hasChanged(value, oldValue)) {
      console.log("属性值被修改", key, value);
    }
    return result;
  };
}
export const mutableHandlers = {
  get, // 当获取属性时调用此方法
  set, // 当修改属性时调用此方法
};

Vue模版编译原理知道吗,能简单说一下吗?

简单说,Vue的编译过程就是将template转化为render函数的过程。会经历以下阶段:

  • 生成AST树
  • 优化
  • codegen

首先解析模版,生成AST语法树(一种用JavaScript对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。

Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的DOM也不会变化。那么优化过程就是深度遍历AST树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。

编译的最后一步是将优化后的AST树转换为可执行的代码

MVVM的优缺点?

优点:

  • 分离视图(View)和模型(Model),降低代码耦合,提⾼视图或者逻辑的重⽤性: ⽐如视图(View)可以独⽴于Model变化和修改,⼀个ViewModel可以绑定不同的"View"上,当View变化的时候Model不可以不变,当Model变化的时候View也可以不变。你可以把⼀些视图逻辑放在⼀个ViewModel⾥⾯,让很多view重⽤这段视图逻辑
  • 提⾼可测试性: ViewModel的存在可以帮助开发者更好地编写测试代码
  • ⾃动更新dom: 利⽤双向绑定,数据更新后视图⾃动更新,让开发者从繁琐的⼿动dom中解放

缺点:

  • バグのデバッグは困難です。双方向バインディング モードのため、インターフェイスで例外が発生した場合、View コードにバグがあるか、Model コードに問題がある可能性があります。 。データ バインディングにより、ある場所のバグが他の場所にすぐに転送されるため、元の問題の場所を特定するのが容易ではなくなります。また、データ バインディングの宣言は View テンプレートに命令的に記述されており、これらの内容を中断してデバッグすることはできません。
  • 大きなモジュール内のモデルは、使用する場合でも非常に大きくなります。使いやすく、データの整合性を確保しやすいです。長時間保持すると、メモリを解放しないとより多くのメモリが消費されます。
  • 大規模なグラフィック アプリケーションの場合、ビュー ステートは比較的多数ある場合、ViewModel の構築と維持のコストが比較的高くなります。

Vue データのプロパティの値が変更された後、ビューはすぐに同期的に再レン​​ダリングされますか?

再レンダリングはすぐには同期的に実行されません。 Vue の応答性は、データが変更された直後に DOM が変更されることを意味するのではなく、DOM が特定の戦略に従って更新されることを意味します。 Vue は DOM を非同期的に更新します。データ変更をリッスンしている限り、Vue はキューを開き、同じイベント ループ内で発生するすべてのデータ変更をバッファーに入れます。

同じウォッチャーが複数回トリガーされた場合、キューにプッシュされるのは 1 回だけです。バッファリング中のこの重複排除は、不必要な計算や DOM 操作を回避するために重要です。次に、次のイベント ループ ティックで、Vue はキューをフラッシュし、実際の (重複排除された) 作業を実行します。

diff アルゴリズム

時間計算量: ツリーの完全な diff アルゴリズムの時間計算量は # です。 # #O(n*3)、Vue が最適化して O(n) に変換します。

理解:

  • 最小限の更新、key は非常に重要です。これは、このノードの一意の識別子となり、変更の前後で同じ DOM ノードであることを diff アルゴリズムに伝えます

    • Extensionv-for key があるのはなぜですか。key がないと激しく再利用されます。ノードの移動やノードの追加 (DOM の変更)、key# の追加などの例を挙げてください。 ## は DOM の操作を減らしてのみ移動します。
  • 同じ仮想ノードのみが詳細に比較されます。そうでない場合は、古い仮想ノードが完全に削除され、新しい仮想ノードが挿入されます。
  • 同じレイヤー内でのみ比較が行われ、レイヤー間の比較は行われません。

diff アルゴリズムの最適化戦略 : 4 つのヒット検索、4 つのポインター

    古いものと新しいもの (最初に先頭を比較し、次に挿入とノードの削除)
  1. 以降の古いものと後の新しいもの (挿入または削除前の末尾の比率)
  2. 以前の古いものと後の新しいもの (先頭と末尾の比率、移動ノードが関係すると、新しいフロントが指すノードは古いフロントの最後に移動されます。古いフロントに移動する前のノード)
  3. ---上記の内容を理解できれば質問がありましたら、基本的には O---
次のような単純な概念です。問題はありませんか?

Vue の利点

軽量フレームワーク: データを構築するビューのコレクションであるビュー レイヤーのみに焦点を当てています。サイズはわずか数十です

kb
    ;
  • シンプルで学習しやすい: によって開発されました。中国語、中国語のドキュメント、言語の壁がなく、理解と学習が簡単;
  • 双方向データ バインディング: 保持
  • angular
  • の特徴は、データ操作がより簡単です;
  • コンポーネント化: react
  • の利点を維持し、
  • html のカプセル化を実現し、再利用することで、単一ページのアプリケーションを構築する際に独自の利点があります。ビュー、データの分離、および構造: ロジック コードを変更せずに、データを操作するだけでデータの変更を簡素化します。関連操作;
  • 仮想 DOM:
  • dom
  • 操作は非常にパフォーマンスを重視しており、ネイティブの
  • dom 操作ノードは使用されなくなり、dom 操作が大幅に解放されますが、特定の操作は依然として dom ですが、別の方法で行われます。 実行速度が速い: react
  • と比較して同じ 仮想
  • dom を操作する場合、パフォーマンスの点では vue に大きな利点があります。
  • vue-router ルーティングフック関数とは何ですか?実行順序は何ですか?

ルーティングフックの実行プロセスとフック関数の種類: グローバル ガード、ルーティング ガード、コンポーネント ガード

完全なナビゲーション解析プロセス:

ナビゲーションがトリガーされます。
  • 非アクティブ化されたコンポーネントで beforeRouteLeave ガードを呼び出します。
  • 各ガードの前にグローバルを呼び出します。
  • 再利用されたコンポーネントで beforeRouteUpdate ガード (2.2) を呼び出します。
  • ルーティング設定に入る前に呼び出します。
  • 非同期ルーティング コンポーネントを解決します。
  • アクティブ化されたコンポーネントのRouteEnterの前に呼び出します。
  • グローバル beforeResolve ガード (2.5) を呼び出します。
  • #ナビゲーションを確認しました。

  • グローバル afterEach フックを呼び出します。

  • DOM 更新をトリガーします。

  • 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

Vue.js的template编译

简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点),详细步骤如下:

首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。

然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)

$nextTick 是什么?

Vue 实现响应式并不是在数据发生后立即更新 DOM,使用 vm.$nextTick 是在下次 DOM 更新循环结束之后立即执行延迟回调。在修改数据之后使用,则可以在回调中获取更新后的 DOM

说说Vue的生命周期吧

什么时候被调用?

  • beforeCreate :实例初始化之后,数据观测之前调用
  • created:实例创建万之后调用。实例完成:数据观测、属性和方法的运算、 watch/event 事件回调。无 $el .
  • beforeMount:在挂载之前调用,相关 render 函数首次被调用
  • mounted:了被新创建的vm.$el替换,并挂载到实例上去之后调用改钩子。
  • beforeUpdate:数据更新前调用,发生在虚拟DOM重新渲染和打补丁,在这之后会调用改钩子。
  • updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用改钩子。
  • beforeDestroy:实例销毁前调用,实例仍然可用。
  • destroyed:实例销毁之后调用,调用后,Vue实例指示的所有东西都会解绑,所有事件监听器和所有子实例都会被移除

每个生命周期内部可以做什么?

  • created:实例已经创建完成,因为他是最早触发的,所以可以进行一些数据、资源的请求。
  • mounted:实例已经挂载完成,可以进行一些DOM操作。
  • beforeUpdate:可以在这个钩子中进一步的更改状态,不会触发重渲染。
  • updated:可以执行依赖于DOM的操作,但是要避免更改状态,可能会导致更新无线循环。
  • destroyed:可以执行一些优化操作,清空计时器,解除绑定事件。

ajax放在哪个生命周期?:一般放在 mounted 中,保证逻辑统一性,因为生命周期是同步执行的, ajax 是异步执行的。单数服务端渲染 ssr 同一放在 created 中,因为服务端渲染不支持 mounted 方法。 什么时候使用beforeDestroy?:当前页面使用 $on ,需要解绑事件。清楚定时器。解除事件绑定, scroll mousemove

Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?

受现代 JavaScript 的限制 ,Vue 无法检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。但是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 来实现为对象添加响应式属性,那框架本身是如何实现的呢?

我们查看对应的 Vue 源码:vue/src/core/instance/index.js

export function set (target: Array<any> | Object, key: any, val: any): any {
  // target 为数组  
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 修改数组的长度, 避免索引>数组长度导致splcie()执行有误
    target.length = Math.max(target.length, key)
    // 利用数组的splice变异方法触发响应式  
    target.splice(key, 1, val)
    return val
  }
  // key 已经存在,直接修改属性值  
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  // target 本身就不是响应式数据, 直接赋值
  if (!ob) {
    target[key] = val
    return val
  }
  // 对属性进行响应式处理
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

我们阅读以上源码可知,vm.$set 的实现原理是:

  • 如果目标是数组,直接使用数组的 splice 方法触发相应式;
  • 如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)

(学习视频分享:web前端开发编程基础视频

以上が2023 年の Vue の高頻度面接質問の共有 (回答分析付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。