React Element /组件/JSX

  学习React的时候,你可能听到最多的就是要先学习webpack, babel,要先学会配置而后才能学react 等等,一堆的配置就把咱们吓着了,根本就没有心情就学习react了。其实在最开始学习react, 想要了解React 是什么的时候,咱们彻底不用配置,直接用script标签 引入React就能够了, 不过要注意,这里须要引入两个库:React 和ReactDom。React 用来建立UI, ReactDom 负责把React 建立的UI 渲染到浏览器中. 初学react只需下面这个模版就能够了。css

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World</title>
    <!-- 引入React 和 ReactDOM -->
    <script src="https://cdn.bootcss.com/react/16.1.0/umd/react.development.js"></script>
    <script src="https://cdn.bootcss.com/react-dom/16.1.0/umd/react-dom.development.js
    "></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
      // react 代码写在这里
    </script>
  </body>
</html>

  这里,页面中还增长了一个id 为root 的div, 它的做用是告诉ReactDom 要把UI 渲染到什么地方。注意,咱们这里先不使用JSX语法,从而了解一下JSX的由来,或它底层的东西。好了,咱们开始学习React 吧. html

  React Elementreact

  在学习React的时候,遇到的第一个概念可能就是virtual DOM或React element, 它们其实对应的是浏览器DOM 或 HTML元素。在作JS 开发的时候, 你们都知道,DOM 操做很慢,咱们都会尽可能避免操做DOM, 那么怎么才能更快地操做DOM呢?React 提出了virtual DOM的概念, 由于这份DOM 不是在浏览器中,而是在内存中,浏览器中的DOM是真实的DOM,它能直接渲染出来,但内存中的DOM 又和浏览器的DOM 一致,因此叫virtual DOM, 同理,浏览器中的DOM是因为一个个HTML element 组成,那么做为对应,virtual DOM 就是由一个个的React element 组成。概念老是抽象的,咱们写一下代码了解一下。webpack

  React element, 是由React 库提供的createElement 函数建立而来,咱们建立一个React element,css3

let h1Elem = React.createElement('h1', {id: 'recipe', 'data-type': 'title'}, 'Hello World');

  你能够看到 createElement 函数接受三个参数,第一个是type, 咱们要建立哪一种类型的React Element, 在这里,咱们建立了h1 element. 第二个是properties, 这个element 有哪些属性,在这里有两个属性,一个是id, 一个是data-type. 第三个参数是children, 这个元素有哪些子元素。经过分析,咱们也能够猜想出h1 大概长什么样子。 <h1 id=’recipe’  data-type=’title’ ></h1>es6

   这时咱们打开浏览器,刷新一下页面,什么也没有,咱们建立了react element, 到底发生了什么事情呢?这时咱们 console.log(h1Elem)  一下web

  咱们看到它只是一个Js 对象,拥有props,type等等属性, 和html element 没有任何关系, 可是经过它,咱们却准确地描述了 html element 长什么样子,因此它是virtual, 用于指导构造真正的html element.数组

  当React element 多了起来,层层嵌套,就成了一个DOM 树,这就是所谓virtual DOM. createElement 方法怎么才能造成层层嵌套关系呢? 这主要在于这个函数能够接受任意个参数,从第三个参数及其之后的参数,都会当成建立的element 的children 来对待。咱们建立一个ul--li 的结构浏览器

let ulElem = React.createElement(
        'ul', 
        null ,
        React.createElement('li', null, 'JS'),
        React.createElement('li', null, 'React'),
        React.createElement('li', null, 'Redux')
      );

   经过这个createElement 函数,咱们也能够很清楚地知道它表明下面的ul-libabel

  当咱们用React 建立出虚拟Dom, 怎样才能渲染到html 页面中,造成真实的DOM呢?那就用到了react-dom 库了,它里面有一个render 方法, 能够把虚拟DOM 转化成真实的DOM,  用法以下

 ReactDOM.render(ulElem, document.getElementById('root'));

  它接受两个参数,一个是要转化的Element, 一个是渲染到什么地方, 这时咱们刷新浏览器,能够看到3个li, 和咱们用createElement 

建立的元素一一对应。

  React Component

  当咱们写大量的React element  的时候,你会发现有些代码能够共用,就像咱们上面建立的ulElem,能够用到不少地方,这时咱们就想把它们封装起来,这就造成了React component, 组件。在js 中代码封装有两种方法,一种是函数,一种是对象,相应的React 也提供了建立组件的两种方法,一种是类式,类式就是对象封装,一种是函数式。

  类式,则是利用es6 class 语法, 咱们全部的组件都继承自React.Component,在render 函数中返回React Element

      class Content extends React.Component {

        render() {
          return React.createElement('ul', null ,
            React.createElement('li', null, 'JS'),
            React.createElement('li', null, 'React'),
            React.createElement('li', null, 'Redux')
          );
        }
      }

  函数式,就是一个函数,返回React element

      const Content = () => {
        return  React.createElement('ul', null, 
          React.createElement('li', null, 'JS'),
          React.createElement('li', null, 'React'),
          React.createElement('li', null, 'Redux')
        )
      }

  咱们声明了组件之后,怎么使用这个组件呢? 这时要注意,咱们的组件名称(Content),只是至关于createElement 函数中的第一个参数type, 它不是一个React element, 它只是返回一个React element, 咱们仍然须要调用React.createElement 来建立一个React element

      // 咱们建立的组件名Content, 只是至关于createElement函数中的第一个参数type, 它是至关于h1的type, 而不是一个React Elemnet. 利用这个type,仍须要建立组件。
      let content = React.createElement(Content, null, null);
      
      ReactDOM.render(content, document.getElementById('root'));

  这时页面中一样显示三个li, 表示成功。

  总结: 建立组件,只是把一段能够重用的React element 进行封装,从而建立一个自定义的type, 而后再利用该type, 随处均可以建立element 元素,进而达到重用的目的。

  React 组件数据

  这时,你会发现咱们组件全部的数据都是硬绑定的,能不能把数据和 UI的建立分开,从而使咱们的组件更具备通用性,不一样的数据渲染出不一样的内容? 这是能够的,当咱们利用组件建立react element 的时候,第二个参数是null, 能够再回顾一下代码

let content = React.createElement(Content, null, null);

  咱们能够利用这个参数向组件内传递数据,由于这个参数就是表示这个组件的属性,它的形式也是一个键值对的形式,好比咱们把JS, React ,Redux 数据提出来,造成一个数组,

const item = ['JS', 'React', 'Redux'];
// 向组件中传递一个数组数据 item 
let content = React.createElement(Content, {item: item}, null);

  那么咱们组件中怎么获取到这个数据呢?这要分两种状况,类式的组件则是经过this.props获取的,而函数式组件,则是经过传参的形式获取

  先看类式的组件, 在组件中经过this.props 来获取, 咱们能够把this.props 找印出来看一下

      class Content extends React.Component {
        render() {
          // 打印props;
          console.log(this.props);  // {item: Array(3), children: null}
          return React.createElement('ul', null ,
            React.createElement('li', null, 'JS'),
            React.createElement('li', null, 'React'),
            React.createElement('li', null, 'Redux')
          );
        }
      }

  能够看到this.props 获取到了咱们传递的item 数组,那咱们就能够直接使用了数组数据了,这时经过数组的map 渲染li

class Content extends React.Component {
  render() {
    return React.createElement('ul', null ,
      // this.props.item 获取到传递过来的数据
      this.props.item.map((item, index) => 
        React.createElement('li', {key:index}, item)
      )
    );
  }
}

  函数式的组件,则是它自动会获取props做为参数,组件中直接使用props.items 获取到数据

// 自动获取props做为参数。
const Content = (props) => {
  return  React.createElement('ul', null, 
    props.item.map((item, index) => 
      React.createElement('li', {key:index}, item)
    )
  )
}

  JSX

  在上面的代码中,咱们每建立一个React Element 都要调用一次React.createElement 函数,很是繁琐,而且它想表达式的意思只是一个类html的元素,再来看一下咱们建立的h1Elem element 

let h1Elem = React.createElement('h1', {id: 'recipe', 'data-type': 'title'}, 'Hello World');

  它实际上表达的意思就是 <h1 id=’recipe’  data-type=’title’ ></h1>, 若是我能在代码中直接写h1 就行了。这就是能够了,它就是JSX 语法, 能够直接在js 代码中写类html 的语法。React 把createElement 函数做了进一步的封装,提供了JSX语法。为何能直接写呢?其实createElement 函数,和html 元素有一一对应的关系。

  在createElement 函数中,它的第一个参数是type,表示建立什么类型,而在html中,表示什么类型直接用html 标签,<h1></h1>  它就表示h1 类型, 第二个参数表示属性,元素有哪些属性,而在html标签中,有什么属性,就直接写在它的标签中,有多少,写多少, 如 <h1 id='recipe' class='title'></h1>. 第三个参数是children, 在html中表示children更简单,直接写在两个标签内部的内容都是children. 这样一一对应之后,就能够理解JSX 写法的用意了,在心理上写起来就比较舒服了,由于明白了。

  对于组件来讲,它也是同样的,由于组件名称,只是一个type, 仍然须要调用createElement 函数来 建立React Element 元素, 只要使用createElement 的地方,咱们均可以使用类html 语法,如Content组件,<Content></Content>  就表示建立了一个element了。它的属性和children和上面的h1 用法一致。对于Content 组件,若是没有 chilren 属性,能够直接写单标签<Content />.   如今用JSX的语法来书写Content 组件。

class Content extends React.Component {
  render() {
    return (
      <ul>
        {
          this.props.item.map((item, index) => 
            <li key={index}>{item}</li>
          )
        }
      </ul>  
    )
  }
}

// 向组件中传递一个数组数据 item 
let content = <Content item ={item}></Content>

  不管是在组件属性,仍是元素属性中,咱们都使用了{}, 如key={index}. 在jsx 中,{}里面的全部东西都看成js表达式进行解析, React 会把里面的内容进行求值计算。只要写表达式,咱们都要用{} 括起来。 在JSX 中还有另一种属性,就是字符串,它能够直接写如name="sam",除此以外,全部的属性都要用{} 括起来。好比咱们向组件中传递一个数字1,咱们就要写 num = {1}, 传递一个布尔值,就要写 bool={false},  咱们传递一个字符串,一个布尔值, 一个数字体验一下。

// 组件添加了三个p, 有来接受数据
class Content extends React.Component {
  render() {
    return (
      <section>
        <ul>
          {
            this.props.item.map((item, index) => 
              <li key={index}>{item}</li>
            )
          }
        </ul>  
        <p>name 的值是{this.props.name},类型是 {typeof this.props.name}</p>
        <p>bool 的值是{this.props.bool},类型是 {typeof this.props.bool}</p>
        <p>num 的值是{this.props.num},类型是 {typeof this.props.num}</p>
      </section>
    )
  }
}
// 向组件中另外传递 字符串name ,布尔值bool, 一个数字num。
let content = <Content item ={item} name="sam" bool={false} num={1}></Content>

  这时刷新浏览器,能够看到报错了,首先,JSX语法,浏览器是不支持的,咱们要把它转换成JS, 因此引入babel库,在head 标签中引入

    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

  其次咱们要告诉babel, 咱们的代码须要转译,因此在写react 代码的script 标签上添加一个type 属性

<script type="text/babel">

  这时再刷新浏览器,没有问题了,咱们也得到了属性,而且它的类型也是对的,num 是Number, 字符串是String。

  可是这里有一个问题,就是若是咱们要传递不少属性,这么 一个一个列出来,很是麻烦,怎么办呢? 这时可使用对象,但若是用对象进行传值,又不符合 属性名=属性值的写法,这时要用到es6中的扩展运算符..., React 对es6 中的扩展运算符(…)进行扩展,它能运用到对象上,对对象进行分割。{…obj}; var obj = {name:”sam”, num: 1}  , …obj  => name=”sam” , num= 1, 注意,...obj 是一个表达式,仍须要把它用{} 括起来

<script type="text/babel">

  // 组件添加了三个p, 有来接受数据
  class Content extends React.Component {
    render() {
      return (
        <section>
          <ul>
            {
              this.props.item.map((item, index) => 
                <li key={index}>{item}</li>
              )
            }
          </ul>  
          <p>name 的值是{this.props.name},类型是 {typeof this.props.name}</p>
          <p>bool 的值是{this.props.bool},类型是 {typeof this.props.bool}</p>
          <p>num 的值是{this.props.num},类型是 {typeof this.props.num}</p>
        </section>
      )
    }
  }

  // 要把传递的属性写到一个对象中,
  const obj = {
    item : ['JS', 'React', 'Redux'],
    name: 'sam', 
    bool: false,
    num: 1
  }

  // 把对象进行分割
  let content = <Content {...obj}></Content>
  ReactDOM.render(content, document.getElementById('root'));
</script>

  在Content 组件中,你可能发现外层包了一个section, 这是由于全部的组件都返回一个单一的根节点,主要仍是createElemet 函数第一个属性type 是一个值, 不能接受多个值。

你可能还发现全部的组件名是大写,仍是由于createElement 的第一个参数type, type 有两种类型,一种是html原有的类型如h1, 一种是自定义的类型,就是component, 当咱们传入时,React  没法区分这两种类型,因此它用大小写进行区分。若是是小写,它就觉得是html原有的类型,若是是大写,就是自定义类型。若是咱们组件使用了小写,React 按html原有的类型进行渲染,可是它又找不到这个类型,因此什么都不会渲染,组件名必须大写。

   在JSX语法中, 咱们还要注意如下两点:

  html 属性关键字: 如 咱们能够给html元素添加属性<h1 class="book">, 但class 在js 中是关键字, 因此class 要变成 className. 因为JSX 最终会转换成原生js 函数,因此js中的一些关键字在JSX中是不能用的,如class, for. 但在JSX 的类html 模版中,html 元素属性中又有class 和for, 这就冲突了。React 对html 元素中有冲突的属性进行了从新命名,for 变成了htmlFor, class 变成了className.  全部变量的命名都要用 驼峰命名法。如label 元素 <label htmlFor=”input” className=”text”></label>

  样式:在JSX 中,给一个html 元素添加样式有两种方法,一种是上面提到的className,  它的取值是一个样式名字符串,一种是内联样式style, 它的取值必须是一个对象。 <div className=”col-md-3” style ={style}></div> ,style 是组件内部定义的一个对象变量。 由于render 是一个函数,里面能够声明变量

  class Content extends React.Component {
    render() {
    // 定义样式变量
  var inlineStyle = {
      color: 'green' ,
   // css3 一些属性有些须要带浏览器厂商前缀,这时厂商前缀首字母必须大写, 全部的样式都是字符串
   WebkitFilter: blur('5px')
  } 

    return (
      <section>
        <p style={inlineStyle}>name 的值是{this.props.name},类型是 {typeof this.props.name}</p>
      </section>
    )
    }
  }
相关文章
相关标签/搜索