Grundlegende Fragen im Vue-Interview

 1. Das Prinzip der bidirektionalen Bindung

Intern wandelt Vue das Lesen und Schreiben aller Daten im Datenobjekt in einen Getter/Setter um, indem es die Methodeneigenschaft Object.defineProperty abfängt und die Ansicht benachrichtigt, dass sie aktualisiert werden soll, wenn sich die Daten ändern.

Um die bidirektionale Datenbindung von MVVM zu realisieren, wird die Methode der Datenentführung in Kombination mit dem Publisher-Subscriber- Modus verwendet. Object.defineProperty() wird verwendet, um Setter und Getter zu jeder Eigenschaft hinzuzufügen , die Überwachung zu kapern und Nachrichten zu veröffentlichen Abonnenten, wenn sich die Daten ändern. Lösen Sie den entsprechenden Listener-Rückruf aus. Die folgenden Punkte müssen realisiert werden:
1. Implementieren Sie einen Daten-Listener-Beobachter, der alle Attribute des Datenobjekts überwachen kann. Bei Änderungen können Sie den neuesten Wert abrufen und die Abonnenten benachrichtigen. 2. Implementieren Sie einen Befehlsparser. Kompilieren
, Scannen und analysieren Sie für jeden die Anweisungen jedes Elementknotens, ersetzen Sie die Daten gemäß der Anweisungsvorlage und binden Sie die entsprechende Aktualisierungsfunktion. 3. Implementieren Sie einen
Watcher als Brücke zwischen Observer und Compile, der jede Attributänderung abonnieren und Benachrichtigungen empfangen kann , und führen Sie die entsprechende Rückruffunktion aus, die an die Anweisung gebunden ist, um die Ansicht zu aktualisieren

 

Verwirklichen Sie das Prinzip der bidirektionalen Bindung

index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<h1 id="name"></h1>
	<input type="text">
	<input type="button" value="改变data内容" onclick="changeInput()">
	
<script src="observer.js"></script>
<script src="watcher.js"></script>
<script>
	function myVue (data, el, exp) {
	    this.data = data;
	    observable(data);                      //将数据变的可观测
	    el.innerHTML = this.data[exp];           // 初始化模板数据的值
	    new Watcher(this, exp, function (value) {
	        el.innerHTML = value;
	    });
	    return this;
	}

	var ele = document.querySelector('#name');
	var input = document.querySelector('input');
	
    var myVue = new myVue({
		name: 'hello world'
	}, ele, 'name');
 	
	//改变输入框内容
    input.oninput = function (e) {
    	myVue.data.name = e.target.value
    }
	//改变data内容
	function changeInput(){
		myVue.data.name = "难凉热血"
	
	}
</script>
</body>
</html>

 Observer.js

	/**
	 * 把一个对象的每一项都转化成可观测对象
	 * @param { Object } obj 对象
	 */
	function observable (obj) {
		if (!obj || typeof obj !== 'object') {
        	return;
    	}
		let keys = Object.keys(obj);
		keys.forEach((key) =>{
			defineReactive(obj,key,obj[key])
		})
		return obj;
	}
	/**
	 * 使一个对象转化成可观测对象
	 * @param { Object } obj 对象
	 * @param { String } key 对象的key
	 * @param { Any } val 对象的某个key的值
	 */
	function defineReactive (obj,key,val) {
		let dep = new Dep();
		Object.defineProperty(obj, key, {
			get(){
				dep.depend();
				console.log(`${key}属性被读取了`);
				return val;
			},
			set(newVal){
				val = newVal;
				console.log(`${key}属性被修改了`);
				dep.notify()                    //数据变化通知所有订阅者
			}
		})
	}
	class Dep {
		
		constructor(){
			this.subs = []
		}
		//增加订阅者
		addSub(sub){
			this.subs.push(sub);
		}
        //判断是否增加订阅者
		depend () {
		    if (Dep.target) {
		     	this.addSub(Dep.target)
		    }
		}

		//通知订阅者更新
		notify(){
			this.subs.forEach((sub) =>{
				sub.update()
			})
		}
		
	}
	Dep.target = null;

watcher.js

	class Watcher {
		constructor(vm,exp,cb){
		    this.vm = vm;
		    this.exp = exp;
		    this.cb = cb;
		    this.value = this.get();  // 将自己添加到订阅器的操作
		}
		get(){
			Dep.target = this;  // 缓存自己
        	let value = this.vm.data[this.exp]  // 强制执行监听器里的get函数
        	Dep.target = null;  // 释放自己
        	return value;
		}
		update(){
			let value = this.vm.data[this.exp];
        	let oldVal = this.value;
        	if (value !== oldVal) {
                this.value = value;
                this.cb.call(this.vm, value, oldVal);
			}
	}
}

2. nextTick-Prinzip 

Es gibt nextTick in Vue, die offizielle Erklärung, es kann einen Rückruf ausführen, nachdem das DOM aktualisiert wurde.

Wenn es um die DOM-Aktualisierung von Vue geht, erklären wir das Prinzip der Aktualisierungsdaten von Vue

Vue asynchrone Aktualisierungswarteschlange (datengesteuerte Ansicht)

Da die Vue-DOM-Aktualisierung asynchron durchgeführt wird , dh wenn die Daten geändert werden, wird die Ansicht nicht sofort aktualisiert.
Solange die Datenänderungen überwacht werden , öffnet Vue eine Warteschlange und puffert alle darin auftretenden Datenänderungen Ereignisschleife und entfernt Duplikate beim Puffern von Daten und vermeidet so unnötige Berechnungen und DOM-Manipulationen.
Beim nächsten „Tick“ der Ereignisschleife leert Vue dann die Warteschlange und führt die eigentliche (deduplizierte) Arbeit aus.
Nachdem alle Datenänderungen im selben Datenzyklus abgeschlossen sind, werden Ansichtsaktualisierungen einheitlich durchgeführt.
Um sicherzustellen, dass das aktualisierte DOM erhalten wird, wird die Methode Vue.nextTick() festgelegt.

nextTick internes Prinzip

  • nextTick verwendet hauptsächlich 宏任务und微任务
  • Promise.thenVue versucht intern ,MutationObserver  native und  für asynchrone Warteschlangen zu verwenden  setImmediate. Wenn die Ausführungsumgebung dies nicht unterstützt, wird  setTimeout(fn, 0)stattdessen verwendet.

Woher weiß Vue, dass das DOM-Update abgeschlossen ist?

Vue-Quellen: 

if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

Schritt:

  1. Versprechen des ersten Gerichts
  2. Bei der Beurteilung von MutationObserver
  3. Bei der Beurteilung setImmediate
  4. Schließlich setTimeout

Das Aktualisieren des DOM durch Vue ist ein asynchroner Vorgang , und das DOM wird erst gerendert, wenn alle Daten aktualisiert sind.

Um nach der Aktualisierung des DOM zu arbeiten, stellt Vue nextTick bereit. Die nextTick-Methode erkennt, ob das DOM durch verschiedene Methoden im Quellcode aktualisiert wurde.

Wenn Promise.then vorhanden ist, verwenden Sie Promise zum Abhören. Wenn nicht, führen Sie ein Downgrade auf MutationObserver durch. Wenn es dies nicht unterstützt, stufen Sie setImmediate herunter. Wenn es dies nicht unterstützt, verwenden Sie setTimeout

 Zusammenfassen

Das Implementierungsprinzip der nextTick-Methode von vue:

  1. Vue verwendet asynchrone Warteschlangen, um DOM-Updates und nextTick-Rückrufe so zu steuern, dass sie nacheinander ausgeführt werden
  2. Aufgrund ihrer hohen Priorität können Mikrotasks sicherstellen, dass die Mikrotasks in der Warteschlange vor einer Ereignisschleife ausgeführt werden
  3. Aufgrund von Kompatibilitätsproblemen zwischen Browsern und mobilen Endgeräten musste Vue eine kompatible (Downgrade-)Lösung für Mikrotasks und Makrotasks entwickeln

 3. Vue-Router-Prinzip

Implementierungsprinzip: Das Prinzip des Vue-Routers besteht darin, die Ansicht zu aktualisieren, ohne die Seite erneut anzufordern

Zwei Routing-Modi

  1. Hash-Modus. Der Standardwert ist der Hash-Modus. Basierend auf der Browserverlaufs-API verwenden Sie window.addEventListener("hashchange", callback, false), um das Surfen zu überwachen. Wenn Push aufgerufen wird, fügen Sie die neue Route oben im Zugriffsverlaufsstapel des Browsers hinzu. Ersetzen Sie bei Verwendung von „Ersetzen“ die Top-Stack-Route des Zugriffsverlaufs des Browsers durch den Hash-Wert der neuen Route (entspricht # und dem folgenden Inhalt in der URL). Der Browser lädt die Seite entsprechend der Änderung des Hash-Werts in den entsprechenden DOM-Speicherort. Die Änderung des Ankerpunkts stellt nur das Verhalten des Browsers dar. Nach jeder Änderung des Ankerpunkts verbleibt weiterhin ein Verlaufseintrag im Browser, und Sie können über die Zurück-Schaltfläche des Browsers zur vorherigen Position zurückkehren.
  2. Verlaufsmodus . Verwenden Sie basierend auf der Browserverlaufs-API window.onpopstate , um die Browseradresse zu überwachen. Kapseln Sie pushState() und replaceState() in der Browser-Verlaufs-API . Wenn die Methode aufgerufen wird, wird der Verlaufsstapel des Browsers geändert. Auf diese Weise kann die URL umgeleitet werden, ohne die Seite zu laden. Das Problem besteht jedoch darin, dass beim Aktualisieren der Seite die Back-End-Route verwendet wird und daher die Unterstützung des Servers erforderlich ist, um eine Rückkehr zu vermeiden zur Seite, wenn die URL nicht mit der Ressource übereinstimmen kann.

Im Vergleich zu den beiden Modi bietet der Verlaufsmodus weitere Vorteile

1) Der Hash-Modus hat #, der Verlaufsmodus hat kein # , was schöner ist
2) Die von pushState festgelegte neue URL ist eine beliebige URL mit derselben Quelle wie die aktuelle URL, und der Hash-Modus kann den Inhalt erst danach ändern #, das heißt, es kann nur das gleiche Dokument wie das aktuelle festgelegt werden.
3) Wenn die von pushState festgelegte neue URL mit der aktuellen URL übereinstimmt, wird der Datensatz ebenfalls zum Datensatzstapel hinzugefügt, und der Hash wird nur hinzugefügt Wird dem Stapel hinzugefügt, wenn sich der neue vom aktuellen unterscheidet. 4) Wenn es sich um den Verlaufsmodus handelt,
muss er übereinstimmen
 

Ich denke du magst

Origin blog.csdn.net/weixin_50543490/article/details/128010586
Empfohlen
Rangfolge