三星a60-photoshop快捷键

schedule_work
2023年4月5日发(作者:wps云文档)

React和Vue中监听变量变化的⽅法

React中

本地调试React代码的⽅法

yarnbuild

场景

假设有这样⼀个场景,⽗组件传递⼦组件⼀个A参数,⼦组件需要监听A参数的变化转换为state。

16之前

在React以前我们可以使⽤componentWillReveiveProps来监听props的变换

16之后

在最新版本的React中可以使⽤新出的getDerivedStateFromProps进⾏props的监听,getDerivedStateFromProps可以返回null或者⼀个对象,如果是对象,则会更新state

getDerivedStateFromProps触发条件

我们的⽬标就是找到getDerivedStateFromProps的触发条件

我们知道,只要调⽤setState就会触发getDerivedStateFromProps,并且props的值相同,也会触发getDerivedStateFromProps(16.3版本之后)

setState在当中

te=function(partialState,callback){

!(typeofpartialState==='object'||typeofpartialState==='function'||partialState==null)?invariant(false,'setState(...):takesanobjectofstatevariablestoupdateorafunctionwhichreturnsanobjectofstatevariables.'):void0;

eSetState(this,partialState,callback,'setState');

};

ReactNoopUpdateQueue{

//...部分省略

enqueueSetState:function(publicInstance,partialState,callback,callerName){

warnNoop(publicInstance,'setState');

}

}

执⾏的是⼀个警告⽅法

functionwarnNoop(publicInstance,callerName){

{

//实例的构造体

var_constructor=uctor;

varcomponentName=_constructor&&(_yName||_)||'ReactClass';

//组成⼀个key组件名称+⽅法名(列如setState)

varwarningKey=componentName+'.'+callerName;

//如果已经输出过警告了就不会再输出

if(didWarnStateUpdateForUnmountedComponent[warningKey]){

return;

}

//在开发者⼯具的终端⾥输出警告⽇志不能直接使⽤te来调⽤

warningWithoutStack$1(false,"Can'tcall%sonacomponentthatisnotyetmounted."+'Thisisano-op,butitmightindicateabuginyourapplication.'+'Instead,assignto``directlyordefinea`state={};`'+'classpropertywiththedesiredstatein

didWarnStateUpdateForUnmountedComponent[warningKey]=true;

}

}

看来ReactNoopUpdateQueue是⼀个抽象类,实际的⽅法并不是在这⾥实现的,同时我们看下最初updater赋值的地⽅,初始化Component时,会传⼊实际的updater

functionComponent(props,context,updater){

=props;

t=context;

//Ifacomponenthasstringrefs,wewillassignadifferentobjectlater.

=emptyObject;

//Weinitializethedefaultupdaterbuttherealonegetsinjectedbythe

//renderer.

r=updater||ReactNoopUpdateQueue;

}

我们在组件的构造⽅法当中将this进⾏打印

classAppextendsComponent{

constructor(props){

super(props);

//..省略

('constructor',this);

}

}

⽅法指向的是,在的classComponentUpdater

varclassComponentUpdater={

//是否渲染

isMounted:isMounted,

enqueueSetState:function(inst,payload,callback){

//inst是fiber

inst=inst._reactInternalFiber;

//获取时间

varcurrentTime=requestCurrentTime();

currentTime=computeExpirationForFiber(currentTime,inst);

//根据更新时间初始化⼀个标识对象

varupdate=createUpdate(currentTime);

d=payload;

void0!==callback&&null!==callback&&(ck=callback);

//排队更新将更新任务加⼊队列当中

enqueueUpdate(inst,update);

//

scheduleWork(inst,currentTime);

},

//..省略

}

enqueueUpdate

就是将更新任务加⼊队列当中

functionenqueueUpdate(fiber,update){

varalternate=ate;

//如果alternat为空并且更新队列为空则创建更新队列

if(null===alternate){

varqueue1=Queue;

varqueue2=null;

null===queue1&&

(queue1=Queue=createUpdateQueue(edState));

}else

(queue1=Queue),

(queue2=Queue),

null===queue1

null===queue2

((queue1=Queue=createUpdateQueue(

edState

)),

(queue2=Queue=createUpdateQueue(

edState

)))

:(queue1=Queue=cloneUpdateQueue(queue2))

:null===queue2&&

(queue2=Queue=cloneUpdateQueue(queue1));

null===queue2||queue1===queue2

appendUpdateToQueue(queue1,update)

:null===date||null===date

(appendUpdateToQueue(queue1,update),

appendUpdateToQueue(queue2,update))

:(appendUpdateToQueue(queue1,update),(date=update));

}

我们看scheduleWork下

functionscheduleWork(fiber,expirationTime){

//获取根node

varroot=scheduleWorkToRoot(fiber,expirationTime);

null!==root&&

(!isWorking&&

0!==nextRenderExpirationTime&&

expirationTime

((interruptedBy=fiber),resetStack()),

markPendingPriorityLevel(root,expirationTime),

(isWorking&&!isCommitting$1&&nextRoot===root)||

requestWork(root,tionTime),

nestedUpdateCount>NESTED_UPDATE_LIMIT&&

((nestedUpdateCount=0),reactProdInvariant("185")));

}

functionrequestWork(root,expirationTime){

//将需要渲染的root进⾏记录

addRootToSchedule(root,expirationTime);

if(isRendering){

//ingworkwillbescheduledattheendof

//thecurrentlyrenderingbatch.

return;

}

if(isBatchingUpdates){

//Flushworkattheendofthebatch.

if(isUnbatchingUpdates){

//...unlesswe'reinsideunbatchedUpdates,inwhichcaseweshould

//flushitnow.

nextFlushedRoot=root;

nextFlushedExpirationTime=Sync;

performWorkOnRoot(root,Sync,true);

}

//执⾏到这边直接return,此时setState()这个过程已经结束

return;

}

//TODO:GetridofSyncandusecurrenttime?

if(expirationTime===Sync){

performSyncWork();

}else{

scheduleCallbackWithExpirationTime(root,expirationTime);

}

}

太过复杂,⼀些⽅法其实还没有看懂,但是根据断点可以把执⾏顺序先理⼀下,在setState之后会执⾏performSyncWork,随后是如下的⼀个执⾏顺序

performSyncWork=>performWorkOnRoot=>renderRoot=>workLoop=>performUnitOfWork=>beginWork=>applyDerivedStateFromProps

最终⽅法是执⾏

functionapplyDerivedStateFromProps(

workInProgress,

ctor,

getDerivedStateFromProps,

nextProps

){

varprevState=edState;

{

if(debugRenderPhaseSideEffects||debugRenderPhaseSideEffectsForStrictMode&&&StrictMode){

//Invokethefunctionanextratimetohelpdetectside-effects.

getDerivedStateFromProps(nextProps,prevState);

}

}

//获取改变的state

varpartialState=getDerivedStateFromProps(nextProps,prevState);

{

//对⼀些错误格式进⾏警告

warnOnUndefinedDerivedState(ctor,partialState);

}//Mergethepartialstateandthepreviousstate.

//判断getDerivedStateFromProps返回的格式是否为空,如果不为空则将由原的state和它的返回值合并

varmemoizedState=partialState===null||partialState===undefined?prevState:_assign({},prevState,partialState);

//设置state

//⼀旦更新队列为空,将派⽣状态保留在基础状态当中

edState=memoizedState;//Oncetheupdatequeueisempty,persistthederivedstateontothe

//basestate.

varupdateQueue=Queue;

if(updateQueue!==null&&tionTime===NoWork){

ate=memoizedState;

}

}

Vue

vue监听变量变化依靠的是watch,因此我们先从源码中看看,watch是在哪⾥触发的。

Watch触发条件

在src/core/instance中有initState()

/core/instance/

在数据初始化时initData(),会将每vue的data注册到objerserver中

functioninitData(vm:Component){

//...省略部分代码

//observedata

observe(data,true/*asRootData*/)

}

/**

*Attempttocreateanobserverinstanceforavalue,

*returnsthenewobserverifsuccessfullyobserved,

*ortheexistingobserverifthevaluealreadyhasone.

*/

exportfunctionobserve(value:any,asRootData:?boolean):Observer|void{

if(!isObject(value)||valueinstanceofVNode){

return

}

letob:Observer|void

if(hasOwn(value,'__ob__')&&value.__ob__instanceofObserver){

ob=value.__ob__

}elseif(

shouldObserve&&

!isServerRendering()&&

(y(value)||isPlainObject(value))&&

nsible(value)&&

!value._isVue

){

//创建observer

ob=newObserver(value)

}

if(asRootData&&ob){

t++

}

returnob

}

来看下observer的构造⽅法,不管是array还是obj,他们最终都会调⽤的是()

constructor(value:any){

=value

=newDep()

t=0

def(value,'__ob__',this)

if(y(value)){

constaugment=hasProto

protoAugment

:copyAugment

augment(value,arrayMethods,arrayKeys)

//遍历array中的每个值,然后调⽤walk

eArray(value)

}else{

(value)

}

}

我们再来看下walk⽅法,walk⽅法就是将object中的执⾏defineReactive()⽅法,⽽这个⽅法实际就是改写set和get⽅法

/**

*Walkthrougheachpropertyandconverttheminto

*getter/thodshouldonlybecalledwhen

*valuetypeisObject.

*/

walk(obj:Object){

constkeys=(obj)

for(leti=0;i<;i++){

defineReactive(obj,keys[i])

}

}

/core/observer/

defineReactive⽅法最为核⼼,它将set和get⽅法改写,如果我们重新对变量进⾏赋值,那么会判断变量的新值是否等于旧值,如果不相等,则会触发()从⽽回调watch中的⽅法。

/**

*DefineareactivepropertyonanObject.

*/

exportfunctiondefineReactive(

obj:Object,

key:string,

val:any,

customSetter?:?Function,

shallow?:boolean

){

//dep当中存放的是watcher数组

constdep=newDep()

constproperty=PropertyDescriptor(obj,key)

if(property&&urable===false){

return

}

//caterforpre-definedgetter/setters

constgetter=property&&

constsetter=property&&

if((!getter||setter)&&===2){

//如果第三个值没有传。那么val就直接从obj中根据key的值获取

val=obj[key]

}

letchildOb=!shallow&&observe(val)

Property(obj,key,{

enumerable:true,

//可设置值

configurable:true,

get:functionreactiveGetter(){

constvalue=getter?(obj):val

if(){

//dep中⽣成个watcher

()

if(childOb){

()

if(y(value)){

dependArray(value)

}

}

}

returnvalue

},

//重点看set⽅法

set:functionreactiveSetter(newVal){

//获取变量原始值

constvalue=getter?(obj):val

/*eslint-disableno-self-compare*/

//进⾏重复值⽐较如果相等直接return

if(newVal===value||(newVal!==newVal&&value!==value)){

return

}

/*eslint-enableno-self-compare*/

if(_ENV!=='production'&&customSetter){

//dev环境可以直接⾃定义set

customSetter()

}

//将新的值赋值

if(setter){

(obj,newVal)

}else{

val=newVal

}

childOb=!shallow&&observe(newVal)

//触发watch事件

//dep当中是⼀个wacher的数组

//notify会执⾏wacher数组的update⽅法,update⽅法触发最终的watcher的run⽅法,触发watch回调

()

}

})

}

⼩程序

⾃定义Watch

⼩程序的data本⾝是不⽀持watch的,但是我们可以⾃⾏添加,我们参照Vue的写法⾃⼰写⼀个。

exportfunctiondefineReactive(obj,key,callbackObj,val){

constproperty=PropertyDescriptor(obj,key);

(property);

constgetter=property&&;

constsetter=property&&;

val=obj[key]

constcallback=callbackObj[key];

Property(obj,key,{

enumerable:true,

get:functionreactiveGetter(){

constvalue=getter?(obj):val

returnvalue

},

set:(newVal)=>{

('startset');

constvalue=getter?(obj):val

if(typeofcallback==='function'){

callback(newVal,val);

}

if(setter){

(obj,newVal)

}else{

val=newVal

}

('finishset',newVal);

}

});

}

exportfunctionwatch(cxt,callbackObj){

constdata=

for(constkeyindata){

(key);

defineReactive(data,key,callbackObj)

}

}

使⽤

我们在执⾏watch回调前没有对新⽼赋值进⾏⽐较,原因是微信当中对data中的变量赋值,即使给引⽤变量赋值还是相同的值,也会因为引⽤地址不同,判断不相等。如果想对新⽼值进⾏⽐较就不能使⽤

===,可以先对obj或者array转换为json字符串再⽐较。

//

//获取应⽤实例

constapp=getApp()

import{watch}from'../../utils/watcher';

Page({

data:{

motto:'helloworld',

userInfo:{},

hasUserInfo:false,

canIUse:e('rInfo'),

tableData:[]

},

onLoad:function(){

tcher();

},

initWatcher(){

watch(this,{

motto(newVal,oldVal){

('newVal',newVal,'oldVal',oldVal);

},

userInfo(newVal,oldVal){

('newVal',newVal,'oldVal',oldVal);

},

tableData(newVal,oldVal){

('newVal',newVal,'oldVal',oldVal);

}

});

},

onClickChangeStringData(){

a({

motto:'hello'

});

},

onClickChangeObjData(){

a({

userInfo:{

name:'helo'

}

});

},

onClickChangeArrayDataA(){

consttableData=[];

a({

tableData

});

}

})

参考

总结

以上所述是⼩编给⼤家介绍的React和Vue中监听变量变化的⽅法,希望对⼤家有所帮助,如果⼤家有任何疑问请给我留⾔,⼩编会及时回复⼤家的。在此也⾮常感谢⼤家对⽹站的⽀持!

更多推荐

schedule_work