react 初级基础

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 元素的自己标识) + ReftxtRef = 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.statereturn <><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.statereturn <><div><div>{/* 将数据传递给子组件 */}<Childrens money={money} changeMoney={ this.changeMoney} /></div></div></>}
}
Children.jsimport { 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.rhkb.cn/news/121807.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

scala基础

scala 基础 1. scala简介 scala是运行在 JVM 上的多范式编程语言&#xff0c;同时支持面向对象和面向函数编程早期scala刚出现的时候&#xff0c;并没有怎么引起重视&#xff0c;随着Spark和Kafka这样基于scala的大数据框架的兴起&#xff0c;scala逐步进入大数据开发者的眼帘…

芯片开发之难如何破解?龙智诚邀您前往DR IP-SoC China 2023 Day

2023年9月6日&#xff08;周三&#xff09;&#xff0c;龙智即将亮相D&R IP-SoC China 2023 Day&#xff0c;呈现集成了Perforce与Atlassian产品的芯片开发解决方案&#xff0c;助力企业更好、更快地进行芯片开发。 龙智资深顾问、技术支持部门负责人李培将带来主题演讲—…

Open3D(C++) 点云格网分块

目录 一、算法概述二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法概述 点云格网分块是点云二维格网化的一个具体应用案例,与Open3D (C++) 使用点云创建数字高程模型DEM类似,对每个格…

MySQL加密的几种常见方式

MySQL提供了多种加密方式来保护数据的安全性。下面是几种常见的MySQL加密方式&#xff1a; 密码加密&#xff1a; MySQL5.7及以上版本使用SHA-256算法对密码进行加密。这种加密方式更安全&#xff0c;可以防止密码泄露。 之前的MySQL版本使用SHA-1算法进行密码加密。这种加密方…

Java 面试 - Redis

Redis Redis 是基于键值对的非关系型数据库。Redis 拥有string、hash、list、set、zset等多种数据结构, redis具有惊人的读写性能, 其优秀的持久化机制是的它在断电和机械故障时也不会发生数据丢失, 可以用于热点数据存放, 还提供了键过期、发布订阅、食物、流水线、LUA脚本等多…

诗诺克科技引领数字资产智能交易革命

在当今全球金融市场中&#xff0c;数字资产的崛起正引发着一场前所未有的变革。随着区块链技术不断演进和数字资产广泛获得认可&#xff0c;智能交易系统正在迅速成为投资者和交易者的首选工具。这一趋势不仅在全球范围内显著&#xff0c;而且为金融领域的未来带来了令人瞩目的…

1.2 互联网概述

思维导图&#xff1a; 主要内容 &#xff1a; 这段话描述了“互联网”的基本概念和组成。首先&#xff0c;强调了该段文本主要是关于计算机网络的讨论&#xff0c;而不是关于电信网络或有线电视网络。 接下来&#xff0c;描述了计算机网络的基本构成&#xff0c;由结点&…

赞奇科技参与华为云828 B2B企业节,云工作站入选精选产品解决方案

8月27日&#xff0c;由华为云携手上万家伙伴共同发起的第二届 828 B2B 企业节拉开帷幕&#xff0c;围绕五大系列活动&#xff0c;为万千中小企业带来精细化商机对接。 聚焦行业数字化所需最优产品&#xff0c;举办超1000场供需对接会&#xff0c;遍及20多个省100多个城市&…

ACL 访问控制 过滤数据 维护网络安全(第七课)

一 ACL 简介 ACL是Access Control List(访问控制列表)的缩写,是一种用于控制文件、目录、网络设备等资源访问权限的方法。ACL可以对每个用户或用户组设置不同的访问权,即在访问控制清单中为每个用户或用户组指定允许或禁止访问该资源的权限。它通常由一系列规则组成,规则…

使用docker容器内的anaconda虚拟环境启动python web项目

1、环境安装 1.1 基础镜像 这里以ubuntu18.04 cuda 11.8为基础镜像&#xff08;主机支持nvidia-gpu&#xff09; &#xff08;1&#xff09;拉取ubuntu18.4 cuda11.8镜像 docker pull nvidia/cuda:11.8.0-devel-ubuntu18.04 1.2 docker下anaconda安装 &#xff08;1&am…

【STL】模拟实现map和set {map和set的封装;核心结构;插入和查找;红黑树的迭代器;STL中的红黑树结构}

模拟实现map和set map和set是红黑树的两种不同封装形式&#xff0c;底层使用同一颗泛型结构的红黑树&#xff0c;只是存储类型不同。set是红黑树的K模型&#xff0c;存储key&#xff1b;map是红黑树的KV模型&#xff0c;存储pair<key,value>。 下面的代码和讲解着重体现…

Linux 进程基础概念-进程状态、进程构成、进程控制

目录 Linux 进程 进程基础概念 进程状态 进程的构成 进程控制 进程创建和终止 Linux 进程 参考&#xff1a; 「linux操作系统」进程的切换与控制到底有啥关系&#xff1f; - 知乎 (zhihu.com)&#xff0c;Linux进程解析_deep_explore的博客-CSDN博客&#xff0c;腾讯面试…

蓝桥杯打卡Day1

文章目录 全排列八皇后 一、全排列IO链接 本题思路:本题是一道经典的全排列问题&#xff0c;深度优先搜索即可解决。 #include <bits/stdc.h>constexpr int N10;std::string s; std::string ans; int n; bool st[N];void dfs(int u) {if(un){std::cout<<ans<…

mysql与msql2数据驱动

mysql基本使用 数据库操作&#xff08;DDL&#xff09; -- 数据考操作 -- 1.查询所有数据库 SHOW DATABASES;-- 2.选择数据库 USE learn_mysql;-- 3.当前正在使用的数据库 SELECT DATABASE();-- 4.创建数据库 CREATE DATABASE IF NOT EXISTS learn_mysql;-- 5.删除数据库 DRO…

《TCP/IP网络编程》阅读笔记--基于TCP的服务器端/客户端

目录 1--TCP/IP协议栈 2--TCP服务器端默认函数调用顺序 3--TCP客户端的默认函数调用顺序 4--Linux实现迭代回声服务器端/客户端 5--Windows实现迭代回声服务器端/客户端 6--TCP原理 7--Windows实现计算器服务器端/客户端 1--TCP/IP协议栈 TCP/IP协议栈共分 4 层&#xf…

聊聊如何玩转spring-boot-admin

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

kali 安装cpolar内网穿透实现 ssh 远程连接

文章目录 1. 启动kali ssh 服务2. kali 安装cpolar 内网穿透3. 配置kali ssh公网地址4. 远程连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 简单几步通过cpolar 内网穿透软件实现ssh 远程连接kali! 1. 启动kali ssh 服务 默认新安装的kali系统会关闭ssh 连接服务,我们通…

接入 NVIDIA A100、吞吐量提高 10 倍!Milvus GPU 版本使用指南

Milvus 2.3 正式支持 NVIDIA A100&#xff01; 作为为数不多的支持 GPU 的向量数据库产品&#xff0c;Milvus 2.3 在吞吐量和低延迟方面都带来了显著的变化&#xff0c;尤其是与此前的 CPU 版本相比&#xff0c;不仅吞吐量提高了 10 倍&#xff0c;还能将延迟控制在极低的水准。…

【sgLazyTree】自定义组件:动态懒加载el-tree树节点数据,实现增删改、懒加载及局部数据刷新。

特性 可以自定义主键、配置选项支持预定义节点图标&#xff1a;folder文件夹|normal普通样式多个提示文本可以自定义支持动态接口增删改节点 sgLazyTree源码 <template><div :class"$options.name" v-loading"rootLoading"><div class&qu…

我使用的Vim插件

2023年9月5日&#xff0c;周二下午 为了方便以后还原自己的Vim插件配置&#xff0c;于是写这篇博客来记录一下 不定期更新 目录 语法检查Syntastic文件树The NERD tree自动补全括号auto-pairs超轻量级自动补全vim-auto-popmenu 我使用的插件管理器是vim-plug 语法检查Syntas…