From 69826ba80ff92df23f2174b8230077d8accdcc85 Mon Sep 17 00:00:00 2001 From: laosu Date: Mon, 6 Apr 2020 17:28:38 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E8=AF=BB=E6=BA=90=E7=A0=81=E6=90=9E?= =?UTF-8?q?=E4=B8=8D=E4=B8=8B=E5=8E=BB=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/react/ReactChildren.js | 181 +++++++++++++++++++++++++++++++++++++ src/react/ReactElement.js | 14 +++ 2 files changed, 195 insertions(+) diff --git a/src/react/ReactChildren.js b/src/react/ReactChildren.js index bbc07d8..3ea8fad 100644 --- a/src/react/ReactChildren.js +++ b/src/react/ReactChildren.js @@ -1,8 +1,189 @@ +import {cloneAndReplaceKey} from './ReactElement' +/** + * @return ".0/.$div0A", ".0/.$div0B", ".$key2/.$div1A", ".$key2/.$div1B", ".$key3/.$div2A", ".$key3/.$div2B", ".3:$key4/.$div3A", ".3:$key4/.$div3B", ".3:$key5/.$div4A", ".3:$key5/.$div4B", ".3:$key6/.$div5A", ".3:$key6/.$div5B" + */ function mapChildren(children, func, context) { //TODO实现此mapChildren方法 + // 1. 扁平化 + let res = [] + let count = 0 // 偏移量 + mapIntoArray(children, res, function(child) { + return func.call(context, child, count++) + }) return children; } +function isValidElement(object) { + return ( + typeof object === 'object' && + object !== null && + object.$$typeof === Symbol.for('react.element') + ); +} +function escape(key) { + // 正则全局匹配'='或者':' + const escapeRegex = /[=:]/g + // 声明一个key: value + const escaperLookup = { + '=': '=0', + ':': '=2' + } + // 替换key + const escapedString = key.replace(escapeRegex, function(match) { + return escaperLookup[match] + }) + + // 拼接'$'返回组装后的key + return '$' + escaperLookup + +} +// 获取key +// 格式如: ".0/.$div0A", ".0/.$div0B", ".$key2/.$div1A", ".$key2/.$div1B", ".$key3/.$div2A", ".$key3/.$div2B", ".3:$key4/.$div3A", ".3:$key4/.$div3B", ".3:$key5/.$div4A", ".3:$key5/.$div4B", ".3:$key6/.$div5A", ".3:$key6/.$div5B" +function getElementKey(element, index) { + // 有key的情况 + if(typeof element === 'object' && element !== null && element.key != null) { + return escape('' + element.key) + } + // 无key 返回 Number.prototy.toString(36) // 转成36进制string返回 + return index.toString(36) +} +function escapeUserProvidedKey(text) { + return text.replace(/\/+/g, '$&/'); +} +function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) { + const type = typeof children + if(type === 'undefined' || type === 'boolean') + children = null + // 触发回调事件条件 + let invokeCallback = false + // 没有字节点 + if(children === null) { + invokeCallback = true + } else { + // 字符串,数字基本及react自定义节点 + switch (type) { + case 'string': + case 'number': + invokeCallback = true + break; + case 'object': + // $$typeof: Symbol(react.element) + switch (children.$$typeof) { + case Symbol.for('react.element'): + case Symbol.for('react.portal'): + invokeCallback = true + } + default: + break; + } + } + + if(invokeCallback) { + const child = children + let mappedChild = callback(child) + const childKey = + nameSoFar === '' ? '.' + getElementKey(child, 0) : nameSoFar; + if (Array.isArray(mappedChild)) { + let escapedChildKey = ''; + if (childKey != null) { + escapedChildKey = escapeUserProvidedKey(childKey) + '/'; + } + // 递归 + mapIntoArray(mappedChild, array, escapedChildKey, '', c => c); + } else if(mappedChild != null) { + // 如果是React节点 + if(isValidElement(mappedChild)){ + mappedChild = cloneAndReplaceKey( + mappedChild, + escapedPrefix + + (mappedChild.key && (!child || child.key !== mappedChild.key) + ? escapeUserProvidedKey('' + mappedChild.key) + '/' + : '') + + childKey, + ); + } + array.push(mappedChild) + } + return 1 + } + let child // 临时节点 + let nextName // key + let subtreeCount = 0 // 索引偏移量 + const nextNamePrefix = + nameSoFar === '' ? '.' : nameSoFar + ':'; + // 数组 + if(Array.isArray(children)) { + console.log('child', children); + for(let i = 0; i < children.length; i++) { + child = children[i] + nextName = nextNamePrefix + getElementKey(child, i); + subtreeCount += mapIntoArray( + child, + array, + escapedPrefix, + nextName, + callback + ) + } + } else { + // 看不懂了。 + + const iteratorFn = getIteratorFn(children); + // 函数组件吗? + if(typeof iteratorFn === 'function') { + const iterableChildren = children + const iterator = iteratorFn.call(iterableChildren) + let step; + let ii = 0 + // 这个里面有迭代器 + while(!(step = iterator.next()).done) { + child = step.value + nextName = nextNamePrefix + getElementKey(child, ii++); + subtreeCount += mapIntoArray( + child, + array, + escapedPrefix, + nextName, + callback, + ); + } + } + // 普通组件? + else if(type === 'object') { + let addendum = ''; + const childrenString = '' + children; + // 看不懂这个invariant + /* + invariant( + false, + 'Objects are not valid as a React child (found: %s).%s', + childrenString === '[object Object]' ? 'object with keys {' + Object.keys((children: any)).join(', ') + '}' + : childrenString, + addendum, + ); + */ + } + } + // 返回展开节点数 + return subtreeCount +} +function getIteratorFn(maybeIterable) { + const MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; + const FAUX_ITERATOR_SYMBOL = '@@iterator'; + // 节点不是对象或者null 返回 null + if (maybeIterable === null || typeof maybeIterable !== 'object') { + return null; + } + // 不太理解这块 + const maybeIterator = + (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || + maybeIterable[FAUX_ITERATOR_SYMBOL]; + // 节点是函数,返回处理过的maybeIterator + if (typeof maybeIterator === 'function') { + return maybeIterator; + } + return null +} export { mapChildren as map, }; \ No newline at end of file diff --git a/src/react/ReactElement.js b/src/react/ReactElement.js index 25fdf5e..888090b 100644 --- a/src/react/ReactElement.js +++ b/src/react/ReactElement.js @@ -71,4 +71,18 @@ function ReactElement(type, key, ref, _self, _source, _owner, props) { _source } return element; +} + +export function cloneAndReplaceKey(oldElement, newKey) { + const newElement = ReactElement( + oldElement.type, + newKey, + oldElement.ref, + oldElement._self, + oldElement._source, + oldElement._owner, + oldElement.props, + ); + + return newElement; } \ No newline at end of file -- Gitee