react基础入门,类组件和函数组件,state,props,refs

本文详细介绍了React入门知识,包括React与Vue的异同点,React的安装、类的基础知识、组件渲染、JSX语法规则、props和state的使用、refs的处理。还探讨了组件化开发、模块化以及React中的生命周期钩子,同时讲解了受控组件和非受控组件的概念。此外,还提到了React Hooks和路由的基本应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

React入门


React 官方中文文档 – 用于构建用户界面的 JavaScript 库 (reactjs.org)

React入门

React 官方中文文档 – 用于构建用户界面的 JavaScript 库 (reactjs.org)

Vue跟React的异同点

相同点
  • 1.都使用了虚拟dom
  • 2.组件化开发
  • 3.都是单向数据流(父子组件之间,不建议子修改父传下来的数据)
  • 4.都支持服务端渲染
不同点
  • 1.React的JSX,Vue的template
  • 2.数据变化,React手动(setState),Vue自动(初始化已响应式处理,Object.defineProperty)
  • 3.React单向绑定,Vue双向绑定
  • 4.React的Redux,Vue的Vuex

Vue小建议

「自我检验」熬夜总结 50 个 Vue 知识点,全都会你就是神!!! (qq.com)

1. 不需要响应式的数据应该怎么处理?
// 方法一:将数据定义在data之外
data(){
    // 不需要做响应式处理的数据
    this.list = {xxxxx}
    return{}
}
// 方法二:Object.freeze()
data(){
    return {
        list1: Object.freeze({xxxxxxx})
    }
}
2. Key
  • 不建议用indexkey

  • 不建议用随机数做key

3. 数据结构
  • new Map();
  • new Set();

React 教程

1、安装React

CDN

<!--	引入react核心库-->
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<!--	引入react-dom,用于支持react操作dom-->
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<!--	引入babel,用于将jsx转换为js -->
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
// 一定要写babel
// 编写jsx语法
</script>
<!-- 用于props的类型校验 -->
<script src=https://cdn.staticfile.org/prop-types/15.5.10/prop-types.js ></script>

脚手架安装

# npx
npx create-react-app my-app
cd my-app
npm start

# ts
yarn create react-app my-app-ts --template typescript

# 全局安装
npm i -g create-react-app

2、类的基础知识

2.1 类的基础知识

// 基础知识
class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    study() {
        console.log(this) // this 指向的是实例对象
        console.log('我在学习')
    }
}
const p1 = new Person('张三', 18);
const p2 = new Person('李四', 18);
console.log(p1)
console.log(p2)
p2.study()

2.2 类的继承

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  speak() {
    //  speak方法放在了哪里?类的原型对象上,供实例使用
    // 通过Person实例调用speak时,speak中的this就是Person实例
    console.log(`我叫${this.name},我的年龄是${this.age}`)
  }
}
// 创建一个Student类,继承Person类
class Student extends Person{
  constructor(name, age, grade) {
    super(name, age);
    this.grade = grade
  }
  // 子类重写父类的方法
  speak() {
    console.log(`我叫${this.name},我的年龄是${this.age},我在读${this.grade}年级`)
  }

  study() {
    console.log('我在学习')
  }
}
const s1 = new Student('小张', 15, '高一')
console.log(s1)
s1.speak()
s1.study()

2.3 类的继承-偏向于react中props

class Person {
  constructor(props) {
    this.name = props.name
    this.age = props.age
  }
  speak() {
    //  speak方法放在了哪里?类的原型对象上,供实例使用
    // 通过Person实例调用speak时,speak中的this就是Person实例
    console.log(`我叫${this.name},我的年龄是${this.age}`)
  }
}
// 创建一个Student类,继承Person类
class Student extends Person{
  constructor(props) {
    super(props);
    this.grade = props.grade
  }
  // 子类重写父类的方法
  speak() {
    console.log(`我叫${this.name},我的年龄是${this.age},我在读${this.grade}年级`)
  }

  study() {
    console.log('我在学习')
  }
}
// 创建student实例
const s1 = new Student({
  name: '小张',
  age: 18,
  grade: '高二'
})
console.log(s1)
s1.speak()
s1.study()

2.4 this的指向问题

// 改变方法中this的指向    babel会启动严格模式 此时的 window会是undefined    
// script type="text/javascript"  时window打印没问题
function demo1() {
    console.log(this)
}
demo1()
const x = demo1.bind({a:1, b:2})
x()

3、简单的组件

  • function 函数组件【推荐】
  • class 类组件
// 简单的函数组件
function Demo1() {
  return <h1>Hello</h1>
}

// 类组件 需要继承React.Component
class DemoClass extends React.Component {
  render() {
    return <h2>Class</h2>
  }
}

// 不使用jsx 时的写法
function Demo3() {
  const li1 = React.createElement('li', {name: 'Jack'}, 'list-1')
  const li2 = React.createElement('li', {name: 'Jack'}, 'list-2')
  return React.createElement('ul', {name: 'Jack'}, li1, li2)
}

// 渲染组件
ReactDOM.render(
  <div>
    <Demo3/>
    <Demo1/>
    <DemoClass/>
  </div>, // 标签必须闭合
  document.getElementById('example')
)

react中类组件的渲染过程

执行了ReactDOM.render() 之后,发生了什么?

    1. React解析组件标签,找到了 MyComponent组件
    1. 发现组件时使用类定义的,随后new出来该类的实例,并通过该实例调用到原型对象上的render方法
    1. 将render返回的虚拟DOM转为真是DOM,随后呈现在页面中
// 创建类组件
class MyComponent extends React.Component {
    // render是放在哪里?  -- MyComponent的原型对象上,供实例使用
    // render中的this是谁? -- MyComponent的实例对象
    render() {
        console.log('render中的this:', this)
        return <div>
            <h2>我是类定义的组件(适用于【复杂组件】的定义)</h2>
        </div>
    }
}
// 渲染组件到页面
ReactDOM.render(
    <MyComponent/>,
    document.getElementById('example')
)

4、jsx语法规则

  1. 定义虚拟DOM时,不要写引号
  2. 标签中混入js表达式时要用{}
  3. 样式的类名指定不要用class,要用className
  4. 内联样式,要用style={{key: value}}的形式去写,样式名用驼峰形式
  5. 只有一个跟标签
  6. 标签必须闭合
  7. 标签首字母解析规则
    1. 若小写首字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错
    2. 若大写首字母开头,react就去渲染对应的组件,若组件没有定义,则报错

5、区分【js语句(代码)】与【js表达式】

  1. 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方

    下面这些都是表达式:

    1. a
    2. a+b
    3. demo(1)
    4. arr.map()
    5. function test(){}
  2. 语句(代码):

    下面这些都是语句(代码):

    1. if(){}
    2. for(){}
    3. switch(){case: xxx}

6、模块和组件、模块化和组件化得理解

模块

  1. 理解:向外提供特定功能的js程序,一般就是一个js文件
  2. 为什么要拆分成模块:随着业务逻辑增加,代码越累越多切复杂
  3. 作用:复用js,简化js的编写,提高js运行效率

组件

  1. 理解:用来实现局部功能效果的代码和资源的集合(html/js/css/image等等)
  2. 为什么:一个界面的功能更负责
  3. 作用:复用编码,简化项目编码,提高运行效率

模块化

当应用的js都是以模块来编写的,这个应用就是一个模块化的应用

组件化

当应用是以组件的方式来实现的,这个应用就是一个组件化的应用

7、 组件中的样式

style 需要使用驼峰写法

<!--
   style 需要使用驼峰写法
   font-size  -> fontSize
  -->  
ReactDOM.render(<div>
    <h2 className="red">Hello</h2>
    <h2 style={{color: 'green', fontSize: '30px'}}>Hello</h2>
</div>, document.getElementById('app'))

8、组件实例的三大核心属性

  • prps
    • 每个组件都会有props属性【只读】
    • 组件标签的所有属性都保存在props中
    • 作用:通过标签属性从组件外 向组件内传递数据(只读 read only)
    • 对props中的属性值进行类型限制和必要性限制
  • state
    • 更改state:setState()方法,不可直接更改数据
  • refs
    • 给DOM元素添加ref属性
    • 给类组件添加ref属性

既然是组件实例的核心属性,由于function函数组件没有实例,所以不能使用state和refs,但是可以使用props【函数组件可以以传参的形式来接收props】

8、类组件

8.1 state

8.1.1 基本用法
// 类组件 有状态
class Hello extends React.Component {
    constructor(props) {
        super(props); // 写了constructor必须写super
        this.state = {
            count: 0
        }
    }
    render() {
        return <div>
            <h2>Hello, {this.state.count}</h2>
        </div>
    }
}
// 简写方式
 class Hello1 extends React.Component{
     state = {
         count: 1
     }
    render() {
        return <h3>{this.state.count}</h3>
    }
}
8.1.2 处理this指向的几种方法
  1. 【推荐】定义方法时使用箭头函数,因为箭头函数没有自己的this
  2. 在onclick方法处通过bind改变this指向
  3. 在构造函数中,将原型对象中的方法赋值给实例对象【直接在class中写的方法会出现在实例对象的原型对象中】这样在调用的时候调用的是实例对象的方法,this即指向实例对象
  // 类组件 有状态
  class Hello extends React.Component {
    constructor(props) {
      super(props);
      // 方法2. 可在构造函数中将发放赋值给实例对象
      // this.handleAddCount = this.handleAddCount.bind(this)
      this.state = {
        count: 0
      }
    }

    /**
     * 这种写法中的this指向的是按钮的onclick
     * 可在onClick={this.handleAddCount.bind(this)} 绑定this
     */
    handleAddCount() {
      console.log(this)
	    let {count} = this.state;
	    this.setState({
		    count: ++count
	    })
    }

    //  方法3. 方法使用箭头函数:因为箭头函数没有this,会继承父级的this
    handleAddCount1 = () => {
      console.log(this)
      let {count} = this.state;
      this.setState({
        count: ++count
      })
    }

    render() {
      return <div>
        <h2>Hello, {this.state.count}</h2>
        <button onClick={this.handleAddCount}> +1 [错误写法]</button><br/><br/>
        {/* 方法1. 可在onClick={this.handleAddCount.bind(this)} 绑定this*/}
        <button onClick={this.handleAddCount.bind(this)}>onclick bind +1</button><br/><br/>
        <button onClick={this.handleAddCount1}>箭头函数+1</button>
      </div>
    }
  }

  ReactDOM.render(
    <Hello/>,
    document.getElementById('example')
  )
8.2 类组件渲染列表数据到页面及按钮点击方法
  /**
   * 类组件
   * state 用法
   */
  class ListClass  extends React.Component{
	  constructor(props) {
      super(props);
      this.state = {
        arr: [1,2]
      }
    }
    handleAddList = () => {
	    const arr = this.state.arr;
	    this.setState({
		    arr: [...arr, arr.length + 1]
	    })
    }
    render() {
	    return <div>
		    <ul>
			    {
			      this.state.arr.map(item => {
			        return <li>{item}</li>
			      })
			    }
		    </ul>
		    <button onClick={this.handleAddList}>添加一个</button>
	    </div>
    }
  }
  ReactDOM.render(<ListClass/>, document.getElementById('app'))

8.2 props

8.2.1 类组件中props基本用法
class Example extends React.Component{
	render() {
	  const {name, age} = this.props
	  return <div>
		  {name}--{age}
	  </div>
	}
}

ReactDOM.render(
  <div>
    <Example name="Jack" age={18}/>
    <Example name="Tom" age={20}/>
  </div>,
  document.getElementById('example')
)
8.2.2 批量传递props
// 批量传递props
const p = {name: 'Jerry', age: 21}
ReactDOM.render(
  <div>
	  <Example {...p}/>
  </div>,
  document.getElementById('example')
)
8.2.3 对类组件props进行限制

需要引入 prop-types.js

自React v15.5 起,React.PropTypes 已移入另一个包中

class Person extends React.Component{
	render() {
	  const {name, age} = this.props
	  return <div>
		  {name}--{age}
	  </div>
	}
}
// 下面这种写法会将 propTypes 和 defaultProps放在Person类中
// 对props进行限制
Person.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number,
	speak: PropTypes.func // 限制为函数
}
// props默认值
Person.defaultProps = {
  age: 20
}
console.log(new Person())
// 批量传递props
const p = {name: 'Jerry', age: 21}
ReactDOM.render(
  <div>
    <Person name="Jack" age={18}/>
    <Person name="Tom" age={20}/>
	  <Person {...p}/>
  </div>,
  document.getElementById('example')
)

将props的限制 propTypes和defaultProps写在类里面

注意:static 将属性加到类本身上,如果不写static会加载类的实例对象上

class Person extends React.Component{
  // 对props进行限制
  static propTypes = {
    name: PropTypes.string.isRequired,
    age: PropTypes.number
  }
// props默认值
  static defaultProps = {
    age: 20
  }
  render() {
    const {name, age} = this.props
    return <div>
      {name}--{age}
    </div>
  }
}

ReactDOM.render(
  <div>
    <Person name="Jack" age={18}/>
    <Person name="Tom" age={20}/>
  </div>,
  document.getElementById('example')
)
8.2.4 函数组件中使用props
function Person(props) {
  const {name, age} = props
  return <div>
    {name}--{age}
  </div>
}

// 对props进行限制
Person.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number
}
// props默认值
Person.defaultProps = {
  age: 20
}
ReactDOM.render(
  <div>
    <Person name="Jack" age={18}/>
    <Person name="Tom" age={20}/>
  </div>,
  document.getElementById('example')
)

8.3 ref

  • 勿过度使用 Refs
8.3.1 String 类型的 refs

过时的api,可能会在未来版本中被弃用

class Example extends React.Component {
  handleClick = () => {
    const {input1} = this.refs
    alert(input1.value)
  }
  showMsg2 = () => {
    const {input2} = this.refs
    alert(input2.value)
  }
  render() {
    return (<div>
      <input ref="input1" type="text" placeholder="ref1"/>
      <button onClick={this.handleClick}>获取数据</button>
      <input ref="input2" onBlur={this.showMsg2} type="text" placeholder="ref1"/>
    </div>)
  }
}
ReactDOM.render(
  <Example/>,
  document.getElementById('example')
)
8.3.2 回调函数 refs

如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

内联函数回调

class Example extends React.Component {
  handleClick = () => {
    const {input1} = this
    alert(input1.value)
  }
  showMsg2= () => {
    const {input2} = this
    alert(input2.value)
  }
  render() {
    return (<div>
      <input ref={currentNode => this.input1 = currentNode} type="text"/>
      <button onClick={this.handleClick}>获取数据</button>
      <input ref={currentNode => this.input2 = currentNode} onBlur={this.showMsg2} type="text"/>
    </div>)
  }
}
ReactDOM.render(
  <Example/>,
  document.getElementById('example')
)

不使用内联回调的方式

class Example extends React.Component {
  handleClick = () => {
    const {input1} = this
    alert(input1.value)
  }
  showMsg2= () => {
    const {input2} = this
    alert(input2.value)
  }

  saveInput = (c) => {
	  this.input1 = c
  }
  saveInput2 = (c) => {
	  this.input2 = c
  }

  render() {
    return (<div>
      <input ref={this.saveInput} type="text" placeholder=""/>
      <button onClick={this.handleClick}>获取数据</button>
      <input ref={this.saveInput2} onBlur={this.showMsg2} type="text" placeholder=""/>
    </div>)
  }
}
ReactDOM.render(
  <Example/>,
  document.getElementById('example')
)
8.3.3 React.createRef() 创建

Refs 是使用 React.createRef() 创建的,并通过 ref 属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。

React.createRef() 调用后可以返回一个容器,容器可以存储被ref所标识的节点,该容器是专人专用的【需要几个创建几个】

class Example extends React.Component {
  myRef = React.createRef()
  handleClick = () => {
    console.log(this)
    const {myRef} = this
    alert(myRef.current.value)
  }
  render() {
    return (<div>
      <input ref={this.myRef} type="text" placeholder=""/>
      <button onClick={this.handleClick}>获取数据</button>
    </div>)
  }
}
ReactDOM.render(
  <Example/>,
  document.getElementById('example')
)

9、函数组件

使用React Hooks

  • useState() 进行状态管理
  • useEffect
/**
 * 函数组件中没有状态及this
 * 可以使用 react Hooks实现
 */
import {useEffect, useState} from 'react'
import { Button, Divider } from 'antd'
function Home () {
  const [count, setCount] = useState(0);
  /**
   * 没有第二个参数的时候会多次调用
   * 为空数组的时候 调用一次后不再调用
   * 数组中包含某个参数的时候,参数改变的时候会调用  如:[count]
   */
  useEffect(() => {
    console.log('home-useEffect');
  }, [])

  const handleCount = () => {
    setCount(preCount => ++preCount)
  }
  function handleCount1() {
    setCount(preCount => ++preCount)
  }
 return(
  <div>
    <Divider>Home</Divider>
    <h2 onClick={handleCount}>{count}</h2>
    <Button type='primary' onClick={handleCount1}>{count}</Button>
  </div>
 )
}

export default Home

10、受控组件和非受控组件

非受控组件 – React (docschina.org)

  • 受控组件: 表单数据是由 React 组件来管理的
  • 非受控组件: 表单数据将交由 DOM 节点来处理
10.1 高阶函数

高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数

  1. 若A函数,接收的参数是一个函数,那么A函数就可以称之为高阶函数

  2. 若A函数,调用的返回值依然是一个函数,那么A函数就可以称之为高阶函数

    常见的高阶函数有:Promise,setTimeout,arr.map()等等

10.2 表单组件实例
使用非受控组件实现
class Example extends React.Component {
  handleSubmit = (evnet) => {
    evnet.preventDefault()
    const {username, password} = this
    console.log(`用户名是${username.value},密码是${password.value}`)
  }
  render() {
    return (<form onSubmit={this.handleSubmit}>
      用户名 <input ref={c => {this.username = c}} type="text" placeholder="用户名"/>
      密码 <input ref={c => {this.password = c}} type="password" placeholder="密码"/>
      <button>登录</button>
    </form>)
  }
}
ReactDOM.render(
  <Example/>,
  document.getElementById('example')
)
使用受控组件实现
class Example extends React.Component {
  state = {
    username: '',
	  password: ''
  }
  saveUsername = (event) => {
    this.setState({
	    username: event.target.value
    })
  }
  savePassword = (event) => {
    this.setState({
      password: event.target.value
    })
  }
  handleSubmit = (event) => {
    event.preventDefault()
    const {username, password} = this
    console.log(`用户名是${username.value},密码是${password.value}`)
  }
  render() {
    return (<form onSubmit={this.handleSubmit}>
      用户名 <input onChange={this.saveUsername } type="text" placeholder="用户名"/>
      密码 <input onChange={this.savePassword} type="password" placeholder="密码"/>
      <button>登录</button>
    </form>)
  }
}
ReactDOM.render(
  <Example/>,
  document.getElementById('example')
)
使用受控组件实现-优化
class Example extends React.Component {
  state = {
    username: '',
	  password: ''
  }
  saveFormData = (dataType) => {
    return (event) => {
      this.setState({
	      [dataType]: event.target.value
      })
	  }
  }
  handleSubmit = (event) => {
    event.preventDefault()
	  const {username, password} = this.state
    console.log(`用户名是${username},密码是${password}`)
  }
  render() {
    return (<form onSubmit={this.handleSubmit}>
      用户名 <input onChange={this.saveFormData('username') } type="text" placeholder="用户名"/>
      密码 <input onChange={this.saveFormData('password')} type="password" placeholder="密码"/>
      <button>登录</button>
    </form>)
  }
}
ReactDOM.render(
  <Example/>,
  document.getElementById('example')
)

11、生命周期钩子

生命周期图谱 React lifecycle methods diagram (wojtekmaj.pl)

文档 State & 生命周期 – React (reactjs.org)

  • componentWillMount

  • componentDidMount

  • shouldComponentUpdate

  • componentWillUpdate

  • componentDidUpdate

  • componentWillUnmount

class Example extends React.Component {
  constructor(props) {
    console.log('constructor')
    super(props);
    this.state = {
      count: 0
    }
  }
  // 渲染前
  componentWillMount() {
    console.log('componentWillMount')
  }
  // 渲染后
  componentDidMount() {
    console.log('componentDidMount')
  }
  // 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
  shouldComponentUpdate() {
    console.log('shouldComponentUpdate')
	  return true // false时不会更新数据
  }
  // 更新前
  componentWillUpdate() {
    console.log('componentWillUpdate')
  }
  // 更新后
  componentDidUpdate() {
    console.log('componentWillUpdate')
  }
  // 卸载前
  componentWillUnmount() {
    console.log('componentWillUnmount')
  }
  handleClick = () => {
    this.setState({
	    count: ++this.state.count
    })
  }
  render() {
    console.log('render')
    return (<div>
	    <h1>{this.state.count}</h1>
      <button onClick={this.handleClick}>count+1</button>
    </div>)
  }
}

ReactDOM.render(
  <Example/>,
  document.getElementById('example')
)

14、路由

/router/index.js

import React, {Component} from 'react'
import {Route, BrowserRouter, Switch, Link} from 'react-router-dom';
// 引入页面
import Home from "../pages/Home";
import About from "../pages/About";
import Welcome from "../pages/Welcome";
import Student from "../pages/student";

// 导航组件
class LinkPage extends Component {
  render() {
    return (
      <div>
        我是导航组件<br/>
        <Link to="/">Welcome</Link><br/>
        <Link to="/home">Home</Link><br/>
        <Link to="/about">About</Link><br/>
        <Link to="/student/1234">Student</Link>
      </div>
    );
  }
}
// 路由
const Root = () => (
  <BrowserRouter>
    {/*导航组件*/}
    <LinkPage/>
    <Switch>
      <Route exact path="/" component={Welcome}/>
      <Route path="/home" component={Home}/>
      <Route path="/about" component={About}/>
      <Route path="/student/:id" component={Student}/>
    </Switch>
  </BrowserRouter>
)
export default Root

/index.js

import React from "react";
import ReactDOM from 'react-dom';
import Root from "./router/index";

ReactDOM.render(
  <Root/>,
  document.getElementById('root')
)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值