React

一、简介

  • 使用组件轻松简化视图设计
  • 高效渲染

二、安装

2.1 免安装,在线练习

CodePen
CodeSandbox

2.2 npm 安装

npm install -g create-react-app
create-react-app my-app

cd my-app
npm start

2.3 CDN

三、基本使用

3.1 hello

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

//JSX 语句
const element = 

Hello, {formatName(user)}!

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

3.2 JSX

  • JSX, 一种 JavaScript 的语法扩展,用来声明 React 当中的元素
  • 可以任意地在 JSX 当中使用 JavaScript 表达式,在 JSX 当中的表达式要包含在大括号里
  • JSX 本身其实也是一种表达式
function getGreeting(user) {
  if (user) {
    return 

Hello, {formatName(user)}!

; } return

Hello, Stranger.

; }
  • JSX 属性
    引号来定义以字符串为值的属性
    大括号来定义以 JavaScript 表达式为值的属性
const element = 
; const element = ;
  • JSX 嵌套
const element = ;

const element = (
  

Hello!

Good to see you here.

);
  • JSX 防注入攻击
    React DOM 在渲染之前默认会 过滤 所有传入的值。它可以确保你的应用不会被注入攻击。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(跨站脚本) 攻击。

  • JSX 代表 Objects
    Babel 转译器会把 JSX 转换成一个名为 React.createElement() 的方法调用。
    下面两种代码的作用是完全相同的:

const element = (
  

Hello, world!

);
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

3.3 元素渲染

  • React 元素的本质是普通对象,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致
  • 使用ReactDOM.render()将React 元素渲染到特定的节点
  • React 元素都是不可变的,当元素被创建之后,无法改变其内容或属性
  • React 元素重新渲染时只会更新必要的部分

3.4 组件

  • 组件名称必须以大写字母开头
  • 创建组件的两种方式
//js 方式
function Welcome(props) {
  return 

Hello, {props.name}

; } //使用 ES6 class class Welcome extends React.Component { render() { return

Hello, {this.props.name}

; } }
  • 自定义组件作为React 元素
const element = ;
  • 复合组件
  • 组件的生命周期
    组件中的一些特殊方法,这也被称作生命周期钩子
    componentWillMoun:渲染之前
    componentDidMount:在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异部操作阻塞UI)
    componentWillReceiveProps:在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
    shouldComponentUpdate:返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
    可以在你确认不需要更新组件时使用。
    componentWillUpdate:在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用
    componentDidUpdate:在组件完成更新后立即调用。在初始化时不会被调用。
    componentWillUnmount:从DOM 中移除时
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

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

3.5 props & state

state 和 props 主要的区别在于 props 是不可变的,props用来为组件传递数据,而 state 用来与用户交互来改变,更新和修改数据。

3.5.1 props
  • 无论使用函数或者类来声明一个组件,它自身都是不可以修改props的
  • 所有的React组件必须像纯函数(不改变自身的输入值)那样使用它们的props
import React from 'react';

export default class Page extends React.Component {
    render() {
        const {title, subTitle, spacing, className, children, footer} = this.props;

        return (
            

{title}

{subTitle}

{children}
{ footer ?
{footer}
: false }
); } };
3.5.2 state
  • 状态不可以直接更新,应当使用setState()方法
  • this.props 和 this.state 可能是异步更新的,所以不能直接依靠它们的值来计算下一个状态,但是可以使用第二种形式的 setState() 来接受一个函数实现
// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));
  • 状态通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问
  • date 从属性移动到状态中步骤
    a. 在 render() 方法中使用 this.state.date 替代 this.props.date
    b. 添加一个类构造函数来初始化状态 this.state
    c. 移除原来创建组件时的data
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

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

d. 在生命周期钩子中更新状态

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } } ReactDOM.render( , document.getElementById('root') );
  • 数据自顶向下流动,

3.6 事件处理

  • React事件绑定属性的命名采用驼峰式写法,而不是小写
  • 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM元素的写法)
  • 不能使用返回 false 的方式阻止默认行为,你必须明确的使用 preventDefault
function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    
      Click me
    
  );
}
  • 必须谨慎对待 JSX 回调函数中的 this,类的方法默认是不会this 的。如果你忘记绑定 this.handleClick 并把它传入 onClick, 当你调用这个函数的时候 this 的值会是 undefined
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      
    );
  }
}

ReactDOM.render(
  ,
  document.getElementById('root')
);
  • 不使用bind第一种方式,使用属性初始化器来正确的绑定回调函数
class LoggingButton extends React.Component {
  // This syntax ensures `this` is bound within handleClick.
  // Warning: this is *experimental* syntax.
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      
    );
  }
}
  • 不使用bind第二种方式,在回调函数中使用 箭头函数
class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // This syntax ensures `this` is bound within handleClick
    return (
      
    );
  }
}
  • 传递参数
//等价


3.7 条件渲染

class LoginControl extends React.Component {
  constructor(props) {
    super(props);
    this.handleLoginClick = this.handleLoginClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {isLoggedIn: false};
  }

  handleLoginClick() {
    this.setState({isLoggedIn: true});
  }

  handleLogoutClick() {
    this.setState({isLoggedIn: false});
  }

  render() {
    const isLoggedIn = this.state.isLoggedIn;

    let button = null;
    if (isLoggedIn) {
      button = ;
    } else {
      button = ;
    }

    return (
      
{button}
); } } ReactDOM.render( , document.getElementById('root') );

在JSX 语法中

  • 与运算符 &&,在 JavaScript 中,true && expression 总是返回 expression,而 false && expression 总是返回 false。因此,如果条件是 true,&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。
function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    

Hello!

{unreadMessages.length > 0 &&

You have {unreadMessages.length} unread messages.

}
); } const messages = ['React', 'Re: React', 'Re:Re: React']; ReactDOM.render( , document.getElementById('root') );
  • 三目运算符
//小段渲染
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    
The user is {isLoggedIn ? 'currently' : 'not'} logged in.
); } //整句渲染 render() { const isLoggedIn = this.state.isLoggedIn; return (
{isLoggedIn ? ( ) : ( )}
); }
  • 阻止组件渲染
function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    
Warning!
); }

3.8 列表 & Keys

  • 使用js的map函数,同时需要指定在兄弟之间唯一的key
function ListItem(props) {
  // 对啦!这里不需要指定key:
  return 
  • {props.value}
  • ; } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 又对啦!key应该在数组的上下文中被指定 ); return (
      {listItems}
    ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( , document.getElementById('root') );

    3.9 表单

    • 受控组件
    class NameForm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value: ''};
    
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }
    
      handleChange(event) {
        this.setState({value: event.target.value});
      }
    
      handleSubmit(event) {
        alert('A name was submitted: ' + this.state.value);
        event.preventDefault();
      }
    
      render() {
        return (
          
    ); } }
    • textarea 标签
    class EssayForm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          value: 'Please write an essay about your favorite DOM element.'
        };
    
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }
    
      handleChange(event) {
        this.setState({value: event.target.value});
      }
    
      handleSubmit(event) {
        alert('An essay was submitted: ' + this.state.value);
        event.preventDefault();
      }
    
      render() {
        return (