当前位置: 首页 > news >正文

网站建设公司汕头的无锡网站优化公司

网站建设公司汕头的,无锡网站优化公司,可以做配音兼职的网站,我不想找之前做网站的续费React 中 本地调试React代码的方法 先将React代码下载到本地,进入项目文件夹后yarn build利用create-react-app创建一个自己的项目把react源码和自己刚刚创建的项目关联起来,之前build源码到build文件夹下面,然后cd到react文件夹下面的buil…

React 中

本地调试React代码的方法

  • 先将React代码下载到本地,进入项目文件夹后yarn build
  • 利用create-react-app创建一个自己的项目
  • 把react源码和自己刚刚创建的项目关联起来,之前build源码到build文件夹下面,然后cd到react文件夹下面的build文件夹下。里面有node_modules文件夹,进入此文件夹。发现有react文件夹和react-dom文件夹。分别进入到这两个文件夹。分别运行yarn link。此时创建了两个快捷方式。react和react-dom
  • cd到自己项目的目录下,运行yarn link react react-dom 。此时在你项目里就使用了react源码下的build的相关文件。如果你对react源码有修改,就刷新下项目,就能里面体现在你的项目里。

场景

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

16之前

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

16之后

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

getDerivedStateFromProps触发条件

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

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

setStatereact.development.js当中

Component.prototype.setState = function (partialState, callback) {!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0;this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
复制代码
ReactNoopUpdateQueue {//...部分省略enqueueSetState: function (publicInstance, partialState, callback, callerName) {warnNoop(publicInstance, 'setState');}
}
复制代码

执行的是一个警告方法

function warnNoop(publicInstance, callerName) {{// 实例的构造体var _constructor = publicInstance.constructor;var componentName = _constructor && (_constructor.displayName || _constructor.name) || 'ReactClass';// 组成一个key 组件名称+方法名(列如setState)var warningKey = componentName + '.' + callerName;// 如果已经输出过警告了就不会再输出if (didWarnStateUpdateForUnmountedComponent[warningKey]) {return;}// 在开发者工具的终端里输出警告日志 不能直接使用 component.setState来调用 warningWithoutStack$1(false, "Can't call %s on a component that is not yet mounted. " + 'This is a no-op, but it might indicate a bug in your application. ' + 'Instead, assign to `this.state` directly or define a `state = {};` ' + 'class property with the desired state in the %s component.', callerName, componentName);didWarnStateUpdateForUnmountedComponent[warningKey] = true;}
}
复制代码

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

function Component(props, context, updater) {this.props = props;this.context = context;// If a component has string refs, we will assign a different object later.this.refs = emptyObject;// We initialize the default updater but the real one gets injected by the// renderer.this.updater = updater || ReactNoopUpdateQueue;
}
复制代码

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

class App extends Component {constructor(props) {super(props);//..省略console.log('constructor', this);}
}
复制代码

 

-w766

 

 

方法指向的是,在react-dom.development.jsclassComponentUpdater

var classComponentUpdater = {// 是否渲染isMounted: isMounted,enqueueSetState: function(inst, payload, callback) {// inst 是fiberinst = inst._reactInternalFiber;// 获取时间var currentTime = requestCurrentTime();currentTime = computeExpirationForFiber(currentTime, inst);// 根据更新时间初始化一个标识对象var update = createUpdate(currentTime);update.payload = payload;void 0 !== callback && null !== callback && (update.callback = callback);// 排队更新 将更新任务加入队列当中enqueueUpdate(inst, update);//scheduleWork(inst, currentTime);},// ..省略
}
复制代码

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

function enqueueUpdate(fiber, update) {var alternate = fiber.alternate;// 如果alternat为空并且更新队列为空则创建更新队列if (null === alternate) {var queue1 = fiber.updateQueue;var queue2 = null;null === queue1 &&(queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState));} else(queue1 = fiber.updateQueue),(queue2 = alternate.updateQueue),null === queue1? null === queue2? ((queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState)),(queue2 = alternate.updateQueue = createUpdateQueue(alternate.memoizedState))): (queue1 = fiber.updateQueue = cloneUpdateQueue(queue2)): null === queue2 &&(queue2 = alternate.updateQueue = cloneUpdateQueue(queue1));null === queue2 || queue1 === queue2? appendUpdateToQueue(queue1, update): null === queue1.lastUpdate || null === queue2.lastUpdate? (appendUpdateToQueue(queue1, update),appendUpdateToQueue(queue2, update)): (appendUpdateToQueue(queue1, update), (queue2.lastUpdate = update));
}
复制代码

我们看scheduleWork下

function scheduleWork(fiber, expirationTime) {// 获取根 nodevar root = scheduleWorkToRoot(fiber, expirationTime);null !== root &&(!isWorking &&0 !== nextRenderExpirationTime &&expirationTime < nextRenderExpirationTime &&((interruptedBy = fiber), resetStack()),markPendingPriorityLevel(root, expirationTime),(isWorking && !isCommitting$1 && nextRoot === root) ||requestWork(root, root.expirationTime),nestedUpdateCount > NESTED_UPDATE_LIMIT &&((nestedUpdateCount = 0), reactProdInvariant("185")));
}
复制代码
function requestWork(root, expirationTime) {// 将需要渲染的root进行记录addRootToSchedule(root, expirationTime);if (isRendering) {// Prevent reentrancy. Remaining work will be scheduled at the end of// the currently rendering batch.return;}if (isBatchingUpdates) {// Flush work at the end of the batch.if (isUnbatchingUpdates) {// ...unless we're inside unbatchedUpdates, in which case we should// flush it now.nextFlushedRoot = root;nextFlushedExpirationTime = Sync;performWorkOnRoot(root, Sync, true);}// 执行到这边直接return,此时setState()这个过程已经结束return;}// TODO: Get rid of Sync and use current time?if (expirationTime === Sync) {performSyncWork();} else {scheduleCallbackWithExpirationTime(root, expirationTime);}
}
复制代码

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

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

最终方法是执行

function applyDerivedStateFromProps(workInProgress,ctor,getDerivedStateFromProps,nextProps
) {var prevState = workInProgress.memoizedState;{if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {// Invoke the function an extra time to help detect side-effects.getDerivedStateFromProps(nextProps, prevState);}}// 获取改变的statevar partialState = getDerivedStateFromProps(nextProps, prevState);{// 对一些错误格式进行警告warnOnUndefinedDerivedState(ctor, partialState);} // Merge the partial state and the previous state.// 判断getDerivedStateFromProps返回的格式是否为空,如果不为空则将由原的state和它的返回值合并var memoizedState = partialState === null || partialState === undefined ? prevState : _assign({}, prevState, partialState);// 设置state// 一旦更新队列为空,将派生状态保留在基础状态当中workInProgress.memoizedState = memoizedState; // Once the update queue is empty, persist the derived state onto the// base state.var updateQueue = workInProgress.updateQueue;if (updateQueue !== null && workInProgress.expirationTime === NoWork) {updateQueue.baseState = memoizedState;}
}
复制代码

Vue

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

Watch触发条件

src/core/instance中有initState()

/core/instance/state.js

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

function initData (vm: Component) {// ...省略部分代码// observe dataobserve(data, true /* asRootData */)
}
复制代码
/*** Attempt to create an observer instance for a value,* returns the new observer if successfully observed,* or the existing observer if the value already has one.*/
export function observe (value: any, asRootData: ?boolean): Observer | void {if (!isObject(value) || value instanceof VNode) {return}let ob: Observer | voidif (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {ob = value.__ob__} else if (shouldObserve &&!isServerRendering() &&(Array.isArray(value) || isPlainObject(value)) &&Object.isExtensible(value) &&!value._isVue) {// 创建observerob = new Observer(value)}if (asRootData && ob) {ob.vmCount++}return ob
}
复制代码

来看下observer的构造方法,不管是array还是obj,他们最终都会调用的是this.walk()

constructor (value: any) {this.value = valuethis.dep = new Dep()this.vmCount = 0def(value, '__ob__', this)if (Array.isArray(value)) {const augment = hasProto? protoAugment: copyAugmentaugment(value, arrayMethods, arrayKeys)// 遍历array中的每个值,然后调用walkthis.observeArray(value)} else {this.walk(value)}}
复制代码

我们再来看下walk方法,walk方法就是将object中的执行defineReactive()方法,而这个方法实际就是改写setget方法

/**
* Walk through each property and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
walk (obj: Object) {const keys = Object.keys(obj)for (let i = 0; i < keys.length; i++) {defineReactive(obj, keys[i])}
}
复制代码

/core/observer/index.js defineReactive方法最为核心,它将set和get方法改写,如果我们重新对变量进行赋值,那么会判断变量的新值是否等于旧值,如果不相等,则会触发dep.notify()从而回调watch中的方法。

/*** Define a reactive property on an Object.*/
export function defineReactive (obj: Object,key: string,val: any,customSetter?: ?Function,shallow?: boolean
) {// dep当中存放的是watcher数组 const dep = new Dep()const property = Object.getOwnPropertyDescriptor(obj, key)if (property && property.configurable === false) {return}// cater for pre-defined getter/settersconst getter = property && property.getconst setter = property && property.setif ((!getter || setter) && arguments.length === 2) { // 如果第三个值没有传。那么val就直接从obj中根据key的值获取val = obj[key]}let childOb = !shallow && observe(val)Object.defineProperty(obj, key, {enumerable: true,// 可设置值configurable: true,get: function reactiveGetter () {const value = getter ? getter.call(obj) : valif (Dep.target) {// dep中生成个watcherdep.depend()if (childOb) {childOb.dep.depend()if (Array.isArray(value)) {dependArray(value)}}}return value},// 重点看set方法set: function reactiveSetter (newVal) {// 获取变量原始值const value = getter ? getter.call(obj) : val/* eslint-disable no-self-compare */// 进行重复值比较 如果相等直接returnif (newVal === value || (newVal !== newVal && value !== value)) {return}/* eslint-enable no-self-compare */if (process.env.NODE_ENV !== 'production' && customSetter) {// dev环境可以直接自定义setcustomSetter()}// 将新的值赋值if (setter) {setter.call(obj, newVal)} else {val = newVal}childOb = !shallow && observe(newVal)// 触发watch事件// dep当中是一个wacher的数组// notify会执行wacher数组的update方法,update方法触发最终的watcher的run方法,触发watch回调dep.notify()}})
}
复制代码

小程序

自定义Watch

小程序的data本身是不支持watch的,但是我们可以自行添加,我们参照Vue的写法自己写一个。 watcher.js

export function defineReactive (obj, key, callbackObj, val) {const property = Object.getOwnPropertyDescriptor(obj, key);console.log(property);const getter = property && property.get;const setter = property && property.set;val = obj[key]const callback = callbackObj[key];Object.defineProperty(obj, key, {enumerable: true,get: function reactiveGetter () {const value = getter ? getter.call(obj) : valreturn value},set: (newVal) => {console.log('start set');const value = getter ? getter.call(obj) : valif (typeof callback === 'function') {callback(newVal, val);}if (setter) {setter.call(obj, newVal)} else {val = newVal}console.log('finish set', newVal);}});
}export function watch(cxt, callbackObj) {const data = cxt.datafor (const key in data) {console.log(key);defineReactive(data, key, callbackObj)}
}
复制代码

使用

我们在执行watch回调前没有对新老赋值进行比较,原因是微信当中对data中的变量赋值,即使给引用变量赋值还是相同的值,也会因为引用地址不同,判断不相等。如果想对新老值进行比较就不能使用===,可以先对obj或者array转换为json字符串再比较。

//index.js
//获取应用实例
const app = getApp()import {watch} from '../../utils/watcher';Page({data: {motto: 'hello world',userInfo: {},hasUserInfo: false,canIUse: wx.canIUse('button.open-type.getUserInfo'),tableData: []},onLoad: function () {this.initWatcher();},initWatcher () {watch(this, {motto(newVal, oldVal) {console.log('newVal', newVal, 'oldVal', oldVal);},userInfo(newVal, oldVal) {console.log('newVal', newVal, 'oldVal', oldVal);},tableData(newVal, oldVal) {console.log('newVal', newVal, 'oldVal', oldVal);}});    },onClickChangeStringData() {this.setData({motto: 'hello'});},onClickChangeObjData() {this.setData({userInfo: {name: 'helo'}});},onClickChangeArrayDataA() {const tableData = [];this.setData({tableData});}
})复制代码

参考

  • 如何阅读React源码
  • React 16.3 ~ React 16.5 一些比较重要的改动

广而告之

本文发布于薄荷前端周刊,欢迎Watch & Star ★,转载请注明出处。


作者:薄荷前端
链接:https://juejin.im/post/5bea8f40518825271b3d6be8
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

http://www.lbrq.cn/news/2772091.html

相关文章:

  • 怎么介绍自己的优势seo手机端优化
  • 做网站给菠菜引流百度运营优化师
  • 曲靖网站建设公司聚名网官网登录
  • 网站建设需注意的问题seo人员是什么意思
  • wordpress ping服务列表山西seo和网络推广
  • 深圳网站的公司最近新闻大事
  • 营销型网站建设制作推广手机百度搜索引擎
  • 企业腾讯邮箱入口鄂州网站seo
  • 学做效果图的网站品牌策划是做什么的
  • 网站怎么做才有收录网站平台搭建
  • 百度做网站推广多少钱网络推广企业
  • 北京怀柔网站建设公司天津百度推广代理商
  • 大港做网站手机怎么建自己的网站
  • 益阳网站建设公司电话公司如何做网络推广营销
  • 大连做网站的科技公司网络推广员压力大吗
  • 一个服务器放多少网站seo企业优化顾问
  • 如何做wap网站西宁网站seo
  • 网站设计 图片重庆网站seo建设哪家好
  • 仙桃做网站找谁域名流量查询工具
  • 中国矿山建设网站淘宝seo优化怎么做
  • 网站论坛做斑竹自己创建个人免费网站
  • 武汉网站制作模板百度app客服电话
  • 中国贸易服务网seo自动刷外链工具
  • wordpress隐藏内容破解成都网站优化seo
  • 找做网站签证西安seo网站建设
  • 网页设计课程期末总结怎么分析一个网站seo
  • 如何看网站点击量全网营销思路
  • 美国cms是什么机构杭州排名优化软件
  • 做网页收集素材常用的网站有哪些新媒体营销策略有哪些
  • 网站的验证码是怎么做的效果好的关键词如何优化
  • [激光原理与应用-308]:光学设计 - 266皮秒深紫外激光器设计图纸示例解析(基于工程实现与专利技术)
  • 疏老师-python训练营-Day50预训练模型+CBAM注意力
  • 蔬菜批发小程序:生产商的数字化转型利器——仙盟创梦IDE
  • Linux内存管理系统性总结
  • 25. 能否创建一个包含可变对象的不可变对象
  • C#WPF实战出真汁13--【营业查询】