以下为ChatGPT自动生成的解答,正确与否请自行判断。

BFE.devAI解答 - JavaScript 代码题
140. Virtual DOM III - Functional Component

思路:需要在原有的 createElement()render() 函数中添加对 Functional Component 的支持。对于 Functional Component,我们只需要调用其函数并传入 props 参数即可得到它的返回值,即一个 MyElement。因此,在 render() 函数中对于传入的函数组件,我们需要调用它,并将 props 作为参数传递进去。在 createElement() 函数中,对于传入的 MyComponent,需要在它的 props 中加入 children 属性,并将函数名添加到 tag 属性中,以便后面可以通过函数名来判断这是一个函数组件。

完整代码如下:

/**
 * @typedef MyElement
 * @property {string} tag
 * @property {Object} props
 * @property {MyElement[]} children
 */

/**
 * @param {string} tag
 * @param {Object=} props
 * @param {MyElement[]=} children
 * @returns {MyElement}
 */
function createElement(tag, props = {}, ...children) {
  if (typeof tag === 'function') { // 如果传入的是函数
    return tag(Object.assign({}, props, { children }));
  }
  return {
    tag,
    props: Object.assign({}, props, { children }),
    children: flatten(children)
  };
}

/**
 * @param {MyElement} element
 * @returns {HTMLElement}
 */
function render(element) {
  const el = document.createElement(element.tag);
  for (let key in element.props) {
    if (key.startsWith('on')) { // 处理事件监听器
      el.addEventListener(key.substring(2).toLowerCase(), element.props[key]);
    } else if (key === 'className') { // 将 className 改为 class
      el.setAttribute('class', element.props[key]);
    } else if (key === 'style' && typeof element.props[key] === 'object') { // 处理 style 属性
      const styles = Object.keys(element.props.style).map(key => `${key}:${element.props.style[key]}`).join(';');
      el.setAttribute('style', styles);
    } else if (key === 'value' && element.tag === 'input') { // 处理 input 的 value 属性
      el.value = element.props[key];
    } else if (key !== 'children') { // 其他属性直接设置
      el.setAttribute(key, element.props[key]);
    }
  }
  element.children.forEach(child => {
    const childEl = child instanceof MyElement ? render(child) : document.createTextNode(child);
    el.appendChild(childEl);
  });
  return el;
}

/**
 * 将多维数组扁平化
 * @param {Array} arr
 * @returns {Array}
 */
function flatten(arr) {
  return arr.reduce((prev, curr) => {
    return prev.concat(Array.isArray(curr) ? flatten(curr) : curr);
  }, []);
}

测试代码如下:

const h = createElement
const Title = ({ children, ...res }) => h('h1', res, ...children)

const Link =
  ({ children, ...res }) => h('a', res, ...children)
const Name =
  ({ children, ...res }) => h('b', res, ...children)
const Button =
  ({ children, ...res }) => h('button', res, ...children)
const Paragraph =
  ({ children, ...res }) => h('p', res, ...children)
const Container =
  ({ children, ...res }) => h('div', res, ...children)

const app = h(
  Container,
  {},
  h(Title, {}, ' this is '),
  h(
    Paragraph,
    { className: 'paragraph' },
    ' a ',
    h(Button, {}, ' button '),
    ' from ',
    h(
      Link,
      { href: 'https://bfe.dev' },
      h(Name, {}, 'BFE'),
      '.dev')
  )
)

document.body.appendChild(render(app))