react 初级基础

news2024/11/27 3:38:00

react基本使用

项目创建

  • 项目的创建命令 npx create-react-app react-basic
  • 创建一个基本元素进行渲染 
// 1 导入react 和 react-dom
import React from "react";
import ReactDOM from "react-dom";

// 2 创建 react 元素 React提供了创建元素的api ReactDOM 渲染页面
/**
 * 第一个参数:要创建的标签名称
 * 第二个参数:元素自身的属性,没有就写null,有就传入一个对象
 * 第三个及以后的所有参数:统统为元素的子节点
 */
const h1 = React.createElement("a", { href: "http://baidu.com" }, "点击去百度");

// 3 渲染 react 元素 ReactDOM提供了渲染元素的api
ReactDOM.render(h1, document.querySelector("#root"));

注意:ReactDOM 渲染在React 18中不再被支持。请使用createRoot。在你切换到新的API之前,你的应用程序会像运行React 17一样运行

import { createRoot } from 'react-dom/client';
const h1 = React.createElement("div", { id:'box',className:'demo' }, "hello");

const el = React.createElement("ul", { className: 'list' }, 
    React.createElement('li', null, '香蕉'),
    React.createElement('li',null,'香蕉'),
    React.createElement('li',null,'香蕉'),
);

const container = document.getElementById('root');
const root = createRoot(container);
root.render(h1)
root.render(el)

JSX语法

jsx是react的核心内容

注意:JSX 不是标准的 JS 语法,是 JS 的语法扩展。脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法。

babel 试一试

在 babel 的试一试中,可以通过切换 React Runtime 来查看:

  1. Classic -> React.createElement:注意这种方式,需要依赖于 React,也就是只要在代码中写 JSX 就必须导入 React 包

  2. Automatic -> _jsxRuntime:不依赖于 React 包,因此,代码中可以省略 React 包的导入【最新的脚手架已经默认值采用这种形式】

总结

  1. 使用jsx创建元素是react声明式UI的体现

  2. 使用jsx语法更加直观,降低学习成本,提高开发效率

jsx在 react 内部的运行做了什么转换?React Runtime(react运行时)

React Runtime(react运行时)分两种形式,一种是通过Classic转换成了传统的 eact.createElement 元素形式,另一种是通过Automatic ->_jsxRuntime.jsx的形式(可见babel官网)

项目配置工作:

  • 使用prettier插件格式化,可以格式化react代码(settings.json)

// 编辑器保存的时候用使用prettier进行格式化

"editor.formatOnSave": true,

// 编辑器默认使用prittier作为格式化工具

"editor.defaultFormatter": "esbenp.prettier-vscode",

// 不要有分号

"prettier.semi": false,

// 使用单引号

"prettier.singleQuote": true,

  •  配置vscode ,在vscode中使用tab键可以快速生成html内容进行标签提示(settings.json)
// 配置vscode ,在vscode中使用tab键可以快速生成html内容
// 在js中启用emmet语法
"emmet.includeLanguages": {
    "vue-html":"html",
    "javascript":"javascriptreact",
    "postcss":"css",
    "wxml":"html"
},
// 按tab键展开emmet语法
"emmet.triggerExpansionOnTab": true,
"emmet.showSuggestionsAsSnippets": true

jsx中渲染JS数据

jsx格式

  • jsx中可以出现任意的js表达式

  • 在jsx中只能出现js表达式,不能出现js语句,比如ifforwhile

const name = 'zs'
const age = 18

// 这就是jsx 表达格式
const title = (
  <h1>
    姓名:{name}, 年龄:{age}
  </h1>
)

// jsx 本身也可以是一个表达式
const span = <span>我是一个span</span>
const title = <h1>JSX 做为表达式:{span}</h1>
使用 单花括号,括号中可以使用js表达式,不能使用语句例如if
JS 表达式:数据类型和运算符的组合(可以单独出现数据类型,也可以数据类型+运算符的组合)
+ + 特点:==有值== 或者说 能够计算出一个值
  + 字符串、数值、布尔值、null、undefined、object( [] / {} )
  + 1 + 2、'abc'.split('')、['a', 'b'].join('-')
  + function fn() {}、 fn() - 注意:*函数调用可以渲染返回值,函数不能直接渲染,但是,
将来可以作为事件处理程序,因此,总的来说也可以出现在 {} 中*
  + 验证是不是 JS 表达式的技巧:看内容能不能作为方法的参数,比如,`console.log( 表达式 )`
  + jsx本身也是一个表达式

+ 在jsx中使用表达式语法:`{ JS 表达式 }`
  - 比如,`<h1>你好,我叫 {name}</h1>`
const name = '强哥'
const h1 = <ul>{ name}</ul>

 jsx条件渲染

  • 在react中,一切都是javascript,所以条件渲染完全是通过js来控制的

  • 在react中,可以使用if/else三元运算符逻辑与(&&)运算符实现条件的渲染

const isLoding = false
const loadData = () => {
  if (isLoding) {
    return <div>数据加载中.....</div>
  } else {
    return <div>数据加载完成,此处显示加载后的数据</div>
  }
}

const title = <div>条件渲染:{loadData()}</div>

jsx列表渲染

  • 作用:重复生成相同的 HTML 结构,比如,歌曲列表、商品列表等

  • 实现:使用数组的 map 方法

  • 注意:需要为遍历项添加 key 属性

    • key 在 HTML 结构中是看不到的,是 React 内部用来进行性能优化时使用的

    • key 在当前列表中要唯一

    • 如果列表中有像 id 这种的唯一值,就用 id 来作为 key 值

    • 如果列表中没有像 id 这种的唯一值,就可以使用 index(下标)来作为 key 值

const songs = [
  { id: 1, name: '痴心绝对' }, 
  { id: 2, name: '像我这样的人' }, 
  { id: 3, name: '南山南' }
]

const dv = (
  <div>
    <ul>{songs.map(song => <li key={song.id}>{song.name}</li>)}</ul>
  </div>
)

jsx样式处理

  • 行内样式 - style

    1. 像 width/height 等属性,可以省略 px,直接使用 数值 即可

    2. 如果是需要使用百分比的单位,此时,继续使用字符串的值即可(比如,"60%"

  • 类名 - className【推荐】实际项目中推荐使用classNames

  • 行内样式-style

const dv = (
  <div style={ { color: 'red', backgroundColor: 'pink' } }>style样式</div>
)
  • 类名-className

// 导入样式
import './base.css'
const dv = <div className="title">style样式</div>

总结:

组件基础

React创建组件-函数组件

  • 函数组件:使用 JS 的函数(或箭头函数)创建的组件,叫做函数组件

    • 约定1:函数名称必须以大写字母开头,React 据此区分组件和普通的 HTML标签

    • 约定2:函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null

  • 使用函数创建组件

// 使用普通函数创建组件:
function Hello() {
  return <div>这是我的第一个函数组件!</div>
}

// 使用箭头函数创建组件:
const Hello = () => <div>这是我的第一个函数组件!</div>
  •  使用组件

    • 组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数返回值对应的内容

    • 使用函数名称作为组件标签名称

// 使用 单标签/双标签 渲染组件:
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
        <>
            <Button />,
            <Hello />
        </>
)

React创建组件-类组件

  • 使用 ES6 的 class 创建的组件,叫做类(class)组件

  • 约定1:类名称也必须以大写字母开头

  • 约定2:类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性

  • 约定3:类组件必须提供 render 方法

  • 约定4:render 方法必须有返回值,表示该组件的 UI 结构

import React, { Component} from "react";
import { createRoot } from 'react-dom/client';
// class Create extends React.Component{}
class Create extends Component{
    render(){
        return <span>React创建组件-类组件</span>
    }
}

const container = document.getElementById('root');
const root = createRoot(container);
root.render(<>
    <Create></Create>
</>)

 将组件提取到单独的js文件中

  • 将react组件提取到独立的js文件中,组件作为一个独立的个体,一般都会放到一个单独的 JS 文件中
// index.js
import Hello from './Hello'
// 渲染导入的Hello组件
root.render(<Create></Create>)


// Hello.js
import { Component } from 'react'
class Hello extends Component {
  render() {
    return <div>Hello Class Component!</div>
  }
}
// 导出Hello组件
export default Hello

有状态组件和无状态组件

  • 函数组件又叫做无状态组件 函数组件是不能自己提供数据【前提:不考虑 hooks 的情况下】,函数组件是没有状态的,只负责页面的展示态,不会发生变化),性能比较高
  • 类组件又叫做有状态组件 类组件可以自己提供数据,负责更新UI,只要类组件的数据发生了改变,UI 就会发生更新态)
  • 函数组件和类组件的区别:有没有状态【前提:不考虑 hooks 的情况下】,状态(state)是组件的私有数据,当组件的状态发生了改变,页面结构也就发生了改变(数据驱动视图),在项目中,一般都是由函数组件和类组件共同配合来完成的
  • 比如计数器案例,点击按钮让数值+1, 0和1就是不同时刻的状态,当状态从0变成1之后,UI也要跟着发生变化。React想要实现这种功能,就需要使用有状态组件来完成

  • 类组件的状态

    • 状态state,即数据,是组件内部的私有数据,只能在组件内部使用

    • 状态 state 的值是对象,表示一个组件中可以有多个数据

    • 通过 this.state.xxx 来获取状态

class Hello extends Component {
  // 为组件提供状态
  state = {
    count: 0,
    list: [],
    isLoading: true
  }

  render() {
   // 通过 this.state 来访问类组件的状态
    return (
      <div>计数器:{this.state.count}</div>
    )
  }
}

 setState 修改状态

  • react核心理念 -- 状态不可变 状态不可变指的是:不要直接修改状态的值,而是基于当前状态创建新的状态值
  • 语法:this.setState({ 要修改的部分数据 })
  • setState() 作用:1 修改 state 2 更新 UI,setState 是从 Component 父类继承过来的
  • 思想:数据驱动视图,也就是只需要修改数据(状态)那么页面(视图)就会自动刷新
class Hello extends Component {
  state = {
    count: 0
  }

  handleClick = () => {
    this.setState({
      count:  this.state.count + 10
    })
  }  

  render() {
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>按钮+1</button>
      </div>
    )
  }
}

setState 进阶

理解setState 延迟更新数据

  • setState 方法更新状态是同步的,但是表现为延迟更新状态(注意:非异步更新状态!!!

  • 延迟更新状态的说明:

    • 调用 setState 时,将要更新的状态对象,放到一个更新队列中暂存起来(没有立即更新)

    • 如果多次调用 setState 更新状态,状态会进行合并,后面覆盖前面

    • 等到所有的操作都执行完毕,React 会拿到最终的状态,然后触发组件更新

  • 优势:多次调用 setState() ,只会触发一次重新渲染,提升性能

setState箭头函数的语法

  • 解决多次调用依赖的问题

  • 推荐:使用 setState((prevState) => {}) 语法

  • 参数 prevState:表示上一次 setState 更新后的状态

this.setState((prevState) => {
  return {
    count: prevState.count + 1
  }
})

 使用setState的回调函数

  • 第二个参数,操作渲染后的DOM
  • 场景:在状态更新(页面完成重新渲染)后立即执行某个操作
  • 语法:setState(updater[, callback])

this.setState(
    (state) => ({}),
    () => {console.log('这个回调函数会在状态更新后并且 DOM 更新后执行')}
)

同步or异步

  • setState本身并不是一个异步方法,其之所以会表现出一种“异步”的形式,是因为react框架本身的一个性能优化机制

  • React会将多个setState的调用合并为一个来执行,也就是说,当执行setState的时候,state中的数据并不会马上更新

  • setState如果是在react的生命周期中或者是事件处理函数中,表现出来为:延迟合并更新(“异步更新”)

  • setState如果是在setTimeout/setInterval或者原生事件中,表现出来是:立即更新(“同步更新”)

class App extends Component {
  state = {
    count: 0
  }
  // 点击按钮,分别查看 情况1 和 情况2 的 render 打印次数
  handleClick = () => {
    // 情况1
    // react框架内部有一个表示批量更新的标记isBatchingUpdates
    // 当我们点击按钮触发了react自己的事件后,这个标记就被修改为true,表示合并批量更新,
    // 所以,此时调用setState的时候,并不会立即更新状态,而是存储到了一个队列中
    // 将来等到所有的操作都执行完成后,Ract就会合并所有的状态更新,一次性的更新状态
    this.setState({
      count: this.state.count + 1
    })
    this.setState({
      count: this.state.count + 1
    })
    
    // 情况2
    // 注意:这种情况下,每次调用setState都会立即更新状态,并且让组件重新渲染
    // 因为定时器中的代码是异步代码,定时器中的代码不会被立即执行而是放到了事件队列
    // 事件队列中的代码,会在JS主线程(同步代码)都执行完成后,再执行。
    // 当主线程中的代码执行完成后,React已经将isBatchingUpdates标记设置为false
    // 此时,再调用setState(),此时,因为批量合并更新的标记已经是false
    // 所以,React会调用一次setState就立即更新一次状态,并且立即渲染
    // setTimeout(() => {
    //   this.setState({
    //     count: this.state.count + 1
    //   })
    //   this.setState({
    //     count: this.state.count + 1
    //   })
    // }, 0)

    // ReactDOM.unstable_batchedUpdates这个api可以解决下面这种叠加更新的问题
    // setTimeout(() => {
    //     ReactDOM.unstable_batchedUpdates(() => {
    //         this.setState({
    //             count: this.state.count + 1
    //         })
    //         this.setState({
    //             count: this.state.count + 1
    //         })
    //     })
    // }, 0)
  }

  render() {
    console.log('render')
    return (
      <div>
        <Child count={this.state.count} />
        <button onClick={this.handleClick}>+1</button>
      </div>
    )
  }
}

表单 -- 受控组件

  •  能够使用受控组件的方式获取文本框的值
  • HTML中表单元素是可输入的,即表单元素维护着自己的可变状态(value)

  • 在react中,可变状态通常是保存在state中的,并且要求状态只能通过setState进行修改

  • React中将state中的数据与表单元素的value值绑定到了一起,由state的值来控制表单元素的值

  • 受控组件:value值受到了react状态控制的表单元素

  • 使用受控组件的方式处理表单元素后,状态的值就是表单元素的值。即:想要操作表单元素的值,只需要操作对应的状态即可

class App extends React.Component {
  state = {
    msg: 'hello react'
  }

  handleChange = (e) => {
    this.setState({
      msg: e.target.value
    })
  }

  render() {
    return (
      <div>
        <input type="text" value={this.state.msg} onChange={this.handleChange}/>
      </div>
    )
  }
}

表单 -- 非受控组件-ref

  •  非受控组件借助于ref,使用原生DOM的方式来获取表单元素的值
// 1 导入 createRef 函数( 用来创建 ref 对象 )
import { createRef } from 'react'

class Hello extends Component {
  // 2 调用 createRef 函数来创建一个 ref 对象
  //   命名要规范: txt(DOM 元素的自己标识) + Ref
  txtRef = createRef()

  handleClick = () => {
    // 4 获取文本框的值:
    console.log(this.txtRef.current.value)
  }

  render() {
    return (
      <div>
         // 3 将创建好的 ref 对象,设置为 input 标签的 ref 属性值
        <input ref={this.txtRef} />
        <button onClick={this.handleClick}>获取文本框的值</button>
      </div>
    )
  }
}

事件处理

注册事件

  • React注册事件与DOM的事件语法非常像

  • 语法on+事件名 ={事件处理程序} 比如onClick={this.handleClick}

  • 注意:React事件采用驼峰命名法,比如onMouseEnter, onClick

- React注册事件与DOM的事件语法非常像

- 语法`on+事件名 ={事件处理程序}` 比如`onClick={this.handleClick}`

- 注意:*React事件采用驼峰命名法*,比如`onMouseEnter`, `onClick`

事件对象 

  • 可以通过事件处理程序的参数获取到事件对象

  • 注意:React 中的事件对象是 React 内部处理后的事件对象,一般称为:SyntheticBaseEvent 合成事件对象。用法与 DOM 原生的事件对象用法基本一致

function handleClick(e) {
  e.preventDefault()
  console.log('事件对象', e)
}

<a onClick={this.handleClick}>点我,不会跳转页面</a>

  this指向问题

  • 事件处理程序中的this指向的是undefined

  • render方法中的this指向的是当前react组件。只有事件处理程序中的this有问题

  • 原因:事件处理程序的函数式函数调用模式,在严格模式下,this指向undefined

  • render函数是被组件实例调用的,因此render函数中的this指向当前组件

下述代码:在react的事件处理函数中,this指向`undefined`
class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this.state.msg) // this是undefined
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}

this指向解决方案 

  • 方式1:箭头函数
class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={() => this.handleClick()}>点我</button>
      </div>
    )
  }
}
  •  方式2:bind,绑定this,返回一个新函数=>事件处理程序
class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick.bind(this)}>点我</button>
      </div>
    )
  }
}
  •  方式3:箭头函数形式的实例方法 - 推荐使用
class App extends React.Component {
  state = {
    msg: 'hello react'
  }

  handleClick = () => {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}

组件通讯

组件是独立且封闭的单元,默认情况下只能使用组件自己的数据,在组件化过程中,通常会将一个完整的功能拆分成多个组件,用这种组件拆分去更好的完成整个应用的功能也会使其代码结构更加清晰化,而在这个过程中,多个组件之间不可避免的要共享某些数据 ,为了实现多个组件之间共享数据 ,就需要打破组件的独立封闭性,让其与外界沟通。这个共享数据的过程就是组件通讯

props基本使用

  • 如何传递?给组件标签添加属性,就表示给组件传递数据

<Common

names='ggc'

age={18}

style={{ color: 'red' }}

obj={{ li: '123' }}

list={[123, 456]}

list2={[{ age: 124, namei: 'io' }]}

jsx={<div>Jsx</div>}> // 还能传jsx 可以当插槽

</Common>

  •  如何接收?函数组件通过参数 props 接收数据,类组件通过 this.props

函数组件:

const Common = (props) => {

    console.log(props);

    return <>

        <div style={props.style}>

            <span>{props.names}</span><br />

            <span>{props.age}</span><br />

            <span>{props.obj.li}</span><br />

            <span>{props.list}</span><br />

            <span>{props.list2[0].age}</span><br />

            <span>{props.jsx}</span><br />

        </div>

    </>

}

类组件:

class Common extends Component {

        render(){

                console.log(this.props) // {names: 'ggc', age: 18}

        }

}

  • props的注意点

    •  props 是只读对象,也就是说:只能读取对象中的属性,无法修改
    • 单向数据流,也叫做:自顶而下(自上而下)的数据流
    • 可以传递任意数据(数字 字符串 布尔类型 数组 对象 函数 jsx)

组件通讯-父传子 

// 父组件 FatherCom
import { Component } from "react";
import ChildrenCom  from './children'
export default class FatherCom extends Component {
    state={
      money:100
    }
    render() {
        const {money} = this.state
        return <>
            <div>
                <div>
                     {/* 将数据传递给子组件 */}
                    <ChildrenCom money={money} />
                </div>
            </div>
        </>
    }
}
// 子组件 Childrens 
export const Childrens = (props) => {
    return <div>
        {/* 接收父组件中传递过来的数据 */}
        <h3>我是子组件 -- {props.money}</h3>
    </div>

}

// 子组件 ChildrenCom
export default class ChildrenCom extends Component{
    render() {
            return  <div>
              {/* 接收父组件中传递过来的数据 */}
    	      <h3>我是子组件 -- {this.props.money}</h3>
            </div>
    }
}

组件通讯-子传父

  • 利用回调函数来实现,父组件提供回调,子组件调用回调,将要传递的数据作为回调函数的参数

     步骤

  1. 父组件提供一个回调函数(用于接收数据)

  2. 将该函数作为属性的值,传递给子组件

  3. 子组件通过 props 调用回调函数

  4. 将子组件的数据作为参数传递给父组件的回调函数,父组件的函数通过setState方法修改数据

// 父组件 Father.js
import { Component } from "react";
import Childrens from './children'
export default class FatherCom extends Component {
    state = {
        money:16
    }
    changeMoney = (count) => {
        this.setState({
            money:this.state.money+count
        })
    }
    render() {
        const { money } = this.state
        return <>
            <div>
                <div>
                    {/* 将数据传递给子组件 */}
                    <Childrens money={money} changeMoney={ this.changeMoney} />
                </div>
            </div>
        </>
    }
}
Children.js

import { Component } from "react";
export default class ChildrenCom extends Component {
    handerClick = () => {
        this.props.changeMoney(50)
    }
    render() {
        return <div>
            {/* 接收父组件中传递过来的数据 */}
            <h3>我是子组件 -- {this.props.money}</h3>
            <button onClick={this.handerClick}>按钮+10</button>
        </div>
    }
}

组件通讯-兄弟组件

  • 思想:状态提升

  • 解释:将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态

  • 公共父组件职责:

    1. 提供共享状态

    2. 提供操作共享状态的方法

  • 要通讯的子组件只需通过 props 接收状态或操作状态的方法

index.js

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

// 导入两个子组件
import Jack from './Jack'
import Rose from './Rose'

// App 是父组件
class App extends Component {
  // 1. 状态提升到父组件
  state = {
    msg: '',
  }

  changeMsg = msg => {
    this.setState({
      msg,
    })
  }
  
  render() {
    return (
      <div>
        <h1>我是App组件</h1>
        {/* 兄弟组件 1 */}
        <Jack say={this.changeMsg}></Jack>
				{/* 兄弟组件 2 */}
        <Rose msg={this.state.msg}></Rose>
      </div>
    )
  }
}

// 渲染组件
ReactDOM.render(<App />, document.getElementById('root'))

Jack.js

import React, { Component } from 'react'

export default class Jack extends Component {
  say = () => {
    // 修改数据
    this.props.say('you jump i look')
  }
  render() {
    return (
      <div>
        <h3>我是Jack组件</h3>
        <button onClick={this.say}>说</button>
      </div>
    )
  }
}

Rose.js

import React, { Component } from 'react'

export default class Rose extends Component {
  render() {
    return (
      <div>
        {/* 展示数据 */}
        <h3>我是Rose组件-{this.props.msg}</h3>
      </div>
    )
  }
}

 组件通讯-跨级通讯Context

  • 实现方式:使用 Context 来实现跨组件传递数据

  • Context 上下文,可以理解一个范围,只要在这个范围内容,就可以直接夸组件传递数据

// 0 导入创建 context 的函数
import { createContext } from 'react'

// 1 创建 Context 对象
//	 createContext 的返回值是一个 对象
//	 对象中包含了两个组件,分别是: Provider 状态的提供者组件(提供状态)  Consumer 状态的消费者组件(使用状态)
const { Provider, Consumer } = createContext()

// 2 使用 Provider 组件包裹整个应用,并通过 value 属性提供要共享的数据
<Provider value="blue">
  <div className="App">
    <Node /> 
  </div>
</Provider>

// 3 使用 Consumer 组件接收要共享的数据
<Consumer>
  {color => <span>data参数表示接收到的数据 -- {color}</span>}
</Consumer>

组件进阶用法

children属性

  • children 属性:表示该组件的子节点,只要组件有子节点,props就有该属性

  • children 属性与普通的 props 一样,值可以是任意值(文本、React元素、组件,甚至是函数

  • 使用场景:做标题栏,或者当默认插槽使用

const Hello = props => {
  return (
    <div>
      该组件的子节点:{props.children}
    </div>
  )
}

<Hello>我是子节点</Hello>
// <Hello>我是子节点</Hello>
// <Hello><span style={{ color: 'red' }}>花哨的标题</span></Hello>
// <Hello>()=>{}</Hello>

props 校验

props 校验文档  

props校验-基本用法

允许在创建组件的时候,就约定props的格式、类型等

  1. 安装属性校验的包:yarn add prop-types

  2. 导入 prop-types

  3. 使用组件名.propTypes = {} 来给组件 List 的props添加校验规则

  4. 为组件添加 propTypes 属性,并通过 PropTypes 对象来指定属性的类型

函数组件-prop属性校验用法:

import PropTypes from 'prop-types'

const List = props => {
  const arr = props.colors
  const lis = arr.map((item, index) => <li key={index}>{item.name}</li>)
  return <ul>{lis}</ul>
}

List.propTypes = {
  colors: PropTypes.array
}

类组件-prop属性校验用法:

  • 第一种 用法和上面一样使用《组件.propTypes》

class List extends Component {
  render() {
    const arr = this.props.colors
    const lis = arr.map((item, index) => <li key={index}>{item.name}</li>)
    return <ul>{lis}</ul>
  }
}

List.propTypes = {
  colors: PropTypes.array
}

  • 第二种 用法组件内部使用-类的静态属性-static

class List extends Component {
  static propTypes = {
    colors: PropTypes.array,
    gender: PropTypes.oneOf(['male', 'female']).isRequired
  }

  static defaultProps = {
        gender: ''
  }
  
  render() {
    const arr = this.props.colors
    const lis = arr.map((item, index) => <li key={index}>{item.name}</li>)
    return <ul>{lis}</ul>
  }
}

扩展:类的静态属性-static:类的static语法简化props校验和默认值 ,静态属性是加给类自身的 

举例: Person 是类,p 是类的实例对象,实例的东西(属性方法)是加给实例的需要new出来访问的,静态属性是加给类自身的 

class Person {
  // 实例属性
  name = 'zs'
    // 实例方法
  sayHi() {
    console.log('哈哈')
  }

  // 静态属性
  static age = 18
    // 静态方法
  static goodBye() {
    console.log('byebye')
  }
}
const p = new Person()

console.log(p.name)         // 访问实例属性
p.sayHi()                                // 调用实例方法

console.log(Person.age)    // 访问静态属性
Person.goodBye()                // 调用静态方法

props校验-规则

  1. 常见类型:array、bool、func、number、object、string

  2. React元素类型:element

  3. 必填项:isRequired

  4. 特定结构的对象:shape({})

// 常见类型
optionalFunc: PropTypes.func,
// 必选
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
})

props默认值

  • 通过defaultProps可以给组件的props设置默认值,在未传入props的时候生效,,对于函数组件来说,新版 React 已经不再推荐使用 defaultProps 来添加默认值,而是推荐使用函数参数的默认值来实现

// 通过函数参数默认值,来提供 props 默认值
const App = ({ pageSize = 10 }) {
  return (
    <div>
      此处展示props的默认值:{props.pageSize}
    </div>
  )
}

// 不传入pageSize属性
<App />

组件生命周期

  • 生命周期:一个事物从创建到最后消亡经历的整个过程

  • 注意:只有 类组件 才有生命周期

React生命周期方法图

组件-挂载阶段

  • 执行顺序: constructor=》render=》componentDidMount

组件的挂载阶段的钩子函数以及执行时机

钩子 函数 触发时机 作用 constructor 创建组件时,最先执行 1. 初始化state 2. 创建 Ref 3. 使用 bind 解决 this 指向问题等 render 每次组件渲染都会触发 渲染UI( 注意: 不能调用setState() ) componentDidMount 组件挂载(完成DOM渲染)后 1. 发送网络请求 2.DOM操作

示例代码:

constructor 这个阶段的钩子作为了解,在新版更新中这种使用方法已经不被推荐
注意:对于class来说,如果继承了父类,并且在class中手动写了constructor, 那么,须手动调用super,super指的是:父类的constructor
注意:对于React组件来说,如果写了constructor,那么,应该将props传递给
super这样,才能保证在constructor中能够通过this.props来拿到props值

class Dadoudou extends Component {
    // constructor(props) {
    //     console.log('constructor---创建组件时1');
    //     super(props)
    //     this.state = {
    //         count: 0
    //     }
    //     this.txtRef = createRef()
    //     this.handleClick = this.handleClick.bind(this)
    // }

    // 初始化state、创建 Ref、使用 bind 解决 this 指向问题等更推荐下面这种用法
    state = {}
    txtRef = createRef()
    handleClick = () => { }

    componentDidMount() {
        // 发送网络请求,dom操作
        console.log('componentDidMount---组件挂载(完成DOM渲染)后3');
    }

    render() {
        // 不能调用setState()会造成死循环
        console.log('render---组件渲染2');
        return (
            <div>
                <h1>统计豆豆被打的次数:{this.state.count}</h1>
                <button>打豆豆</button>
            </div>
        )
    }
}

组件-更新阶段

执行顺序: render =》componentDidUpdate

组件的更新阶段的钩子函数以及执行时机

钩子函数 触发时机 作用 render 每次组件渲染都会触发 渲染UI(与 挂载阶段 是同一个render) componentDidUpdate 组件更新(完成DOM渲染)后 DOM操作,可以获取到更新后的DOM内容, 不要直接调用setState

执行时机:以下三者任意一种变化,组件就会重新渲染

  • setState()

  • forceUpdate() 强制组件更新

  • 组件接收到新的props(实际上,只需要父组件更新,子组件就会重新渲染)

注意:不要直接在 componentDidUpdate 钩子函数中调用setstate ( 这个意思是说:可以调用,但是,必须要放在某个条件判断中 不能直接调用,因为直接调用就会循环更新导致死循环)官方文档说明: React.Component – React

componentDidUpdate 钩子函数的应用,可以参考这个视频:TabBar菜单高亮Bug分析和修复

组件-卸载阶段

  • 执行时机:组件从页面中消失

    钩子函数触发时机作用
    componentWillUnmount组件卸载(从页面中消失)执行清理工作(比如:清理定时器等)

 示例代码:清理工作指的是你自己手动做的一些动作

import { Component } from 'react'

class Child extends Component {
    timer = ''
    handleResize() {
        console.log('window窗口改变了');
    }
    componentDidMount() {
        this.timer = setInterval(() => {
            console.log('我是定时器');
        }, 1000);
        window.addEventListener('resize', this.handleResize)
    }
    componentWillUnmount() {
        clearInterval(this.timer)
        window.removeEventListener('resize', this.handleResize)
    }
    render() {
        return <h1>统计豆豆被打的次数:</h1>
    }
}

export default class App extends Component {
    state = {
        count: 0
    }
    handleClick = () => {
        this.setState({
            count: this.state.count + 1
        })
    }
    render() {
        return (
            <div>
                {this.state.count === 0 ? <Child /> : <p>豆豆消失了</p>}
                <button onClick={this.handleClick}>打豆豆</button>
            </div>
        )
    }
}

json-server

  •  json-server 文档

  • 作用:根据 json 文件创建 RESTful 格式的 API 接口,模拟虚假数据

  • json-server采用的就是REST API的设计风格

REST API:说明

RESTFul API (或 REST API)是一套流行的接口设计风格,参考 RESTFul API 设计指南

使用 RESTFul API 时,常用请求方式有以下几种:

  • GET(查询):从服务器获取资源

  • POST(新建):在服务器新建一个资源

  • DELETE(删除):从服务器删除资源

  • PUT(更新):在服务器更新资源(注意:需要提供接口所需的完整数据)

  • PATCH(更新):在服务器更新资源(只需要提供接口中要修改过的数据)

约定:请求方式是动词,接口地址使用名词

 // PUT 和 PATCH 请求的区别演示:

比如,动物园数据包含:{ name, address, animals },假设要修改 name 属性,此时:
使用 patch 请求:只需要传入 { name } 即可
使用 put 请求:需要传入完成数据 { name, address, animals }

 

用法

  1. 准备文件data.json文件

{
  "tabs": [
    {
        "id": 1,
        "name": "热度",
        "type": "hot"
      },
      {
        "id": 2,
        "name": "时间",
        "type": "time"
      }
  ],
  "list": [
    {
      "id": 1,
      "author":"89 18k",
      "comment":"瓜子 啤酒",
      "time":"2021-11-24T03:13:40.644Z",
      "attitude":0
   	}
  ]
}

  2. 使用 json-server 启动接口

# 使用 npx 命令调用 json-server
# 指定用于提供数据的 json 文件
# --port 用于修改端口号
# --watch 表示监听 json 文件的变化,当变化时,可以不重启终端命令的前提下读取到json 文件中
的最新的内容
npx json-server ./data.json --port 8888 --watch

# 接口地址为:
http://localhost:8888/tabs
http://localhost:8888/list

3. 使用接口

axios.get('http://localhost:8888/tabs')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/976738.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QCAD for Mac免费下载:卓越的2D辅助设计工具

QCAD是一款功能强大的2D辅助设计画图软件&#xff0c;现已适配Mac系统&#xff0c;为Mac用户提供了便捷高效的设计工具。 QCAD提供了丰富的绘图功能&#xff0c;可以轻松绘制各种平面图形&#xff0c;包括直线、圆、椭圆、弧线等。 同时&#xff0c;QCAD还支持多种绘图工具&am…

webpack打包常用配置项

webpack打包配置项 参考链接 文件结构&#xff1a;最基础版 先安装 npm i webpack webpack-cli --dev 运行命令&#xff1a;npx webpack 进行打包 1. 配置webpack.config.js文件&#xff1a; const path require(path); module.exports {mode: development, // 开发环境 …

聊聊如何玩转spring-boot-admin

前言 1、何为spring-boot-admin&#xff1f; Spring Boot Admin 是一个监控工具&#xff0c;旨在以良好且易于访问的方式可视化 Spring Boot Actuators 提供的信息 快速开始 如何搭建spring-boot-admin-server 1、在服务端项目的POM引入相应的GAV <dependency><grou…

Linux之NFS服务器

目录 Linux之NFS服务器 简介 NFS背景介绍 生产应用场景 NFS工作原理 NFS工作流程图 流程 NFS的安装 安装nfs服务 安装rpc服务 启动rpcbind服务同时设置开机自启动 启动nfs服务同时设置开机自启动 NFS的配置文件 主配置文件分析 示例 案例 --- 建立NFS服务器&#…

4、QT中的网络编程

一、Linux中的网络编程 1、子网和公网的概念 子网网络&#xff1a;局域网&#xff0c;只能进行内网的通信公网网络&#xff1a;因特网&#xff0c;服务器等可以进行远程的通信 2、网络分层模型 4层模型&#xff1a;应用层、传输层、网络层、物理层 应用层&#xff1a;用户自…

C++核心基础教程之STL容器详解 vector容器的概述 vector常见的API

容器作用域迭代器 就是定义一个迭代器&#xff0c;迭代器的名称叫it 保存起始迭代器 *it int 相当与取内容&#xff0c;像指针&#xff0c;但不是指针&#xff0c;因为底层很多细节 vector 一次开辟两倍原来的空间 另辟空间 迭代器右边是开区间&#xff0c;不包含右端…

sql中的排序函数dense_rank(),RANK()和row_number()

dense_rank()&#xff0c;RANK()和row_number()是SQL中的排序函数。 为方便后面的函数差异比对清晰直观&#xff0c;准备数据表如下&#xff1a; 1.dense_rank() 函数语法&#xff1a;dense_rank() over( order by 列名 【desc/asc】) DENSE_RANK()是连续排序&#xff0c;比如…

避雷,软件测试常见的误区之一

随着软件规模的不断扩大&#xff0c;软件设计的复杂程度不断提高&#xff0c;软件开发中出现错误或缺陷的机会越来越多。同时&#xff0c;市场对软件质量重要性的认识逐渐增强。所以&#xff0c;软件测试在软件项目实施过程中的重要性日益突出。但是&#xff0c;现实情况是&…

RS-485/RS-422收发器电路 DP3085 国产低成本替代MAX3085

DP3085是5V、半双工、15kV ESD 保护的 RS-485/RS-422 收发器电路&#xff0c;电路内部包含一路驱动器和一路接收器。 DP3085具有增强的摆率限制&#xff0c;助于降低输出 EMI 以及不匹配的终端连接引起的反射&#xff0c;实现 500kbps 的无误码数据传输。 DP3085芯片接收器输入…

Uniapp学习之从零开始写一个简单的小程序demo(新建页面,通过导航切换页面,发送请求)

先把官网文档摆在这&#xff0c;后面会用到的 [uniapp官网文档]: https://uniapp.dcloud.net.cn/vernacular.html# 一、开发工具准备 1-1 安装HBuilder 按照官方推荐&#xff0c;先装一个HBuilder 下载地址&#xff1a; https://www.dcloud.io/hbuilderx.html1-2 安装微信开…

论文复现--VideoTo3dPoseAndBvh(视频转BVH和3D关键点开源项目)

分类&#xff1a;动作捕捉 github地址&#xff1a;https://github.com/HW140701/VideoTo3dPoseAndBvh 所需环境&#xff1a; Windows10&#xff0c;CUDA11.6&#xff0c;conda 4.13.0&#xff1b; 目录 环境搭建conda list配置内容演示生成文件说明 环境搭建 # 创建环境 conda…

淘宝天猫API技术解析,实现关键词搜索淘宝商品(商品详情接口等)批量获取,可高并发

淘宝和天猫提供了官方API接口&#xff0c;开发者可以通过这些接口获取商品信息、进行交易操作等。下面我将简要介绍如何使用淘宝API进行关键词搜索商品并批量获取商品详情。 首先&#xff0c;需要了解淘宝API的几个主要接口&#xff1a; 搜索接口&#xff1a;用于根据关键词搜…

软件与系统安全复习

软件与系统安全复习 课程复习内容 其中 软件与系统安全基础 威胁模型 对于影响系统安全的所有信息的结构化表示 本质上&#xff0c;是从安全的视角解读系统与其环境 用于理解攻击者 什么可信、什么不可信攻击者的动机、资源、能力&#xff1b;攻击造成的影响 具体场景…

Android studio实现自定义圆形进度条 带刻度进度条 计步效果 时速表 水波纹效果

目录 原文链接效果图values /layout /activity原文链接 效果图 点击重置后: 该项目总共实现了三种圆形进度条效果 CircleProgress:圆形进度条,可以实现仿 QQ 健康计步器的效果,支持配置进度条背景色、宽度、起始角度,支持进度条渐变DialProgress:类似 CircleProgress,…

linux jar包class热部署 工具 arthas安装及使用

在不改变类、方法 的前提下&#xff0c;在方法中对业务逻辑做处理 或 打日志等情况下使用。 建议线上日志调试时使用&#xff1a; arthas安装 1. 下载文件 arthas-packaging-3.7.1-bin.zip https://arthas.aliyun.com 2. 服务器安装arthas 2.1 服务器指定目录下创建目录 c…

双系统备忘

1.装了双系统&#xff0c; 找不到ubuntu,只有windows 从windows启动cmd bcdedit /set “{bootmgr}” path \EFI\ubuntu\grubx64.efi双系统分别装在两块磁盘&#xff0c; windows装在硬盘0&#xff0c; ubuntu装在硬盘1 然后启动是从硬盘0读的boot, 现在我要移除windows硬盘0…

Vue + Element UI 前端篇(一):搭建开发环境

Vue Element UI 实现权限管理系统 前端篇&#xff08;一&#xff09;&#xff1a;搭建开发环境 技术基础 开发之前&#xff0c;请先熟悉下面的4个文档 vue.js2.0中文, 优秀的JS框架vue-router, vue.js 配套路由vuex&#xff0c;vue.js 应用状态管理库Element&#xff0c;饿…

基环树和点度数相关的计数:CF1863G

https://codeforces.com/contest/1863/problem/G 首先建图&#xff0c;然后分析出交换在图上的变化&#xff0c;发现每条点最多只有一个入边标粗&#xff0c;求最终形态。 首先可以猜答案为 ∏ v ( i n v 1 ) \prod_{v}(\mathrm{in}_v 1) ∏v​(inv​1)&#xff0c;但是环…

FOXBORO P0926KP控制器

应用领域&#xff1a; FOXBORO P0926KP 控制器广泛应用于工业自动化和过程控制领域&#xff0c;包括化工、石油和天然气、电力、制造业等各种行业。 控制能力&#xff1a; 该控制器具有高性能的控制能力&#xff0c;可执行复杂的控制策略和算法&#xff0c;以确保工业过程的高…

监控 -- linux中的一些系统性能状态指令、Prometheus

目录 监控查看性能相关命令Prometheus1、安装和配置2、将 NFS服务器和LB服务器作为exporter采集数据3、在prometheus server里添加安装exporter程序的服务器 grafana出图工具 监控 监控的目的是获取数据&#xff0c;通过数据分析了解机器是否正常运行 查看性能相关命令 查看c…