Principle questions involved in Vue interview

 1. The principle of two-way binding

Internally, Vue converts the reading and writing of each data in the data object into a getter/setter by intercepting the Object.defineProperty method property, and notifies the view to update when the data changes.

To realize the two-way data binding of MVVM, it adopts the method of data hijacking combined with the publisher-subscriber mode. Object.defineProperty() is used to add setters and getters to each property and hijack the monitoring, and publish messages to subscribers when the data changes. Trigger the corresponding listener callback. The following points must be realized:
1. Implement a data listener Observer, which can monitor all attributes of the data object. If there is any change, you can get the latest value and notify the subscribers.
2. Implement a command parser Compile, for each Scan and analyze the instructions of each element node, replace the data according to the instruction template, and bind the corresponding update function
3. Implement a Watcher as a bridge connecting Observer and Compile, which can subscribe and receive notifications of each attribute change, and execute The corresponding callback function bound by the instruction to update the view

 

Realize the principle of two-way binding

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 principle 

There is nextTick in vue, the official explanation, it can execute a callback after the DOM is updated.

When it comes to vue's DOM update, let's explain the principle of vue's update data

Vue asynchronous update queue (data-driven view)

Since the Vue DOM update is performed asynchronously , that is, when the data is modified, the view will not be updated immediately.
As long as the data changes are monitored , Vue will open a queue and buffer all data changes that occur in the same event loop , and will remove duplication during buffering data, thereby avoiding unnecessary calculations and DOM manipulations.
Then, on the next event loop "tick", Vue flushes the queue and performs the actual (deduplicated) work.
After all data changes in the same data cycle are completed, view updates are performed uniformly.
In order to ensure that the updated DOM is obtained, the Vue.nextTick() method is set.

nextTick internal principle

  • nextTick mainly uses 宏任务and微任务
  • Vue internally tries to use native  Promise.then,MutationObserver  and  for asynchronous queues setImmediate. If the execution environment does not support it, it will use  setTimeout(fn, 0)instead.

How does vue know that the DOM update is complete

vue sources: 

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)
  }
}

step:

  1. First Judgment Promise
  2. In judging MutationObserver
  3. In judging setImmediate
  4. Finally setTimeout

Vue updating the DOM is an asynchronous operation , and the DOM will not be rendered until all data is updated.

So to operate after updating the DOM, vue provides nextTick. The nextTick method will detect whether the DOM has been updated through various methods in the source code.

If there is promise.then, use promise to listen, if not, downgrade to MutationObserver, if it does not support it, downgrade setImmediate, if it does not support it, use setTimeout

 Summarize

The implementation principle of vue's nextTick method:

  1. Vue uses asynchronous queues to control DOM updates and nextTick callbacks to be executed successively
  2. Because of its high priority, microtasks can ensure that the microtasks in the queue are executed before an event loop
  3. Because of compatibility issues between browsers and mobile terminals, Vue had to do a compatible (downgrade) solution for microtasks and macrotasks

 3. vue-router principle

Implementation principle: The principle of vue-router is to update the view without re-requesting the page

Two Routing Modes

  1. hash mode. The default is hash mode, based on the browser history api, use window.addEventListener("hashchange", callback, false) to monitor browsing. When push is called, add the new route to the top of the browser access history stack. When using replace, replace the top-stack route of the browser's access history with the hash value of the new route (equal to # and the following content in the url). The browser loads the page to the corresponding DOM location according to the change of the hash value. The anchor point change is only the behavior of the browser. After each anchor point change, a history record will still be left in the browser, and you can return to the previous position through the back button of the browser.
  2. history mode . Based on the browser history api, use window.onpopstate to monitor the browser address. Encapsulate the pushState() and replaceState() in the browser history api . When the method is called, the history stack of the browser will be modified. In this way, the URL can be redirected without loading the page, but its problem is that when the page is refreshed, it will take the back-end route, so it needs the assistance of the server to complete it, so as to avoid returning to the page when the url cannot match the resource.

Compared with the two modes, the history mode has more advantages

1) The hash mode has #, the history mode does not have # , which is more beautiful
2) The new url set by pushState is any url with the same source as the current url, and the hash mode can only modify the content after #, that is, only the same document as the current one can be set
3) When the new url set by pushState is the same as the current url, the record will also be added to the record stack, and the hash will only be added to the stack when the new one is different from the current one. 4) If it is the history mode, he
needs to Match /, if the match is not found, an error will be reported
 

Guess you like

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