4-1 React 基础

87 阅读4分钟

原文链接(格式更好):《4-1 React 基础》

发展要史

1、JS 能操作 DOM

<div id="app">
	<h1>Hello</h1>
  <h2>my name is</h2>
  <h3>react</h3>
</div>

// 可以使用 JS 实现
function createElememt(tag, props, children) {
  const el = document.createElememt(tag)

  Object.entries(props).forEach(([key, value]) => {
    el.setAttribute(key, value)
  })
  

  children.forEach(child => el.appendChild(child))
}

createElememt('div', { id: 'app' }, [
  createElememt('h1', null, ['Hello']),
  createElememt('h2', null, ['my name is']),
  createElememt('h3', null, ['react']),
])

2、根据createElememt就能生成对应的 DOM 结构,但发现有点麻烦,所以 React 让 Babel 帮忙实现

const App = () => (
  <div id="app">
  	<h1>Hello</h1>
    <h2>my name is</h2>
    <h3>react</h3>
  </div>
)

// 经过 Babel的 @babel/preset-react 编译后
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
const App = () => _jsxs("div", {
  id: "app",
  children: [
    _jsx("h1", { children: "Hello"}), 
    _jsx("h2", { children: "my name is"}),
    _jsx("h3", { children: "react" })
  ]
});

总结:React 核心是写 HTML 片段(JSX),然后让 Babel 编译为可执行的渲染函数,调用该函数能创建 DOM,但 React 做的事情不止于此,但可以这样简单的理解。

基础

创建一个基础的 React 项目

npx create-react-app projectName

组件分类

类组件

基础写法

import React, { Component } from 'react'
export default class ClassComName extends Component {
  render() {
    return <div>I am Class Component</div>
  }
}

响应式数据写法

this.statethis.setState

import React, { Component } from "react";
export default class ClassComName extends Component {
  constructor() {
    super();

    this.state = {
      name: "Class Component~",
      number: 1,
    };
  }

  handleClick() {
    this.setState({ number: this.state.number + 1 });
  }

  render() {
    return (
      <div>
        <h1>I am 【{this.state.name}】</h1>
        <button onClick={() => this.handleClick()}>Add Number</button>
        <div>Number: {this.state.number}</div>
        <hr />
      </div>
    );
  }
}

父子传参

import React, { Component } from "react";
export default class ClassComName extends Component {
  constructor(props) {
    super(props); // ⭐️ 父子传参-接收传入参数

    this.state = {
      name: "Class Component~",
      number: props.initNumber, // ⭐️ 父子传参-使用父传入的参数
    };
  }

  handleClick() {
    const number = this.state.number + 1;
    this.setState({ number });

     // ⭐️ 父子传参-调用父传入的函数
    this.props.callback(number, this.state.name);
  }

  render() {
    return (
      <div>
        <h1>I am 【{this.state.name}】</h1>
        <div>props: {JSON.stringify(this.props)}</div>

        <button onClick={() => this.handleClick()}>Add Number</button>
        <div>Number: {this.state.number}</div>
        <hr />
      </div>
    );
  }
}

生命周期

初始化阶段

constructor:初始化调用,初始化实例状态(state)和绑定事件处理器,不推荐在这里做数据获取或订阅操作。

static getDerivedStateFromProps:在实例创建及后续每次 props 改变时都会调用,返回对象将与当前state合并,生成新的state

componentWillMount已弃用,渲染之前执行,若有了getDerivedStateFromPropsgetSnapshotBeforeUpdate时不执行

render:执行,生成新的虚拟 DOM 用于 DIFF(若需要),但还未去更新 DOM 哦

componentDidMount:渲染完成时调用,常用于网络请求、订阅或者手动DOM操作等。

更新阶段

componentWillReceiveProps已弃用,props 改变时触发,建议用getDerivedStateFromProps

shouldComponentUpdate:更新拦截,根据返回的布尔值决定是否更新。可用于数据提交中时锁定其他操作

componentWillUpdate已弃用,更新前调用,建议用getSnapshotBeforeUpdate

render:执行,生成新的虚拟 DOM 用于 DIFF(若需要),但还未去更新 DOM 哦

getSnapshotBeforeUpdate:获取更新 DOM 前的快照,获取元素某些信息(如滚动位置)

componentDidUpdate:更新完成时调用,谨慎调用setState会触发重新渲染

销毁阶段

componentWillUnmount:销毁之前调用,用于清理任何定时器、取消网络请求、解绑事件监听器等资源清理工作。

函数式组件

基础写法

export default function FuncComName() {
  return <div>I am Function Component</div>
}

响应式数据写法

useState

import { useState } from "react";

export default function FuncComName() {
  const [name] = useState("Function Component~");

  const [number, setNumber] = useState(1);

  const handleClick = () => {
    setNumber(number + 1);
  };
  return (
    <div>
      <h1>I am 【{name}】</h1>
      <button onClick={() => handleClick()}>Add Number</button>
      <div>Number: {number}</div>
      <hr />
    </div>
  );
}

父子传参

import { useState } from "react";

export default function FuncComName(props) { // ⭐️ 父子传参-接收传入参数
  const [name] = useState("Function Component~");

  const [number, setNumber] = useState(props.initNumber); // ⭐️ 父子传参-使用父传入的参数

  const handleClick = () => {
    const _number = number + 1;
    setNumber(_number);
    
    // ⭐️ 父子传参-调用父传入的函数
    props.callback(_number, name);
  };
  return (
    <div>
      <h1>I am 【{name}】</h1>
      <div>props: {JSON.stringify(props)}</div>

      <button onClick={() => handleClick()}>Add Number</button>
      <div>Number: {number}</div>
      <hr />
    </div>
  );
}

生命周期

函数式组件是没有生命周期的,只有一些钩子函数来替代生命周期。

useEffect

处理副作用。

useEffect(fun, deps)
fun:它的返回值将在下一次 fun 执行前调用
deps:数组,当值改变时,执行上一次的 fun 返回值,然后再执行 fun
// 代替:getDerivedStateFromProps
  // 实例创建时触发:
  useEffect(() => { console.log('getDerivedStateFromProps') }, [])
  
  // props 改变时触发:
  useEffect(() => { console.log('getDerivedStateFromProps') }, [props])
// 代替:componentDidMount
  // 渲染完成后触发:
  useEffect(() => { console.log('componentDidMount') }, [])
// 代替:componentWillUnmount
  //  销毁前触发:
  useEffect(() => {
    return () => {
      console.log('componentWillUnmount')
    }
  }, [])
// 代替:componentDidUpdate
  // 更新完成后触发:
  useEffect(() => {
		console.log('componentDidUpdate')
  })

补充知识

Fiber

参考资料:

万字长文介绍React Fiber架构的原理和工作模式

带你彻底读懂React VDOM DIFF - 掘金

Fiber:本质是 JS 对象,也可以称为 React 版的虚拟 DOM。链表结构

Vue vs React

Vue:使用 template,写法比较固定,但编译时可以做很多优化。

React:使用 JSX,写法非常灵活