范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

VUE3响应式设计原理(对象和数组响应方案)

  /* *原理:当触发数据读取操作时,执行副作用函数并存储到桶中 *当设置数据操作时,再将副作用函数从桶中取出并执行 */ //用一个全局变量activeEffect存储被注册过的副作用函数 let activeEffect //const buket=new Set() /* *weakMap为弱引用,不影响垃圾回收机制工作,当用户代码对一个 *对象没有引用关系时,垃圾会收器会回收该对象,避免引起栈堆的溢出 */ const bucket=new WeakMap() const effectStack=[] //定义一个宏任务队列 const jobQueue=new Set() //定义一个Promose,将一个任务添加到微任务队列 const p=Promise.resolve() //是否正在刷新队列 let isFlushing=false //Symbol唯一,可以作为对象属性标识符使用 const ITERATE_KEY=Symbol() /* const data={ 	foo:1,bar:2, 	get tep(){ 		return this.foo 	} } */ /* const obj={} const proto={bar:1} const child=reactive(obj) const parent=reactive(proto) //使用parent作为child的原型 Object.setPrototypeOf(child,parent)  //此处打印true,因为代理对象可以通过raw属性读取原始数据 console.dir(child.raw===obj) console.dir(parent.raw===proto)  effect(()=>{ 	//会执行两次 	console.log(child.bar) }) */ let arr=[1,2,3] //const obj=readonly(arr) const obj=reactive(arr)   //重新建立副作用函数 effect( 	()=>{ 		 		for(const key of obj){ 			console.log(key) 			document.getElementById("test").innerHTML=key 		} 	} )  setTimeout(()=>obj[3]=10,1000)  //封装代理对象 //isShallow浅响应,isReadonly,浅只读 function createReactive(obj,isShallow=false,isReadonly=false){ 	return new Proxy(obj,{ 		//对原始数据的代理 		//拦截读取操作 		get(target,key,receiver){ 			//代理对象可以通过raw属性访问原始数据 			if(key==="raw"){ 				return target 			} 			/* 			*因为数组的for of会读取symbol.iterator属性 			*为避免错误和性能开销,要避免副作用函数与之建立响应 			*如果key的类型是symbol则不进行追踪 			*/ 			if(!isReadonly && typeof key!=="symbol"){ 				track(target,key) 			} 			//返回属性值 			//如果对象自身不存在该属性,会从对象原型寻找对应属性,       //并调用原型get方法得到最终结果 			const res=Reflect.get(target,key,receiver) 			if(isShallow){ 				return res 			} 			//深响应 			/* 			*对于obj.foo.bar来说,当修改obj.foo.bar的值时,并不能触发响应 			*为了解决这个问题,需要递归地调用reactive函数,直到能返回深响应地数据 			*/ 			if(typeof res==="object"  && res!==null){ 				/* 				*实现深只读 				*如果是只读对象,则调用readyonly对数据,返回只读对象 				*/ 				return isReadonly?readonly(res):reactive(res) 			} 			return res 		}, 		//拦击in操作符读取属性值 		has(target,key){ 			track(target,key) 			return Reflect.has(target,key) 		}, 		//拦截for in 循环读取属性值 		ownKeys(target){ 			//将副作用函数和ITERATE_KEY关联 			//如果操作的是数组,则用length作为key并建立响应 			track(target,Array.isArray(target)?"length":ITERATE_KEY) 			return Reflect.ownKeys(target) 		}, 		//拦截设置操作 		set(target,key,newvalue,receiver){ 			//数据是只读的,则打印错误并返回 			if(isReadonly){ 				console.warn(`属性${key}是只读的`) 				return true 			} 			//获取旧值 			const oldval=target[key] 			 			//如果属性不存在,说明是添加新属性,否则是设置已有属性 			//Object.prototype.hasOwnProperty检查当前操作的属性是否已经存在对象身上 			/* 			*如果代理目标是数组,检测被设置的索引是否小于数组长度 			*如果是,为SET操作,否则ADD操作 			*/ 			const type=Array.isArray(target) 				?Number(key){ 			if(fn!==activeEffect){ 				effectsToRun.add(fn) 			} 		}) 	} 	/* 	*当直接设置了数组的length属性时,只需要对大于新length的元组进行操作即可 	*如果操作的是数组的length属性 	*那么取出大于新length的所有元素对应的副作用函数执行 	*/ 	if(Array.isArray(target) && key==="length"){ 		depsMap.forEach((effects,key)=>{ 			if(key>=newValue){ 				effects.forEach(fn=>{ 					if(fn!==activeEffect){ 						effectsToRun.add(fn) 					} 				}) 			} 		}) 	} 	 	//取得与INTERATE_KEY相关的副作用函数 	const interateEffects=depsMap.get(ITERATE_KEY) 	//避免自增导致无限循环 	//ECMA规范:再调用foreach遍历set集合时,如果一个值已经被访问过 	//但这个值被删除并重新添加到集合,如果遍历没有结束,那么这个值 	//又会重新被访问,解决办法是建立一个新的Set来遍历 	effects && effects.forEach(f=>{ 		if(f!=effectsToRun){ 			effectsToRun.add(f) 		} 	}) 	//将ITERATE_KEY相关联的副作用函数6添加到effectsToRun 	//删除属性会导致ITERATE_KEY减少,所以需要重新触发 	if(type==="ADD" || type==="DELETE"){ 		interateEffects && interateEffects.forEach(effect=>{ 			if(effect!==activeEffect){ 				effectsToRun.add(effect) 			} 		}) 	} 	effectsToRun.forEach(fn=>{ 		//如果副作用函数存在调度函数,那么执行调度函数,否则执行原函数 		if(fn.options.scheduler){ 			fn.options.scheduler(fn) 		}else{ 			fn() 		} 	}) }  //通过修改第二个参数来实现只读深浅,此处浅只读 function readonly(obj){ 	return createReactive(obj,false,true) } //此处深只读 function shallowReadonly(obg){ 	return createReactive(obj,true,true) }  //深响应 function reactive(obj){ 	return createReactive(obj) } //实现浅响应 function shallowReactive(obj){ 	return createReactive(obj,true) }  //options对象动态调度副作用函数的执行时机 function effect(fn,options={}){ 	const effectFn=()=>{ 		//例如effet(function effectFn(){document.body.inntext=obj.ok?obj.text:"not"}) 		//清除工作 		cleanup(effectFn) 		//存储被注册过的副作用函数 		activeEffect=effectFn 		//嵌套的副作用函数 		//在调用副作用函数前将其压入栈中,首先压入的内层副作用函数 		effectStack.push(effectFn) 		let res=fn() 		//调用完之后,将其弹出栈,弹出内层的副作用函数 		effectStack.pop() 		activeEffect=effectStack[effectStack.length-1] 		//返回fn的结果 		return res 	} 	//存储与该副作用相关的依赖集合 	effectFn.deps=[] 	//将options挂在到副作用函数 	effectFn.options=options 	if(!options.lazy) effectFn() 	return effectFn }  function cleanup(effectFn){ 	//遍历副作用函数的deps数组 	for(let i=0;i{console.log(obj.foo)}, 	scheduler(fn){ 		//执行调度时,将其添加到微任务队列 		jobQueue.add(fn) 		//刷新队列 		flushJob() 	} ) obj.foo++ obj.foo++ *最终输出 1 3 *微任务队列最终执行的只有一次,而此时obj.foo的值已经是3. */ function flushJob(){ 	//如果正在刷新任务队列,什么都不做,否则isFlushing=true 	if(isFlushing) return 	isFlushing=true 	//将任务添加到微任务队列 	p.then(()=>{ 		jobQueue.forEach(job=>job()) 	}).finally(()=>{isFlushing=false}) }  /* *计算属性与懒执行 */  function computed(getter){ 	let value 	//是否需要重新计算值,true代表需要计算 	let dirty=true 	//只有调用value的时候才会执行 	const effectFn=effect(getter,{ 		//不执行 		lazy:true, 		//当值发生变化时,在跳读器中重新设置diarty。 		scheduler(){ 			if(!dirty){ 				dirty=true 				//当计算属性依赖的响应数据发生变化时,手动调用函数触发响应 				trigger(obj, "value") 			} 		} 	}) 	const obj={ 		get value(){ 			if(dirty){ 				//执行副作用函数 				value=effectFn() 				//设置为false,下次访问时,直接使用原来的值 				dirty=false 			} 			//当读取value时,手动调用track函数进行追踪 			track(obj, "value") 			//返回值为fn的值 			return value 		} 	} 	return obj }  /* *wach的实现原理 *当数据发生变化时,执行回调 */ function watch(source,cb,options={}){ 	let getter 	//如果source是函数,则执行函数,否则调用traverse函数递归地读取属性 	if(typeof source==="function"){ 		getter=source 	}else{ 		getter=()=>traverse(source) 	} 	//旧值与新值 	let oldValue,newValue  	let cleanup 	function onInvalidate(fn){ 		cleanup=fn 	}  	//对scheduler函数的封装 	const job=()=>{ 		newValue=effectFn()  		if(cleanup){ 			cleanup() 		} 		//返回旧值,新值,已经回调给用户使用 		cb(newValue,oldValue,onInvalidate) 		//已经触发了回调函数,所以这里重新赋值 		oldValue=newValue 	} 	 	//出发操作,建立回调 	const effectFn=effect( 		//调用函数递归地读取数据 		()=>getter() 	,{ 		lazy:true, 		//调度函数 		scheduler:()=>{ 			//创建微任务队列,再DOM加载完成后再执行 			if(options.flush==="post"){ 				const p=Promise.resolve() 				p.then(job) 			}else{ 				job() 			} 		} 	}) 	if(options.immediate){ 		job() 	}else{ 		//调用副作用函数,拿到旧值 		oldValue=effectFn() 	} }  function traverse(value,seen=new Set()){ 	//如果数据是原始值或者已经被读取过了,则什么都不做 	if(typeof value!=="object" || value===null || seen.has(value)) return 	seen.add(value) 	//堆对象内部地属性,递归地读取数据 	for(const k in value){ 		traverse(value[k],seen) 	} 	return value }  /* watch(()=>obj.foo,(newValue,oldValue)=>alert(oldValue+":"+newValue)) setTimeout(()=>obj.foo++,1000)  const sum=computed(()=>{ 	document.getElementById("test").innerHTML=obj.tep })  //重新建立副作用函数 effect(function effectFn(){ 	sum.value }) */

美丽镇江镇美管家凝聚红色动能靓城管城暖城行动提升城市治理能力首次成立城管系统行业党委新增3000个停车泊位等民生实事超序时完成大件垃圾拆解中心正式投产运营城市治理环境秩序有效改善新修订的镇江市市区机动车停车场管理办法颁布实施今年以来,江苏省横商印记美超科技从空调买卖到制冷研发的进阶之路受疫情防控影响之下,2000多套房间的空调热水器等家用电器配套,从采购到安装要经历几个日夜?对此,经营了30年的美超科技义新冷气(下称美超科技)交出的答卷是从下单到交付只需要7天。押注非人民币玩家,史玉柱的产品心理学还奏效吗?闭关研发三个月的原始征途,在史玉柱回归研发一线,修改108天并提出473条意见之后,终于开启了终极测试。今年9月,在原始征途宣布公测延期后,外界目光聚焦于史玉柱将会给这款游戏带来哪AI招聘,对企业有哪些好处?视频面试和AI招聘相结合,是在企业的招聘工作流程中增加了一个环节。企业的人力部门在接到了员工的简历后,首先会根据求职者提供的信息进行初步的筛选和评估。接下来人力部门会进行视频面试,2022年底清库存倒计时,哪些车型可以出手?随着梅西率领的阿根廷最终捧起了大力神杯,2022年也即将落下帷幕。按照惯例,不少车企也于近期开启了年终让利清库存预案。降幅由几万元到几十万元不等,那么具体有哪些车型在这波清库存中值2022西安车展开展,免费参展门票今日开抢,准备买车的千万别错过2022第十七届西安国际车展2022年12月30日01月03日将在西安国际会展中心重磅开展,车展现场吸引60余品牌携近500款车型亮相,涵盖新能源乘用商用休旅豪华等,品类齐全,年前阿维塔11和011量产车正式下线,12月底开启批量交付作为长安宁德时代和华为三位大佬联合打造的品牌,阿维塔科技相信你并不会陌生。而就在12月20日,阿维塔11和011首批量产车型正式在重庆工厂正式下线。而到12月底,阿维塔11将正式开线上下单线下检,出门不便上门检,晓飞检让你足不出户完成医学诊断钱江晚报小时新闻记者王燕平上个月,在2022直通乌镇全球互联网大赛数字医疗专题赛中,迪安诊断旗下从事创新互联网医疗ToC服务的杭州晓飞检健康科技有限公司,凭借晓飞检健康项目夺得一等2022年度陈嘉庚科学奖和陈嘉庚青年科学奖揭晓中新网北京12月20日电(记者孙自法)陈嘉庚科学奖基金会12月20日对外公布了2022年度陈嘉庚科学奖获奖项目和陈嘉庚青年科学奖获奖人名单,复微分几何及其应用等6个项目获2022年突发事故!25人遇难,8人下落不明!普京将召开国防会议特斯拉市值一夜蒸发超2600亿元!马斯克正积极寻找推特CEO当地时间12月20日,美股三大指数震荡收涨。明星个股中,特斯拉大跌逾8,市值一夜蒸发381亿美元,约合人民币2652亿元。大宗商品市场表现活跃。受巨头拟减产消息刺激,LME镍主力合荣耀Magic5系列曝光,还是那个套壳的华为吗?说到荣耀手机这个品牌,大家肯定第一时间会想到是华为旗下的子品牌,但其实荣耀已经是从独立出去快两年了,这个消息知道的人还是不会很多,加上现在华为在手机业务方面遇到了困难,这就造成了很
上海刚刚解放,干部竟借机霸占国民党姨太老婆,陈帅必须枪毙点击麻辣知识局关注我这里有最有趣的历史故事1949年,华东公安部长,后来成为最高检副检察长的李士英,极为谨慎地起草了一份判决书一封死刑判决书。工作人员把这份判决书放在了时任上海市长林彪赴兰州视察,人群中一眼认出李福泽,惊喜地问你怎么在这儿在诸多开国将军中,富有传奇经历的将军多的数不胜数,但高学历的开国将军,却十分稀少。毕竟在旧社会,普通人家能够吃饱饭已经是很不容易的一件事,更不要说能完完整整地接受教育。不过这也不能喜迎二十大中国共产党历次全国代表大会回顾之中共十一大1921年至今,中国共产党举行了十九次全国代表大会,为中国革命确立了新民主主义革命的正确道路为中国人民推翻了帝国主义封建主义官僚资本主义的三座大山为中华民族开启了发展进步的新纪元。清朝十二帝超高清画像,乾隆光绪堪称颜值担当清太祖清王朝的奠基者,1616年创建后金,少年以采人参为生。善于用兵,重杀戮,一生少尝败绩,但死前败于与袁崇焕对决之宁远之战。清太宗1636年在沈阳创立大清的有为之君,改女真族为满商务部继续推动落实扩大汽车消费系列政策措施视频加载中中证网声明凡本网注明来源中国证券报。中证网的所有作品,版权均属于中国证券报中证网,中国证券报。中证网与作品作者联合声明,任何组织未经中国证券报。中证网以及作者书面授权不得蛋白吃得好,健康不会跑,6高蛋白美食别错过,常吃腿脚麻利蛋白质可是人体不可或缺的营养物质,不仅是细胞生长的必要养分,还是肌肉活动所必须的燃料,所以想要留住健康,蛋白质的补充显得至关重要的重要,尤其是中老年人们。推荐6款高蛋白美食,秋天不泰国人文历史溯源素可泰王朝(公元1238年公元1356年)素可泰原先只是吴哥王朝下的一个城市,当地泰人起义建立独立国家,兰甘杏大帝在位时首创暹罗文字和宋加洛陶瓷。大城王朝(公元1350年公元176泰国驻华大使阿塔育习萨目一行来朔参观访问来源阳朔县人民政府阳朔动态近日,泰国驻华大使阿塔育习萨目及泰国驻南宁总领事馆总领事彬嘉玛塔维她雅浓一行6人来朔参观访问,市外事办及我县外事相关负责人陪同参加。阿塔育习萨目一行参观了自驾旅行各地美食美酒全国自驾旅行三年,最西去过西藏阿里狮泉河和新疆喀什,给我感受在中国大西北地域宽阔,游牧生活居多,西藏的饮食给我印象只有三种藏餐川菜和兰州拉面。一般的游客会尝一次藏餐,也就一次。我吃最佳观赏季!如果你在厦门,快看秋分已过厦门街角的多种植物焕发出了新的生机进入了最佳观赏期你是否感觉到了它们带来的变化又是否沉醉其中?厦门的九月不是单一的色彩是五颜六色的蔚蓝天空下粉色红色黄色紫色各色鲜花迎风笑鹭泰国特色服务,服务员都是娇小女性,许多人表示根本承受不住泰国是我国周边的诸多邻国中旅游业的发展势头最好的一个国家,每年都会接待大量的外国游客,其中团体数量最多的就是我们中国游客。(此处已添加小程序,请到今日头条客户端查看)那么,为什么中