




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第一文帶你深入剖析vue3的響應(yīng)式緩存功能,如果值沒(méi)有變化,就會(huì)返回上一次的執(zhí)行結(jié)果在實(shí)現(xiàn)這兩個(gè)核心功能之前,我們先來(lái)改造一下之前實(shí)現(xiàn)的effect函數(shù)。
怎么能使effect函數(shù)變成懶執(zhí)行呢,比如計(jì)算屬性的這種功能,我們不想要他立即執(zhí)行,而是希望在它需要的時(shí)候才執(zhí)行。
這時(shí)候我們可以在effect函數(shù)中傳遞第二個(gè)參數(shù),一個(gè)對(duì)象,用來(lái)設(shè)置一些額外的功能。
functioneffect(fn,options={}){//修改
leteffectFn=function(){
activeEffect=effectFn
effectStack.push(effectFn)
fn()
effectStack.pop()
activeEffect=effectStack[effectStack.length-1]
//只有當(dāng)非lazy的時(shí)候才直接執(zhí)行
if(!options.lazy){
effectFn()
//將依賴函數(shù)組為返回值進(jìn)行返回
returneffectFn//新增
}
這時(shí),如果傳遞了lazy屬性,那么該effect將不會(huì)立即執(zhí)行,需要手動(dòng)進(jìn)行執(zhí)行:
consteffectFn=effect(()={
console.log(obj.foo)
},{lazy:true})
//手動(dòng)執(zhí)行
effectFn()
但是如果我們想要獲取手動(dòng)執(zhí)行后的值呢,這時(shí)只需要在effect函數(shù)中將其返回即可。
functioneffect(fn,options={}){
leteffectFn=function(){
activeEffect=effectFn
effectStack.push(effectFn)
//保存返回值
constres=fn()//新增
effectStack.pop()
activeEffect=effectStack[effectStack.length-1]
returnres//新增
//只有當(dāng)非lazy的時(shí)候才直接執(zhí)行
if(!options.lazy){
effectFn()
//將依賴函數(shù)組為返回值進(jìn)行返回
returneffectFn
}
接下來(lái)開(kāi)始實(shí)現(xiàn)computed函數(shù):
functioncomputed(getter){
//創(chuàng)建一個(gè)可手動(dòng)調(diào)用的依賴函數(shù)
consteffectFn=effect(getter,{
lazy:true
//當(dāng)對(duì)象被訪問(wèn)的時(shí)候才調(diào)用依賴函數(shù)
constobj={
getvalue(){
returneffectFn()
returnobj
}
但是此時(shí)還做不到對(duì)值進(jìn)行緩存和對(duì)比,增加兩個(gè)變量,一個(gè)存儲(chǔ)執(zhí)行的值,另一個(gè)為一個(gè)開(kāi)關(guān),表示是否可以重新執(zhí)行依賴函數(shù):
functioncomputed(getter){
//定義value保存執(zhí)行結(jié)果
//isRun表示是否需要執(zhí)行依賴函數(shù)
letvalue,isRun=true;//新增
consteffectFn=effect(getter,{
lazy:true
constobj={
getvalue(){
//增加判斷,isRun為true時(shí)才會(huì)重新執(zhí)行
if(isRun){//新增
//保存執(zhí)行結(jié)果
value=effectFn()//新增
//執(zhí)行完畢后再次重置執(zhí)行開(kāi)關(guān)
isRun=false//新增
returnvalue
returnobj
}
但是上面的實(shí)現(xiàn)還有一個(gè)問(wèn)題,就是好像isRun執(zhí)行一次后好像永遠(yuǎn)都不會(huì)變成true了,我們的本意是在數(shù)據(jù)發(fā)生變動(dòng)的時(shí)候需要再次觸發(fā)依賴函數(shù),也就是將isRun變?yōu)閠rue,實(shí)現(xiàn)這種效果,需要我們?yōu)閛ptions再傳遞一個(gè)函數(shù),用于用戶自定義的調(diào)度執(zhí)行。
functioneffect(fn,options={}){
leteffectFn=function(){
activeEffect=effectFn
effectStack.push(effectFn)
constres=fn()
effectStack.pop()
activeEffect=effectStack[effectStack.length-1]
returnres
//掛載用戶自定義的調(diào)度執(zhí)行器
effectFn.options=options//新增
if(!options.lazy){
effectFn()
returneffectFn
}
接下來(lái)需要修改一下trigger如果傳遞了scheduler這個(gè)函數(shù),那么只執(zhí)行scheduler這個(gè)函數(shù)而不執(zhí)行依賴函數(shù):
functiontrigger(target,key){
letdepsMap=store.get(target)
if(!depsMap)return
consteffects=depsMap.get(key)
leteffectsToRun=newSet()
effectseffects.forEach(effectFn={
if(effectFn!==activeEffect){
effectsToRun.add(effectFn)
effectsToRun.forEach(effect={
//如果存在調(diào)度器scheduler,那么直接調(diào)用該調(diào)度器,并將依賴函數(shù)進(jìn)行傳遞
if(effectFn.options.scheduler){//新增
effectFn.options.scheduler(effect)//新增
}else{
effect()
}
那么在computed中就可以實(shí)現(xiàn)重置執(zhí)行開(kāi)關(guān)isRun的操作了:
functioncomputed(getter){
//定義value保存執(zhí)行結(jié)果
//isRun表示是否需要執(zhí)行依賴函數(shù)
letvalue,isRun=true;//新增
consteffectFn=effect(getter,{
lazy:true,
scheduler(){
if(!isRun){
isRun=true
constobj={
getvalue(){
//增加判斷,isRun為true時(shí)才會(huì)重新執(zhí)行
if(isRun){//新增
//保存執(zhí)行結(jié)果
value=effectFn()//新增
//執(zhí)行完畢后再次重置執(zhí)行開(kāi)關(guān)
isRun=false//新增
returnvalue
returnobj
}
當(dāng)computed傳入的依賴函數(shù)中的值發(fā)生改變時(shí),會(huì)觸發(fā)響應(yīng)式對(duì)象的trigger函數(shù),而計(jì)算屬性創(chuàng)建響應(yīng)式對(duì)象時(shí)傳入了scheduler,所以當(dāng)數(shù)據(jù)改變時(shí),只會(huì)執(zhí)行scheduler函數(shù),在scheduler函數(shù)內(nèi)我們將執(zhí)行開(kāi)關(guān)重置為true,再下次訪問(wèn)數(shù)據(jù)觸發(fā)get函數(shù)時(shí),就會(huì)重新執(zhí)行依賴函數(shù)。這也就實(shí)現(xiàn)了當(dāng)數(shù)據(jù)發(fā)生改變時(shí),會(huì)再次觸發(fā)依賴函數(shù)的功能了。
為了避免計(jì)算屬性被另外一個(gè)依賴函數(shù)調(diào)用而失去響應(yīng),我們還需要為計(jì)算屬性單獨(dú)進(jìn)行綁定響應(yīng)式的功能,形成一個(gè)effect嵌套。
functioncomputed(getter){
letvalue,isRun=true;
consteffectFn=effect(getter,{
lazy:true,
scheduler(){
if(!isRun){
isRun=true
//當(dāng)計(jì)算屬性依賴的響應(yīng)式數(shù)據(jù)發(fā)生變化時(shí),手動(dòng)調(diào)用trigger函數(shù)觸發(fā)響應(yīng)
trigger(obj,value)//新增
constobj={
getvalue(){
if(isRun){
value=effectFn()
isRun=false
//當(dāng)讀取value時(shí),手動(dòng)調(diào)用track函數(shù)進(jìn)行追蹤
track(obj,value)
returnvalue
returnobj
}
五.watch
先來(lái)看一下watch函數(shù)的用法,它的用法也非常簡(jiǎn)單:
watch(obj,()={
console.log(改變了)
//修改數(shù)據(jù),觸發(fā)watch函數(shù)
obj.age++
watch接受兩個(gè)參數(shù),第一個(gè)參數(shù)為綁定的響應(yīng)式數(shù)據(jù),第二個(gè)參數(shù)為依賴函數(shù),我們依然可以沿用之前的思路來(lái)進(jìn)行處理,利用effect以及scheduler來(lái)改變觸發(fā)執(zhí)行時(shí)機(jī)。
functionwatch(source,fn){
effect(
//遞歸讀取對(duì)象中的每一項(xiàng),變?yōu)轫憫?yīng)式數(shù)據(jù),綁定依賴函數(shù)
()=bindData(source),
scheduler(){
//當(dāng)數(shù)據(jù)發(fā)生改變時(shí),調(diào)用依賴函數(shù)
fn()
//readData保存已讀取過(guò)的數(shù)據(jù),防止重復(fù)讀取
functionbindData(value,readData=newSet()){
//此處只考慮對(duì)象的情況,如果值已被讀取/值不存在/值不為對(duì)象,那么直接返回
if(typeofvalue!==object||value==null||readData.has(value))return
//保存已讀取對(duì)象
readData.add(value)
//遍歷對(duì)象
for(constkeyinvalue){
//遞歸進(jìn)行讀取
bindData(value[key],readData)
returnvalue
}
watch函數(shù)還有另外一種用法,就是除了接收對(duì)象,還可以接受一個(gè)getter函數(shù),例如:
watch(
()=obj.age,
()={
console.log(改變了)
)
這種情況下只需要將用戶傳入的getter將我們自定義的bindData替代即可:
functionwatch(source,fn){
letgetter=typeofsource===functionsource:(()=bindData(source))
effect(
//執(zhí)行g(shù)etter
()=getter(),
scheduler(){
//當(dāng)數(shù)據(jù)發(fā)生改變時(shí),調(diào)用依賴函數(shù)
fn()
}
其實(shí)watch函數(shù)還有一個(gè)很重要的功能:就是在用戶傳遞的依賴函數(shù)中可以獲取新值和舊值,但是我們目前還做不到這一點(diǎn)。實(shí)現(xiàn)這個(gè)功能我們可以配置前文中的lazy屬性來(lái)實(shí)現(xiàn)。來(lái)回顧一下lazy屬性:設(shè)置了lazy之后一開(kāi)始不會(huì)執(zhí)行依賴函數(shù),手動(dòng)執(zhí)行時(shí)會(huì)返回執(zhí)行結(jié)果:
functionwatch(source,fn){
letgetter=typeofsource===functionsource:(()=bindData(source))
//定義新值與舊值
letnewVal,oldVal;//新增
consteffectFn=effect(
//執(zhí)行g(shù)etter
()=getter(),
lazy:true,
scheduler(){
//在scheduler重新執(zhí)行依賴函數(shù),得到新值
newVal=effectFn()//新增
fn(newVal,oldVal)//新增
//執(zhí)行完畢后更新舊值
oldVal=newVal//新增
//手動(dòng)調(diào)用依賴函數(shù),取得舊值
oldVal=effectFn()//新增
}
此外,watch函數(shù)還有一個(gè)功能,就是可以自定義執(zhí)行時(shí)機(jī),比如immediate屬性,他會(huì)在創(chuàng)建時(shí)立即執(zhí)行一次:
watch(obj,()={
console.log(改變了)
immediate:true
})
我們可以把scheduler封裝為一個(gè)函數(shù),以便在不同的時(shí)機(jī)去調(diào)用他:
functionwatch(source,fn,options={}){
letgetter=typeofsource===functionsource:(()=bindData(source))
letnewVal,oldVal;
construn=()={//新增
newVal=effectFn()
fn(newVal,oldVal)
oldVal=newVal
consteffectFn=effect(
()=getter(),
lazy:true,
//使用run來(lái)執(zhí)行依賴函數(shù)
scheduler:run//修改
//當(dāng)immediate為true時(shí),立即執(zhí)行一次依賴函數(shù)
if(options.immediate){//新增
run()//新增
}else{
oldVal=effectFn()
}
watch函數(shù)還支持其他的執(zhí)行調(diào)用時(shí)機(jī),這里只實(shí)現(xiàn)了immediate。
六.淺響應(yīng)與深響應(yīng)
深響應(yīng)和淺響應(yīng)的區(qū)別:
constobj=reatcive({foo:{bar:1}})
effect(()={
console.log(obj.foo.bar)
//修改obj.foo.bar的值,并不能觸發(fā)響應(yīng)
obj.foo.bar=2
因?yàn)橹皩?shí)現(xiàn)的攔截,無(wú)論對(duì)于什么類型的數(shù)據(jù)都是直接進(jìn)行返回的,如果實(shí)現(xiàn)深響應(yīng),那么首先應(yīng)該判斷是否為對(duì)象類型的值,如果是對(duì)象類型的值,應(yīng)當(dāng)遞歸調(diào)用reactive方法進(jìn)行轉(zhuǎn)換。
//接收第二個(gè)參數(shù),標(biāo)記為是否為淺響應(yīng)
functioncreateReactive(obj,isShallow=false){
returnnewProxy(obj,{
get(target,key,receiver){
//訪問(wèn)raw時(shí),返回原對(duì)象
if(key===raw)returntarget
track(target,key)
constres=Reflect.get(target,key,receiver)
//如果是淺響應(yīng),直接返回值
if(isShallow){
returnres
//判斷res是否為對(duì)象并且不為null,循環(huán)調(diào)用reatcive
if(typeofres===objectres!==null){
returnreatcive(res)
returnres
//...省略其他
})
將創(chuàng)建響應(yīng)式對(duì)象的方法抽離出去,通過(guò)傳遞isShallow參數(shù)來(lái)決定是否創(chuàng)建深響應(yīng)/淺響應(yīng)對(duì)象。
//深響應(yīng)
functionreactive(obj){
returncreateReactive(obj)
//淺響應(yīng)
functionshallowReactive(obj){
returncreateReactive(obj,true)
}
七.淺只讀與深只讀
有時(shí)候我們并不需要對(duì)值進(jìn)行修改,也就是需要值為只讀的,這個(gè)操作也分為深只讀和淺只讀,首先需要在createReactive函數(shù)中增加一個(gè)參數(shù)isReadOnly,代表是否為只讀屬性。
//淺只讀
functionshallowReadOnly(obj){
returncreateReactive(obj,true,true)
//深只讀
functionreadOnly(obj){
returncreateReactive(obj,false,true)
}
set(target,key,newValue,receiver){
//是否為只讀屬性,如果是則打印警告信息并直接返回
if(isReadOnly){
console.log(`屬性${key}是只讀的`)
returnfalse
constoldVal=target[key]
consttype=Ototype.hasOwnProperty.call(target,key)triggerType.SET:triggerType.ADD
constres=Reflect.set(target,key,newValue,receiver)
if(target===receiver.raw){
if(oldVal!==newValue(oldVal===oldVal||newValue===newValue)){
trigger(target,key,type)
returnres
}
如果為只讀屬性,那么也不需要為其建立響應(yīng)聯(lián)系如果為只讀屬性,那么在進(jìn)行深層次遍歷的時(shí)候,需要調(diào)用readOnly函數(shù)對(duì)值進(jìn)行包裝
functioncreateReactive(obj,isShallow=false,isReadOnly=false){
returnnewProxy(obj,{
get(target,key,receiver){
//訪問(wèn)raw時(shí),返回原對(duì)象
if(key===raw)returntarget
//只有在非只讀的時(shí)候才需要建立響應(yīng)聯(lián)系
if(!isReadOnly){
track(target,key)
constres=Reflect.get(target,key,receiver)
//如果是淺響應(yīng),直接返回值
if(isShallow){
returnres
//判斷res是否為對(duì)象并且不為null,循環(huán)調(diào)用creative
if(typeofres===objectres!==null){
//如果數(shù)據(jù)為只讀,則調(diào)用readOnly對(duì)值進(jìn)行包裝
returnisReadOnlyreadOnly(res):creative(res)
returnres
}
八.處理數(shù)組
數(shù)組的索引與length
如果操作數(shù)組時(shí),設(shè)置的索引值大于數(shù)組當(dāng)前的長(zhǎng)度,那么要更新數(shù)組的length屬性,所以當(dāng)通過(guò)索引設(shè)置元素值時(shí),可能會(huì)隱式的修改length的屬性值,因此再j進(jìn)行觸發(fā)響應(yīng)時(shí),也應(yīng)該觸發(fā)與length屬性相關(guān)聯(lián)的副作用函數(shù)重新執(zhí)行。
constarr=reactive([foo])//數(shù)組原來(lái)的長(zhǎng)度為1
effect(()={
console.log(arr.length)//1
//設(shè)置索引為1的值,會(huì)導(dǎo)致數(shù)組長(zhǎng)度變?yōu)?
arr[1]=bar
在判斷操作類型時(shí),新增對(duì)數(shù)組類型的判斷,如果代理目標(biāo)是數(shù)組,那么對(duì)于操作類型的判斷作出處理:
如果設(shè)置的索引值小于數(shù)組的長(zhǎng)度,就視為SET操作,因?yàn)樗粫?huì)改變數(shù)組長(zhǎng)度,如果設(shè)置的索引值大于當(dāng)前數(shù)組的長(zhǎng)度,那么應(yīng)該被視為ADD操作。
//定義常量,便于修改
consttriggerType={
ADD:add,
SET:set
set(target,key,newValue,receiver){
if(isReadOnly){
console.log(`屬性${key}是只讀的`)
returnfalse
constoldVal=target[key]
//如果目標(biāo)對(duì)象是數(shù)組,檢測(cè)被設(shè)置的索引值是否小于數(shù)組長(zhǎng)度
consttype=Array.isArray(target)(Number(key)target.lengthtriggerType.ADD:triggerType.SET)
constres=Reflect.set(target,key,newValue,receiver)
trigger(target,key,type)
returnres
},
functiontrigger(target,key,type){
constdepsMap=store.get(target)
if(!depsMap)return
consteffects=depsMap.get(key)
leteffectsToRun=newSet()
effectseffects.forEach(effectFn={
if(effectFn!==activeEffect){
effectsToRun.add(effectFn)
//當(dāng)操作類型是ADD并且目標(biāo)對(duì)象時(shí)數(shù)組時(shí),應(yīng)該取出執(zhí)行那些與length屬性相關(guān)的副作用函數(shù)
if(Array.isArray(target)type===triggerType.ADD){
//取出與length相關(guān)的副作用函數(shù)
constlengthEffects=deps.get(length)
lengthEffectslengthEffects.forEach(effectFn={
if(effectFn!==activeEffect){
effectsToRun.add(effectFn)
effectsToRun.forEach(effect={
if(effectFn.options.scheduler){
effectFn.options.scheduler(effect)
}else{
effect()
}
還有一點(diǎn):其實(shí)修改數(shù)組的length屬性也會(huì)隱式的影響數(shù)組元素:
constarr=reactive([foo])
effect(()={
//訪問(wèn)數(shù)組的第0個(gè)元素
console.log(arrr[0])//foo
//將數(shù)組的長(zhǎng)度修改為0,導(dǎo)致第0個(gè)元素被刪除,因此應(yīng)該觸發(fā)響應(yīng)
arr.length=0
如上所示,在副作用函數(shù)內(nèi)部訪問(wèn)了第0個(gè)元素,然后將數(shù)組的length屬性修改為0,這回隱式的影響數(shù)組元素,及所有的元素都會(huì)被刪除,所以應(yīng)該觸發(fā)副作用函數(shù)重新執(zhí)行。
然而并非所有的對(duì)length屬性值的修改都會(huì)影響數(shù)組中的已有元素,如果設(shè)置的length屬性為100,這并不會(huì)影響第0個(gè)元素,當(dāng)修改屬性值時(shí),只有那些索引值大于等于新的length屬性值的元素才需要觸發(fā)響應(yīng)。
調(diào)用trigger函數(shù)時(shí)傳入新值:
set(target,key,newValue,receiver){
if(isReadOnly){
console.log(`屬性${key}是只讀的`)
returnfalse
constoldVal=target[key]
//如果目標(biāo)對(duì)象是數(shù)組,檢測(cè)被設(shè)置的索引值是否小于數(shù)組長(zhǎng)度
consttype=Array.isArray(target)(Number(key)target.lengthtriggerType.ADD:triggerType.SET)
constres=Reflect.set(target,key,newValue,receiver)
//將新的值進(jìn)行傳遞,及觸發(fā)響應(yīng)的新值
trigger(target,key,type,newValue)//新增
returnres
}
判斷新的下標(biāo)值與需要操作的新的下標(biāo)值進(jìn)行判斷,因?yàn)閿?shù)組的key為下標(biāo),所以副作用函數(shù)搜集器是以下標(biāo)作為key值的,當(dāng)length發(fā)生變動(dòng)時(shí),只需要將新值與每個(gè)下標(biāo)的key判斷,大于等于新的length值的需要重新執(zhí)行副作用函數(shù)。
如上圖所示,Map為根據(jù)數(shù)組的key,也就是id組成的Map結(jié)構(gòu),他們的每一個(gè)key都對(duì)應(yīng)一個(gè)Set,用于保存這個(gè)key下面的所有的依賴函數(shù)。
當(dāng)length屬性發(fā)生變動(dòng)時(shí),應(yīng)當(dāng)取出所有key值大于等于length值的所有依賴函數(shù)進(jìn)行執(zhí)行。
functiontrigger(target,key,type,newValue){
constdepsMap=store.get(target)
if(!depsMap)return
consteffects=depsMap.get(key)
leteffectsToRun=newSet()
effectseffects.forEach(effectFn={
if(effectFn!==activeEffect){
effectsToRun.add(effectFn)
//如果操作目標(biāo)是數(shù)組,并且修改了數(shù)組的length屬性
if(Array.isArray(target)key===length){
//對(duì)于索引值大于或等于新的length元素
//需要把所有相關(guān)聯(lián)的副作用函數(shù)取出并添加到effectToRun中待執(zhí)行
depsMap.forEach((effects,key)={
//key與newValue均為數(shù)組下標(biāo),因?yàn)閿?shù)組中key為index
if(key=newValue){
effects.forEach(effectFn={
if(effectFn!==activeEffect){
effectsToRun.add(effectFn)
//...省略
}
本文的實(shí)現(xiàn)數(shù)組這種數(shù)據(jù)結(jié)構(gòu)只考慮了針對(duì)長(zhǎng)度發(fā)生變化的情況。
九.ref
由于Proxy的代理目標(biāo)是非原始值,所以沒(méi)有任何手段去攔截對(duì)原始值的操作:
letstr=hi
//無(wú)法攔截對(duì)值的修改
str=pino
解決方法是:使用一個(gè)非原始值去包裹
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- T/CBMMA 2-2019輥壓機(jī)用減速機(jī)在線監(jiān)測(cè)系統(tǒng)與功能規(guī)范
- T/CAQI 95-2019家用和類似用途凈化型淋浴器
- T/CAPMA 5-2018熟狐皮質(zhì)量檢驗(yàn)
- 勻富尚品java面試題及答案
- 公共醫(yī)療面試題及答案
- 翻譯助力面試題及答案
- 人的管理課件
- T/CADBM 69-2023環(huán)氧卷材地板
- 公寓轉(zhuǎn)租三方合同范本
- 小區(qū)玻璃破碎補(bǔ)償協(xié)議書(shū)
- 警企共建合作協(xié)議書(shū)范本
- 2025信息技術(shù)綠色發(fā)展行動(dòng)計(jì)劃
- CNAS-CL31-2011 內(nèi)部校準(zhǔn)要求
- 2024年7月國(guó)家開(kāi)放大學(xué)??啤陡叩葦?shù)學(xué)基礎(chǔ)》期末紙質(zhì)考試試題及答案
- 福建省普通高中2023年學(xué)業(yè)水平合格性考試數(shù)學(xué)試題(原卷版)
- 2025年小學(xué)一年級(jí)數(shù)學(xué)下冊(cè)無(wú)紙筆化評(píng)價(jià)方案及檢測(cè)題
- 法規(guī)解讀丨2024新版《突發(fā)事件應(yīng)對(duì)法》及其應(yīng)用案例
- 變更羈押強(qiáng)制措施申請(qǐng)書(shū)
- 【MOOC】電化學(xué)-浙江大學(xué) 中國(guó)大學(xué)慕課MOOC答案
- 八項(xiàng)工程統(tǒng)計(jì)工作方案
- 2024年建設(shè)工程監(jiān)理人員技能競(jìng)賽理論考試題庫(kù)(含答案)
評(píng)論
0/150
提交評(píng)論