React/Jsx

123 阅读1分钟

简介

JSX(Javascript Syntax Extension)是 JavaScrip 的一种扩展语法,使我们能用类似HTML的形式去编写JS。

作用

JSX并不是React等框架的必要条件。它能定义简洁且我们熟悉的包含属性的树状结构语法,相对于方法调用和对象字面量的书写形式,更易于阅读。

使用方法

自定义组件(classes)的标签名需以大写字母开头,除此以外所有标签都按照HTML标签(string)渲染。JSX本身还是JavaScript,所以classfor等标识符不能用作标签属性名。可以使用className``htmlFor来代替。

// 渲染HTML
const divEl = <div className="demo">demo</div>
React.render(divEl, document.body);

// 渲染自定义组件
const myComp = <MyComp sthProps={true} />
React.render(myComp, document.body);
复制代码

JavaScript表达式

// 属性表达式
const a = <div id={window.inited && "active"}></div>;

// 子节点表达式
const content = <div>{ window.inited ? <img src="" /> : <p>loading</p> }</div>;

// 遍历
const list = <ul>{ [0,1,2].map(it=><li>{it}</li>) }</ul>
复制代码

Babel与JSX

React发明了JSX,利用HTML语法来穿件虚拟DOM。当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析。Babel 是React团队选择的在使用React过程中转换ES5以上和JSX为ES5语句的工具。

通过babel预编译和解析后,将结果通过React.createElement完成替换,继而创建成一个个包含节点所有信息的reactElement,即虚拟DOM

React.createElement的源码中做了如下几件事

  • 处理config,把除了保留属性外的其他config赋值给props
  • 把children处理后赋值给props.children
  • 处理defaultProps
  • 调用ReactElement返回一个jsx对象(virtual-dom)
/**
React的创建元素方法
*/
export function createElement(type, config, children) {
  // propName 变量用于储存后面需要用到的元素属性
  let propName;

  // props 变量用于储存元素属性的键值对集合
  const props = {};

  // key、ref、self、source 均为 React 元素的属性,此处不必深究
  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  // config 对象中存储的是元素的属性
  if (config != null) {
    // 进来之后做的第一件事,是依次对 ref、key、self 和 source 属性赋值
    if (hasValidRef(config)) {
      ref = config.ref;
    }

    // 此处将 key 值字符串化
    if (hasValidKey(config)) {
      key = "" + config.key;
    }
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;

    // 接着就是要把 config 里面的属性都一个一个挪到 props 这个之前声明好的对象里面
    for (propName in config) {
      // 筛选出可以提进 props 对象里的属性
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
  }

  // childrenLength 指的是当前元素的子元素的个数,减去的 2 是 type 和 config 两个参数占用的长度
  const childrenLength = arguments.length - 2;

  // 如果抛去type和config,就只剩下一个参数,一般意味着文本节点出现了
  if (childrenLength === 1) {
    // 直接把这个参数的值赋给props.children
    props.children = children;

    // 处理嵌套多个子元素的情况
  } else if (childrenLength > 1) {
    // 声明一个子元素数组
    const childArray = Array(childrenLength);

    // 把子元素推进数组里
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }

    // 最后把这个数组赋值给props.children
    props.children = childArray;
  }

  // 处理 defaultProps
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }

  // 最后返回一个调用ReactElement执行方法,并传入刚才处理过的参数
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props
  );
}

function ReactElement(type, key, ref, self, source, owner, props) {
  const element = {
    $$typeof: REACT_ELEMENT_TYPE, //表示是ReactElement类型
    type: type, //class或function
    key: key, //key
    ref: ref, //ref属性
    props: props, //props
    _owner: owner,
  };

  return element;
}

作者:想当厨子的小左
链接:juejin.cn/post/715942…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。