最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 实现简易版的virtual Dom | 刷题打卡

    正文概述 掘金(Djmughal)   2021-03-08   332

    题目描述

    HTML可以被当作某种意义上的 序列化→浏览器解释(反序列化)HTML文本→ 然后构建DOM tree。
    除了基于XML的方案,我们也可以试试JSON。如果打印出React中对element的表示,像这样:

    const el = <div>
     <h1> this is </h1>
     <p className="paragraph"> a <button> button </button> from <a href="https://bfe.dev"><b>BFE</b>.dev</a>
     </p>
    </div>;
    
    console.log(el)
    

    得到如下数据结构:

    {
      type: 'div',
      props: {
        children: [
          {
            type: 'h1',
            props: {
              children: ' this is '
            }
          },
          {
            type: 'p',
            props: {
              className: 'paragraph',
              children: [
                ' a ',
                {
                  type: 'button',
                  props: {
                    children: ' button '
                  }
                },
                ' from',
                {
                  type: 'a',
                  props: {
                    href: 'https://bfe.dev',
                    children: [
                      {
                        type: 'b',
                        props: {
                          children: 'BFE'
                        }
                      },
                      '.dev'
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
    }
    

    题目链接

    思路分析

    要实现jsx <=> virtual dom,上来不太好判断难度,从处理数据生成html的流程更符合平时开发逻辑,就从 vdom -> jsx 开始吧!
    最直观的规律,virtual dom 是一棵树,我们需要确定父子关系

    • 一个 type 对应 *个children
    • children 包含字符串节点和带标签的节点
    • 一个 type 对应 一个 props
    • props 中除了 children 其余都是元素上的属性

    以上4条理顺了,就可以开始写代码了

    function render(json) {
      // textNode
      if (typeof json === 'string') {
        return document.createTextNode(json);
      }
      
      // element
      const {type, props: {children, ...attrs}} = json;
      // 出现 type 就创建一个节点
      const element = document.createElement(type);
      
      // 给节点加上属性
      Object.entries(attrs).forEach(([attr, value]) => element[attr] = value);
      
      // 把 children 添加到节点内
      const childrenArr = Array.isArray(children) ? children : [children];
      childrenArr.forEach(child => element.append(render(child)));
      
      return element;
    }
    

    然后需要完成jsx => vdom,第一反应是 vdom 数据结构(长相)和 jsx 是一样的,是对数据结构进行扩展
    大致应该是这样

    function virtualize(element) {
      const result = {
        type: element.tagName,
        props: {}
      };
      
      // 先添加属性
      element.attributes.forEach... props[k] = v;
      
      // 再添加 children,循环节点递归(添加属性和children)
      element.childNodes.forEach... props.children = newChildren
    }
    

    顺着这个思路把代码补全:

    function virtualize(element) {
      const result = {
        type: element.tagName.toLowerCase(),
        props: {}
      }
    
      // attrs
      element.attributes.forEach(attr => {
        const name = attr.name === 'class' ? 'className' : attr.name;
        result.props[name] = attr.value;
      });
      
      // children
      const children = [];
      element.childNodes.forEach(node => {
        if (node.nodeType === 3) {
          children.push(node.textContent);
        } else {
          children.push(virtualize(node));
        }
      });
      
      result.props.children = children.length === 1 ? children[0] : children;
    
      return result;
    }
    

    AC代码

    function render(json) {
      // textNode
      if (typeof json === 'string') {
        return document.createTextNode(json);
      }
      
      // element
      const {type, props: {children, ...attrs}} = json;
      // 出现 type 就创建一个节点
      const element = document.createElement(type);
      
      // 给节点加上属性
      Object.entries(attrs).forEach(([attr, value]) => element[attr] = value);
      
      // 把 children 添加到节点内
      const childrenArr = Array.isArray(children) ? children : [children];
      childrenArr.forEach(child => element.append(render(child)));
      
      return element;
    }
    
    function virtualize(element) {
      const result = {
        type: element.tagName.toLowerCase(),
        props: {}
      }
    
      // attrs
      element.attributes.forEach(attr => {
        const name = attr.name === 'class' ? 'className' : attr.name;
        result.props[name] = attr.value;
      });
      
      // children
      const children = [];
      element.childNodes.forEach(node => {
        if (node.nodeType === 3) {
          children.push(node.textContent);
        } else {
          children.push(virtualize(node));
        }
      });
      
      result.props.children = children.length === 1 ? children[0] : children;
    
      return result;
    }
    

    总结

    这是个 vdomjsx 的简单转换,再次加强递归的操作,给之后的 React.createElement 做个铺垫。
    本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情


    起源地下载网 » 实现简易版的virtual Dom | 刷题打卡

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元