欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

剖析vue原理及运行机制(下)

程序员文章站 2022-04-02 21:16:16
...

上文链接:
剖析vue原理及运行机制(上)

专栏链接(免费):
vue内部实现原理分析


让我们跨过一切,来看一个很重要的知识点:

批量异步更新——nextTick原理

回顾前面一堆知识点,我们大概已知晓Vue是如何在我们修改data后修改视图了:这其实是一个 setter - Dep - Watcher - patch - 视图 的过程
现在假设有如下情况:

<templete>
	<div>
		<div>{{num}}</div>
		<div @click="hClick">click me</div>
	</div>
</templete>
<script>
	export default{
		data(){
			return{
				num:0
			}
		},
		methods:{
			hClick(){
				for(let i=0;i<100;i++){
					this.num++;
				}
			}
		}
	}
</script>

这乍一看没啥 —— 不就是按下按钮,num被循环自增1000次嘛。

但我们很快想到:按我们之前的理解,每次num自增时,都会触发num的setter。也就是说,上面提到的“流程”在这个demo中要被跑1000次!太可怕了。
Vue肯定不会用这样的方式。实际上vue默认每当触发某个数据的setter时,对应的Watcher对象其实会被push进一个队列queue中,在下一个tick时将这个queue全部拿出来run(Watcher内一个方法,用来触发patch操作)一遍。
简单实现nextTick:(这里用的是setTimeout)

let callbacks=[];
let pending=false;
function nextTick(cb){
	callbacks.push(cb);
	if(!pending){
		pending=true;
		setTimeout(flushCallbacks,0);
	}
}
function flushCallback(){
	pending=false;
	const pipes=callbacks.slice(0);
	callbacks.length=0;
	for(let i=0;i<pipes.length;i++){
		pipes[i]();
	}
}

既然如此,在上面自增1000的例子中,我们并不需要在下一个tick时执行1000个同样的Watcher对象去修改界面,而只需一个Watcher对象,使其将界面上的0变成1000即可。

那么,我们就需要一个“过滤”操作 —— 同一个Watcher在同一个tick时应该只被执行一次。即 队列中不应该出现重复的Watcher对象


重写Watcher
实现update,在修改数据后由Dep来调用,而run才是真正触发patch的方法:

let uid=0;
let has={};
let queue=[];
let waiting=false;

class Watcher{
	constructor(){
		this.id=++uid;
		Dep.target=this;
	}
	update(){
		console.log('watch'+this.id+'update');
		queueWatcher(this);   //将update自身传进去
	}
	run(){
		console.log('watch'+this.id+'->视图更新啦...');
	}
}
Dep.target=null;

function queueWatcher(watcher){
	const id=watcher.id;
	if(has[id]===null){
		has[id]=true;
		queue.push(watcher);
		if(!waiting){
			waiting=true;
			nextTick(flushScheduleQueue);   //上面重写nextTick部分的代码
		}
	}
}
function flushScheduleQueue(){
	let watcher,id;
	for(index=0;index<queue.length;index++){
		watcher=queue[index];
		id=watcher.id;
		has[id]=null;
		watcher.run();
	}
	waiting=false;
}

Vuex

Vue还有个比较重要的机制 —— vuex —— 项目内数据共享(数据通信)
剖析vue原理及运行机制(下)
首先安装vuex:

cnpm install vuex --save

下面以我近期所做项目(仿去哪网App)部分内容来分析:

在src目录下单独创建文件夹store,在其中创建文件index.js:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

let defaultVity='北京'
try{
	if(localStorage.city){
		defaultCity=localStorage.city
	}
}catch(e){}
export default new Vuex.Store({
	state:{
		city:defaultCity
	},
	//actions:{
	//	changeCity(ctx,city){
	//		ctx.commit('changeCity',city)
	//	}
	//}
	mutations:{
		changeCity(state,city){
			state.city=city
			try{
				localStorage.city=city
			}catch(e){}
		}
	}
})

在main.js中引入:

import store from './store'
//在new Vue({...})中加入:
new Vue({
	//...
	store,
	//...
})

在需要用到“city”的地方改写,如下:

{{this.$store.state.city}}

在需要改变city值的地方改写,如下:

methods:{
	handleCity(city){
		this.$store.commit('changeCity',city);   
		//this.$store.dispatch('changeCity',city);   //dispatch调用的是store中的actions属性(异步方法聚集地)
	}
}