- // 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 不是标准的 JS 语法,是 JS 的语法扩展。脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法。
在 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进行格式化
"editor.formatOnSave": true,
// 编辑器默认使用prittier作为格式化工具
"editor.defaultFormatter": "esbenp.prettier-vscode",
// 不要有分号
"prettier.semi": false,
// 使用单引号
"prettier.singleQuote": true,
- // 配置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中只能出现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>
在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>
作用:重复生成相同的 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>
- )
行内样式 - style
像 width/height 等属性,可以省略 px,直接使用 数值 即可
如果是需要使用百分比的单位,此时,继续使用字符串的值即可(比如,"60%")
类名 - className【推荐】实际项目中推荐使用classNames
行内样式-style
const dv = (style样式)
类名-className
// 导入样式 import './base.css' const dv =style样式
总结:
函数组件:使用 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 />
- </>
- )
使用 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>
- </>)
- // 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
静态,不会发生变化),性能比较高动态)比如计数器案例,点击按钮让数值+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 (
- 计数器:{this.state.count}
- )
- }
- }
this.setState({ 要修改的部分数据 })- 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 更新状态,状态会进行合并,后面覆盖前面
等到所有的操作都执行完毕,React 会拿到最终的状态,然后触发组件更新
优势:多次调用 setState() ,只会触发一次重新渲染,提升性能
解决多次调用依赖的问题
推荐:使用 setState((prevState) => {}) 语法
参数 prevState:表示上一次 setState 更新后的状态
this.setState((prevState) => {
return {
count: prevState.count + 1
}
})
setState(updater[, callback])
this.setState(
(state) => ({}),
() => {console.log('这个回调函数会在状态更新后并且 DOM 更新后执行')}
)
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>
- )
- }
- }
- // 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指向的是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>
- )
- }
- }
- class App extends React.Component {
- state = {
- msg: 'hello react'
- }
- handleClick() {
- console.log(this.state.msg)
- }
- render() {
- return (
- <div>
- <button onClick={() => this.handleClick()}>点我</button>
- </div>
- )
- }
- }
bind,绑定this,返回一个新函数=>事件处理程序- class App extends React.Component {
- state = {
- msg: 'hello react'
- }
- handleClick() {
- console.log(this.state.msg)
- }
- render() {
- return (
-
-
-
- )
- }
- }
- class App extends React.Component {
- state = {
- msg: 'hello react'
- }
-
- handleClick = () => {
- console.log(this.state.msg)
- }
- render() {
- return (
- <div>
- <button onClick={this.handleClick}>点我</button>
- </div>
- )
- }
- }
组件是独立且封闭的单元,默认情况下只能使用组件自己的数据,在组件化过程中,通常会将一个完整的功能拆分成多个组件,用这种组件拆分去更好的完成整个应用的功能也会使其代码结构更加清晰化,而在这个过程中,多个组件之间不可避免的要共享某些数据 ,为了实现多个组件之间共享数据 ,就需要打破组件的独立封闭性,让其与外界沟通。这个共享数据的过程就是组件通讯。
如何传递?给组件标签添加属性,就表示给组件传递数据
names='ggc'
age={18}
style={{ color: 'red' }}
obj={{ li: '123' }}
list={[123, 456]}
list2={[{ age: 124, namei: 'io' }]}
jsx={
Jsx}> // 还能传jsx 可以当插槽
props 接收数据,类组件通过 this.props函数组件:
const Common = (props) => {
console.log(props);
return <>
{props.names}
{props.age}
{props.obj.li}
{props.list}
{props.list2[0].age}
{props.jsx}
>
}
类组件:
class Common extends Component {
render(){
console.log(this.props) // {names: 'ggc', age: 18}
}
}
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>
- }
- }
步骤:
父组件提供一个回调函数(用于接收数据)
将该函数作为属性的值,传递给子组件
子组件通过 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.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>
- }
- }
思想:状态提升
解释:将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
公共父组件职责:
提供共享状态
提供操作共享状态的方法
要通讯的子组件只需通过 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 上下文,可以理解一个范围,只要在这个范围内容,就可以直接夸组件传递数据
- // 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 属性:表示该组件的子节点,只要组件有子节点,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的格式、类型等
安装属性校验的包: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) =>- {item.name}
)
return{lis}
}List.propTypes = {
colors: PropTypes.array
}
类组件-prop属性校验用法:
class List extends Component {
render() {
const arr = this.props.colors
const lis = arr.map((item, index) =>- {item.name}
)
return{lis}
}
}List.propTypes = {
colors: PropTypes.array
}
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) =>- {item.name}
)
return{lis}
}
}
扩展:类的静态属性-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() // 调用静态方法
常见类型: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
})
通过defaultProps可以给组件的props设置默认值,在未传入props的时候生效,,对于函数组件来说,新版 React 已经不再推荐使用 defaultProps 来添加默认值,而是推荐使用函数参数的默认值来实现:
// 通过函数参数默认值,来提供 props 默认值
const App = ({ pageSize = 10 }) {
return (
此处展示props的默认值:{props.pageSize}
)
}// 不传入pageSize属性
生命周期:一个事物从创建到最后消亡经历的整个过程
注意:只有 类组件 才有生命周期

执行顺序: 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 文件创建 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')