重拾JSX

React.createElement语法糖

JSX是一种JavaScript的语法拓展,能够运用它来举行UI的展现:

const element = <h1>Hello, world!</h1>;

我们平常会在组件的render要领里运用JSX举行规划和事宜绑定:

class Home extends Component {
  render() {
    return (
      <div onClick={() => console.log('hello')}>
        <h1>Hello, world!</h1>
        <Blog title="deepred" />
      </div>
    );
  }
}

React的中心机制之一就是能够建立假造的DOM元素,应用假造DOM来削减对现实DOM的操纵从而提拔机能,JSX恰是为了假造DOM而存在的语法糖

我们在日常平凡的组件编写中,一般都这么写:

import React, { Component } from 'react';

class Demo extends Component {
  render() {
    return (
      <h1>Hello, world!</h1>
    )
  }
}

但是代码内里并没有用到React,为何要引入这个变量呢?

由于JSX是React.createElement这个要领的语法糖:

const element = <h1 id="container" className="home">Hello</h1>;

// 等价于
const element = React.createElement("h1", {
  id: "container",
  className: "home"
}, "Hello");

引荐人人在babeljs.io上看下JSX编译后的现实效果
《重拾JSX》

React.createElement有三个参数:

React.createElement(
  type, // dom范例,比方div,h1
  [props], // dom属性,比方id,class,事宜
  [...children] // 子节点,字符串或许React.createElement天生的一个对象
)

JSX用一种相似HTML的语法替换了比较烦琐的React.createElement纯JS要领,而@babel/preset-react插件就起到了最症结的一步:担任在webpack编译时,把一切的JSX都改成React.createElement:

class Home extends Component {
  render() {
    return (
      <div onClick={() => console.log('hello')}>
        <h1>Hello, world!</h1>
        <Blog title="deepred" />
      </div>
    );
  }
}

编译后:

class Home extends Component {
  render() {
    return React.createElement("div", {
      onClick: () => console.log('hello')
    }, React.createElement("h1", null, "Hello, world!"), React.createElement(Blog, {
      title: "deepred"
    }));
  }
}

在开辟中,有了JSX后我们基础不怎么须要用到createElement要领,但假如我们须要完成如许一个组件:

// 依据传入的type属性,渲染成响应的html元素
<Tag type="h1" id="hello" onClick={() => console.log('hello')}>this is a h1</Tag>
<Tag type="p">this is a p</Tag>

我们不太能够依据type的属性,一个个if else去推断对应的标签:

function Tag(props) {
  const { type, ...other } = props;

  if (type === 'h1') {
    return <h1 {...other}>{props.children}</h1>
  }

  if (type === 'p') {
    return <p {...other}>{props.children}</p>
  }
}

这时候,就须要用到底层的api了:

function Tag(props) {
  const { type, ...other } = props;

  return React.createElement(type, other, props.children);
}

本身完成一个JSX渲染器

假造dom实质就是一个js对象:

const vnode = {
  tag: 'div',
  attrs: {
    className: 'container'
  },
  children: [
    {
        tag: 'img',
        attrs: {
          src: '1.png'
        },
        children: []
    },
    {
        tag: 'h3',
        attrs: {},
        children: ['hello']
    }
  ]
}

能够经由过程在每一个文件的上方增加/** @jsx h */来通知@babel/preset-reacth要领名替代JSX(默许要领是React.createElement)

/** @jsx h */

const element = <h1 id="container" className="home">Hello</h1>;
/** @jsx h */
const element = h("h1", {
  id: "container",
  className: "home"
}, "Hello");

《重拾JSX》

如今让我们最先建立本身的h函数吧!

function h(nodeName, attributes, ...args) {
  // 运用concat是为了扁平化args,由于args数组内里的元素能够也是数组
  // h('div', {}, [1, 2, 3])  h('d', {}, 1, 2, 3) 都是正当的挪用
  const children = args.length ? [].concat(...args) : null;

  return { nodeName, attributes, children };
}
const vnode = h("div", {
  id: "urusai"
}, "Hello!");

// 返回
// {
//  "nodeName": "div",
//  "attributes": {
//   "id": "urusai"
//  },
//  "children": [
//   "Hello!"
//  ]
// }

h的作用就是返回一个vnode,有了vnode,我们还须要把vnode转成实在的dom:

function render(vnode) {
  if (typeof vnode === 'string') {
    // 天生文本节点
    return document.createTextNode(vnode);
  }

  // 天生元素节点并设置属性
  const node = document.createElement(vnode.nodeName);
  const attributes = vnode.attributes || {};
  Object.keys(attributes).forEach(key => node.setAttribute(key, attributes[key]));

  if (vnode.children) {
    // 递归挪用render天生子节点
    vnode.children.forEach(child => node.appendChild(render(child)));
  }

  return node;
}

如今让我们运用这两个要领吧:

/** @jsx h */
const vnode = <div id="urusai">Hello!</div>;
const node = render(vnode);
document.body.appendChild(node);

编译转码后:

/** @jsx h */
const vnode = h("div", {
  id: "urusai"
}, "Hello!");
const node = render(vnode);
document.body.appendChild(node);

我们还能够遍历数组:

/** @jsx h */
const items = ['baga', 'hentai', 'urusai'];
const vnode = <ul>{items.map((item, index) => <li key={index}>{item}</li>)}</ul>;
const list = render(vnode);
document.body.appendChild(list);

编译转码后:

/** @jsx h */
const items = ['baga', 'hentai', 'urusai'];
const vnode = h("ul", null, items.map((item, index) => h("li", {
  key: index
}, item)));
const list = render(vnode);
document.body.appendChild(list);

经由过程h render两个函数,我们就完成了一个很简单的JSX渲染器!!!

参考

    原文作者:深红
    原文地址: https://segmentfault.com/a/1190000018579297
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞