1 react 入门

1.1起步

创建项目:npx create-react-app my-app
打开项目: cd my-app
启动项目:npm start
暴露配置项:npm run eject

理解npx create-react-app my-app 指令

npx是npm的附带产物
执行 npx create-react-app my-app命令流程如下:

  1. 一旦使用npx去执行一段命令,那么npx会首先看第一个参数对应的工具是否被安装
  2. 如果没有被安装,npx就会告诉npm临时安装一下,临时安装进内存
  3. 当临时安装好了以后,npx会再次直接执行整段命令"create-react-app my-app"

create-react-app是react的官方脚手架

1.2文件结构

README.md--文档
public--存放静态资源
src--具体代码
App.js--根组件
index.css--全局样式
index.js--入口文件
package.json---npm 依赖
config-项目的具体配置

项目中config文件夹一般是隐藏掉的,如果想要出现可使用yarn eject,一旦执行,不可恢复

1.3React和ReactDom

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

ReactDOM.render(
  <h1>hello React</h1>,
  document.getElementById('root')
);

React负责逻辑控制,数据->VDOM,是react的核心库
ReactDom渲染实际Dom,VDOM->DOM

React可以使用jsx来描述ui

<script type="text/babel">
//script上有type="text/babel"属性, 将意味babel将接管所有的代码解析
      const reactDivElement = (
        <div>
          hello jsx
          <span>i am child span</span>
        </div>
      )
      const root1 = ReactDOM.createRoot(document.getElementById("root1"))
      root1.render(reactDivElement)
</script>

babel解析JSX原理

babel会监听全局的document.contentLoad,具体流程如下:

  1. 当前页面的所有的script标签全部生成完毕,babel会通过document.getElementByTagName,拿到所有的script标签
  2. 分别通过getAttributes函数,读取script上面的属性
  3. 如果type=“text/babel”,就把里面的代码全部拿过来,通过tansform方法转换一遍,通过新建一个script标签,将转换后的代码插入script中,将script插入到head标签
  4. 如果不写type,type默认位text/Javascript,此时义Javascript的形式解析
  5. 如果以上两种都不是,浏览器不会看script里面的代码

babel-loader把jsx编译成相应的js对象.
React.createElement再把这个JS对象构造成React需要的虚拟dom

2 jsx

jsx是一种javascript的语法扩展,其语言比较像模板语言,但实际上完全是javaScript内部实现的,jsx可以很好的描述UI,能够有效地提高开发效率

2.1基本使用

//index.js
import React from 'react';
import ReactDOM from 'react-dom';

//基本使用
const name = "react"
const jsx = <div>hello,{name}</div>
ReactDOM.render(
  jsx,
  document.getElementById('root')
);

2.2函数

函数也是合法的表达式

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

//函数
const user = {
  firstName:"Harry",
  lastName:"Potter"
}

function formatName(name){
  return name.firstName+" "+ name.lastName
}

const jsx = <div>{formatName(user)}</div>

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

2.3对象

jsx也是js对象,也是合法表达式

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

const greet = <div>good</div>
const jsx = <div>{greet}</div>

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

2.4条件语句

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

//条件语句
const show = true;//false;
const greet = <div>good</div>;
const jsx = (
  <div>
    {
      show?greet:"登录"
    }
    {show&&greet}
  </div>
)
ReactDOM.render(
  jsx,
  document.getElementById('root')
);

2.5数组

数组作为一组子元素对待,数组中存放一组jsx用于显示列表数据

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

//数组
const a = [0,1,2]
const jsx = (
  <div>
    <ul>
      {
        //diff时候,首先比较type,然后是key,所以同级同类型元素,key值必须唯一
        a.map(item=>(
          <li key={item}>{item}</li>
        ))
      }
    </ul>
  </div>
)
ReactDOM.render(
  jsx,
  document.getElementById('root')
);

2.6属性的使用

属性:静态值用双引号,动态值用花括号,class、for要特殊处理
注意:class是保留字,如果要增加class,需要使用ClassName

import React from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg'

const jsx = (
  <div>
    <img src = {logo} style={{width:100}} className="img"/>
  </div>
)

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

2.7模块化

css模块化,创建index.css

import React from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg'
import './index.css'

const jsx = (
  <div className = {styles.app}>
    <img 
      src = {logo} 
      className = "logo" 
      style={{width:"50px",height:"30px"}} 
      alt="这个一个图片"
    />
  </div>
)

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

或者npm install sass -D

import React from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg'
import styles from './index.module.scss'

const jsx = (
  <div className = {styles.app}>
    <img 
       src = {logo} 
      className={styles.logo} 
      style={{width:"50px",height:"30px"}} 
      alt="这个一个图片"
    />
  </div>
)

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

3 组件

组件,从概念上类似javaScript函数,它可以接受任何形式的入参(props),并返回用于描述页面展示内容的React元素,组件有两种形式:class组件和function组件

3.1class组件

class组件(有状态组件)通常拥有状态和生命周期。(18以后很少使用,比较推崇无状态组件)

import React,{Component} from "react"

export default class ClassComponent extends Component{
  constructor(props){
    super(props);
    //使用state属性维护状态,在构造函数中初始化状态
    this.state = {
      date:new Date()
    }
  }
  componentDidMount(){
    //组件挂载之后启动定时器每秒更新状态
    this.timerID = setInterval(()=>{
      //使用setState方法更新状态
      this.setState({
        date:new Date()
      })
    },1000)
  }
  componentWillUnmount(){
    //组件卸载前停止定时器
    clearInterval(this.timerID)
  }
  componentDidUpdate(){
    console.log("compoentDidUpdate");
  }
  render(){
    return (
      <div>
        {this.state.date.toLocaleTimeString()}
      </div>
    )
  }
}

// 上述代码可简写为
import React,{Component} from "react"

export default class ClassComponent extends Component{
  state = {
    date:new Date()
  }
  componentDidMount(){
    //组件挂载之后启动定时器每秒更新状态
    this.timerID = setInterval(()=>{
      //使用setState方法更新状态
      this.setState({
        date:new Date()
      })
    },1000)
  }
  componentWillUnmount(){
    //组件卸载前停止定时器
    clearInterval(this.timerID)
  }
  componentDidUpdate(){
    console.log("compoentDidUpdate");
  }
  render(){
    return (
      <div>
        {this.state.date.toLocaleTimeString()}
      </div>
    )
  }
}

3.2 function组件

函数组件(function组件)通常无状态和生命周期(react16.8开始引入hooks,函数组件也能拥有状态和相应的生命周期)
hook 允许开发者在不写类组件的情况下,生成状态(state以及一些其他曾经是类组件专属的东西

3.2.1 useState

useState会返回一个数组,里面有两个成员

  1. 以初始化为值的变量 。在调用的时候可以传递函数,也可以传递具体的值。如果传递的是函数,则会直接执行这个函数,将返回值作为初始化的状态。
    注意: 虽然在初始化的时候允许传递函数(纯函数),我们也尽量不要传递函数,因为初始化只会执行一次。初始化不仅需要纯函数,还得是没有任何参数 【因为react在调用你的这个initial function的时候是不会给你传参数】

  2. 修改该变量的函数。这个函数的调用会导致函数组件的重新渲染。调用该函数的时候,可以直接传递一个值,也可以传递一个函数。如果你传递的是一个函数,则react会将上一次的状态传递给你 帮助你,进行计算。(如果你传递的是一个函数,react会将这个函数放到一个队列里面等待执行,那如果我们想每次都时时拿到上一次的值,我们得写成一个函数 。此时状态得更新是批量进行的。

3.2.2 useEffect

useEffect:处理副作用
副作用:完全不依赖React功能的外部操作【这些外部操作不经过react的手,但是却对react产生了一些影响】
例如:
1.http请求
2.dom操作
3.异步请求多数都是会产生副作用的
虽然我们不是所有的副作用操作都是在useEffect里进行,但是官方建议我们尽可以将副作用放在useEffect中运行。 因为副作用操作是会产生意外之外的结果,如果我们想要更好的追踪副作用的执行时机,就可以将副作用都归纳进useEffect里方便追踪

useEffect(setup,dependencies)

setup:初始化的意思,是一个函数
dependencies:依赖,是一个数组

useEffect的执行时机

  1. 当我们使用useEffect去注册setup以后,React会在该组件每次挂载完毕(渲染完毕)到页面时都会执行对应的setup函数,但是是异步执行的setup
  2. 当依赖项发生变更时,useEffect会重新执行对应的setup

**setup函数会有一个返回值,这个返回值称为清理函数,清理函数会在组件卸载的时候执行 **

实际场景
1.http请求
2.访问真实dom

副作用的清除
1.dom事件的绑定清除
2.计时器绑定清除

下面是useState、useEffect实例:

import React,{useState,useEffect} from 'react'

export function FunctionComponent(props){
  const [date,setDate] = useState(new Date());
  useEffect(()=>{
    const timer = setInterval(()=>{
      setDate(new Date());
    },1000)
    return()=>clearInterval(timer);
  },[])
  return(
    <div>
      <h3>FunctionComponent</h3>
      <p>{date.toLocaleTimeString()}</p>
    </div>
  )  
}

setState只有在合成事件和生命周期函数中是异步的,在原生事件和
setTimeout都是同步的,这里的异步其实是批量更新。

可以把useEffect Hook看做componentDidMount、componentDidUpdate和componentwillUnmount这是三个组合

4 生命周期

4.1 生命周期方法

生命周期方法,用于在组件不同阶段执行自定义功能。在组件被创建并插入到DOM时(挂载中阶段)组件更新时,组件取消挂载或从DOM中删除时,都可以使用的生命周期方法
ReactV16.3之前的生命周期
在这里插入图片描述

ReactV16.4之后的生命周期
在这里插入图片描述

v17可能会废弃的三个生命周期函数用getDerivedStateFormProps替代,目前使用的话加上UNSAFE_:

componentWillMount
componentWillReceiveProps
componentWillUpdate

4.2 两个新的生命周期函数

static getDerivedStateFromProps

getDerivedStateFromProps会在render方法之前调用,并在初始化挂载及后续更新时都会被调用。它应返回一个对象来更新state,如果返回的为null,不更新任何内容
注意:不管什么原因,每次渲染前都会触发这个方法,相对于UNSAFE_componentWillReceiveProps而言,后者仅在父组件重新渲染时触发,而不在内部调用setState时触发

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate在render之后,在componentDidUpdate之前触发,会在最近一次渲染输出(提交到DOM节点)之前调用。它使得组件能够在发生更改之前从DOM中捕获一些信息。此生命周期的任何返回值都将作为参数传递给componentDidUpdate(prevProps,prevState,snapshot)
如果不想手动给将要废弃的生命周期添加UNSAFE_前缀,可以使用下面的命令

npx react-codemd react-unsafe-lifecycles

代码学习:

import React ,{Component, component} from "react"
import PropTypes from 'prop-types'

export default class LifeCyclePage extends Component{
  static defaultProps = {
    msg:"omg"
  }
  static propTypes = {
    msg:PropTypes.string.isRequired
  }
  constructor(props){
    super(props)
    this.state = {
      count:0
    }
    console.log("constructor",this.state.count)
  }
  static getDerivedStateForProps(props,state){
    const {count} = state
    console.log("getDerivedStaticFormProps",count)
    return count<5 ?null :{count:0}
  }
  getSnapshotBeforeUpdate(prevProps,prevState,snapshot){
    const {count} = prevState
    console.log("getSnapshotBeforeUpdate",count)
    return null
  }
  componentDidMount(){
    console.log("componentDidMount",this.state.count)
  }
  componentWillUnmount(){
    console.log("componentWillUnmount",this.state.count)
  }
  componentDidUpdate(){
    console.log("componentDidUpdate",this.state.count)
  }
  shouldComponentUpdate(nextProps,nextState){
    const {count} = nextState;
    console.log("shouldComponentUpdate",count,nextState.count)
    return count !==3
  }
  setCount = ()=>{
    this.setState({
      count:this.state.count+1
    })
  }

  render(){
    const {count} = this.state;
    console.log("render",this.state);
    return (
      <div>
        <h1>LifeCyclePage</h1>
        <p>{count}</p>
        <button onClick={this.setCount}>改变count</button>
        <Child count = {count}/>
      </div>
    )
  }
}

class Child extends Component{
    UNSAFE_componentWillReceiveProps(nextProps){
      //在已挂载的组件接收新的 props 之前被调⽤
      console.log("componentWillReceiveProps")
    }
    componentWillUnmount(){
      //组件卸载之前
      console.log("componentWillUnmount")
    }
    render(){
      return(
        <div
          style={{border:"1px solid black",margin:"10px",padding:"10px" }}
        >
          我是child组件
          <div>child count:{this.props.count}</div>
        </div>
      )
    }
}

代码下载地址:https://gitee.com/JingYaBei/my-app.git

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐