导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

业务技能

针对性攻坚

AI


JSX 是 createElement 的语法糖

JSX 经过编译,变成 React.createElement 调用形式

App1JSX是createElement的语法糖.jsx

class App extends React.Component {
  render() {
    return (
      // <div id="J_Box" className="box">
      //   <h1 className="title">
      //     This is a <span>TITLE</span>
      //   </h1>
      // </div>
      React.createElement(
        'div',
        {
          className: 'box',
          id: "J_Box"
        },
        React.createElement(
          'h1',
          {
            className: 'title'
          },
          'This is a ',
          React.createElement(
            'span',
            null,
            'TITLE'
          )
        )
      )
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

React 元素类型

App2React元素类型.jsx

class MyButton extends React.Component {
  render() {
    return (
      <button>Click</button>
    )
  }
}
class App extends React.Component {
  render() {
    return (

      /**
       * React 元素类型
       * MyButton -> React 元素
       * 用到了一个 JSX 的组件,那么这个组件必须存在在当前模块的作用域中
       * React 通过编译 JSX,将其转为 React.createElement 调用形式
       *    React 要使用 createElement ,就必须让 React 库存在在当前的模块作用域中
       *    开发环境: import React from 'react';
       *    生产环境: 在 index.html 的 script 的 src 中引入 React CDN
       *             不需要 import React ,因为此时 React 是挂载到全局的
       */
      <MyButton />
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

使用点语法

React 允许你把“组件”当作对象的属性来用

App3使用点语法.jsx

const colorSystem = {
  'success': 'green',
  'primary': 'blue',
  'warning': 'orange',
  'danger': 'red'
}
const MyUI = {
  // 类组件
  Button: class extends React.Component {
    render() {
      const { type, children } = this.props
      return (
        <button
          style={{
            color: '#fff',
            backgroundColor: colorSystem[type]
          }}
        >{ children }</button>
      )
    }
  },
  // 函数组件
  input: function(props) {
    const { placeholder, changeValue } = props
    return (
      <input type="text"
        placeholder={ placeholder }
        onChange={ e => changeValue(e) } />
    )
  }
}
class App extends React.Component {
  changeValue(e) {
    console.log(e.target.value)
  }
  render() {
    return (
      <>
        **<MyUI.Button** type="danger">Click</MyUI.Button>
        **<MyUI.input** placeholder="请输入..." changeValue={ this.changeValue.bind(this) }></MyUI.input>
      </>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

书写规范

  1. 小写字母开头代表 HTML 的内置组件(<div><h1>
    1. 标签转换为 'div'、'h1' -> 作为 React.createElement 的第一个参数(React.createElement('div'))
  2. 大写字母开头的自定义组件 <MyButton />
    1. 编辑为 React.createElement(MyButton)

运行时选择 React 类型

App4运行时才选择React组件类型.jsx

class LoginBtnGroup extends React.Component {
  render() {
    return (
      <div>
        <button>登录</button>
        <button>注册</button>
      </div>
    )
  }
}
class WelcomeInfo extends React.Component {
  render() {
    return (
      <div>
        <h1>欢迎您, { this.props.username }</h1>
      </div>
    )
  }
}
class Header extends React.Component {
  static components = {
    'login': LoginBtnGroup,
    'welcome': WelcomeInfo
  }
  render() {
    const HeaderUser = Header.components[this.props.type]
    return (
      // <components[this.props.type] { ...this.props } /> // 这样写会报错
      <HeaderUser { ...this.props } />
    )
  }
}
class App extends React.Component {
  render() {
    return (
      <Header
        type="welcome"
        username="Lance"
      />
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

JSX 中的 props

Props

App5JSX中的props.jsx 01

function MyTitle(props) {
  const { title, author } = props
  return (
    <div>
      <h1>{ title }</h1>
      <p>{ author }</p>
    </div>
  )
}
class App extends React.Component {
  render() {
    return (
      <MyTitle
        title="This is a title"
        author="Lance"
      />
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

表达式与非表达式

App5JSX中的props.jsx 02

class App extends React.Component {
  state = {
    mainTitle: 'This is a MAIN TITLE',
    subTitle: 'This is a SUB TITLE',
    titleShow: 'main'
  }
  render() {
    let title = ''
    // if (this.state.titleShow === 'sub') {
    //   title = <h2>{ this.state.subTitle }</h2>
    // } else if (this.state.titleShow === 'main') {
    //   title = <h1>{ this.state.mainTitle }</h1>
    // } else {
    //   title = <h3>This is no TITLE</h3>
    // }
    switch (this.state.titleShow) {
      case 'main':
        title = <h1>{ this.state.mainTitle }</h1>
        break;
      case 'sub':
        title = <h2>{ this.state.subTitle }</h2>
        break
      default:
        title = <h3>This is no TITLE</h3>
        break;
    }
    return (
      <div>
        { title }
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

class App extends React.Component {
  state = {
    mainTitle: 'This is a MAIN TITLE',
    subTitle: 'This is a SUB TITLE',
    titleShow: 'main'
  }
  render() {
    return (
      <div>
        {
          this.state.titleShow === 'main'
            ? <h1>{ this.state.mainTitle }</h1>
            : <h2>{ this.state.subTitle }</h2>
        }
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

字面量字符串

App5JSX中的props.jsx 03

function MyTitle(props) {
  const { title, author } = props
  return (
    <div>
      <h1>{ title }</h1>
      <p>{ author }</p>
    </div>
  )
}
class App extends React.Component {
  render() {
    return (
      <>
        <MyTitle
          title="这是标题"
          author="Lance"
        />
        <MyTitle
          title={ '这是标题' }
          author={ 'Lance' }
        />
        <MyTitle
          title="<这是标题>"
          // JS表达式中,会准转义HTML实体(渲染的还是 &lt; 而不是 <)
          author={ '&lt;Lance&gt;' }
        />
        <MyTitle
          // 字符串字面量,不会转义HTML实体(会显示 < 而不是 &lt;)
          title="&lt;这是标题&gt;"
          author={ '<Lance>' }
        />
      </>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

Boolean 问题

App5JSX中的props.jsx 04

function MyTitle(props) {
  const { title, author, authorShow } = props
  return (
    <div>
      <h1>{ title }</h1>
      {
        // 真假 boolean
        authorShow === true
          ? <p>{ author }</p>
          : ''
      }
    </div>
  )
}
class App extends React.Component {
  render() {
    return (
      <>
        <MyTitle
          title="这是标题"
          author="Lance"
          // 不推荐:字符串true,false代表字符串,不代表真假
          // authorShow="true"
          // 推荐:Boolean 的 true、false 才代表真假
          authorShow={ true }
          // 下面写法OK,但不推荐,有意思是ES6的省略属性值的歧义 {authorShow} => {authorShow: authorShow}
          // authorShow
        />
      </>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

属性展开操作

App5JSX中的props.jsx 05

function MyTitle(props) {
  const { children, title, author, authorShow } = props
  return (
    <div>
      <h1>{ title }</h1>
      {/* 获取 App 标签的 children */}
      <h2>{ children }</h2>
      {
        // 真假 boolean
        authorShow === true
          ? <p>{ author }</p>
          : ''
      }
    </div>
  )
}
class App extends React.Component {
  // props全都要时,不推荐做法
  // render() {
  //   const { title, author, authorShow } = this.props
  //   return (
  //     <>
  //       <MyTitle
  //         // props都要的话,推荐展开符,而不是下面这样
  //         title={ title }
  //         author={ author }
  //         authorShow={ authorShow }
  //       />
  //     </>
  //   )
  // }

  // props全都要时,推荐做法
  // render() {
  //   return (
  //     <>
  //       <MyTitle
  //         // 推荐:
  //         { ...this.props }
  //       />
  //     </>
  //   )
  // }

  // props部分要时,可以排除不要的
  // render() {
  //   const { a, ...others } = this.props
  //   return (
  //     <>
  //       <MyTitle
  //         // 推荐:
  //         { ...others }
  //       />
  //     </>
  //   )
  // }

  // children 传递
  render() {
    // others 包含 children ,因为 children 在 props 里(this.props.children)
    const { a, ...others } = this.props
    return (
      <>
        <MyTitle
          // 推荐:
          { ...others }
        />
      </>
    )
  }
}
ReactDOM.render(
  // <App
  //   title="This is a title"
  //   author="Lance"
  //   authorShow={ true }
  //   a="123" />,
  // children 获取:
  <App
    title="This is a title"
    author="Lance"
    authorShow={ true }
    a="123">
    This is a App
  </App>,
  document.getElementById('app')
)

字符串字面量

  1. 首尾空格自动去掉了
  2. 字符串之间多个空格,压缩为一个空格(想要多个空格,可以用字符实体 &nbsp;
  3. 字符串之间的换行,压缩成一个空格(想要换行,用 <br/>

App6JSX字符串字面量.jsx

class MyTitle extends React.Component {
  render() {
    return (
      <h1>{ this.props.children }</h1>
    )
  }
}
class App extends React.Component {
  render() {
    return (
      // 字符串字面量
      // 1. 首尾空格自动去掉了
      // <MyTitle>  This is a TITLE  </MyTitle>
      // 2. 字符串之间多个空格,压缩为一个空格(想要多个空格,可以用字符实体 &nbsp;)
      // <MyTitle>  This  is a TITLE  </MyTitle>
      // <MyTitle>  This&nbsp;&nbsp;is a TITLE  </MyTitle>
      // 3. 字符串之间的换行,压缩成一个空格(想要换行,用 <br/>)
      // <MyTitle>  This
      //   is a
      //   TITLE  </MyTitle>
      // <MyTitle>  This is a<br/> TITLE  </MyTitle>
      <MyTitle>  { 'This is a &lt;TITLE&gt;' }  </MyTitle>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

列表

App7JSX中的列表.jsx

class MyList extends React.Component {
  render() {
    return (
      <div className={ this.props.listClassName }>
        <h1>{ this.props.listTitle }</h1>
        <ul className="my-list">
          { this.props.children }
        </ul>
      </div>
    )
  }
}
class ListItem extends React.Component {
  render() {
    return (
      <li>{ this.props.children }</li>
    )
  }
}
class ListItems extends React.Component {
  render() {
    return [
      <li key="1">This is my content1</li>,
      <li key="2">This is my content2</li>,
      <li key="3">This is my content3</li>
    ]
  }
}
class ListItems2 extends React.Component {
  render() {
    return this.props.listData.map((item, index) => {
      return <li key={ index }>{ item }</li>
    })
  }
}
class App extends React.Component {
  state = {
    listData: [
      'This is my content1',
      'This is my content2',
      'This is my content3',
    ]
  }
  render() {
    return (
      <MyList
        listClassName="my-list-container"
        listTitle="This is my list"
      >
        {/* {
          this.state.listData.map((item, index) => {
            return <ListItem key={ index }>Hello, { item }</ListItem>
          })
        } */}
        {/* 或者 */}
        {/* <ListItems /> */}

        {/* JSX 作为 JSX 子元素 */}
        <ListItems2 listData={ this.state.listData } />
      </MyList>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

null、undefined、boolean、0

App8null_undefined_boolean.jsx

class App extends React.Component {
  state = {
    data: [],
    show: true
  }
  render() {
    return (
      <div>
        {/*
          null, undefined, bool 都是可以作为 JSX 的子元素
          这些子元素是会被忽略不会渲染的,因为他们可以用来解决条件渲染的问题
        */}
        <div>{ true }</div>
        <div>{ false }</div>
        <div>{ undefined }</div>
        <div>{ null }</div>
        {/* 下面 String(null) 显示 null 字符串 */}
        <div>{ String(null) }</div>

        <div>{
          this.state.show ? 'OK' : '不OK'
        }</div>
        <div>{
          this.state.show && 'OK'
        }</div>

        {/* JSX中,0是会渲染的 */}
          {/* 这样可以,显示 无数据 */}
        <div>{ this.state.data.length ? '有数据' : '无数据' }</div>
          {/* 但这样会显示0 */}
        <div>{ this.state.data.length && '有数据' }</div>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

函数子元素

App9JSX函数子元素的应用于思考.jsx

// JSX函数子元素
/**
 * JSX的 props.children 跟 props 本身有一致性的特性
 * props.children 可以传递任何类型的子元素
 */

class Repeat extends React.Component {
  render() {
    const jsxArr = []

    for (let i = 0; i < this.props.num; i++) {
      jsxArr.push(this.props.children(i))
    }
    return jsxArr
    /**
     * [
     *  <p>This is item 1</p>,
     *  <p>This is item 2</p>,
     *  <p>This is item 3</p>,
     *  ...
     * ]
     */
  }
}

class App extends React.Component {
  render() {
    return (
      <div>
        <Repeat num={ 10 }>
          {
            (index) => <p key={ index }>This is item {index + 1}.</p>
          }
        </Repeat>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

函数子元素的小案例

App9JSX函数子元素的应用于思考2.jsx

// 接口启动地址:<http://localhost:8080/getStudents>
// 接口地址:23高阶组件1/server

import Http from './Http'
class App extends React.Component {
  render() {
    return (
      <table border="1">
        <thead>
          <tr>
            <td>ID</td>
            <th>姓名</th>
            <th>年级</th>
          </tr>
        </thead>
        <tbody>
          {/* 这里用到的 Http.Get 点语法的形式,就类似 React.Provider 的点语法 */}
          <Http.Get
            url="<http://localhost:8080/getStudents>"
            loading={
              <tr>
                <td colSpan="3">正在加载中...</td>
              </tr>
            }
          >
            {
              data => {
                console.log(data)
                return data.map(item => {
                  return <tr key={ item.id }>
                    <td>{ item.id }</td>
                    <td>{ item.name }</td>
                    <td>{ item.grade }</td>
                  </tr>
                })
              }
            }
          </Http.Get>
        </tbody>
      </table>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

Http/GET.jsx

class Get extends React.Component {
  state = {
    data: [],
    component: this.props.loading || 'Loading'
  }
  async componentDidMount() {
    const result = await axios(this.props.url)
    this.setState({
      data: result.data
    }, () => {
        this.setState({
          component: this.props.children(this.state.data)
        })
    })
  }
  render() {
    return this.state.component
  }
}
export default Get

Http/index.jsx

import Get from './Get'
export default {
  Get
}