React速查

React速记,分为3个部分。每一个部分由简短且精悍的知识点组成。开发忘记可查看,初见学习可常翻

基础

一、JSX

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

JavaScript和UI代码的结合

const element = <h1>Hello, {name}</h1>;

{}包裹表达式(js表达式,变量)

const element = <img className={imgClass} src={user.avatarUrl} />;
  • class`属性写为`className
  • 属性用小驼峰语法
const element = <h1>Hello, world!</h1>;
ReactDOM.render(
  element,
  document.getElementById('root')
);

一个可运行的React Hello World例子

二、组件

  1. 类式组件
  2. 函数式组件
function MyComponent(){
  return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
export defualt class MyComponent extends React.Component {
  render(){
    return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
  }
}

插入组件使用import,使用组件用 开头大写 的标签,组件可嵌套

import Hello from './components/Hello'

var MyComponent = function(){
  return <Hello />
}

渲染组件,ReactDOM的使用

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

三、组件上的props和state

组件接收的入参为props,组件自身的状态为state

props

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

传值

const el = <Welcome name="嘉然" />

传入值的类型检查,PropTypes的使用

static propTypes = {
  name:PropTypes.string.isRequired, //限制name必传,且为字符串
  sex:PropTypes.string,//限制sex为字符串
  age:PropTypes.number,//限制age为数值
}

默认值,defaulyProps的使用

static defaultProps = {
    sex: "male",
    age: 20
}

props不可修改

state

函数式组件需借助Hooks使用state

class Weather extends React.Component{

  constructor(props){
    super(props)
    //初始化状态
    this.state = {isHot:false,wind:'微风'}
}

不可以直接修改state,使用setState()修改

this.setState({isHot:!isHot})

四、事件处理

  1. 事件处理回调
  2. [Ref]()

类式组件里,在回调函数中要注意this指向的问题

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 为了在回调中使用 `this`,这个绑定是必不可少的
    this.handleClick = this.handleClick.bind(this);
  }

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

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

函数式组件

function ActionLink() {
  handleClick = (e) => {
    console.log('The link was clicked.');
  }

  return (
    <button href="#" onClick={handleClick}>Click me</button>
  );
}

五、虚拟DOM、真实DOM

虚拟DOM

开销极小的普通对象

const Velement = <div>Hello, world</div>;

真实DOM

const Relement = document.getElementById("root");

两者关系

  • 调用ReactDOM.render将虚拟DOM渲染成真实DOM,组件state改变时,React会创建新VDOM并生成RDOM
  • 虚拟DOM没有真实DOM过多的属性

六、Diff算法

对象差异比对调和(Reconciliation)算法

  • 虚拟DOM的比对

  • 三种比对策略

    1. Tree Diff
    2. Component Diff
    3. Element Diff

Tree Diff:DOM树的同层比对,跨层时,直接创建新节点及节点下的树


Component Diff:

  • 不同类型组件:使用Tree Diff
  • 同类型组件,比对 属性 - 子节点 子节点如根节点比对形式 属性 - 子节点 比对。碰到不同类型节点便使用Tree Diff规则。也可以使用 生命周期函数 来手动控制组件更新
    不同类型组件

    <div>
      <Counter />
    </div>
    <span>
      <Counter />
    </span>

    同类型组件

    <div className="before" title="stuff" />
    
    <div className="after" title="stuff" />
    <ul>
      <li>first</li>
      <li>second</li>
    </ul>
    
    <ul>
      <li>first</li>
      <li>second</li>
      <li>third</li>
    </ul>

Element Diff

节点在同一层级,会使用 删除、插入、移动 来更新
插入

  <ul>
    <li>first</li>
  </ul>

  <ul>
    <li>first</li>
    <li>second</li>
  </ul>

每个节点带有key,避免 一些破坏渲染顺序的操作引发的性能问题

  <ul>
    <li>first</li>
  </ul>

  <ul>
    <li>second</li>
    <li>first</li>
  </ul>

PS. React不会做移动操作更新ul下面的两个li,而是直接删除原来ul的li,生成新的两个li。根据key判断原节点是否在原VDOM中存在

七、生命周期函数

生命周期钩子,特定时刻执行

  • React16.4 旧生命周期

初始化阶段: 由ReactDOM.render()触发---初次渲染

  1. constructor()
  2. componentWillMount()
  3. render()
  4. componentDidMount()

更新阶段: 由组件内部this.setSate()或父组件render触发

  1. shouldComponentUpdate()
  2. componentWillUpdate()

    1. render()
    2. componentDidUpdate()

    卸载组件: 由ReactDOM.unmountComponentAtNode()触发

componentWillUnmount()

  • 新生命周期

  1. 初始化阶段: 由ReactDOM.render()触发---初次渲染

    1. constructor()
    2. getDerivedStateFromProps()
    3. render()
    4. componentDidMount()
  2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发

    1. getDerivedStateFromProps()
    2. shouldComponentUpdate()
    3. render()
    4. getSnapshotBeforeUpdate()
    5. componentDidUpdate()
  3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发

    componentWillUnmount()

PS.手动控制组件更新

shouldComponentUpdate(){
    //do something
    reture true // 或者填写false
}

进阶

八、官方脚手架

Create a New React App – React (reactjs.org)

安装

npm install -s create-react-app

使用

create-react-app myapp

九、网络请求

  • axios
  • fetch

axios

axios中文网

简单使用实例

axios.get(`/api1/users?q=${keyWord}`).then(
    response => {
        // 成功请求后返回的响应
    error => {
        //请求失败后的处理
    }
)

fetch

MDN:WorkerOrGlobalScope.fetch()

实例

fetch.get(`/api1/users?q=${keyWord}`)
.then( resp => {} err => {})
.then( resp(data) => {} err => {})

async await语法

async() => {
    const response= await fetch(`/api1/search/users2?q=${keyWord}`)
    const data = await response.json()   
}

十、ref

在React中获取DOM

ref.createRef()

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

回调ref

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };
  }

  render() {
    // 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
    // 实例上(比如 this.textInput)
    return (
        <input
          type="text"
          ref={this.setTextInputRef}
        />
    );
  }
}
  • 函数式组件不可以使用Ref,因为没有this
  • string形式ref不推荐使用

十一、react中state的管理

  • redux
  • react-redux

redux

Don't use Redux just because someone said you should - take some time to understand the potential benefits and tradeoffs of using it

-https://redux.js.org/introduction/getting-started#should-you-use-redux

是否使用redux在于自己的权衡

Redux = Reducer + Flux

解决跨组件数据传递问题

redux简单的工作流程

// 引入redux
import { createStore } from 'redux'
// 创建reducer,从store中根据其他组件传递的action来取store中对应的数据
function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
}

// 创建一个store
let store = createStore(counterReducer)
// 组件订阅store,store相应数据变化就会返回给订阅的组件
store.subscribe(() => console.log(store.getState()))
// 组件将自己的数据发送给store
store.dispatch({ type: 'counter/incremented' })

react-redux

React-Redux is the official Redux UI binding library for React

-https://redux.js.org/faq/react-redux#react-redux

连接UI组件和数据

import React from 'react';
import {connect} from 'react-redux';
import {pushState} from 'redux-router';

export function requireAuthentication(Component) {
    class AuthenticatedComponent extends React.Component {
        componentWillMount() {
            this.checkAuth();
        }
        componentWillReceiveProps(nextProps) {
            this.checkAuth();
        }
        checkAuth() {
            if (!this.props.isAuthenticated) {
                let redirectAfterLogin = this.props.location.pathname;
                this.props.dispatch(pushState(null, `/login?next=${redirectAfterLogin}`));
            }
        }

        render() {
            return (
                <div>
                    {this.props.isAuthenticated === true
                        ? <Component {...this.props}/>
                        : null
                    }
                </div>
            )

        }
    }

    const mapStateToProps = (state) => ({
        token: state.auth.token,
        userName: state.auth.userName,
        isAuthenticated: state.auth.isAuthenticated
    });
    return connect(mapStateToProps)(AuthenticatedComponent);
}

十二、路由

拓展