07-项目打包 React Hooks

项目打包

项目打包是为了把整个项目都打包成最纯粹的js,让浏览器可以直接执行

打包命令已经在package.json里面定义好了
在这里插入图片描述
运行命令:npm run build,执行时间取决于第三方插件的数量以及电脑配置

打包完之后再build文件夹下,这个文件夹下都是静态资源文件,需要web服务器运行
在这里插入图片描述
我们可以用serve来临时运行这个项目,看看好不好用
这是运行打包命令后的提示,提示我们可以用serve运行项目
在这里插入图片描述
尝试一下用serve,当然这种是临时的

在这里插入图片描述
访问一下

在这里插入图片描述

当然,实际开发肯定也不会只是用这种打包方式,这种打包太简陋了。一般都是用webpack,并且打包之后也不会用serve部署(这里只是临时看看),而是把静态文件交给后端的小伙伴,后端的小伙伴把文件部署在Nginx这种服务器上

类组件知识补充

setState

首先看一个案例证明setState操作是异步的
这是一个很简单的加和操作

在这里插入图片描述

首先我在加和的这个动作里console.log一下新的state

import React, { Component } from "react";export default class Test extends Component {state = {count: 0,};add = () => {const { count } = this.state;this.setState({ count: count + 1 });console.log("+1操作", this.state.count);};render() {return (<div><h1>当前count值{this.state.count}</h1><button onClick={this.add}>+1操作</button></div>);}
}

可以发现,setState并不是同步的操作,而是异步的
在这里插入图片描述
这里我们看一下setState更新状态的2种写法

(1). setState(stateChange, [callback])------对象式的setState1.stateChange为状态改变对象(该对象可以体现出状态的更改)2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用(2). setState(updater, [callback])------函数式的setState1.updater为返回stateChange对象的函数。2.updater可以接收到state和props。4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:1.对象式的setState是函数式的setState的简写方式(语法糖)2.使用原则:(1).如果新状态不依赖于原状态 ===> 使用对象方式(2).如果新状态依赖于原状态 ===> 使用函数方式(3).如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读取

上面的比较抽象,我们还是看代码,其他部分没变,集中在setState的部分

  add = () => {const { count } = this.state;this.setState({ count: count + 1 }, () => {// 在直接set完state后,立即执行一个回调函数// 这个回调函数是等setState动作做完之后才执行的console.log("+1操作执行了", this.state.count);});};

在这里插入图片描述
综上可以确定,setState是异步的操作

(1). setState(stateChange, [callback])------对象式的setState
//实际上对象式setState就是函数式setState的简写
add = () => {const { count } = this.state;this.setState({ count: count + 1 },()=>{可以选传一个函数进来})};
(2). setState(updater, [callback])------函数式的setState
add = () => {// 函数式的setStatethis.setState((state, props) => {return { count: state.count + 1 };},()=>{可以选传一个函数进来});
};

LazyLoad懒加载

首先,代码中懒加载的机制是无论页面上有多少个路由标签。1个也好,100个也好,在页面加载出来的时候,都是一次性全部加载出来
页面一加载,路由就会把要用的组件全都加载出来,后续用的话就不用单独请求加载了,但是这样如果组件过多就会造成负载很大,不太好。所以引入我们的懒加载机制
在这里插入图片描述

所以我们之前的import组件方式就都不要了,因为只要一用import导入就会自动加载,采用懒加载的导入方式
对比一下加载方式

// 这种import形式会默认一上来就加载,所以我们不能用这种了
// import About from "./pages/lazyload/About/About";
// import Home from "./pages/lazyload/Home/Home";// 改用懒加载写法,并用变量接住
const About = lazy(() => import("./pages/lazyload/About/About"));
const Home = lazy(() => import("./pages/lazyload/Home/Home"));

但是此时改完懒加载后,点击运行会报错,提示没有用<Suspense fallback={标签}>

image-20210911114307684

之所以强制用这个,是因为:我们的组件都是现用现请求,但这就带来了一个问题,网速慢我请求不来,或者请求不到数据怎么办?
我们就需要用一个兜底的方法,在这种请求失败或者网慢的时候给用户看。这个兜底的操作就是由Suspense 标签来做,注意,Suspense标签来自于React依赖
import React, { Component, Suspense, lazy } from "react";

// fallback引入的是兜底标签,比如模态窗口等等
<Suspense fallback={<h1>loading.....</h1>}><Switch><Route path="/xxx" component={Xxxx}/><Route path="/about" component={About}></Route><Route path="/home" component={Home}></Route><Redirect to="/login"/></Switch>
</Suspense>

此时点击某个route link,就会触发加载

在这里插入图片描述

改用慢速网,或者离线状态测试,可以发现兜底的标签已经显示出来了
在这里插入图片描述

Context

理解

一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

比如A组件与C,D组件通信这种就叫祖组件通信,就需要用Context
AB就是正常的父子组件用props通信
在这里插入图片描述
再比如,组件树结构如下,现在想从根节点传递一个 userName 的属性到叶子节点 A D F,通过 props 的方式传递,会不可避免的传递通过 B C E,即使这些组件也没有使用这个 userName 属性。如果这样的嵌套树形结构有5层或10层,那么将是灾难式的开发维护体验。如果能不经过中间的节点直接到达需要的地方就可以避免这种问题,这时 Context api 就是来解决这个问题的。
Context api 是在组件树中传递数据但不用每层都经过的一种 api。
在这里插入图片描述

我们用完Context之后,就会出现类似这种的数据传递流程

在这里插入图片描述

首先使用类组件实现一个祖组件通信的例子(就是套娃,A用props给B,B用props给C,C用props给D)

import React, { Component } from "react";export default class A extends Component {state = {data: "123456",};render() {return (<div><B data={this.state} /></div>);}
}class B extends Component {render() {return (<div><C data={this.props.data} /></div>);}
}class C extends Component {render() {console.log("C组件", this.props.data);return <div>C</div>;}
}

在这里插入图片描述
但是这种写法非常不推荐,如果有n个组件,就要套n层,或者B组件中断了,就没法继续传递了,所以我们引入了Context传递

Context使用

1) 创建Context容器对象:const XxxContext = React.createContext()  2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:<xxxContext.Provider value={数据}>子组件</xxxContext.Provider>3) 后代组件读取数据://第一种方式:仅适用于类组件 static contextType = xxxContext  // 声明接收contextthis.context // 读取context中的value数据//第二种方式: 函数组件与类组件都可以<xxxContext.Consumer>{value => ( // value就是context中的value数据要显示的内容)}</xxxContext.Consumer>

创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。

这里以第二种方式为例,分文件跨组件调用
A,B组件在同一文件,C组件单独一个文件

A,B组件

import React, { Component } from "react";
import C from "./C";export const MyContext = React.createContext();
export default class A extends Component {state = {data: "123456",};render() {return (<div><MyContext.Provider value={this.state}>{/* 不再需要声明props了 <B data={this.state} /> */}<B /></MyContext.Provider></div>);}
}class B extends Component {render() {return (<div>{/* 不再需要声明props了 <C data={this.state} /> */}{/* 这里用到C只是为了桥接祖孙组件的关系 */}<C /></div>);}
}

C组件

import React, { Component } from "react";
import { MyContext } from "./A";export default class C extends Component {render() {return (<div>C组件{/* 导入A定义好的MyContext,并用.Consumer定义为消费者在这个<MyContext.Consumer>标签里就可以使用祖组件的传值了,value即为全部信息,当props用即可 */}<MyContext.Consumer>{(value // value就是MyContext中的value数据) => console.log(value)}</MyContext.Consumer></div>);}
}

这样就可以实现祖组件与孙组件之间通信
但是实际上,在应用开发中一般不用context, 一般都用它的封装react插件
而且后续还有useContext,所以这里只做补充知识了解

Fragment

编译完实际上就是一个<></>空标签(编译之后就什么都没有了)
效果上二者等价,但是Fragment的意义是可以传一个参数,而完全空的标签是无法传递参数的
可以传递参数也就成为了Fragment存在的意义(比如循环的时候,传一个)

import { Fragment } from 'react'
//类似于一个<div>
<Fragment></Fragment>    //可以添加一个属性 'key',用于遍历比如 <Fragment key={i}>{v}</Fragment>
<></>  //不能添加任何属性

在这里插入图片描述
编译之后就自动消失了
在这里插入图片描述


举一个遍历时用Fragment传入key的操作

export default function App() {const arr = React.useState([{ id: "1", name: "a" },{ id: "2", name: "b" },{ id: "3", name: "c" },]);return (<Fragment>{arr.map((id, name) => {return <Fragment key={id}>name:{name}</Fragment>;})}</Fragment>);
}

遍历完成后,所有的标签有了key的同时,Fragment也自动去掉了

在这里插入图片描述

从作用上来看:

可以不用必须有一个真实的DOM根标签了,同时也可以满足空标签传递一个变量的这种需求

PureComponent

父组件里用到了子组件,父组件每次state改变,导致重新render的时候,就会触发子组件渲染。但其实有些时候,子组件没有用到父组件的state,就不需要重新渲染,所以这种重新渲染是浪费的,可以看这个例子

import React, { Component } from "react";export default class A extends Component {state = {data: "123456",};updateState = () => {// 手动触发更新statethis.setState({ data: "123" });};render() {console.log("父组件render调用");return (<div>{/* 我们这里只传递了一个固定值,没有用到state的值 */}<B data="data" />{/* 手动触发更新state */}<button onClick={this.updateState}>UPDATE</button></div>);}
}// 子组件
class B extends Component {render() {console.log("子组件render调用");return <div>B组件</div>;}
}

在这里插入图片描述
这种渲染的控制阀门,可以参考之前我们生命周期里学习的
shouldComponentUpdate()这个函数,这个函数可以接收到两个值(nextProps, nextState),这两个值就是未来要用于更新的值,我们可以将其与当前值(state,props)比较,判断是否需要开启更新的阀门(true更新,false不更新)

比如:

shouldComponentUpdate(nextProps, nextState) {// nextProps用不用取决于你想不想判断,不想判断不传也行// xxx是state里的某个属性名// 如果判断相同,则不需要更新,所以true的条件取反为false,关闭更新阀门return !this.state.xxx === nextState.xxx;
}

from之前的笔记:
shouldComponentUpdate(nextProps, nextState)
在setState以后,state发生变化,组件会进入重新渲染的流程时执行的逻辑。在这个生命周期中return false可以阻止组件的更新,主要用于性能优化。


所以针对于这种情况,React官方已经提供好了,我们直接继承PureComponent即可,已经给我们重写好了

import React, { PureComponent } from "react";// 继承PureComponent
export default class A extends PureComponent {state = {data: "123456",};updateState = () => {// 手动触发更新state// 注意,这里并不是给state设置为空,而是setState函数里传了个空对象进去// 代表什么都不更新的意思this.setState({ data: "123" });};render() {console.log("父组件render调用");return (<div>{/* 我们这里只传递了一个固定值,没有用到state的值 */}<B data="data" />{/* 手动触发更新state */}<button onClick={this.updateState}>UPDATE</button></div>);}
}// 继承PureComponent
class B extends PureComponent {render() {console.log("子组件render调用");return <div>B组件</div>;}
}

在这里插入图片描述


总结PureComponent

原始Component的2个问题

  1. 只要执行setState(),即使不改变状态数据(setState传一个空对象也会触发state更新,只不过state的内容不动), 组件也会重新render() ==> 效率低

  2. 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

效率高的做法

只有当组件的state或props数据发生改变时才重新render()

原因

Component中的shouldComponentUpdate()总是返回true

解决

办法1: 重写shouldComponentUpdate()方法比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
办法2:  使用PureComponentPureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true注意: 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false  不要直接修改state数据, 而是要产生新数据
项目中一般使用PureComponent来优化

再次注意:PureComponent 只是进行state和props数据的浅比较(只比较内存地址)。如果只是数据对象内部数据变了,而数据对象本身的引用地址没有变化,则被认为没有变化,返回false。不要直接修改state数据, 而是要产生新数据(新对象,新内存地址),再更新state

render props(插槽技术,封装组件必用)

场景:想给某个组件标签传入一个组件
即这种类似场景:
A标签引入了B,B在A组件里显示,结构上看起来确实是A与B为父子组件。但这就带来了一个问题,表面上是AB父子可以通过props传参,但实际上,这个操作只能在A标签里写,并且传什么值只能写死,没办法灵活修改。所以就会出现render props这种技术

export default class Parent extends Component {render() {return (<div className="parent">Parent<A><B></B></A></div>);}
}

简单来说,就是运用箭头函数的return操作,给A标签里传一个属性,这个属性return一个B标签,在目标组件里,调用这个属性的返回值,即:

// 这个组件参数的调用,就是传入组件的插槽插进来的地方!!!
// 这其实就是jsx语法
{this.props.render()}

注意,我们一般把这个传参的属性叫render,这只是约定俗成,其实叫啥都行

修改一下:,此时在祖组件Parent里面一看就知道A与B的父子关系,同时知道了A中用到了B做插槽

export default class Parent extends Component {render() {return (<div className="parent">Parent<A render={() => <B/>} /></div>);}
}export default class A extends Component {render() {return (<div className="A">这里是A标签<br />{/* 调用render属性,一定要加括号,变成函数返回值这样才能拿到传入的B标签*/}// 这个组件参数的调用,就是B的插槽插进来的地方!!!{this.props.render()}</div>);}
}

运行一下,发现B组件已经被插入了A组件的插槽里了
在这里插入图片描述

个人感觉就是类组件属性的灵活调用,好处在于我们组件有了插槽,可以灵活在祖组件替换,不必在父组件里再写死组件。同时传参也可以正常进行

插槽传参

传参就是在箭头函数的基础上加入了参数传递,很好理解

export default class Parent extends Component {render() {return (<div className="parent">Parent<A render={(data) => <B data={data} />} /></div>);}
}
export default class A extends Component {state = {data: "这是A传来的参数",};render() {return (<div className="A">这里是A标签<br />{/* 插槽,并且传入A组件的参数给插槽插进来的组件,不再局限于某一固定组件*/}{this.props.render(this.state.data)}</div>);}
}
export default class B extends Component {render() {return (<div className="B">B标签做插槽!!!<br />接收到A传来的参数: {this.props.data}</div>);}
}

在这里插入图片描述
参数传递顺序:

A组件:{this.props.render(this.state.data)}
Parent组件接收A组件传来的参数: <A render={(data) => <B data={data} />} />
B组件解析props: {this.props.data}

总结render props

如何向组件内部动态传入自定义组件标签(插槽技术)

Vue中: 使用slot技术, 也就是通过组件标签体传入结构  <A><B/></A>
React中:使用children props: 通过组件标签体传入结构使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

children props

<A><B>xxxx</B>
</A>
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到 ,因为这种解析仅限于H5的标签children属性,React并没有办法直接支持

render props

A组件:{this.props.render(内部state数据)}
Parent组件接收A组件传来的参数: <A render={(data) => <B data={data} />} />
B组件解析props: 读取A组件传入的数据显示{this.props.data}

错误边界

说白了,就是个兜底的方法,当组件的子组件出现错误时,不至于崩掉,将错误控制在一个小小的子组件里,不至于让整个应用崩掉

当组件的子组件出现错误时会调用,也就是始终去找出错组件的父组件去处理
static getDerivedStateFromError(error) 的钩子,并且得到错误信息,并且将错误信息return到state里,触发页面渲染,做出相应的处理,比如错误页面,弹窗等等友好提示

但是注意,这个东西只能在prod生产环境生效,对于dev模式下,只会给你闪一下处理的页面,然后就继续该报错报错,因为要在dev模式下告诉程序员哪里出错了,才能做出相应的处理

同时,错误边界的捕捉,仅限于生命周期函数,比如render,componentDidMount......这种生命周期函数里发生的错误

class Children extends Component {// 比如这种胡乱调用的某个函数(非生命周期的),就不会被catch住	test()render() {return (<div></div>);}
}

例子:

import React, { Component } from "react";export default class Parent extends Component {state = {// 异常FlaghasError: false,};static getDerivedStateFromError(error) {// 子组件生命周期函数里出现的异常会在这里被捕捉,同时获取异常信息console.log(error);// 在render之前触发// 返回新的state,触发组件重新渲染return {hasError: true,};}render() {return (<div><div>父组件<br />{/* 根据子组件是否有错误,来判断显示内容 */}{this.state.hasError ? <>子组件网络出错,请重试</> : <Children></Children>}</div>);}
}class Children extends Component {state = {// 空的内容,想map遍历直接报错了// users: [//   { id: "123", name: "Tony" },//   { id: "456", name: "Jerry" },// ],};render() {return (<div>子组件{this.state.users.map((user) => {return <div key={user.id}>name:{user.name}</div>;})}</div>);}
}

开发模式下的控制台:
在这里插入图片描述


生产环境下例子:
在这里插入图片描述

componentDidCatch钩子(属于生命周期钩子,但不常见)

这个钩子一般是和getDerivedStateFromError配合使用,每次出现错误就会调用这个函数,一般用于统计错误次数以及错误信息,并打包发给后端,同样也是生产环境下才生效

// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {console.log(error);// 在render之前触发// 返回新的statereturn {hasError: true,};
}componentDidCatch(error, info) {// 统计页面的错误。发送请求发送到后台去等一系列操作都可以在这做console.log("componentDidCatch钩子运行", error, info);
}

在这里插入图片描述

组件通信方式总结

组件间的关系

  • 父子组件
  • 兄弟组件(非嵌套组件)
  • 祖孙组件(跨级组件)

几种通信方式

1.props:(1).children props(2).render props
2.消息订阅-发布:pubs-sub、event等等
3.集中式管理:redux、dva等等
4.conText:生产者-消费者模式

比较好的搭配方式

父子组件:props
兄弟组件:消息订阅-发布、集中式管理
祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)

React-Hooks

React16.8之前,函数式组件本身并没有this,打印一下组件的this结果是undefined,因此函数式组件无法通过this.setState等操作本身状态,或者操作生命周期,但在16.8之后,React提出来Hooks的概念。函数式组件就可以使用Hooks来操作自身的状态,生命周期等等,函数式组件就突然变得香了起来。

useState

hooks 解决了函数式组件和类式组件的差异,让函数式组件拥有了类式组件所拥有的 state ,同时新增了一些 API ,让函数式组件,变得更加的灵活

首先我们需要明确一点,函数式组件没有自己的 this现在常用的组件都是函数式组件,不再是类式组件

function Demo() {const [count, setCount] = React.useState(0)console.log(count, setCount);function add() {setCount(count + 1)}return (<div><h2>当前求和为:{count}</h2><button onClick={add}>点我加1</button></div>)
}
export default Demo

利用函数式组件完成的 点我加1 案例

这里利用了一个 Hook :useState

它让函数式组件能够维护自己的 state ,它接收一个参数,作为初始化 state 的值,赋值给 count,因此 useState 的初始值只有第一次有效,它所映射出的两个变量 countsetCount 我们可以理解为 setState 来使用

useState 能够返回一个数组,第一个元素是 state ,第二个是更新 state 的函数

// 打印一下这个Hooks的内容
const [count, setCount] = React.useState(0);
console.log(count, setCount);

控制台输出:
0就是我们传入初始化的count,后面的函数就是setCount的函数
在这里插入图片描述


count 是初始化的值,而 setCount 就像是一个 action 对象驱动状态更新

我们可以通过 setCount 来更新 count 的值,setCount可以传入两种形式(直接传值 或 箭头函数)

setCount(count + 1)

除了这种直接传值的操作,我们还可以用箭头函数的写法,二者等价,视情况使用

setCount((count) => {
// 把计算结果return给count
return count + 1;
});

总结useState

(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
(2). 语法: const [xxx, setXxx] = React.useState(initValue)  
(3). useState()说明:参数: 第一次初始化指定的值在内部作缓存,读取的时候可以直接调用返回值: 包含2个元素的数组,1个为内部当前状态值, 第2个为更新状态值的函数
(4). setXxx()2种写法:setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

useEffect

在类式组件中,提供了一些声明周期钩子给我们使用,我们可以在组件的特殊时期执行特定的事情,例如 componentDidMount ,能够在组件挂载完成后执行一些东西(这是类组件生命周期常用的写法)

在函数式组件中也可以实现,它采用的是 useEffect Hook ,它的语法更加的简单,同时融合了 componentDidUpdata 生命周期,极大的方便了我们的开发

注意点:useEffect 是在render结束之后才执行的。

React.useEffect(() => {console.log('被调用了');
})

由于函数的特性,我们可以在函数中随意的编写函数,这里我们调用了 useEffect 函数,这个函数有多个功能

当我们像上面代码那样使用时,它相当于 componentDidUpdatacomponentDidMount 一同使用,也就是在组件挂载和组件更新的时候都会调用这个函数

react-extension-hook-1


它还可以接收第二个参数,这个参数表示它要监测的数据,也就是他要监视哪个数据的变化,分三种情况传递参数

当我们监听页面上所有的状态变化的时候,第二个参数上就可以什么都不传,当页面上有任意状态变化,useEffect就会执行

React.useEffect(() => {console.log('被调用了');
})

当我们不需要监听任何状态变化的时候,我们可以就传递一个空数组,这样它就能当作 componentMidMount 来使用(比如一上来就加载数据这种操作),这样我们只有在组件第一次挂载的时候触发

React.useEffect(() => {console.log('被调用了');
}, [])

当我们需要监听一个数据或多个数据变化的时候,我们也可以在数组里选择指定的数据来进行监控,当监听的数据发生变化时,相应的函数体就会被执行

React.useEffect(() => {console.log('被调用了');
}, [被监听的变量1,被监听的变量2,被监听的变量3...])

当我们想要在卸载一个组件之前进行一些清除定时器的操作,在类式组件中,我们会调用生命周期钩子 componentDidUnmount 来实现,但在函数式组件中,我们的写法更为简单,我们直接在 useEffect 的第一个参数传入返回值实现即可

也就是useEffect的return值,当组件卸载时,这个函数会被执行。这可以帮助我们取消或清理副作用,比如取消网络请求、关闭定时器等。如果不需要清理副作用,useEffect可以不返回任何值。

测试一下

手动实现卸载

function unmount() {ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}

卸载前输出

React.useEffect(() => {console.log('被调用了');return () => {console.log('我要被卸载了');}
}, [count])

react-extension-hook-2

useEffect里写了return就可以实现在组件即将被卸载的时候输出

因此 useEffect 相当于三个生命周期钩子

[componentDidMount()] 组件刚挂载好就调用,第二个参数传入空数组的场景
[componentDidUpdate()] 某一个state发生变化时调用,第二个参数传入某个state来监听的场景
[componentWillUnmount() ] 组件即将卸载时调用,再useEffect里写了return的场景

除此以外的就是监听普通数据的变化,所以不算在生命周期钩子里面

总结useEffect

(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用(useEffect)操作:发ajax请求数据获取设置订阅 / 启动定时器手动更改真实DOM
(3). 语法和说明: useEffect(() => { // 在此可以执行任何带副作用操作return () => { // 在组件卸载前执行(可选,非必须项目)// 在此做一些收尾工作, 比如清除定时器/取消订阅等}}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
(4). 可以把 useEffect Hook 看做如下三个生命周期函数的组合[componentDidMount()]   组件刚挂载好就调用,第二个参数传入空数组的场景[componentDidUpdate()]   某一个state发生变化时调用,第二个参数传入某个state来监听的场景[componentWillUnmount() ]	组件即将卸载时调用,再useEffect里写了return的场景

useLayoutEffect

useEffect 很类似,用的不多,一般都是用useEffect。官方也并不推荐用useLayoutEffect,会阻塞浏览器重新绘制,从而影响性能。useLayoutEffect里的操作都执行完毕之后,才会继续让浏览器往下走,所以会影响性能。

它的作用是:在 DOM 更新完成之后执行某个操作

注意:

执行时机在 useEffect 之前,其他都和 useEffect 都相同

useEffect 执行时机在 render 之后

useLayoutEffect 执行时机在 DOM 更新之后,比render还要晚一点

打印一下运行顺序

  React.useEffect(() => {console.log("useEffect被调用了");}, []);React.useLayoutEffect(() => {console.log("useLayoutEffect被调用了");}, []);

总的来说不推荐用,了解即可。

useRef

当我们想要获取组件内的信息时,在类式组件中,我们会采用 ref 的方式来获取。在函数式组件中,我们可以采用也可以采用 ref 但是,我们需要采用 useRef 函数来创建一个 ref 容器,保存目标节点信息,这和 createRef 很类似。

const myRef= React.useRef();
// 获取到input标签的全部信息,并且绑定给myRef上
<input type="text" ref={myRef} />
...butten触发打印

获取 ref 值,即可打印节点里的信息

function show() {console.log(myRef)
}

即可成功的获取到 input 框中的值,包括value等一系列的目标标签属性
打印标签全部属性
在这里插入图片描述

总结useRef

(1). Ref Hook可以在函数组件中存储/查找 组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象并随变化更新,功能与类组件的React.createRef()一样

useContext

相比于Context,useContext是为函数式组件打造的Hook

举个例子:
祖组件内容:

// 句柄创建一次即可,规定作用范围,将需要传递的孙组件一并传入进去
export const MyContext = React.createContext();export default function App() {return (<BrowserRouter>{/* 确定作用范围的同时,value属性传入参数 */}<MyContext.Provider value={"123456"}><A /></MyContext.Provider></BrowserRouter>);
}

祖孙组件通过A组件来桥接,即 App祖组件 -> A子组件 -> B孙组件

在孙组件中,通过 useContext(祖组件中定义的Context) 来获取数据

import React from "react";
// 导入定义好的范围句柄,用于Hooks接收
import { MyContext } from "../../App";export default function B() {// Hoooks接收const num = React.useContext(MyContext);return <div>B组件接收:{num}</div>;
}

useMemo

待更新

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

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

相关文章

【unity学习笔记】配置模型,实现眨眼和口型效果

一、vriod捏人 1.在vroidstudio软件中捏人 2.导出模型&#xff08;.vrm) 二、vrid导入unity的插件 1.在Git上搜索、打开univrm。 2.找到release页面找到合适的插件版本。&#xff08;VRM-0.116.0_0f6c&#xff09; 3.将univrm导入到工程中&#xff08;assets&#xff09;。 三…

查看IOS游戏FPS

摘要 本篇技术博客将介绍如何使用克魔助手工具来查看iOS游戏的帧率&#xff08;FPS&#xff09;。通过克魔助手&#xff0c;开发者可以轻松监测游戏性能&#xff0c;以提升用户体验和游戏质量。 引言 在iOS游戏开发过程中&#xff0c;了解游戏的帧率对于优化游戏性能至关重要…

第一届能源电子产业创新大赛太阳能光伏赛道在京顺利完成初赛评审

近日&#xff0c;第一届能源电子产业创新大赛太阳能光伏赛道初赛在北京顺利举行。本次太阳能光伏赛道赛事由工业和信息化部产业发展促进中心、宜宾市人民政府主办&#xff0c;宜宾市经济和信息化局、宜宾高新技术产业园区承办&#xff0c;中国国检测试控股集团股份有限公司协办…

【 C语言 】| C程序百例 - 绘制余弦曲线

【 C语言 】| C程序百例 - 绘制余弦曲线 时间&#xff1a;2023年12月29日12:56:29 文章目录 【 C语言 】| C程序百例 - 绘制余弦曲线1.要求2.问题分析与算法设计3.程序3-1.源码3-2.makefile 4.运行 1.要求 在屏幕上用"*"显示0~360的余弦曲线cos(x)曲线。 2.问题分析与…

代码随想录刷题 | Day1

今日学习目标 一、基础 数组 array类 模板类vector 数组是存放在连续内存空间上的相同类型数据的集合。 数组可以方便的通过下标索引的方式获取到下标下对应的数据。 需要两点注意的是 数组下标都是从0开始的。 数组内存空间的地址是连续的 而且大家如果使用C的话&…

【AIGC表情prompt】提示词练习技巧

表情类提示词练习技巧 医疗机器人&#xff0c;男人笑脸景深&#xff0c;数据&#xff0c;座标&#xff0c;12k,c4d渲染&#xff0c;高分辨率&#xff0c;,暖色调&#xff0c;高清对比 医疗机器人&#xff0c;男人微笑&#xff0c;景深&#xff0c;数据&#xff0c;座标&#xf…

nginx日志常见报错解决

目录 一&#xff1a;报错 二&#xff1a;php查看后台内容有的栏目出现502&#xff1f; 三&#xff1a;413 Request Entity Too Large? 四&#xff1a;Request Header Or Cookie Too Large 400 一&#xff1a;报错 upstream prematurely closed connection while reading r…

【C语言数组传参】规则详解

目录 数组传参介绍 数组传参规则 数组传参的实参 特殊情况一&#xff1a;sizeof&#xff08;数组名&#xff09; 特殊情况二&#xff1a;&数组名 数组传参的形参 数组传参使用数组名作为形参接收 形参如果是⼀维数组 形参如果是⼆维数组 数组传参使用指针作为形参…

Linux:apache优化(1)—— 长链接/保持连接

系统:CentOS 7.9 apache版本为&#xff1a;2.4.25 需要使用源码包进行安装才能够使用这些扩展模块 在使用这些扩展模块前要先下载zlib-devel 安装--enable-deflate选项需要的网页压缩传输的软件包 yum -y install zlib-devel 在配置编译安装时需要使用扩展配置 ./config…

如何使用Docker将.Net6项目部署到Linux服务器(三)

目录 四 安装nginx 4.1 官网下载nginx 4.2 下载解压安装nginx 4.3 进行configure 4.4 执行make 4.5 查看nginx是否安装成功 4.6 nginx的一些常用命令 4.6.1 启动nginx 4.6.2 通过命令查看nginx是否启动成功 4.6.3 关闭Nginx 4.6.5 重启Nginx 4.6.6 杀掉所有Nginx进程 4.…

C# 使用ZXing.Net识别二维码和条码

目录 写在前面 代码实现 调用示例 写在前面 上一篇写了 C# 使用ZXing.Net生成二维码和条码-CSDN博客 使用ZXing.Net解码非常简单&#xff0c;事实上就只用一行代码就好了&#xff0c;这么简单那为什么还要贴在这里呢&#xff0c;原因是开始时&#xff0c;在网上看资料看到…

Linux 编写脚本定时发送天气预报

1 首先要配置smtp服务 我这里使用的是qq邮箱 拿到smtp的密钥 2 配置mail.rc文件 在配置文件末尾加上 set from109456****qq.com #这里是发送邮件的地址 set smtpsmtp.qq.com:587 #阿里云一定要带上这个端口号&#xff0c;其他云服务商不用 set smtp-auth-user109456**** #…

Mybatis行为配置之Ⅳ—日志

专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 再谈动态SQL Mybatis配置入门 Mybatis行为配置之Ⅰ—缓存 Mybatis行为配置…

系统启动流程 - 理解modules加载流程

​编辑 Hacker_Albert    202 linux 启动流程module加载 1.启动过程分为三个部分 BIOS 上电自检&#xff08;POST&#xff09;引导装载程序 (GRUB2)内核初始化启动 systemd&#xff0c;其是所有进程之父。 1.1.BIOS 上电自检&#xff08;POST&#xff09; BIOS stands for…

图像拼接——基于homography的特征匹配算法

目录 1. 任务要求2. 数据集3. 基于homography的特征匹配算法4. 拼接流程展示4.1 图片实例4.2 特征点位图4.3 特征点匹配结果4.4 相机校准结果4.5 拼接结果 5. 部分图像拼接结果展示 1. 任务要求 输入&#xff1a;同一个场景的两张待拼接图像&#xff08;有部分场景重合&#x…

macOS系统打开Linux的方法

第一步 按下[command空格键]调出搜索框&#xff0c;输入“终端”&#xff0c;打开图上第一个 第二步 如图先输入"sudo -i"&#xff0c;敲回车键&#xff0c;再输入开机密码&#xff0c;再敲回车键就可以打开。注意&#xff1a;这里的密码输入不会显示在页面。 如果要…

JavaEE - 网络编程之回显服务器

目录 一.什么是回显服务器&#xff1f; 二.UDP是什么&#xff1f; 1.TCP 是有链接的&#xff0c; UDP 是无连接的 2.TCP是可靠传输的,UDP是不可靠传输的 3.TCP是面向字节流的&#xff0c;UDP是面向数据报 4.TCP和UDP是全双工的 三.UDP的 socket api 四. 具体代码实现 …

Qt Designer中各个模块的详细介绍,小白一看就会!!第3部分——Item Views (Model-Based) 模块介绍

Item Views (Model-Based) 模块的详细介绍 在Qt Designer中&#xff0c;Item Views (Model-Based) 模块是一组基于模型/视图&#xff08;Model/View&#xff09;架构的控件&#xff0c;用于展示和操作数据。这些控件与数据模型紧密结合&#xff0c;使得数据展示变得更加灵活和…

SAP问题 OPEN SQL 取不到值

关键&#xff1a;数据库中有数据&#xff0c;但是open sql取不到数据 背景&#xff1a; 标准程序在测试环境正常执行&#xff0c;在生产环境报错。 解决过程&#xff1a; 第一步&#xff1a;分析执行结果不一致可能的原因&#xff1a; 1.测试数据问题&#xff0c;可能性小&…

lv13 内核模板编译方法 7

1 内核模块基础代码解析 Linux内核的插件机制——内核模块 类似于浏览器、eclipse这些软件的插件开发&#xff0c;Linux提供了一种可以向正在运行的内核中插入新的代码段、在代码段不需要继续运行时也可以从内核中移除的机制&#xff0c;这个可以被插入、移除的代码段被称为内…