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

佰联轴承网做的网站景县有专业做网站人员吗

佰联轴承网做的网站,景县有专业做网站人员吗,怎么开发手机网页,栖霞酒店网站设计价格本文基于 React V16.8.6#xff0c;本文代码地址测试代码 源码讲解React 中一个元素可能有 0 个、1 个或者多个直接子元素#xff0c;React 导出的 Children 中包含 5 个处理子元素的方法。map 类似 array.mapforEach 类似 array.forEachcount 类似 array.lengthtoArrayonlyR…本文基于 React V16.8.6本文代码地址测试代码 源码讲解React 中一个元素可能有 0 个、1 个或者多个直接子元素React 导出的 Children 中包含 5 个处理子元素的方法。map 类似 array.mapforEach 类似 array.forEachcount 类似 array.lengthtoArrayonlyReact 内部处理 Children 的几个重要函数包括mapChildrentraverseAllChildrenImplmapIntoWithKeyPrefixInternal mapSingleChildIntoContextgetPooledTraverseContextreleaseTraverseContext源码都在 packages/react/src/ReactChildren.js 中。导出的语句export {forEachChildren as forEach,mapChildren as map,countChildren as count,onlyChild as only,toArray, };Children API map类似 array.map但有一下几个不同点返回的结果一定是一个一维数组多维数组会被自动摊平对返回的每个节点如果 isValidElement(el) true 则会给它加上一个 key如果元素本来就有 key则会重新生成一个新的 keymap 的用法第一个参数是要遍历的 children第二个参数是遍历的函数第三个是 context执行遍历函数时的 this。如果 children null则直接返回了。mapChildren/*** Maps children that are typically specified as props.children.* 用来遍历 props.children** param {?*} children Children tree container.* param {function(*, int)} func The map function.* param {*} context Context for mapFunction.* return {object} Object containing the ordered map of results.*/ function mapChildren(children, func, context) {if (children null) {return children;}// 遍历出来的元素会丢到 result 中最后返回出去const result [];mapIntoWithKeyPrefixInternal(children, result, null, func, context);return result; }mapIntoWithKeyPrefixInternal将 children 完全遍历遍历的节点最终全部存到 array 中是 ReactElement 的节点会更改 key 之后再放到 array 中。function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {// 这里是处理 key不关心也没事let escapedPrefix ;if (prefix ! null) {escapedPrefix escapeUserProvidedKey(prefix) /;}// getPooledTraverseContext 和 releaseTraverseContext 是配套的函数// 用处其实很简单就是维护一个大小为 10 的对象重用池// 每次从这个池子里取一个对象去赋值用完了就将对象上的属性置空然后丢回池子// 维护这个池子的用意就是提高性能毕竟频繁创建销毁一个有很多属性的对象消耗性能const traverseContext getPooledTraverseContext(array, // result escapedPrefix, // func, // mapFunccontext, // context);// 最核心的一句traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);releaseTraverseContext(traverseContext); }getPooledTraverseContextgetPooledTraverseContext 和 releaseTraverseContext这两个函数是用来维护一个对象池池子最大为10。Children 需要频繁的创建对象会导致性能问题所以维护一个固定数量的对象池每次从对象池拿一个对象进行复制使用完将各个属性 reset。const POOL_SIZE 10; const traverseContextPool []; // 返回一个传入参数构成的对象 // traverseContextPool 长度为 0 则自己构造一个对象出来否则从 traverseContextPool pop 一个对象 // 再对这个对象的各个属性进行赋值 function getPooledTraverseContext(mapResult,keyPrefix,mapFunction,mapContext, ) {if (traverseContextPool.length) {const traverseContext traverseContextPool.pop();traverseContext.result mapResult;traverseContext.keyPrefix keyPrefix;traverseContext.func mapFunction;traverseContext.context mapContext;traverseContext.count 0;return traverseContext;} else {return { result: mapResult,keyPrefix: keyPrefix,func: mapFunction,context: mapContext,count: 0,};} }releaseTraverseContext将 getPooledTraverseContext 产生的对象加入数组中对象池 10 则不用管function releaseTraverseContext(traverseContext) {traverseContext.result null;traverseContext.keyPrefix null;traverseContext.func null;traverseContext.context null;traverseContext.count 0;if (traverseContextPool.length POOL_SIZE) {traverseContextPool.push(traverseContext);} }traverseAllChildren没太多好说的function traverseAllChildren(children, callback, traverseContext) {if (children null) {return 0;}return traverseAllChildrenImpl(children, , callback, traverseContext); }traverseAllChildrenImpl它的作用可以理解为children 是可渲染节点则调用 mapSingleChildIntoContext 把 children 推入 result 数组中children 是数组则再次对数组中的每个元素调用 traverseAllChildrenImpl传入的 key 是最新拼接好的children 是对象则通过 children[Symbol.iterator] 获取到对象的迭代器 iterator 将迭代的结果放到 traverseAllChildrenImpl 处理函数核心作用就是通过把传入的 children 数组通过遍历摊平成单个节点然后去执行 mapSingleChildIntoContext。这个函数比较复杂函数签名是这样的children 要处理的 childrennameSoFar 父级 key会一层一层拼接传递用 : 分隔callback 如果当前层级是可渲染节点undefined、boolean 会变成 nullstring、number、$$typeof 是 REACT_ELEMENT_TYPE 或者 REACT_PORTAL_TYPE会调用 mapSingleChildIntoContext 处理traverseContext 对象池中拿出来的一个对象/*** param {?*} children Children tree container. Children.map 的第一个参数要处理的 children* param {!string} nameSoFar Name of the key path so far.* param {!function} callback Callback to invoke with each child found. map 时 callback 是* mapSingleChildIntoContext* param {?*} traverseContext Used to pass information throughout the traversal* process. 对象池的一个对象* return {!number} The number of children in this subtree.*/ function traverseAllChildrenImpl(children,nameSoFar,callback,traverseContext, ) {// 这个函数核心作用就是通过把传入的 children 数组通过遍历摊平成单个节点// 然后去执行 mapSingleChildIntoContext// 开始判断 children 的类型const type typeof children;if (type undefined || type boolean) {// All of the above are perceived as null.children null;}// 决定是否调用 callback// 是可渲染的节点则为 truelet invokeCallback false;// 判断是否调用children null、type 为可渲染的节点则 invokeCallback 为 trueif (children null) {invokeCallback true;} else {switch (type) {case string:case number:invokeCallback true;break;case object:switch (children.$$typeof) {case REACT_ELEMENT_TYPE:case REACT_PORTAL_TYPE:invokeCallback true;}}}// 如果 children 是可以渲染的节点的话就直接调用 callback// callback 是 mapSingleChildIntoContext// 我们先去阅读下 mapSingleChildIntoContext 函数的源码if (invokeCallback) {callback(traverseContext,children,// If its the only child, treat the name as if it was wrapped in an array// so that its consistent if the number of children grows.// const SEPARATOR .;nameSoFar ? SEPARATOR getComponentKey(children, 0) : nameSoFar,);return 1;}// nextName 和 nextNamePrefix 都是在处理 key 的命名let child;let nextName;let subtreeCount 0; // Count of children found in the current subtree.// const SUBSEPARATOR :;const nextNamePrefix nameSoFar ? SEPARATOR : nameSoFar SUBSEPARATOR;// 节点是数组的话就开始遍历数组并且把数组中的每个元素再递归执行 traverseAllChildrenImpl// 这一步操作也用来摊平数组的// React.Children.map(this.props.children, c [[c, c]])// c [[c, c]] 会被摊平为 [c, c, c, c]// 这里如果看不明白的话过会在 mapSingleChildIntoContext 中肯定能看明白if (Array.isArray(children)) {for (let i 0; i children.length; i) {child children[i];nextName nextNamePrefix getComponentKey(child, i); // .$dasdsa:subtreeCount traverseAllChildrenImpl(child,nextName, // 不同点是 nameSoFar 变了它会在每一层不断拼接用 : 分隔callback,traverseContext,);}} else {// 不是数组的话就看看 children 是否可以支持迭代// 通过 obj[Symbol.iterator] 的方式去取const iteratorFn getIteratorFn(children);// ... 中间有部分 __DEV__ 下检测使用正确性的代码// 只有取出来对象是个函数类型才是正确的// 然后就是执行迭代器重复上面 if 中的逻辑const iterator iteratorFn.call(children);let step;let ii 0;while (!(step iterator.next()).done) {child step.value;nextName nextNamePrefix getComponentKey(child, ii);subtreeCount traverseAllChildrenImpl(child,nextName,callback,traverseContext,);}}return subtreeCount; }mapSingleChildIntoContext将 child 推入 traverseContext 的 result 数组中child 如果是 ReactElement则更改 key 了再推入。只有当传入的 child 是可渲染节点才会调用。如果执行了 mapFunc 返回的是一个数组则会将数组放到 mapIntoWithKeyPrefixInternal 继续处理。/*** param bookKeeping 就是我们从对象池子里取出来的东西traverseContext* param child 传入的节点children* param childKey 节点的 keynameSoFar*/ function mapSingleChildIntoContext(bookKeeping, child, childKey) {const {result, keyPrefix, func, context} bookKeeping; // traverseContext// func 就是我们在 React.Children.map(this.props.children, c c)// 中传入的第二个函数参数let mappedChild func.call(context, child, bookKeeping.count);// 判断函数返回值是否为数组// 因为可能会出现这种情况// React.Children.map(this.props.children, c [c, c])// 对于 c [c, c] 这种情况来说每个子元素都会被返回出去两次// 也就是说假如有 2 个子元素 c1 c2那么通过调用 React.Children.map(this.props.children, c [c, c]) 后// 返回的应该是 4 个子元素c1 c1 c2 c2if (Array.isArray(mappedChild)) {// 是数组的话就回到最先调用的函数中// 然后回到之前 traverseAllChildrenImpl 摊平数组的问题// 假如 c [[c, c]]当执行这个函数时返回值应该是 [c, c]// 然后 [c, c] 会被当成 children 传入// traverseAllChildrenImpl 内部逻辑判断是数组又会重新递归执行// 所以说即使你的函数是 c [[[[c, c]]]]// 最后也会被递归摊平到 [c, c, c, c]mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c c);} else if (mappedChild ! null) {// 不是数组且返回值不为空判断返回值是否为有效的 Element// 是的话就把这个元素 clone 一遍并且替换掉 keyif (isValidElement(mappedChild)) {mappedChild cloneAndReplaceKey(mappedChild,// Keep both the (mapped) and old keys if they differ, just as // traverseAllChildren used to do for objects as childrenkeyPrefix (mappedChild.key (!child || child.key ! mappedChild.key)? escapeUserProvidedKey(mappedChild.key) /: ) childKey,);}result.push(mappedChild);} }map 测试map 代码就是上面这些写一个 demo 看看执行过程。class Child extends Component {render() {console.log(this.props.children, this.props.children)const c React.Children.map(this.props.children, c {debuggerreturn c})console.log(mappedChildren, c)return div{c}/div} }export default class Children extends Component {render() {// return 的代码包含 2 种情况children 是和不是数组return (Childdivchildrendasddadasdivchildrendasddadas/divdivchildrendasddadas/div/divdiv keykey2childrendasddadas/divdiv keykey3childrendasddadas/div{[div keykey4childrendasddadas/div,div keykey5childrendasddadas/div,div keykey6:childrendasddadas/div,]}/Child)} }打印的结果如下React.Children.map 就是把传进去的 this.props.children 全部摊平最后返回的一定是一维数组数组中的对象都会添加上 key 属性。对 mappedChildren key 的生成做分析如下。this.props.children 自身是一个数组在第一次调用 traverseAllChildrenImpl 时nextName 为 .0第一个 child 执行 traverseAllChildrenImpl 时invokeCallback 为 truenameSoFar 为 .0再执行 mapSingleChildIntoContext 走到 cloneAndReplaceKey 新 key 生成为 .0因为 (mappedChild.key (!child || child.key ! mappedChild.key) 为 falsekeyPrefix 为空字符串。第二个和第三个 child 的 key 加上了 .$在 traverseAllChildrenImpl 中遍历到第二个和第三个下标时 nextName nextNamePrefix getComponentKey(child, i);nextNamePrefix 是 .i 是 2、3getComponentKey 执行由于它有自己的 key所以 escape 后变成 . $key2 .$key2.$key3 同理。function escape(key) {const escapeRegex /[:]/g;const escaperLookup {: 0, // 替换 :: 2, // 替换 :};const escapedString ( key).replace(escapeRegex, function(match) {return escaperLookup[match];});return $ escapedString; // 返回的字符串前面加上 $ }第四、五、六个是嵌套在数组里面的同上面this.props.children 遍历到这个数组的时候索引为 3。传给下一轮 traverseAllChildrenImpl 的 nameSoFar 为 .3、child 为数组下一 轮traverseAllChildrenImpl children 是一个数组对其进行遍历nextNamePrefix 是 .3:由下面这句计算出来。const nextNamePrefix nameSoFar ? SEPARATOR : nameSoFar SUBSEPARATOR;而 getComponentKey(child, i)由于数组中的每个元素有自己的 key所以返回的是 $key4、 $key50 和 $key62拼接出来就是 .3:$key4 、.3:$key50、.3:$key62这里第五、六个的 和 : 被 escape 处理成了 0 和 2。上面例子代码 debugger 时的调用栈下面贴一张 map 的流程图。forEach类似 array.forEach。和 map 的不同之处是传给 getPooledTraverseContext 的参数 result 为 null因为 forEach 只需要遍历不需要返回一个数组。另外 traverseAllChildren 它的第二个参数变成了 forEachSingleChild。它没有 map 那么复杂。forEachChildren调用 traverseAllChildren 让每个 child 都被放到 forEachSingleChild 中执行/*** Iterates through children that are typically specified as props.children.* The provided forEachFunc(child, index) will be called for each* leaf child.** param {?*} children Children tree container. this.props.children* param {function(*, int)} forEachFunc 遍历函数* param {*} forEachContext Context for forEachContext. 遍历函数的上下文*/ function forEachChildren(children, forEachFunc, forEachContext) {if (children null) {return children;}const traverseContext getPooledTraverseContext(null,null,forEachFunc,forEachContext,);traverseAllChildren(children, forEachSingleChild, traverseContext);releaseTraverseContext(traverseContext); }forEachSingleChild把 children 中的每个元素放到 func 中执行/*** 把 children 中的每个元素放到 func 中执行** param bookKeeping traverseContext* param child 单个可 render child* param name 这里没有用到*/ function forEachSingleChild(bookKeeping, child, name) {const {func, context} bookKeeping;func.call(context, child, bookKeeping.count); }count计算 children 的个数计算的是摊平后数组元素的个数countChildrentraverseAllChildren 有一个返回值 subtreeCount表示子节点的个数traverseAllChildren 遍历所有 child 之后subtreeCount 会统计出结果。/*** 计算 children 的个数计算的是摊平后数组元素的个数* Count the number of children that are typically specified as* props.children.** param {?*} children Children tree container.* return {number} The number of children.*/ function countChildren(children) {return traverseAllChildren(children, () null, null); }toArray同 mapChildren(children, child child, context)/*** 是 mapChildren(children, child child, context) 版本* Flatten a children object (typically specified as props.children) and* return an array with appropriately re-keyed children.*/ function toArray(children) {const result [];mapIntoWithKeyPrefixInternal(children, result, null, child child);return result; }only如果参数是一个 ReactElement则直接返回它否则报错用在测试中正式代码没什么用。/*** Returns the first child in a collection of children and verifies that there* is only one child in the collection.* The current implementation of this function assumes that a single child gets* passed without a wrapper, but the purpose of this helper function is to* abstract away the particular structure of children.** param {?object} children Child collection structure.* return {ReactElement} The first and only ReactElement contained in the* structure.*/ function onlyChild(children) {invariant(isValidElement(children),React.Children.only expected to receive a single React element child.,);return children; }function isValidElement(object) {return (typeof object object object ! null object.$$typeof REACT_ELEMENT_TYPE); }导出的函数中map 是最复杂的把每个函数的意义和签名都读懂之后我对整体有了比较深的认识。看一看流程图整个过程就清楚了。
http://www.zqtcl.cn/news/448918/

相关文章:

  • 网站制作与维护费用wordpress文章页不显示侧边
  • 嘉兴网站建设正规公司做室内设计人喜欢的网站
  • 入侵dedecms网站管理员密码百度注册域名免费建站
  • 找晚上做的工作去哪个网站企业开发软件公司拓展方案
  • 济宁建站公司wordpress博客入门
  • 做外贸需要网站wordpress app 打包
  • 免费网站站长查询丽水微信网站建设公司
  • 广州品牌网站建设先做网站 先备案
  • jsp系统网站建设带源代码梧州网页设计
  • 二手书籍交易网站开发方式关键词seo排名优化如何
  • 陕西西安潍坊网站seo外包
  • 计算机专业网站开发开题报告网站推广营销怎么做
  • 比较大的做网站的公司电影网站盗链怎么做
  • 江苏响应式网站建设哪里有台州网站制作方案
  • 深圳设计网站有哪些展览展会策划公司
  • 微信生活门户网站源码河北建设厅网站初始密码
  • 企业如何做网站推广成都外贸网站建设
  • 网页设计 网站建设 哪个好佛山网站建设推广服务
  • 东莞网站建设技术支持产品推广怎么写
  • 银川app购物网站制作公司网站建设怎样提升形象与品牌价值
  • 中山城市建设集团网站信誉好的邯郸网站建设
  • 做网站很赚钱吗贵阳网站建设费用
  • 设计网站的关键点用ps做招生网站
  • 制作网站公司服务器租赁一年的费用网页动画是如何制作出来的
  • 佛山网站优化有莱芜房产网新房
  • 西安英文旅游网站建设中国建筑工程门户商城
  • 山东企业建站软件购物网站是多少
  • 外链收录网站语音识别程序代做网站
  • 天津平台网站建设公司wordpress删除页头页尾
  • 网站加入站长统计wordpress设置手机浏览器