react 教程—核心概念

react 核心概念 : https://react.docschina.org/docs/getting-started.html(官网) 或 https://www.w3cschool.cn/react/react-tutorial.html


一、react 的 JSX:

  1、概念: react 本质上是js,但是react自己用创造了一个表达式,即JSX表达式。(可以理解为,react中的js经过react编译为真正的js。原生的js就不变,像JSX这种react的语法就编译成原生的js)

  官方上把 JSX 语法称为 表达式,个人觉得也可以把 JSX 称为一种 数据类型 。类似正则表达式,等号右边只有一个 正则字面量(表达式可以是只有这个字面量的,https://www.cnblogs.com/fangsmile/p/8337021.html)。

  如下面的 JSX 语法:等号右边只有一个类似字面量的值,就叫 JSX字面量好了(类比正则字面量)。

const element = <h1>Hello, world!</h1>;    // 等号右边的这个表达式,原生js是会报错的,无法识别。但是react 的可以识别的。

  JSX 的基本语法规则 :遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。 http://www.ruanyifeng.com/blog/2015/03/react.html

  注意:JSX 语法不是 JS 引擎 解析的,所以不要完全使用js的语法规则去分析 JSX 代码。JSX 编译后的代码才是 js 代码。

     JSX 表达式最外层只能有一个标签,这点 和 vue 是一样的。

  2、JSX表达式外面的括号:JSX 标签里能够包含很多子元素,前后要加上括号。如:

const element = <img src={user.avatarUrl} />;   // img标签内没有其他的子元素,可以不用括号
const element = (
  <div>                  // div 标签里有子元素
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

  3、JSX表达式,经过 babel转译,所有的JSX表达式会变成调用 React 的 createElement 方法,变成如下代码。(JSX编译后的代码用到了React对象,所以必须要引入React对象)

import React, { Component } from 'react';

class Process extends Component {
  render() {
    return (<div>哈哈哈</div>)
  }
}

/*  babel 编译后 */
import React, { Component } from 'react';

class Process extends Component {
  render() {
    return React.createElement(
      'div',
      null,
      '\u54C8\u54C8\u54C8'
    );
  }
}

二、react元素渲染:https://react-1251415695.cos-website.ap-chengdu.myqcloud.com/docs/rendering-elements.html (一个 JSX 表达式 就是一个 react元素)

  react 元素 渲染 是通过 ReactDOM.render()方法进行渲染的,将 react元素 和 挂载的 DOM对象,作为参数传递给这个方法就了。如:

const element = <h1>Hello, world</h1>;   // react 元素
ReactDOM.render(element, document.getElementById('root'));

三、react组件:https://react-1251415695.cos-website.ap-chengdu.myqcloud.com/docs/components-and-props.htmlhttps://www.jianshu.com/p/f5c9ec0917bb(React创建组件的三种方法)

  定义组件的三种方法: 函数组件与 class 组件(创建后两个组件在 React 里是等效的,class组件有他的额外特性)

    1、函数组件:(函数组件,本质上是一个函数。相比较 类组件,函数组件有很多东西是没有的。) 开始使用react,不清楚什么时候使用函数组件好。就全部使用class组件吧。

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

    2、React.Component组件:以ES6的形式来创建react的组件的(React目前极为推荐的创建有状态组件的方式,https://www.jianshu.com/p/f5c9ec0917bb

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

    3、React.createClass 组件:以 ES5的原生的JavaScript来实现的React组件。【这个基本和class的一样,现在开发基本 不予考虑,16.0版本已经抛弃了React.createClass方法】

var HelloMessage = React.createClass({
  render: function() {
    return <h1>Hello {this.props.name}</h1>;
  }
});

ReactDOM.render(
  <HelloMessage name="fannieGirl" />,
  document.getElementById('example')
);

注意: 组件名称必须以大写字母开头。

总结:1、react 组件的本质是函数 返回一个JSX 表达式(react元素)。class组件也是一样,在内部 render 方法中,返回一个JSX 表达式。

   2、函数组件和类组件的区别。 参考 https://blog.csdn.net/wu_xianqiang/article/details/91320529

     函数组件 的性能比 类组件 的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。

区别函数组件类组件
是否有 this没有
是否有生命周期没有
是否有状态 state没有

   3、需要使用到state的地方就必须要使用类组件。一般当前组件,调用ajxa请求数据,都好用到state

四、react元素 渲染:react组件要渲染成 DOM,需要先将组件变成 react元素(把组件当标签使用就可以变成react元素),再使用react元素的渲染方法进行渲染。如:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;   // 把 Welcome组件 当标签使用就可以变成react元素了
ReactDOM.render( element, document.getElementById('root') );

五、Props:父组件传递给子组件的数据。

  1、Props 的只读性:组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。

this.props.name    //class组件中获取父组件传递过来的值,函数组件中去掉 this

  2、很多文章介绍,props改变,会重新渲染子组件。这个其实是错误的,props 修改不会,引起子组件的如何变化,只是父组件中某个变量的值改变了。

    往往 props 的值 是state值传递过来的。修改这个state 才导致,父组件 调用 render 方法,递归更新所有的组件树。这个时候新的props才传递进去的。

六、State:react组件的状态 ,state状态一旦改变就会调用 组件的 render 方法,重新生成虚拟DOM树,通过diff比较,更新视图(虚拟DOM本身的更新,对ui没有任何的影响,只有挂载到真实DOM上才是有效的)。

  理解:state 中的属性的数据变化,与其他的变量的主要区别是,state的变化会驱动 ui 重新渲染的。

  this.state.name  // 读取 state 内的属性值
  this.setState({   // 这个方法就是修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件
      name:'dd'
  })
   

  1、设置state使用setState方法,setState方法 可能是一个异步方法(不能保证 同步执行,大部分情况是同步执行的。开发还是把它 当成异步处理,这个可以避免错误),

    一个生命周期内所有的 setState 方法会合并操作。所以不会改变一个state数据就更新下 ui组件,一次性把state都设置好,在更新ui组件。

  2、this.setState 修改某一对象的某个属性值: 其它属性保留不变:https://blog.csdn.net/Wcharles666/article/details/90269837https://yq.aliyun.com/articles/667986(推荐,提供了3钟方法)

        let paginationParam = Object.assign({}, this.state.paginationParam, {current: 1})  // 这里用到 Object.assign 将所有可枚举属性的值从一个或多个源对象复制到目标对象
        this.setState({
          paginationParam:paginationParam
        })

七、React 组件生命周期: 详细介绍,在另外一篇文章上有写 https://www.cnblogs.com/wfblog/p/11842622.html(说明了各个生命周期执行的顺序)

  a、componentWillMount:

  b、componentDidMount : 在第一次渲染后调用,之后组件已经生成了对应的DOM结构。

  c、componentWillReceiveProps :在组件接收到一个新的prop时被调用

  d、shouldComponentUpdate

  e、componentWillUpdate:在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

  f、componentDidUpdate :在组件完成更新后立即调用。在初始化时不会被调用。

  g、componentWillUnmount:在组件从 DOM 中移除的时候立刻被调用。

八、事件处理:https://react.docschina.org/docs/handling-events.html

<button onClick={activateLasers}>
  Activate Lasers
</button>

  1、react中不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault

  2、JSX表达式中DOM元素绑定的事件,需要手动绑定 this,不然 事件函数中使用this会报错。https://www.cnblogs.com/yuyujuan/p/10111164.html

    绑定的方法:

    a、通过bind来指明当前方法中的this指向当前Home.js组件

render() {
// 通过bind方法实现,可以传递参数
return <button onClick={this.handleClick.bind(this, 'test')}>Test</button>;
}

    b、在构造函数constructor中改变this指向。

class App extends Component {
    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick(e) {
        console.log(e);
    }
    render() {
        return <button onClick={this.handleClick}>Test</button>;
    }
}    

    c、使用箭头函数改变this指向。

render() {
    return <button onClick={() => this.handleClick()}>Test</button>
}

  3、事件函数中 带自定义的参数:

    上面的 3种 绑定 this 的事件函数方法中,其中第二种是无法 传入自定义参数的。但是可以把需要的参数,作为这个DOM对象的属性放上去。

class Alphabet extends React.Component {
  handleClick(e) {
    this.setState({
      justClicked: e.target.dataset.letter  // 这里通过 获取DOM的自定义属性,获取了事件函数 需要的参数
    });
  }
 
  render() {
    return (
            <div data-letter={letter} onClick={this.handleClick}>
              {letter}
            </div>
    )
  }
}

九、 条件渲染: https://react.docschina.org/docs/conditional-rendering.html

  条件渲染的方式:

  1、react的条件渲染是 ,通过再创建一个组件组件,这个组件中通过js的条件判断返回不同的组件。 https://react.docschina.org/docs/conditional-rendering.html

function UserGreeting(props) {
  return <h1>Welcome back!</h1>;
}

function GuestGreeting(props) {
  return <h1>Please sign up.</h1>;
}

function Greeting(props) {  // 这个就是另外创建的组件
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

  2、JSX 花括号中,使用 运算符 && 。 这个方法只能 相当于 if 的功能,不能作为 if...else... 的功能。

function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
         {unreadMessages.length > 0 && (<h2>hello</h2>)}
    </div>
  );
}

  3、JSX 花括号中,使用 三目运算符 condition ? true : false

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn ? (
        <LogoutButton onClick={this.handleLogoutClick} />
      ) : (
        <LoginButton onClick={this.handleLoginClick} />
      )}
    </div>
  );
}

  重点:JSX表达式中花括号也是可以使用JSX元素的(把JSX元素当成一个数据,就像字符串)

十、列表 渲染 :https://react.docschina.org/docs/lists-and-keys.html

  1、通过map渲染 (JSX表达式中,如果花括号只有一个数组,则react编译时,会把所有的项拼接在一起)

 const numbers = [1, 2, 3, 4, 5];
 const listItems = numbers.map((number,index) =>
      <li key={index}>{number}</li>
  );
 return(  
      <div>
        {listItems}     // 这里 listItems 是一个数组,react编译后,会把这个数组中的每项拼接在一个
      </div>
    )
  }

  注意:列表渲染的标签必须有一个 key 属性,这点 和 VUE 是一样的原理。 官网上说,不建议使用 索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。

十一、表单:https://react.docschina.org/docs/forms.html

  1、受控组件:表单的数据保存在组件的 state 属性中,并且只能通过使用 setState()来更新。从效果来看的话,受控组件 实现了 双向绑定的功能。

         受控组件,如果没有绑定 Change 事件,通过setState()来更新,则输入不了内容。不同的 表单元素,select 、textarea 怎么处理看上面网站

class NameForm extends React.Component {
  state = {value: ''};
  handleChange(event) {
    this.setState({value: event.target.value});
  }

  render() {
    return (
       <input type="text" value={this.state.value} onChange={this.handleChange.bind(this)} />
    );
  }
}

    a、多个受控组件,给每一个表单设置一个 onChange 事件函数,这种处理方式肯定会导致代码不好维护。处理方式是: https://blog.csdn.net/weixin_30371875/article/details/98777554

  handleChange(val,event){
      this.inputData[val] = event.target.value  
  }
 <input onChange={this.handleChange.bind(this, 'gtPersonNum')}/> 
 <input onChange={this.handleChange.bind(this, 'ltPersonNum')}/> 

2、非受控组件:和 以前 html 的差不多的使用, 使用 ref 指向 这个表单对象。下面的,this.input 表示的就是这个input表单。

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
  }

  handleSubmit(event) {
    alert(this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
          <input type="text" ref={this.input} />   // this.input 指向当前的DOM节点
      </form>
    );
  }
}

  test(){
     console.log(this.input.value); 这里直接使用DOM节点的方法,获取节点数据
  }
  render() {
     return (
        <input type="text" ref={(input) => this.input = input} 
  onChange={this.test.bind(this)}/>  // 这里 把 this.input 指向当前DOM 节点
      )
  }