React - Initial Rendering(初始化渲染)

React - Initial Rendering(初始化渲染)

  • 以下内容均从源码角度分析,UI更新本质上是数据更改(props或者state) = render(state)。React提供了一种直接且直观的方法,所有移动部分都以**状态(states)**的形式聚合,那么问题来了?React究竟是如何渲染的了?内部的渲染原理是什么样的?接下来我们一步一步的分析。
标题内容
JSXJSX是如何生效的?
React.createElementReact.createElement工作原理
ReactDOM.renderReacrDOM.render工作原理

JSX

  • JSX: 一种在JavaScript代码中交织HTML标签的语法。

  • create-react-app创建的应用中的App.js或者可以下载,这就是JSX的写法案例。

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img scr={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;
  • 这段代码能够在浏览器中直接运行生效吗?答案是否定的,那么JSX是如何生效的了,现在开始解答。
  • 在编译时,JSX中定义的组件会被Babel编译成React.createElement()带有适当参数的函数形式。
  • Babel的配置:
{
  "plugins": [
    ["transform-react-jsx", {
      "pragma": "dom" 
    }]
  ]
}
  • 这块这里想简单理解一下,下面会详细介绍一下。

  • 上面的App.js编译成:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return React.createElement(
      'div',
      { className: 'App' },
      React.createElement(
        'header',
        { className: 'App-header' },
        React.createElement('img', { src: logo, className: 'App-logo', alt: 'logo' }),
        React.createElement(
          'h1',
          { className: 'App-title' },
          'Welcome to React'
        )
      ),
      React.createElement(
        'p',
        { className: 'App-intro' },
        'To get started, edit ',
        React.createElement(
          'code',
          null,
          'src/App.js'
        ),
        ' and save to reload.'
      )
    );
  }
}

export default App;
  • 这是浏览器执行的真实代码。
  • 在应用层,App.js这个组件将被渲染:
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <App />,
  document.getElementById('root');
)
  • 这个嵌套的组件树有点过于复杂,不是一个理想的起点,所以我们现在先过滤掉它,转而看一些更加简单的东西——呈现一个简单的HTML元素。
import ReactDOM from 'react-dom';

ReactDOM.render(
  <h1 style={{ "color": "blue" }}>Hello JackDan! Welcome to React!</h1>,
  document.getElementById('root');
)

React.createElement —— 创建一个ReactElement

import ReactDOM from 'react-dom';

ReactDOM.render(React.createElement(
  'h1',
  { style: {"color": "blue"} },
  'Hello JackDan! Welcome to React!'
), document.getElementById('root'));
  • 这个流程是什么样的了?

  • 第一步实际上并没有多大作用。它只是构造了一个ReactElement实例,其中填充了传递给调用堆栈的任何内容。结果数据结构为:

| ReactElement[2] |
| — | — |
| type | TopLevelWrapper |
| props | child | ReactElement[1] |
| | N/A | |

  • 调用栈是:
React.createElement
  |=ReactElement.createElement(type, config, children)
    |-ReactElement(type,...,props)
  • React.createElement(type, config, children)只是
...
var createElement = ReactElement.createElement;
...

var React = {
...
  createElement: createElement.
...
};

module.exports = React;

// React@isomorphic/React.js
  • ReactElement.createElement(type, config, children)进行了两步操作。
  • 第一步: 拷贝config中的元素属性到props
  • 第二步: 拷贝childrenprops.children
  • 第三步: 拷贝type.defaultPropsprops
...
// 第一步: 拷贝`config`中的元素属性到`props`中
if (config !== null) {
  ...过滤掉一下一些无关紧要的config属性...
  // 
  for(propName in config) {
    if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
      props[propName] = config[propName];
    }
  }
}

// 第二步: 拷贝`children`到`props.children`中


const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
  props.children = children; // 只有一个子属性是object
} else if (childrenLength > 1) {
  const childArray = Array(childrenLength);
  for (let i = 0; i < childrenLength; i++) {
    childArray[i] = arguments[i + 2]; // 多个子属性是一个对象数组
  }

  props.children = childArray;
}

// 第三步: 处理默认的props
if (type && type.defaultProps) {
  const defaultProps = type.defaultProps;
  for (propName in defaultProps) {
    if (props[propName] === undefined) {
      props[propName] = defaultProps[propName];
    }
  }
}

return ReactElement(
  type,
  key,
  ref,
  self,
  source,
  ReactCurrentOwner.current,
  props,
);
...

// ReactElement.createElement@isomorphic/classic/element/ReactElement.js
  • 然后ReactElement(type, ..., props)原样复制ReactElement中的typeprops并且返回一个实例.
...
const ReactElement = function(type, key, ref, self, source, owner, props) {
  // 
  $$typeof: REACT_ELEMENT_TYPE,
  type: type, // 源代码中`h1`
  key: key, // 实例代码中还没有声明这个属性
  ref: ref, // 实例代码中还没有声明这个属性
  props: props,
  // props其实有两部分
  // { children: // 示例代码中'Hello JackDan! Welcome to React!', other props: { style: {"color": "blue"} } }
  _owner: owner, // 
}

- 

...

// https://github.com/facebook/react/blob/v15.6.2/src/isomorphic/classic/element/ReactElement.js

ReactDOM.render() - 渲染组件


https://holmeshe.me/bak/understanding-react-js-source-code-initial-rendering-I/

JackDan Thinking

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值