三星a60-photoshop快捷键
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
发布评论