ReactJs基础

263 阅读8分钟

React的引入

CDN引入

// react 引入,用来生成伪dom
<script src="https://cdn.bootcss.com/react/16.10.2/umd/react.development.js"></script>
// react-dom引入,用来渲染界面
<script src="https://cdn.bootcss.com/react-dom/16.10.2/umd/react-dom.development.js"></script>

cjs与umd的区别

  • cjs全称commonJS,是Node.js支持的模块规范
  • umd是统一模块定义,兼容各种模块规范(含浏览器)
  • 理论上有限使用umd,同时支持Node.js和浏览器
  • 最新的模块规范是使用import和export关键字

通过webpack引入React

  • import ... from ...
// 注意大些写
yarn add react react-dom
import React from 'react'
import ReactDOM from 'react-dom'

用create-react-app

create-react-app 项目名字

JSX使用

创建项目

yarn global add create-react-app // 全局安装
create-react-app 项目名字 // 初始化目录
cd 项目名字 // 进入目录
yarn start // 开始开发

使用JSX的注意事项

  • 注意className
<div className="red">n</div>

被转义为

React.createElement('div',{className:'red'},"n")
  • 插入变量
  1. 标签里面的所有JS代码都要用{}包起来
  2. 如果需要变量n,那么就用{}把n包起来
  3. 如果需要对象,那么就要用{}把对象包起来,比如{{name:'frank'}}

JSX的条件判断

  • 在React里
const Component = () => {
    return n%2 === 0 ? <div>n是偶数</div> : <span>n是奇数</span>
}

// 如果需要外面的div, 可以写成
const Component = () -> {
    return {
        <div>
            { n%2 ==== 0 ? <div>n是偶数</div> : <span>n是奇数</span>}
        </div>
    }
}
  • 还可以写成
const Component - () => {
    const content = (
        <div>
            { { n%2 ==== 0 ? <div>n是偶数</div> : <span>n是奇数</span>} }
        </div>
    )
    return content
}

React里面编写循环

const Component = (props) => {
    return props.numbers.map((n,index) => {
        retuen <div>下标{index}的值为{n}</div>
    })
}

// 如果你需要外面的div,可以写成
const Component - (props) => {
    return (
        <div>
            { props.numbers.map((n,index) => {
                return <div>下标{index}的值为{n}</div>
            })}
        </div>
    )
}

// 还可以这样写
const Component = (props) => {
    const array = []
    for(let i=0; i<props.numbers.length;i++){
        array.push(<div>下标{index}的值为{props.numbers[i]}</div>)
    }
    return <div>{ array }</div>
}

组件

Element VS Component

  • 元素与组件
// 这是一个react元素(d小写)
const div = React.createElement('div', ...)
// 这是一个React组件(D大写)
const Div = () => React.createElement('div',...)

React两种组件

  • 函数组件
function Welcome(props){
    return <h1>Hello, {props.name} </h1>;
}
// 使用方法: <Welcome name="frank"/>
  • 类组件
class Welcome extend React.Component {
    render(){
        return <h1>Hello, {props.name} </h1>;
    }
}
// 使用方法: <Welcome name="frank"/>

Welcome解析

  • 会被翻译成什么
// >>> 表示翻译成
<div> >>> React.createElement('div')
<Welcome> >>> React.createElement(Welcome)
  • React.createElement的逻辑
  1. 传入一个字符串'div', 会创建一个div
  2. 传入一个函数,会调用函数,获取其返回值
  3. 传入一个类,在类前面加上new,这回执行constructor,获取一个组件对象,然后调用对象的render方法,获取其返回值

props和state

  • props(外部数据)

类组件直接读取属性 this.props.xxx 函数组件直接读取 参数.props.xxx

import React from "react";
import ReactDOM from "react-dom";


function App() {
  return (
    <div className="App">
      爸爸
      <Son messageForSon="儿子你好" />
    </div>
  );
}

class Son extends React.Component {
  render() {
    return (
      <div className="Son">
        我是儿子,我爸对我说「{this.props.messageForSon}」
        <Grandson messageForGrandson="孙贼你好" />
      </div>
    );
  }
}

const Grandson = props => {
  return (
    <div className="Grandson">
      我是孙子,我爸对我说「{props.messageForGrandson}」
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

  • state(内部数据)
import React from 'react'
import ReactDOM from 'react-dom'

function App(){
    return (
        <div className="App">
            爸爸
            <Son />
        </div>    
    );
}

class Son extend React.Component {
    constructor(){
        super()
        this.state = {
            n : 0
        };
    }
    
    add(){
        this.setState({ n: this.state.n+1 });
    }
    
    render(){
        return (
          <div className="Son">
            儿子 n: {this.state.n}
            <button onClick={() => this.add()}>+1</button>
            <Grandson />
          </div>
        )
    }
}

const Grandson = () => {
  const [n, setN] = React.useState(0);
  return (
    <div className="Grandson">
      孙子 n:{n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

类组件注意事项

  • this.state.n +=1 无效?

    1. 其实n已经改变了
    2. 调用setState才会触发UI更新(异步更新)
    3. 因为React没有像Vue监听data一样监听state
  • setState会异步更新UI

    1. setState之后,state不会马上改变
    2. 更推荐的方式是setState(函数)
  • this.setState(this.state) 不推荐?

    1. React希望我们不要修改旧state(不可变数据)
    2. 常用代码: setState({ n: state.n+1})

函数组件的注意事项

  • 也要通过setX(新值)来更新UI
  • 没有this,一律使用参数和变量

复杂state

  • 类组件有n/m
import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}

class Son extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0,
      m: 0
    };
  }
  addN() {
    this.setState({ n: this.state.n + 1 });
    // m 会被覆盖为 undefined 吗?
  }
  addM() {
    this.setState({ m: this.state.m + 1 });
    // n 会被覆盖为 undefined 吗?
  }
  render() {
    return (
      <div className="Son">
        儿子 n: {this.state.n}
        <button onClick={() => this.addN()}>n+1</button>
        m: {this.state.m}
        <button onClick={() => this.addM()}>m+1</button>
        <Grandson />
      </div>
    );
  }
}

const Grandson = () => {
  const [n, setN] = React.useState(0);
  return (
    <div className="Grandson">
      孙子 n:{n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
  • 函数组件有n/m
import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}

class Son extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0,
      m: 0
    };
  }
  addN() {
    this.setState({ n: this.state.n + 1 });
    // m 会被覆盖为 undefined 吗?
  }
  addM() {
    this.setState({ m: this.state.m + 1 });
    // n 会被覆盖为 undefined 吗?
  }
  render() {
    return (
      <div className="Son">
        儿子 n: {this.state.n}
        <button onClick={() => this.addN()}>n+1</button>
        m: {this.state.m}
        <button onClick={() => this.addM()}>m+1</button>
        <Grandson />
      </div>
    );
  }
}

const Grandson = () => {
  const [n, setN] = React.useState(0);
  const [m, setM] = React.useState(0);
  return (
    <div className="Grandson">
      孙子 n:{n}
      <button onClick={() => setN(n + 1)}>n+1</button>
      m:{m}
      <button onClick={() => setM(m + 1)}>m+1</button>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
  • 类组件setState会自动合并第一层属性,但是第二层不会,用...解决
import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}

class Son extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0,
      m: 0,
      user: {
        name: "frank",
        age: 18
      }
    };
  }
  addN() {
    this.setState({ n: this.state.n + 1 });
    // m 会被覆盖为 undefined 吗?
  }
  addM() {
    this.setState({ m: this.state.m + 1 });
    // n 会被覆盖为 undefined 吗?
  }
  changeUser() {
    this.setState({
      // m 和 n 不会被置空
      user: {
        ...this.state.user, // 复制之前的所有属性
        name: "jack"
        // age 被置空
      }
    });
  }
  render() {
    return (
      <div className="Son">
        儿子 n: {this.state.n}
        <button onClick={() => this.addN()}>n+1</button>
        m: {this.state.m}
        <button onClick={() => this.addM()}>m+1</button>
        <hr />
        <div>user.name: {this.state.user.name}</div>
        <div>user.age: {this.state.user.age}</div>
        <button onClick={() => this.changeUser()}>change user</button>
        <Grandson />
      </div>
    );
  }
}

const Grandson = () => {
  const [n, setN] = React.useState(0);
  return (
    <div className="Grandson">
      孙子 n:{n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

事件绑定

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}

class Son extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0,
      m: 0
    };
  }
  addN = () => {
    this.setState({ n: this.state.n + 1 });
  };
  addM() {
    // 这样写 this 可能变成 window
    this.setState({ m: this.state.m + 1 });
  }
  render() {
    return (
      <div className="Son">
        儿子 n: {this.state.n}
        <button onClick={this.addN}>n+1</button>
        m: {this.state.m}
        <button onClick={this.addM}>m+1</button>
        <Grandson />
      </div>
    );
  }
}

const Grandson = () => {
  const [n, setN] = React.useState(0);
  return (
    <div className="Grandson">
      孙子 n:{n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

class组件详解

类组件创建

import React from 'react'

class B extends React.Component {
    constructor(props){
        super(props)
    }
    render(){
        return (
            <div> hello </div>
        )
    }
}
export default B;

Props外部数据

  • 传入props给B组件
class Parent extends React.Component {
    constructor(props){
        super(props)
        this.state = {name:'frank'}
    }
    onClick = () => {}
    render(){
        return <B name = {this.state.name} onClick = this.onClick> hi </B>
    }
}
// 外部数据被封装成一个对象,传入B组件
  • 读取props外部数据
class B extend React.Component {
    constructor(props){
        super(props)
    }
    render(){
        return <div onClick = {this.props.onClick}> 
                {this.props.name}
                <div>
                    {this.props.children}
                </div>
            </div>
    }
}
  • 不准写props数据

state内部数据

class B extends React.Component {
    constructor(props){
        super(props)
        this.state - {
            user:{name:'frank', age:18}
        }
    }
    render(){....}
}

声明周期

必须会的声明周期

  • constructor() 在这里初始化state
  • shouldComponentUpdate() return false阻止更新
  • render() 创建虚拟dom
  • componentDidMount() 组件已经出现在页面
  • componentDidUpdate() 组件已经更新
  • componentWillUnmount() 组件将死

constructor

  • 用途

    1. 初始化props
    2. 初始化state,但此时不能调用setState
    3. 用来写bind this
constructor(){
    // 其他代码
    this.onClick =this.onClick.bind(this)
}

// 可以用新语法替代
    onClick =() => {}
    constructor(){ // 其他代码 }

shouldComponentUpdate

  • 用途
    1. 返回true表示不足时UI更新
    2. 返回false表示阻止UI更新
  • 代码
class App extends React.Component {
    constructor(props){
        super(props)
        this.state = {
            n: 1
        }
    }
    
    onClick = () =>{
        this.setState(state => ({ n: state.n+1}))
        this.setState(state => ({ n: state.n-1}))
    }
    
    shouldComponentUpdate(newProps, newState){
        if(newState.n === this.state.n){
            return false
        }else{
            return true
        }
    }
    
    render(){
        ...
    }
}

render

  • 用途
  1. 展示视图
return (<div>...</div>)
  1. 只能有一个根元素
  2. 如果有两个根元素,就要React.Fragment包起来

componentDidMount

  • 用途
  1. 在元素插入页面后执行代码,这些代码以来DOM
  2. 比如你像获取div的高度,最好写进这里
  3. 此处可以发起加载数据的AJAX请求
  4. 首次渲染会执行此钩子

componentDidUpdate

  • 用途
  1. 在视图更新后执行代码
  2. 此处也可以发起AJAX请求,用于更新文档
  3. 首次渲染不会执行此钩子
  4. 在此处setState可能会引起无限循环,除非放在if里面
  5. 若shouldComponentUpdate返回false,则不会出发此钩子

componentWillUnmount

  • 用途
  1. 组将将要被移除页面然后被销毁时执行代码
  2. unmount过的组件不会再次mount
  • 举例
  1. 如果在componentDidMount里面监听了window scroll,那么就要在componentWillUnmount里面取消监听

函数组件

创建方式

const Hello = () => {
    return <div> {props.message}</div>
}

const Hello = props => <div>{props.message}</div>

function Hello(props){
    return <div>{props.message}</div>
}

useEffect

  • 模拟componentDidMount
useEffect(() => { console.log('第一次渲染')},[])
  • 模拟componentDidUpdate
useEffect(() => { console.log('任意属性变更')})
useEffect(() => { console.log('n变更了')},[n])
  • 模拟componentWillUnmount
useEffect(
    console.log('第一次渲染')
    return () => {
        console.log('组件要卸载了')
    }
)

useState原理

用法

function App(){
    const [n, setN] = React.useState(0);
    return (
        <div className="App">
            <p>[n]]</p>
            <p> 
                <button onClick = {() => setN(n+1)}> +1 </button>
            </p>
        </div>
    
    )
}

ReactDOM.render(<App/>, rootElement)