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';
import App from './App'
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)
root.render(<App></App>)
JSX语法
jsx是react的核心内容
注意:JSX 不是标准的 JS 语法,是 JS 的语法扩展。脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法。
babel 试一试
在 babel 的试一试中,可以通过切换 React Runtime 来查看:
Classic -> React.createElement:注意这种方式,需要依赖于 React,也就是只要在代码中写 JSX 就必须导入 React 包
Automatic -> _jsxRuntime:不依赖于 React 包,因此,代码中可以省略 React 包的导入【最新的脚手架已经默认值采用这种形式】
总结
使用jsx创建元素是react声明式UI的体现
使用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语句,比如
if
、for
、while
等
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
-
像 width/height 等属性,可以省略 px,直接使用
数值
即可 -
如果是需要使用百分比的单位,此时,继续使用字符串的值即可(比如,
"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>}
}
组件通讯-子传父
- 利用回调函数来实现,父组件提供回调,子组件调用回调,将要传递的数据作为回调函数的参数
步骤:
-
父组件提供一个回调函数(用于接收数据)
-
将该函数作为属性的值,传递给子组件
-
子组件通过 props 调用回调函数
-
将子组件的数据作为参数传递给父组件的回调函数,父组件的函数通过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>}
}
组件通讯-兄弟组件
-
思想:状态提升
-
解释:将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
-
公共父组件职责:
-
提供共享状态
-
提供操作共享状态的方法
-
-
要通讯的子组件只需通过 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的格式、类型等
-
安装属性校验的包:
yarn add prop-types
-
导入
prop-types
包 -
使用
组件名.propTypes = {}
来给组件 List 的props添加校验规则 -
为组件添加
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校验-规则
-
常见类型:array、bool、func、number、object、string
-
React元素类型:element
-
必填项:isRequired
-
特定结构的对象: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 }
用法
-
准备文件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')
相关文章:

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 Rea…...

linux学习书籍推荐
《Linux程序设计(第4版)》,Neil Matthew和Richard Stones编写。这本书是Linux/UNIX专家编写的,详细介绍了Linux系统以及其他UNIX风格的操作系统上的程序开发,包括标准Linux C语言函数库和各种由Linux或UNIX标准指定的工…...

LeetCode 428. Serialize and Deserialize N-ary Tree【树,BFS,DFS】困难
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...

javascript | 变量、函数、属性的命名规则
javascript标识符的命名规则 变量、函数、属性的名字、或者函数的参数,都可称为标识符。标识符可以是按照下列格式规则组合起来的一个或者多个字符。 第一个字符必须是一个字母、下划线_、或美元符号$。数字不可以作为标识符的首字符。其他字符可以是数字、字母、…...

手写Ribbon基本原理
本文已收录于专栏 《中间件合集》 目录 概念说明什么是RibbonRibbon和Nginx负载均衡的区别 工作流程代码实现RibbonSDK发送请求端引入RibbonSDK和Nacos的依赖配置文件中填写负载均衡策略调用代码 接收请求端执行效果发送请求端接收请求端 总结提升 概念说明 什么是Ribbon Ribb…...

k8s集群中ETCD备份和恢复
文章目录 [toc]一、etcd 概述二、安装etcdctl工具三、kubeadm部署方式部署1)备份2)恢复四、定时备份 五、二进制部署备份1)备份2)恢复1、停止apiserver和etcd2、etcd_1恢复3、etcd_2恢复4、etcd_3恢复5、启动etcd和apiserver6、检…...

node版本问题
服务器下载下来的vue项目启动出现下列问题 npm ERR! path E:\vueEnv\app\node_modules\node-sass npm ERR! command failed npm ERR! command C:\Windows\system32\cmd.exe /d /s /c node scripts/build.js npm ERR! Building: C:\Program Files\nodejs\node.exe E:\vueEnv\ap…...

四)Stable Diffussion使用教程:图生图
这一篇来说说图生图。 除了文生图之外,SD常用的还有图生图模式。 图生图,顾名思义就是使用一张图去让AI生成自己喜欢的另一张图。 有时候我们有一张喜欢的图,但是希望换一种颜色方案,这时就可以通过图生图的方式去实现了&#…...

yolov7简化yaml配置文件
yolov7代码结构简单,效果还好,但是动辄超过70几个模块的配置文件对于想要对网络进行魔改的朋友还是不怎么友好的,使用最小的tiny也有77个模块 代码的整体结构简单,直接将ELAN结构化写成一个类就能像yolov5一样仅仅只有20几个模块&…...

pprof火焰图性能优化
pprof火焰图性能优化 火焰图(flame graph)是性能分析的利器,在go1.1之前的版本我们需要借助go-torch生成,在go1.1后go tool pprof集成了此功能,今天就来说说如何使用其进行性能优化 在你启动http server的地方直接加入导入: _ “net/http/pprof” 获取…...

Greenplum 查找数据目录占用最大的表
背景 社区中某同学提出问题: 某环境磁盘占用空间较大,于是想找到数据目录占用最大的表。使用常规查询找不出来,于是到数据目录下分析filenode,找到3个filenode占了400G。然而根据filenode从pg_class中确找不到对应的relfilenode。…...

Java 基于 SpringBoot 的酒店管理系统,附源码和数据库
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 一、前言介绍二、系统结构三、系统详细实现3.1用户信息管理3.2会员信息管理3.3客房信息管理3.4收藏…...

LinkedList(4):多线程LinkedList 不安全情况
多线程不安全演示,线程越多,现象越明显,这边只启了四个线程。 package com.example.demo;import java.util.LinkedList; import java.util.UUID;public class LInkedListThread {public static void main(String[] args) {final LinkedList&…...

3D印刷电路板在线渲染查看工具
从概念上讲,这是有道理的,因为PCB印制电路板上的走线从一个连接到下一个连接的路线基本上是平面的。 然而,我们生活在一个 3 维世界中,能够以这种方式可视化电路以及相应的组件,对于设计过程很有帮助。本文将介绍KiCad…...

【mysql】出现 slow sql 问题及建议
文章目录 1. SQL 执行什么情况下会变慢?2. 影响 SQL 语句执行效率的主要因素有哪些?3. 慢 SQL 是如何拖垮数据库的?4. 最佳实践建议 1. SQL 执行什么情况下会变慢? ● 数据量增加:数据库中的数据量可能会逐渐增加&…...

element树形筛选
<el-inputv-model"projectName"placeholder"请输入名称"clearablemaxlength"10"clear"clearTree" /> <el-divider /> <el-treeref"tree"class"filter-tree":data"treeList":props"…...

打字侠:一款专业的中文打字网站
打字侠第一个正式版发布啦!!! 虽然离期望的样子还有一段路要走,不过能看到它正式发布,我还是很激动哟! 打字侠是一款面向中学生和大学生的在线打字软件,它通过合理的课程设计和精美的图形界面帮…...

C++ std::default_random_engine的使用
使用std::default_random_engine可生成不同分布的随机数,下面使用实例来说明其使用。 随机生成0-1间的实数 //利用当前时间生成的种子,可保证每次生成的值都不一样 unsigned seed std::chrono::system_clock::now().time_since_epoch().count(); std:…...

软件设计模式(二):工厂、门面、调停者和装饰器模式
前言 在这篇文章中,荔枝将会梳理软件设计模式中的四种:工厂模式、Facade模式、Mediator模式和装饰器Decorator模式。其中比较重要的就是工厂模式和装饰器模式,工厂模式在开发中使用的频数比较高。希望荔枝的这篇文章能讲清楚哈哈哈哈…...

pdf文件签名的问题解决
今天解决冲突的jar,结果出现下面的问题 java.lang.IllegalAccessError: tried to access method org.bouncycastle.asn1.DERNull.<init>()V from class com.itextpdf.text.pdf.security.PdfPKCS7at com.itextpdf.text.pdf.security.PdfPKCS7.getEncodedPKCS7…...

Node.js安装使用
目录 一、安装 Node.js二、环境变量配置三、npm常用命令 Node.js 是一个强大的运行时环境,它使您能够在服务器端运行 JavaScript 代码。它非常流行,用于构建 Web 应用程序、API 和各种后端服务。 一、安装 Node.js 1、访问 Node.js 官方网站。 在主页上…...

sql:SQL优化知识点记录(七)
(1)索引优化5 (2)索引优化6 (3)索引优化7 查询*, 百分号加右边,否则索引会失效 没建立索引之前都是全表扫描 没建立索引 建立索引: 建立索引 id是主键,他也…...

机器学习:基于梯度下降算法的线性拟合实现和原理解析
机器学习:基于梯度下降算法的线性拟合实现和原理解析 线性拟合梯度下降算法步骤算法实现数据可视化(动态展示)应用示例 当我们需要寻找数据中的趋势、模式或关系时,线性拟合和梯度下降是两个强大的工具。这两个概念在统计学、机器…...

关键点数据增强
1.关键点数据增强 # 关键点数据增强 from PIL import Image, ImageDraw import random import json from pathlib import Path# 创建一个黑色背景图像 width, height 5000, 5000 # 图像宽度和高度 background_color (0, 0, 0) # 黑色填充# 随机分布图像 num_images 1 # …...

最小化安装移动云大云操作系统--BCLinux-for-Euler-22.10-everything-x86_64-230316版
CentOS 结束技术支持,转为RHEL的前置stream版本后,国内开源Linux服务器OS生态转向了开源龙蜥和开源欧拉两大开源社区,对应衍生出了一系列商用Linux服务器系统。BCLinux-for-Euler-22.10是中国移动基于开源欧拉操作系统22.03社区版本深度定制的…...

003传统图机器学习、图特征工程
文章目录 一. 人工特征工程、连接特征二. 在节点层面对连接特征进行特征提取三. 在连接层面对连接特征进行特征提取四. 在全图层面对连接特征进行特征提取 一. 人工特征工程、连接特征 节点、连接、子图、全图都有各自的属性特征, 属性特征一般是多模态的。除属性特…...

Apache Tomcat 漏洞复现
文章目录 Apache Tomcat 漏洞复现1. Tomcat7 弱密码和后端 Getshell 漏洞1.1 漏洞描述1.2 漏洞复现1.3 漏洞利用1.3.1 jsp小马1.3.2 jsp大马 1.4 安全加固 2. Aapache Tomcat AJP任意文件读取/包含漏洞2.1 漏洞描述2.1 漏洞复现2.2 漏洞利用工具2.4 修复建议 3. 通过 PUT 方法的…...

Oracle-常用权限-完整版
-- 创建用户 create user TCK identified by oracle; -- 赋权 grant connect,resource to TCK; -- 删除权限 revoke select any table from TCK -- 删除用户 CASCADE(用户下的数据级联删除) drop user TCK CASCADE -- 查询权限列表 select * from user_role_privs; select * fr…...

jenkins 发布job切换不同的jdk版本/ maven版本
1. 技术要求 因为有个新的项目需要使用jdk17 而旧的项目需要jdk1.8 这就需要jenkins在发布项目的时候可以指定jdk版本 2. 解决 jenkins全局工具配置页面 配置新的jdk 路径 系统管理-> 全局工具配置 如上新增个jdk 名称叫 jdk-17 然后配置jdk-17的根路径即可(这…...

如何在小程序中给会员设置备注
给会员设置备注是一项非常有用的功能,它可以帮助商家更好地管理和了解自己的会员。下面是一个简单的教程,告诉商家如何在小程序中给会员设置备注。 1. 找到指定的会员卡。在管理员后台->会员管理处,找到需要设置备注的会员卡。也支持对会…...