react笔记-01基础篇

react笔记

一、使用js创建虚拟dom

const VDOM = React.createElement(标签名, 标签属性, 标签内容)

const VDOM = React.createElement('h1', { id: 'title' }, 'hello react') 

二、使用jsx创建虚拟dom

<div id="app"></div>
<!-- 引入react核心库 -->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<!-- 引入react-dom 支持react操作dom -->
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<!-- babel 用于将jsx转为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
    // 1. 创建虚拟dom
    const VDOM = <h1>Hello React</h1>
    // 2. 渲染虚拟dom到页面
    // ReactDOM.render(虚拟dom, 容器)
    ReactDOM.render(VDOM, document.querySelector('#app'))
</script>
const VDOM = (
    <h1>
        <span>Hello React</span>    
    </h1>
)

三、关于虚拟dom

  1. 本质是Object类型的对象(一般对象)
  2. 虚拟dom比较“轻”
  3. 虚拟dom最终会被react转化为真实dom,呈现在页面上

四、jsx语法规则

  1. 定义虚拟dom时,不要写引号

  2. 标签中混入js表达式时要用花括号{}(vue中是使用{{}},有点区别)

    const title = 'Hello React'
    const data  = {
        name: '小天',
        age: 20
    }
    
    const VDOM = (
        <h1 id={title}>
            <span>{data.name}</span>    
        </h1>
    )
    
  3. 样式的类名不要用class,要用className

  4. 内联样式,要用style={{key:value}}的形式写。

<span style={{ color: 'red', fontSize: '20px' }}>{data.name}</span>
  1. 只能有一个根标签
  2. 标签必须闭合
  3. 标签首字母
1. 若小写字母开头,则将标签转为html同名元素,若html中无该标签对应的同名元素,则报错。
2. 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
  1. jsx的注释
{/* <h1>hello react</h1> */}

五、react组件

1. 函数式组件(适用于简单组件的定义)

// 创建函数式组件
function Compontent () {
    let VDOM = (
        <div>
            <h2>我是函数式组件</h2>
        </div>
    )
    return VDOM
}

// 渲染组件到页面
ReactDOM.render(<Compontent />, document.querySelector('#app'))

2. 类式组件

2.1 类
// 创建一个Person类
class Person {
  // 构造器方法
  // 构造器的this是类的实例对象
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  
  // 类中可以直接写赋值语句,含义是:给Person实例对象添加一个属性,名为a,值为1
  a = 1
  
  // 一般方法
  speak() {
    // speak方法放在了Person类的原型对象上,供实例使用。this是指向调用他的Person实例
    console.log(`我叫${this.name},我的年龄是${this.age}`)
  }
}

// 创建一个Person的实例对象
let p = new Person('小天', 20)
// 调用
p.speak()


// 创建一个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}年级`)
  }
}

let s = new Student('wifi', 20, '大三')

总结:

  1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
  2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
  3. 类中定义的方法,都是放在了类的原型对象上,供实例去使用。
2.2 类中方法的this指向
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  study() {
    console.log(this)
  }
}

const p = new Person('xiaotian', 20)
p.study() // 通过实例调用的study方法
const x = p.study
x() // 函数的直接调用,this指向window,因为类中定义的方法自动开启了局部的严格模式,this值为undefined
2.3 类式组件(适用于复杂组件)
// 创建类式组件
class MyCompontent extends React.Component {
    render() {
        return <h1>hello react</h1>
    }
}

3. 如何区分简单组件和复杂组件:是否有状态(state)

4. 组件实例的三大属性

4.1 属性1: state(状态)
class MyCompontent extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            isHot: false
        }
    }
    render() {
        return (
            <div>
                { this.state.isHot ? '炎热' : '寒冷' }
            </div>
        )
    }
}
4.2 react如何绑定事件
// 创建类式组件
class MyCompontent extends React.Component {
  	// 构造器只调用一次
    constructor(props) {
        super(props)
        this.state = {
            isHot: false
        }
      	// 解决handleClick中的this指向问题
      	// 第二个handleClick是原型链上的,第一个是给原型上添加一个属性
      	this.handleClick = this.handleClick.bind(this)
    }
  	// render至少调用1次,数据更新才会再次调用
    render() {
        return (
            <div onClick={this.handleClick}>
                {this.state.isHot ? '炎热' : '寒冷'}
            </div>
        )
    }
  	// 由于handleClick是作为onClick的回调,所以不是通过实例调用的,是直接调用,this为undefined(看2.2 类中this的指向)
    handleClick() {
      console.log(this);
    }
}

⚠️注意:状态不可直接更改,要通过内置的apisetState()去修改

handleClick() {
  	// 更新数据不是替换,是合并
    this.setState({
        isHot: !this.state.isHot
    })
    console.log(this.state.isHot);
}
4.3 state简写方式
class MyCompontent extends React.Component {
    // 初始化状态
    state = { isHot: false }

    render() {
        return <h1 onClick={this.handleClick}>{ this.state.isHot ? '炎热' : '寒冷' }</h1>
    }

    // 自定义方法:要用赋值语句 + 箭头函数
    handleClick = () => {
        // 获取状态
        const { isHot } = this.state
        // 修改状态
        this.setState({ isHot: !isHot })
    }
}

state是组件对象的重要属性,值是对象(可以包含多个key-value的组合)

⚠️注意:

  1. 组件中render方法中的this为组件实例对象
  2. 组件自定义的方法中this为undefined,如何解决?
    • 强制绑定this:通过函数对象的bind()
    • 箭头函数
  3. 状态数据,不能直接修改或更新
4.4 类式组件使用props
class MyCompontent extends React.Component {
    render() {
        const { name, age } = this.props
        return (
            <div>{ name }{ age }</div>
        )
    }
}

const data = {
    name: 'xiaotian',
    age: 18
}

ReactDOM.render(<MyCompontent name={data.name} age={data.age} />, document.querySelector('#app'))
// 批量传递
ReactDOM.render(<MyCompontent {...data} />, document.querySelector('#app'))
4.5 展开运算符
// 展开运算符不能展开对象
let person = { name: 'xiaotian', age: 20 }
// 外层有{},可以复制一个对象
let person2 = { ...person }
// 但是在react组件传值的时候,并不是复制的一个对象
4.6 对props进行限制,和给默认值
class MyCompontent extends React.Component {
    render() {
      	// props是只读的
        const { name, age } = this.props
        return (
            <div>{ name }{ age }</div>
        )
    }
}

// 给props限制类型
MyCompontent.propsTypes = {
    name: PropTypes.string.isRequired, // isRequired必传
    age: PropTypes.number,
  	speak: ProTypes.func // 类型为函数
}
// 给props默认值
MyCompontent.defaultProps = {
    name: 'xiaoming',
    age: 18
}

ReactDOM.render(<MyCompontent name='xiaotian' age={18} speak="{speak}" />, document.querySelector('#app'))

const speak = () => {
  ...
}

在上述写法已被react移除,需要使用第三方库完成prop-types

  1. 安装库prop-types
npm i prop-types
  1. 使用
import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class GetMsg extends Component {
  static propTypes = { // 小写
    handleChange: PropTypes.func.isRequired // 大写
  }

  render() {
    return (
      <div>
        <input type="text" onChange={(e) => {
          this.props.handleChange(e.target.value)
        }} />
      </div>
    )
  }
}
4.7 props的简写方式

给类自身添加属性(不是给实例添加),可以写在外面

class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
}

Person.data = 100
console.log(Person.data) // 100

写在里面要用关键字static

class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    static data2 = 200
}

console.log(Person.data2) // 200

props的简写

class MyCompontent extends React.Component {
    static propsTypes = {
        name: PropTypes.string.isRequired, // isRequired必传
        age: PropTypes.number
    }
    static defaultProps = {
        name: 'xiaoming',
        age: 18
    }
  
  	state = {}

    render() {
        const { name, age } = this.props
        return (
            <div>{name}{age}</div>
        )
    }
}

ReactDOM.render(<MyCompontent name='xiaotian' age={18} />, document.querySelector('#app'))
4.8 类式组件的构造器(一般不写)
class MyCompontent extends React.Component {
    constructor(props) {
      	// 构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
        super(props)
    }
    render() {
        const { name, age } = this.props
        return (
            <div>{name}{age}</div>
        )
    }
}
4.9 函数式组件使用props

函数式组件只能使用props

function MyCompontent(props) {
    console.log(props);
    const { name, age } = props
    return (
        <div>{name}{age}</div>
    )
}

// 给props限制类型
MyCompontent.propsTypes = {
    name: PropTypes.string.isRequired, // isRequired必传
    age: PropTypes.number,
  	speak: ProTypes.func // 类型为函数
}
// 给props默认值
MyCompontent.defaultProps = {
    name: 'xiaoming',
    age: 18
}

ReactDOM.render(<MyCompontent name='xiaotian' age={18} />,document.querySelector('#app'))
4.10 字符串形式的refs(react新版本不推荐使用)

组件内的标签可以定义ref属性来标识自己

class MyCompontent extends React.Component {
    showData = () => {
      	// 获取input的dom节点
        this.refs.input
    }

    render() {
        return (
            <div>
                <input type="text" ref="input"/>
                <button ref="button" onClick={this.showData}>点击</button>
            </div>
        )
    }
}
4.11 回调函数形式的ref
<input ref={(currentNode) => {console.log(currentNode)}} type="text"/>
// 将节点添加到类上
<input ref={(currentNode) => {this.input = currentNode}} type="text"/>

回调ref中调用次数的问题:当数据更新的时候,会被执行两次,第一次参数currentNodenull,第二次才是当前元素的dom节点。因为每次渲染时会创建一个新的函数实例(会重新调用render),清空旧的ref并设置新的ref,通过将ref的回调函数定义成class的绑定函数的方式可以避免该问题。例如:

class MyCompontent extends React.Component {
    saveData = (currentNode) => {
        this.input = currentNode
    }

    render() {
        return (
            <div>
                <input ref={this.saveData} type="text" />
            </div>
        )
    }
}
4.12 createRef的使用

React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点。

该容器只能存放一个dom节点

class MyCompontent extends React.Component {
    myRef = React.createRef()
  	myRef2 = React.createRef()

    render() {
        return (
            <div>
                <input ref={this.myRef} type="text" />
            		<input ref={this.myRef2} type="text" />
                <button onClick={this.showData}>Click</button>
            </div>
        )
    }

    showData = () => {
     		console.log(this.myRef.current)
     		console.log(this.myRef2.current)
    }
}

5. react中的事件处理

  1. 通过onXxx属性指定事件处理函数(注意大小写)
    • react使用的是自定义(合成)事件,而不是使用的原生dom事件(为了更好的兼容性)
    • react中的事件是通过事件委托方式处理的(委托给组件最外层的元素,为了更高效)
  2. 通过event.target得到发送事件的dom元素对象(不要过度的使用ref)
  3. react收集表单数据,包含表单的组件分类:
    • 非受控组件
    • 受控组件(类似于vue中的v-model绑定的,双向数据绑定)

6. 组件间的数据通信

  • 父传子:通过props
  • 子传父:通过父组件向子组件props传递一个函数,由子组件向函数中传递参数,父组件接收

父组件

import React, { Component } from "react"
import Son from "../Son"

export default class Father extends Component {
    sendData = (data) => {
        console.log(data) // 子组件的数据
    }

    render() {
        return (
            <div>
                <h1>父组件</h1>
                <hr />
                <Son sendData={this.sendData} />
            </div>
        )
    }
}

子组件

import React, { Component } from "react"

export default class Son extends Component {
    handleClick = () => {
        this.props.sendData("子组件向父组件传递数据")
    }

    render() {
        return (
            <div>
                <h1>子组件</h1>
                <button onClick={this.handleClick}>点击按钮:子组件向父组件传递数据</button>
            </div>
        )
    }
}
  • 子孙组件通信(useContext)

useContext是一个 React Hook,可让您从组件读取和订阅上下文。

用法:const value = useContext(SomeContext)

1、参数的含义:SomeContext:使用createContext创建的上下文。上下文本身并不包含信息,它只代表您可以提供或从组件中读取的信息类型。
2、返回值的含义:value:useContext返回调用组件的上下文值,传递给最接近的SomeContext的值。

  • 兄弟组件间通信:

消息订阅和与发布机制,react借助 PubSubJS 库实现

  1. 安装
npm install pubsub-js
  1. 使用
import PubSub from 'pubsub-js'

// 兄弟组件1:发布消息
PubSub.publish('message_name', { name: 'xiaotian', age: 20 })

// 兄弟组件2:订阅消息
PubSub.subscribe('message_name', (msg, data) => {
	// msg:消息名 data:数据
	...
})

六、高阶函数

  1. 高阶函数:如果一个函数符合下面2个规范中的任何一个,那就是高阶函数(例如:Promise、setTimeout)

    • 若A函数,接收的参数是一个函数,那么A就可以称为高阶函数
    • 若A函数,调用返回值是一个函数,那么A就可以称为高阶函数
  2. 函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码方式

    function sun(a) {
      return (b) => {
        return (c) => {
          return a+b+c
        }
      }
    }
    
    const result = sum(1)(2)(3)
    console.log(result)
    
class MyCompontent extends React.Component {
    state = {
        data: ''
    }

    saveData = (event) => {
        console.log(event);
        this.setState({
            data: event.target.value
        })
    }

    render() {
        return (
            <div>
                {/* this.saveData('123') 会直接调用该函数,而saveData的参数event就不是dom节点,而是123 */}
                <input onChange={this.saveData('123')} type="text" />
            </div>
        )
    }
}

解决办法:saveData返回一个函数

class MyCompontent extends React.Component {
    state = {
        data: ''
    }

  	// 函数柯里化 这个event可以不用写,是react自动生成的
    saveData = (msg, [event]) => {
        console.log(msg); // 123
        return (event) => {
          	// event:dom节点
            this.setState({
                data: event.target.value
            })
        }
    }

    render() {
        return (
            <div>
                {/* this.saveData('123') 会直接调用该函数,而saveData的参数event就不是dom节点,而是123 */}
                <input onChange={this.saveData('123')} type="text" />
            </div>
        )
    }
}

例子:

class MyCompontent extends React.Component {
    state = {
        username: '',
        password: ''
    }

    saveData = (inputType) => {
        // inputType: username or password
        return (event) => {
            this.setState({
                // 使用[变量] 可以动态给属性赋值
                [inputType]: event.target.value
            })
            console.log(this.state);
        }
    }

    render() {
        return (
            <div>
                用户:<input onChange={this.saveData('username')} type="text" />
                密码:<input onChange={this.saveData('password')} type="text" />
            </div>
        )
    }
}

七、react脚手架

1. 安装并使用

react脚手架库:create-react-app

  1. 全局安装:
npm i create-react-app -g
  1. 创建项目:
# 创建项目
create-react-app <项目名称>
# 进入项目目录
cd <项目名称>
# 启动项目
npm start

2. 脚手架配置代理

  1. package.json中添加配置,但只能配置一个代理
"proxy": "服务器地址(写到端口为止)"
  1. src/setupProxy.js(文件名为setupProxy,不能修改)中添加配置,可以配置多个代理
const { createProxyMiddleware } = require('http-proxy-middleware')

module.exports = (app) => {
    app.use(
        createProxyMiddleware('/api1', {
            target: 'http://localhost:5000', // 带有/api1路径,就代理到localhost:5000 
            changeOrigin: true,
            pathRewrite: {
                '^/api1': '' // 重新路径,把api1这个前缀替换成''
                // 前面的时候 请求路径会变成 http://localhost:5000/api1 ,但是这个请求路径是找不到的,需要把/api1,重新替换成''
            }
        })
    ),
    app.use(
        createProxyMiddleware('/api2', {
            target: 'http://localhost:5001',
            changeOrigin: true,
            pathRewrite: {
                '^/api2': ''
            }
        })
    )
}
  1. 在组件里面请求
// http://localhost:3000 是react项目端口
// http://localhost:5000/getData 是服务器接口
axios.get('http://localhost:3000/api1/getData')

3. 配置路径别名@

  • 路径解析配置(webpack):把@/解析成src/

配置步骤:

  1. 安装craco
npm i @craco/craco -D
  1. 项目根目录下创建配置文件craco.config.js
  2. 配置文件中添加路径解析配置
const path = require('path')

module.exports = {
    // webpack配置
    webpack: {
        // 配置别名
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    }
}
  1. 包文件中配置启动和打包命令
"scripts": {
  "start": "craco start",
  "build": "craco build"
}
  • vite配置
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
})

⚠️注意:如果path模块爆红,需要安装@types/node

npm i @types/node -D
  • vscode路径提示

jsconfig.jsontsconfig.jsoncompilerOptions字段添加下面内容:

{
	...,
	"compilerOptions": {
		"baseUrl": "./",
		"paths": {
			"@/*": ["./src/*"]      
		}
	}
}

八、CSS

1. CSS-Module样式隔离

在组件内通过import './xxx.css'引入的样式是全局共享的,如果要实现样式的隔离,可以通过以下步骤:

  1. 新建xxx.module.css,后缀名要是.module.css

  2. 在组件内引入,在根标签通过className={style.类名}的形式就可以完成样式的隔离

import React from 'react'
import style from './index.module.css'

export default function about() {
  console.log(style);
  return (
    <div className={style.box}>about</div>
  )
}

可以查看log的结果:

在这里插入图片描述

2. css-in-js(styled-components)

常用库:styled-components

  • 安装
npm install styled-components --save
npm install @types/styled-components -D
  • 使用
1. 基本使用
import styled from "styled-components"
// 类样式当作组件使用 <Button>Normal</Button>
const Button = styled.button`
  background: transparent;
  border-radius: 3px;
  border: 2px solid #BF4F74;
  color: #BF4F74;
  margin: 0 1em;
  padding: 0.25em 1em;
`

const App = () => {
  return <>
    <Button>Normal</Button>
  </>
}

export default App
const Button = styled.button`
  background: "black";
  color: "white";
`;
<Button>Normal</Button>
2. 嵌套使用
import React from "react";
import styled from "styled-components";

const Title = styled.h1`
  font-size: 20px;
  text-align: center;
  color: red;
`;

export default function App() {
  return <Title>Hello World</Title>;
}
3. 组件传参和条件渲染
import styled, { css } from "styled-components"

// 写法1
const Button = styled.button<{ dark?: boolean; }>`
  ${
    props => props.dark && css`
      background: black;
      color: white;
    `
  }
`
// 写法2
const Button = styled.button<{ dark?: boolean; }>`
  background: ${props => props.dark ? "black" : "white"};
  color: ${props => props.dark ? "white" : "black"};
`

const App = () => {
  return <>
    <Button dark>按钮</Button>
  </>
}

export default App
4. 全局样式
import { createGlobalStyle } from "styled-components";
export const GlobalStyle = createGlobalStyle`
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    background: pink;
  }
`
5. 样式组件的继承
const BoringButton = styled.button`
    color: blue;
    background-color: green;
`;

const CoolButton = styled(BoringButton)`
  color: pink;
`;
6. 使用css辅助函数(公共样式)

在多个样式组件中使用通用的样式可以使用css辅助函数

import React from "react";
import styled, {css} from "styled-components";

const commonStyle = css`
  color: white;
  font-size: 20px;
`;

const Button = styled.button`
  ${commonStyle};
  background-color: red;
`;

const AnotherButton = styled.button`
  ${commonStyle};
  background-color: green;
`;
7. 元素属性
  1. style.ts
import styled from 'styled-components'

export const AppStyled = styled.a.attrs({
  href: 'www.baidu.com',
  target: '_blank'
})`
  color: red;
  font-size: 30px;
`
  1. App.tsx
import { AppStyled } from "./style";

export default function App() {
  return <AppStyled>App</AppStyled>
}
  1. 效果

在这里插入图片描述

九、补充语法

1. 如何连续结构赋值

let obj = {
  name: '张三',
  age: 18,
  info: {
    address: '合肥'
  }
}

const { info: { address  } } = obj
console.log(address)
console.log(info) // 但是不能log info,报错
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值