华光尊者 發表於 2019-7-23 20:14:00

没有用到React,为什么我需要import引入React?

<h1>没有用到React,为什么我需要import引入React?</h1>
<p>本质上来说JSX是<code>React.createElement(component, props, ...children)</code>方法的语法糖。</p>
<p>所以我们如果使用了JSX,我们其实就是在使用React,所以我们就需要引入React</p>
<h1>前言</h1>
<p>React是前端最受欢迎的框架之一,解读其源码的文章非常多,但是我想从另一个角度去解读React:从零开始实现一个React,从API层面实现React的大部分功能,在这个过程中去探索为什么有虚拟DOM、diff、为什么setState这样设计等问题。</p>
<p>提起React,总是免不了和Vue做一番对比</p>
<p>Vue的API设计非常简洁,但是其实现方式却让人感觉是“魔法”,开发者虽然能马上上手,但其原理却很难说清楚。</p>
<p>相比之下React的设计哲学非常简单,虽然有很多需要自己处理的细节问题,但它没有引入任何新的概念,相对更加的干净和简单。</p>
<h1>关于jsx</h1>
<p>在开始之前,我们有必要搞清楚一些概念。</p>
<p>我们来看一下这样一段代码:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">const title = &lt;h1 className="title"&gt;Hello, world!&lt;/h1&gt;;
</pre>
</div>
<p>  </p>
<p>这段代码并不是合法的js代码,它是一种被称为jsx的语法扩展,通过它我们就可以很方便的在js代码中书写html片段。</p>
<p>本质上,jsx是语法糖,上面这段代码会被babel转换成如下代码:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">const title = React.createElement(
    'h1',
    { className: 'title' },
    'Hello, world!'
);
</pre>
</div>
<p>  </p>
<h1>React.createElement和虚拟DOM</h1>
<p>前文提到,jsx片段会被转译成用<span style="font-size: 16px; background-color: rgba(204, 255, 255, 1)"><strong><code>React.createElement</code></strong></span>方法包裹的代码。所以第一步,我们来实现这个<strong><span style="background-color: rgba(204, 255, 255, 1)"><code>React.createElement</code></span></strong>方法</p>
<p>从jsx转译结果来看,createElement方法的参数是这样:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">createElement( tag, attrs, child1, child2, child3 );
</pre>
</div>
<p>  </p>
<p>第一个参数是DOM节点的标签名,它的值可能是<code>div</code>,<code>h1</code>,<code>span</code>等等<br>第二个参数是一个对象,里面包含了所有的属性,可能包含了<code>className</code>,<code>id</code>等等<br>从第三个参数开始,就是它的子节点</p>
<p>我们对createElement的实现非常简单,只需要返回一个对象来保存它的信息就行了。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">function createElement( tag, attrs, ...children ) {
    return {
      tag,
      attrs,
      children
    }
}
</pre>
</div>
<p>  </p>
<p>函数的参数<code>&nbsp;...children</code>使用了ES6的rest参数,它的作用是将后面child1,child2等参数合并成一个数组children。</p>
<p>现在我们来试试调用它</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">// 将上文定义的createElement方法放到对象React中
const React = {
    createElement
}

const element = (
    &lt;div&gt;
      hello&lt;span&gt;world!&lt;/span&gt;
    &lt;/div&gt;
);
console.log( element );
</pre>
</div>
<p>  打开调试工具,我们可以看到输出的对象和我们预想的一致</p>
<p><img src="https://user-images.githubusercontent.com/13267437/37565774-89d1c788-2aea-11e8-9b2a-e55acaecbe77.png" alt="" width="392" height="221"></p>
<p>我们的createElement方法返回的对象记录了这个DOM节点所有的信息,换言之,通过它我们就可以生成真正的DOM,这个记录信息的对象我们称之为虚拟DOM。</p>
<h1>ReactDOM.render</h1>
<p>接下来是ReactDOM.render方法,我们再来看这段代码</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">ReactDOM.render(
    &lt;h1&gt;Hello, world!&lt;/h1&gt;,
    document.getElementById('root')
);
</pre>
</div>
<p>  经过转换,这段代码变成了这样</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">ReactDOM.render(
    React.createElement( 'h1', null, 'Hello, world!' ),
    document.getElementById('root')
);
</pre>
</div>
<p>  </p>
<p>所以<code>render</code>的第一个参数实际上接受的是<span style="background-color: rgba(204, 255, 255, 1)">createElement</span>返回的对象,也就是虚拟DOM<br>而第二个参数则是挂载的目标DOM</p>
<p>总而言之,render方法的作用就是将虚拟DOM渲染成真实的DOM,下面是它的实现:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">function render( vnode, container ) {
   
    // 当vnode为字符串时,渲染结果是一段文本
    if ( typeof vnode === 'string' ) {
      const textNode = document.createTextNode( vnode );
      return container.appendChild( textNode );
    }

    const dom = document.createElement( vnode.tag );

    if ( vnode.attrs ) {
      Object.keys( vnode.attrs ).forEach( key =&gt; {
            const value = vnode.attrs[ key ];
             setAttribute( dom, key, value );    // 设置属性
      } );
    }

    vnode.children.forEach( child =&gt; render( child, dom ) );    // 递归渲染子节点

    return container.appendChild( dom );    // 将渲染结果挂载到真正的DOM上
}
</pre>
</div>
<p>  设置属性需要考虑一些特殊情况,我们单独将其拿出来作为一个方法setAttribute</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">function setAttribute( dom, name, value ) {
    // 如果属性名是className,则改回class
    if ( name === 'className' ) name = 'class';

    // 如果属性名是onXXX,则是一个事件监听方法
    if ( /on\w+/.test( name ) ) {
      name = name.toLowerCase();
      dom[ name ] = value || '';
    // 如果属性名是style,则更新style对象
    } else if ( name === 'style' ) {
      if ( !value || typeof value === 'string' ) {
            dom.style.cssText = value || '';
      } else if ( value &amp;&amp; typeof value === 'object' ) {
            for ( let name in value ) {
                // 可以通过style={ width: 20 }这种形式来设置样式,可以省略掉单位px
                dom.style[ name ] = typeof value[ name ] === 'number' ? value[ name ] + 'px' : value[ name ];
            }
      }
    // 普通属性则直接更新属性
    } else {
      if ( name in dom ) {
            dom[ name ] = value || '';
      }
      if ( value ) {
            dom.setAttribute( name, value );
      } else {
            dom.removeAttribute( name );
      }
    }
}
</pre>
</div>
<p>  这里其实还有个小问题:当多次调用<code>render</code>函数时,不会清除原来的内容。所以我们将其附加到ReactDOM对象上时,先清除一下挂载目标DOM的内容:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">const ReactDOM = {
    render: ( vnode, container ) =&gt; {
      container.innerHTML = '';
      return render( vnode, container );
    }
}
</pre>
</div>
<p>  </p>
<h1>渲染和更新</h1>
<p>到这里我们已经实现了React最为基础的功能,可以用它来做一些事了。</p>
<p>我们先在index.html中添加一个根节点</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">&lt;div id="root"&gt;&lt;/div&gt;
</pre>
</div>
<p>  我们先来试试官方文档中的Hello,World</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">ReactDOM.render(
    &lt;h1&gt;Hello, world!&lt;/h1&gt;,
    document.getElementById('root')
);
</pre>
</div>
<p>  可以看到结果:</p>
<p><img src="https://user-images.githubusercontent.com/13267437/37565800-1d4e23e4-2aeb-11e8-8349-9b56e96fb569.png" alt="" width="709" height="235"></p>
<h1>&nbsp;</h1><br><br>
来源:https://www.cnblogs.com/zhaohongcheng/p/11234166.html
頁: [1]
查看完整版本: 没有用到React,为什么我需要import引入React?