React复习日志大纲

文章目录

  • React基础篇
    • 创建项目
    • 启动项目
    • 项目目录说明调整
    • 项目src剩余目录
    • 01基本使用
    • 02 列表渲染
    • 03 条件渲染
    • 04 样式处理
    • 05 函数和类组件创建和渲染
    • 06 事件绑定
    • 07 事件对象e
    • 08 传递额外参数
    • 09 组件状态修改
    • 10 受控组件
    • 11 非受控组件
    • 12 组件通信父传子
    • 13 Props说明
    • 14 组件通信子传父
    • 15 组件通信兄弟传值
    • 16 createContext跨组件通信
    • 17 组件的children属性
    • 18 props校验和默认值
    • 19 生命周期挂载阶段
    • 20 更新和卸载阶段
    • 21 Hooks函数useState
    • 22 useEffect
    • 23 清除副作用
    • 24 发送网络请求
    • 25 useRef使用
    • 26 useContext使用
  • React路由篇
    • 路由demo案例
      • 项目结构
      • About.js内容为
      • Home.js内容为
      • App.js内容为
      • 效果图
    • 路由各个组件说明
    • 编程式导航使用
      • 改写App.js
      • Login.js组件代码(使用编程式导航三步走)
      • 跳转携带参数(两种方式)
        • 1.searchParams传参(在跳转前的组件传参)
        • 2.params传参
    • 嵌套路由
      • 基础配置
      • 默认二级路由设置
      • 404页配置
  • Mobx
    • 简介
    • 环境配置
    • 编写第一个Mobx的store
      • 初始化Mobx
      • 连接react
    • Mobx的计算属性computed
    • 拆分子模块和创建根模块(模块化)
      • 怎么做??
      • 项目目录
        • 子模块一counter.store.js的代码如下
        • 子模块二list.store.js代码如下
        • 业务模块index.js代码如下
        • App.js代码如下
        • App.js里面打印的结果图
  • Redux使用

本套笔记对应视频

本套笔记对应语雀笔记

React基础篇

创建项目

npx create-react-app 项目名
  • a. npx create-react-app 是固定命令,create-react-app是React脚手架的名称
  • b. react-basic表示项目名称,可以自定义,保持语义化
  • c. npx 命令会帮助我们临时安装create-react-app包,然后初始化项目完成之后会自自动删掉,所以不需要全局安装create-react-app

启动项目

$ yarn start
or
$ npm start

项目目录说明调整

  • 目录说明
    – a. src 目录是我们写代码进行项目开发的目录
    – b. package.json 中俩个核心库:react 、react-dom
  • 目录调整
    – a. 删除src目录下自带的所有文件,只保留app.js根组件和index.js
    – b. 创建index.js文件作为项目的入口文件,在这个文件中书写react代码即可
  • 入口文件说明
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
// 引入根组件App
import App from './App'
// 通过调用ReactDOM的render方法渲染App根组件到id为root的dom节点上
ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root')
)

index.js改造如下

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(// 去掉严格模式节点// 会添加useEffect的执行时机// <React.StrictMode><App />// </React.StrictMode>
);

项目src剩余目录

在这里插入图片描述

后续所有代码片段都是在App.js里面写的

01基本使用

// 1.识别常规变量
const name = "萧寂";
// 2.原生js方法调用
const getAge = () => {return 18;
};
// 3.三元运算符
const flag = true;function App() {return (<div className="App"><div>{name}</div><div>{getAge()}</div><div>{flag ? "1234" : "456"}</div></div>);
}export default App;

02 列表渲染

// 列表渲染
// 注意事项,遍历列表时,同样需要一个不可重复的key
// key仅仅在内部使用,不会渲染到页面上const songs = [{ id: 1, name: "痴心绝对" },{ id: 2, name: "像我这样的人" },{ id: 3, name: "南山南" },
];function App() {return (<div className="App"><ul>{songs.map((item) => (<li key={songs.id}>{item.name}</li>))}</ul></div>);
}export default App;

03 条件渲染

// 条件渲染
// 技术方案:1.三元表达式,2.逻辑运算符
const flag = true
function App() {return (<div className="App">{/* 条件渲染字符串 */}{flag ? "react真有趣" : "vue真有趣"}{/* 条件渲染标签/组件 */}{flag ? <h2>this is span</h2> : null}{/* 逻辑运算 */}{true && <h2>this is span</h2>}</div>);
}export default App;

04 样式处理

// 样式处理
// 1.类名样式 导入样式
import "./04 app.css";
// 2.内联/行内样式,在元素身上绑定一个style即可
const style = { color: "red", fontSize: "30px" };// 动态控制类名
const activeflag = true;
function App() {return (<div className="App">{/* 测试类名样式,通过className */}<span className={activeflag ? "active" : ""}>测试类名样式</span><br></br>{/* 内联/行内样式 */}<span style={{ color: "red", fontSize: "30px" }}>this is span</span><br></br><span style={style}>this is span</span></div>);
}export default App;

样式渲染对应的css文件内容为

.active{color: blue;
}

05 函数和类组件创建和渲染

import React from "react"
// 函数组件的创建和渲染
// 创建函数组件
// 1. 组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签
// 2. 函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null
// 3. 组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容
// 4. 使用函数名称作为组件标签名称,可以成对出现也可以自闭合
function Hello () {return <div>我是函数组件创建的组件</div>
}// 创建类组件
// 1. 类名称也必须以大写字母开头
// 2. 类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性
// 3. 类组件必须提供 render 方法render 方法必须有返回值,表示该组件的 UI 结构
class HelloComponent extends React.Component {render () {return <div>我是类组件创建的组件</div>}
}function App () {return <div className="App">{/* 渲染函数组件 */}<Hello></Hello>{/* 渲染类组件创建的 */}<HelloComponent></HelloComponent></div>
}export default App

06 事件绑定

import React from "react"
function Hello () {// 事件绑定// 绑定函数组件的点击事件const clickHandler = () => { console.log('函数组件的点击事件被触发了') }return <div onClick={clickHandler}>我是函数组件创建的组件</div>
}class HelloComponent extends React.Component {// 绑定类组件的点击事件// 事件回调函数标准写法,避免this指向问题// 这样写,回调函数中的this指向的是当前组件实例对象clickHandler2 = () => { console.log('类组件的点击事件被触发了') }render () {return <div onClick={this.clickHandler2}>我是类组件创建的组件</div>}
}function App () {return <div className="App">{/* 渲染函数组件 */}<Hello></Hello>{/* 渲染类组件创建的 */}<HelloComponent></HelloComponent></div>
}export default App

07 事件对象e

import React from "react"
function Hello () {const clickHandler = (e) => {console.log('函数组件的点击事件被触发了', e)// 阻止a链接跳转e.preventDefault()}return <div onClick={clickHandler}><a href="http://baidu.com">点击跳转百度</a></div>
}class HelloComponent extends React.Component {clickHandler2 = (e) => { console.log('类组件的点击事件被触发了', e) }render () {return <div onClick={this.clickHandler2}>我是类组件创建的组件</div>}
}function App () {return <div className="App">{/* 渲染函数组件 */}<Hello></Hello>{/* 渲染类组件创建的 */}<HelloComponent></HelloComponent></div>
}export default App

08 传递额外参数

import React from "react"
function Hello () {// 传递额外参数// 当需要有参数传入并且也需要事件对象时,传参时需要把箭头函数的事件对象传入函数,只需要这样一个额外参数// 只需要一个额外参数  () => onDel(item.id)// 既需要事件对象也需要额外参数  (e) => onDel(e,item.id)const clickHandler = (msg, e) => { console.log('函数组件的点击事件被触发了', msg, e) }// 传参时候需要使用箭头函数return <div onClick={(e) => clickHandler("this is msg", e)}>我是函数组件创建的组件</div>
}class HelloComponent extends React.Component {clickHandler2 = () => { console.log('类组件的点击事件被触发了') }render () {return <div onClick={this.clickHandler2}>我是类组件创建的组件</div>}
}function App () {return <div className="App">{/* 渲染函数组件 */}<Hello></Hello>{/* 渲染类组件创建的 */}<HelloComponent></HelloComponent></div>
}export default App

09 组件状态修改

// 组件状态  类组件进行演示
import React from "react"
class TextComponent extends React.Component {// 定义组件状态state = {// 这里定义各种属性  全都是当前组件状态name: "cp teacher",count: 0};changeName = () => {// 修改state中的状态name// 注意:不可以直接做复制修改,必须通过一个方法 setStatethis.setState({ name: "萧寂" })};changeCount = () => {// 让count的值加1this.setState({ count: this.state.count + 1 })};render () {return (<div>{/* 渲染name */}当前name为:{this.state.name}{/* 修改name状态 */}<button onClick={this.changeName}>修改name状态</button><button onClick={this.changeCount}>修改count++,当前为{this.count}</button></div>)}
}
function App () {return <div className="App"><TextComponent></TextComponent></div>
}export default App/*** 总结* 1. 编写组件其实就是编写原生js类或者函数* 2. 定义状态必须通过state 实例属性的方法 提供一个对象 名称是固定的就叫做state* 3. 修改state中的任何属性 都不可以通过直接赋值 必须走setState方法 这个方法来自于继承得到* 4. 这里的this关键字 很容易出现指向错误的问题 上面的写法是最推荐和最规范的 没有this指向问题*/

10 受控组件

// 表单处理
// 目标任务:  能够使用受控组件的方式获取文本框的值// 使用React处理表单元素,一般有俩种方式:
// 1. 受控组件 (推荐使用)
// 2. 非受控组件 (了解)// 什么是受控组件?  input框自己的状态被React组件状态控制
// React组件的状态的地方是在state中,input表单元素也有自己的状态是在value中,
// React将state与表单元素的值(value)绑定到一起,由state的值来控制表单元素的值,
// 从而保证单一数据源特性// 以获取文本框的值为例,受控组件的使用步骤如下:
// 1. 在组件的state中声明一个组件的状态数据
// 2. 将状态数据设置为input标签元素的value属性的值
// 3. 为input添加change事件,在事件处理程序中,通过事件对象e获取到当前文本框的值(即用户当前输入的值)
// 4. 调用setState方法,将文本框的值作为state状态的最新值import React from 'react'class InputComponent extends React.Component {// 声明组件状态state = {message: 'this is message',}// 声明事件回调函数changeHandler = (e) => {this.setState({ message: e.target.value })}render () {return (<div>{this.state.message}<br></br>{/* 绑定value 绑定事件*/}<input value={this.state.message} onChange={this.changeHandler} /></div>)}
}function App () {return (<div className="App"><InputComponent /></div>)
}
export default App

11 非受控组件

// 非受控组件
import React, { createRef } from "react"class Input extends React.Component {msgRef = createRef()  // 创建一个容器getValue = () => {// 通过ref获取输入框的值// this.msgRef.current为绑定的原生dom元素console.log(this.msgRef.current.value)}render () {return (<><input type="text" ref={this.msgRef} /><button onClick={this.getValue}>点击获取输入框的值</button></>)}
}
function App () {return (<div className="App"><Input></Input></div>)
}
export default App

12 组件通信父传子

// 组件通信// 组件是独立且封闭的单元,默认情况下组件只能使用自己的数据(state)
// 组件化开发的过程中,完整的功能会拆分多个组件,在这个过程中不可避免的需要互相传递一些数据
// 为了能让各组件之间可以进行互相沟通,数据传递,这个过程就是组件通信
// 1. 父子关系 -  最重要的
// 2. 兄弟关系 -  自定义事件模式产生技术方法 eventBus  /  通过共同的父组件通信
// 3. 其它关系 -  mobx / redux / zustand// 父传子实现
// 1.  父组件提供要传递的数据  -  state 
// 2.  给子组件标签添加属性值为 state中的数据 
// 3.  子组件中通过 props 接收父组件中传过来的数据 
//   a. 类组件使用this.props获取props对象
//   b. 函数式组件直接通过参数获取props对象
import React from "react"
// App父组件(类组件) Son子组件(函数式和类组件)
function SonB (props) {// props是一个对象  里面存着通过父组件传入的所有数据// props为只读对象,不能修改// props可以传递任何类型的数据(数组,对象,字符串,布尔,数字,JSX...)return (<div>我是函数子组件,--{props.msg}</div>)
}class SonA extends React.Component {// 在类组件直接使用this.props.属性名拿到父组件传来的数据,这里的prop是固定的render () {return (<div>我是类子组件,--{this.props.msg}</div>)}
}class App extends React.Component {state = {message: "this is message"}render () {return (<div>{/* 子组件身上绑定属性,属性名可以自定义 保持语义化 */}<SonA msg={this.state.message}></SonA><SonB msg={this.state.message}></SonB></div>)}
}export default App

13 Props说明

// 组件通信// props是一个对象  里面存着通过父组件传入的所有数据
// props为只读对象,不能修改
// props可以传递任何类型的数据(数组,对象,字符串,布尔,数字,JSX...)
import React from "react"function SonA (props) {console.log('props', props)// 这里也可以将props进行解构赋值取数据,也可以直接在形参那里解构return (<div>{/* 数组 */}我是函数子组件,--{props.list.map(item => <p key={item}>{item}</p>)}{/* 对象 */}{props.userinfo.name}{/* 函数 */}<button onClick={props.getMes}>-- 父组件的函数</button><br />{/* JSX */}{props.child}</div>)
}class App extends React.Component {state = {list: [1, 2, 3],userinfo: {name: "cp",age: 30},};getMes = () => {console.log('父组件中的函数')}render () {return (<div>{/* 模拟传入数组,对象,函数和JSX的场景 */}<SonA list={this.state.list} userinfo={this.state.userinfo} getMes={this.getMes} child={<span>我是传过来的JSX</span>}></SonA></div>)}
}export default App

14 组件通信子传父

// 组件通信// 父传子 props 函数
// 子传父:子组件调用父组件传递过来的函数,并且把想要传递的数据当成函数的实参
// 传入即可
import React from "react"function Son (props) {const { getSonMsg } = propsreturn (<div>this is son{/* 子组件向父组件传递参数 */}<button onClick={() => getSonMsg("这里是来自于子组件的数据") }>点击执行</button></div>)
}class App extends React.Component {// 准备数据state = {list: [1, 2, 3]};// 准备一个函数,传给子组件getSonMsg = (a) => {console.log('a',a);}render () {return (<div><Son getSonMsg={this.getSonMsg}></Son></div>)}
}export default App

15 组件通信兄弟传值

// 组件通信// 目标,把B的数据传入A
// 技术方案:
//   1.先把B中的数据通过子传父传给App
//   2.再把App接收到的SonB的数据 通过父传子 传给A
import React from "react"function SonA (props) {return (<div><div>this is A</div><p>B组件传来的值---({props.num})</p></div>)
}function SonB (props) {const bMsg = "这是来自于B组件的数据"return (<div><div>this is B</div><button onClick={() => { props.getMsg(bMsg) }}>点击按钮将B组件的数据传给A组件</button></div>)
}class App extends React.Component {state = {num: ""}// 声明一个传递给B组件的方法getMsg = (msg) => {console.log('msg',msg);this.setState({num : msg})}render () {return (<div><SonA num={this.state.num}></SonA><SonB getMsg={this.getMsg}></SonB></div>)}
}export default App

16 createContext跨组件通信

// 组件通信// 爷孙组件跨组件通信
// app - a - c
// app - c// 注意事项:上层组件和下层组件关系是相对的,只要存在就可以使用,通常我们会以app作为数据提供方
// 这里涉及的语法都是固定的,有两处,提供的位置value提供位置 获取的位置 {value=><div>{value}</div>获取// 步骤:
// 1.导入createContent方法并执行
import React, { createContext } from "react"
// 解构提供者和消费者
const { Provider, Consumer } = createContext()
function SonA (props) {return (<div><div>这是A组件</div><SonC></SonC></div>)
}function SonC (props) {return (<div><div>这是C组件</div>{/* 3. 这里的value接收上层传来的数据 */}<Consumer>{value => <span>{value}</span>}</Consumer></div>)
}class App extends React.Component {state = {message: "this is message"}render () {return (// 2. 使用包裹Provider根组件,value就是传递的数据<Provider value={this.state.message}><div><SonA></SonA></div></Provider>)}
}export default App

17 组件的children属性

// children属性import React from "react"
// 渲染列表function ListItem ({ children }) {// 这里可以直接解构出children属性,当children内容多时,会形成一个数组,需要那部分可以直接通过索引取得return (<div>ListItem{children[0]}<button onClick={children[1]}>点击触发函数</button></div>)
}class App extends React.Component {render () {return (<div><ListItem>{/* 打开调试工具会发现在props里面多了个children属性,值就是组件内部的值children的值可以为普通文本,普通标签元素,函数和JSX*/}<div>this is children</div>{() => { console.log('12345') }}</ListItem></div>)}
}export default App

18 props校验和默认值

// props校验// 实现步骤
// 1. 安装属性校验包:npm install prop-types
// 2. 导入prop-types 包
// 3. 使用 组件名.propTypes = {} 给组件添加校验规则import React from "react"
// 里面有各种各样的内置校验规则
import propTypes from "prop-types"function Test ({ list }) {return (<div><div>this is list</div>{list.map(item => <p key={item}>{item}</p>)}</div>)
}// 定义规则
Test.propTypes = {list: propTypes.array.isRequired //限定Test组件的prop的list属性类型为array,并且为必传项
}// 定义默认值(函数组件和类组件不一样)
// 函数组件
// 第一种方式(官方比较推荐的方式)
function List1 ({ pageSize = 10, pageSize2 }) {return (<div>此处展示函数组件props第一种显示的默认值:{pageSize}此处展示函数组件props第二种显示的默认值:{pageSize2}</div>)
}
// 第二种方式,设置pageSize2的默认值为20
List1.defaultProps = {pageSize2: 20
}// 类组件
// 第一种传参方式(比较推荐)
class List2 extends React.Component {static defaultProps = {pageSize1: 10}render () {return (<div>此处展示类组件props第一种显示的默认值:{this.props.pageSize1}{/* 这里有问题,把传参方式2注释掉第一种方式才能正常显示,如果同时存在只会显示方式2,我也不知道是什么原因 */}此处展示类组件props第二种显示的默认值:{this.props.pageSize2}</div>)}
}
// 第二种传参方式
List2.defaultProps = {pageSize2: 30
}class App extends React.Component {render () {return (<div><Test list={[1, 2, 3]}></Test><List1></List1><List2></List2></div>)}
}export default App

19 生命周期挂载阶段

// 生命周期//只有类组件才有生命周期,函数组件无生命周期// 生命周期图示
// https://cdn.nlark.com/yuque/0/2022/png/274425/1654490729034-d2d80cce-7fab-4dd8-bcbc-29e33bdffb63.png?date=1694351792480//   钩子函数	              触发时机                           	             作用
// constructor	          创建组件时,最先执行,初始化的时候只执行一次	         1. 初始化state  2. 创建 Ref 3. 使用 bind 解决 this 指向问题等
// render	                每次组件渲染都会触发(例如按钮数字自增页面重新变化)	    渲染UI(注意: 不能在里面调用setState() )
// componentDidMount	    组件挂载(完成DOM渲染)后执行,初始化的时候执行一次	    1. 发送网络请求   2.DOM操作
import React from "react"class App extends React.Component {constructor() {super()  //这个需要加,不然会报错console.log('counstructor')}componentDidMount () {// 在这个生命周期里面适合发送网络请求和对DOM操作console.log('componentDidMount')}state = {count: 0}clickHandler = () => {this.setState({count: this.state.count + 1})}render () {console.log('render')return (<div><button onClick={this.clickHandler}>点击自增加1,当前count为{this.state.count}</button></div>)}
}export default App

20 更新和卸载阶段

// 生命周期//只有类组件才有生命周期,函数组件无生命周期// 生命周期图示
// https://cdn.nlark.com/yuque/0/2022/png/274425/1654490729034-d2d80cce-7fab-4dd8-bcbc-29e33bdffb63.png?date=1694351792480// 钩子函数	             触发时机	                作用
// render	              每次组件渲染都会触发	     渲染UI(与 挂载阶段 是同一个render)
// componentDidUpdate	  组件更新后(DOM渲染完毕)	 DOM操作,可以获取到更新后的DOM内容,不要直接调用setState
// componentWillUnmount	组件卸载(从页面中消失)	 执行清理工作(比如:清理定时器等)
import React from "react"// 演示组件卸载
class Test extends React.Component {// 如果数据是组件的状态需要去影响视图,定义到state中// 而如果我们需要的数据状态,不和视图绑定,定义成一个普通的实例属性就可以啦// state中尽量保持简洁timer = null// 组件挂载开启定时器componentDidMount () {this.timer = setInterval(() => {console.log('定时器开启')}, 0)}componentWillUnmount () {// 组件卸载时清理定时器clearInterval(this.timer)console.log('组件销毁了')}render () {return (<div>Test</div>)}
}
class App extends React.Component {componentDidUpdate () {console.log('页面更新啦')}state = {count: 0,flag: true}clickHandler = () => {this.setState({count: this.state.count + 1,flag: !this.state.flag})}render () {console.log('render')return (<div>{/* 通过一个数据状态的切换,让test组件销毁重建 */}<button onClick={this.clickHandler}>点击自增加1,当前count为{this.state.count}</button>{this.state.flag ? (<Test></Test>) : null}</div>)}
}export default App

21 Hooks函数useState

// Hooks函数
// 快速使用
// useState
// 1.导入useState函数
// 2.执行这个函数并且传入初始值 必须在函数组件中
// 3.[数据,修改数据的方法]
// 4.使用数据 修改数据// 状态的读取和修改
// const [count, setCount] = useState(0)
// 1.useState传过来的参数作为count的初始值
// 2.[count, setCount]这里的写法是一个解构赋值 useState的返回值是一个数组
//   名字可以自定义吗??  当然可以自定义,保持语义化,第二个参数为set+count的首字母大写
//   这两个参数顺序可以换吗??? 当然不可以  第一个参数就是数据状态,第二个参数就是修改数据的方法
// 3.setCount函数  作用用来修改count  依旧保持不能修改原值还是生成一个新值替换原值
//   setCount(基于原值计算得到的新值,还是不能使用++  --)
// 4.count和setCount是一对  是绑在一起的  setCount只能用来修改对应的count值// 组件的更新过程
// 当调用setCount的时候,更新过程// 首次渲染
// 首次被渲染的时候  组件内部的代码会被执行一次
// 其中useState也会跟着执行  这里重点注意// 更新渲染(setCount都会更新)
// 1.app组件会再次渲染 这个函数会再次执行
// 2.useState再次执行  得到的新的count值不是0,而是修改之后的1 模板会用新值渲染// 重点一句话:useState初始值只在首次渲染生效,后续只要调用setCoun整个app代码都会执行
// 此时count每次拿到的都是最新值// useState的使用规则
// 1.  useState 函数可以执行多次,每次执行互相独立,每调用一次为函数组件提供一个状态 
// 2.  useState 注意事项 
//   a.  只能出现在函数组件或者其他hook函数中 
//   b.  不能嵌套在if/for/其它函数中(react按照hooks的调用顺序识别每一个hook) 写到最外层函数中
// 3.  可以通过开发者工具查看hooks状态 
import { useState } from 'react'
function App () {// count:数据状态// setCount:修改count的函数(专有函数)const [count, setCount] = useState(0)return (<div><button onClick={() => setCount(count + 1)}>{count}</button></div>)
}export default App

22 useEffect

// Hooks函数// 什么是副作用
// 副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)// 常见的副作用
// 1. 数据请求 ajax发送
// 2. 手动修改dom
// 3. localstorage操作// useEffect函数的作用就是为react函数组件提供副作用处理的!// 在修改数据之后将count值放到页面标题中
// 1.导入useEffect函数
// 2.在函数组件中执行 传入回调 并且定义副作用
// 当我们修改状态更新组件时,副作用也会不断执行// 依赖项控制副作用的执行时机
// 1.默认状态(无依赖项)组件初始化的时候先执行一次 等到每次数据修改组件更新再次执行
// 2.参数二添加空数组,只会在组件初始化时执行一次,后续不会执行
// 3.添加特定依赖项,组件初始化执行一次,参数二数组内部添加指定数据,代表只有指定数据修改才会执行,数组内部没有的数据不会再次执行(依赖的特定项发生变化才会再次执行)
// 4.注意事项:只要在useEffect回调函数中用到的数据状态就应该出现在依赖项数组中声明 否则可能有bug// 某种意义上 hook的出现 就是想不用生命周期概念也可以写业务代码
import { useEffect, useState } from 'react'
function App () {const [count, setCount] = useState(0)const [name, setName] = useState('')useEffect(() => {console.log('副作用执行了')// 定义副作用document.title = countconsole.log('name', name)}, [count, name])return (<div><button onClick={() => setCount(count + 1)}>{count}</button><button onClick={() => setName("cp")}>{name}点击</button></div>)
}export default App

23 清除副作用

// Hooks函数// 清理副作用
import { useEffect, useState } from 'react'
function Text () {useEffect(() => {let timer = setInterval(() => {console.log('定时器执行了')}, 1000)return () => {// 这里写清理副作用的代码(当组件销毁时会清理副作用)clearInterval(timer)}}, [])return (<div>this is Text</div>)
}function App () {const [flag, setFlag] = useState(true)return (<div>{flag ? (<Text></Text>) : null}<button onClick={() => setFlag(!flag)}>swith</button></div>)
}
export default App

24 发送网络请求

// Hooks函数import { func } from "prop-types"
import { useEffect } from "react"// 发送网络请求
// 类组件  如何发送网络请求
// 生命周期钩子 componentDidMount
// 执行时机?
// 在初始化的时候dom渲染完毕只执行一次// useEffect
// 1.不加依赖项 -初始化 +重新渲染
// 2.第二个参数加[],初始化执行一次
// 3.加特定依赖项[count,name] - 首次执行+任一变化执行function App () {useEffect(() => {function data () {fetch("https://api.vvhan.com/api/weather?ip=180.149.130.16").then((response) => response.json()).then((data) => console.log(data))}data()})return (<div></div>)
}
export default App

25 useRef使用

// Hooks函数
import React, { useEffect, useRef } from "react"
// 组件实例(类组件)
// dom对象 标签
class Test extends React.Component {state={name:"test name"}getName=()=>{return "this is child Test"}render () {return (<div>我是类组件</div>)}
}function App () {const testRef = useRef(null)const h1Ref = useRef(null)useEffect(() => {console.log('testRef', testRef.current) //组件实例对象console.log('testRef', testRef.current.state.name) //组件实例对象里面的数据console.log('testRef', testRef.current.getName()) //组件实例对象里面的方法console.log('h1Ref', h1Ref.current)   //dom对象}, [])return (<div><Test ref={testRef}></Test><div ref={h1Ref}>this is div</div></div>)
}
export default App

26 useContext使用

// Hooks函数
import React, { createContext, useContext, useState } from "react"
const Context = createContext()
function ComA () {// 这里可以接收上层传来的value的数据const count = useContext(Context)return (<div>this is ComA<br></br>顶层传来的count数据为---{count}<ComC></ComC></div>)
}function ComC () {// 可以嵌套const count = useContext(Context)return (<div>this is ComC<br></br>顶层传来的count数据为---{count}</div>)
}function App () {const [count, setCount] = useState(0)return (// 这里提供数据给下层使用//下面这个标签也可以直接包裹index.js里面的<App/>,相当于在顶层组件传递数据了<Context.Provider value={count}>  <div><ComA></ComA><button onClick={() => setCount(count + 1)}>点击修改count数据</button></div></Context.Provider>)
}
export default App

React路由篇

路由demo案例

创建项目
npx create-react-app react-router
安装最新版本路由
cnpm install react-router-dom@6

项目结构

在这里插入图片描述
创建两个组件Home.js和About.js

About.js内容为

function About(){return(<div>about</div>)
}
export default About

Home.js内容为

function Home(){return(<div>home</div>)
}
export default Home

App.js内容为

//  安装react-router包
//  npm install react-router-dom@6// 进行路由配置
import { BrowserRouter, Link, Routes, Route } from 'react-router-dom'// 引入About和Home组件
import About from './About'
import Home from './Home'function App () {return (<div>{/* 最外层包裹,用来声明一个非hash模式的路由 */}<BrowserRouter>{/* link相当于按钮,可以实现跳转,to属性代表跳转的路由地址 */}<Link to="/">首页</Link><Link to="/about">关于</Link>{/* 路由出口,路由对应的组件会在这里渲染 */}<Routes>{/* 这里是路由的配置,path代表路径,element代表当前路径需要匹配的组件 */}<Route path='/' element={<Home></Home>}></Route><Route path='/about' element={<About></About>}></Route></Routes></BrowserRouter></div>)
}
export default App

效果图

在这里插入图片描述

路由各个组件说明

路由组件说明
BrowserRouter,HashRouter(路由模式,包裹整个项目,一个项目只用到一次)
BrowserRouter(history模式:http://localhost:3000/first)[推荐]
HashRouter(hash模式:http://localhost:3000/#/first)Link:作用用于指定导航链接,完成路由跳转
语法说明:组件通过to属性指定路由地址,最终会渲染为a链接元素Routes:作用提供一个路由出口,满足条件的路由组件会渲染到组件内部Route:作用用于指定导航链接,完成路由匹配
语法说明:path属性指定匹配的路径地址,element属性指定要渲染的组件
以上面的小demo为例,当url路径为'/about',会渲染<About/>组件

编程式导航使用

改写App.js

在项目中加入一个login组件,以上面的demo为例,将App.js改写如下:
简单来说就是引入login组件并且在路由出口也配置了一下


// 进行路由配置
import { BrowserRouter, Link, Routes, Route } from 'react-router-dom'// 引入About和Home和Login组件
import About from './About'
import Home from './Home'
import Login from './Login'function App () {return (<div><BrowserRouter><Link to="/">首页</Link><Link to="/about">关于</Link><Routes><Route path='/' element={<Home></Home>}></Route><Route path='/about' element={<About></About>}></Route><Route path='/login' element={<Login></Login>}></Route></Routes></BrowserRouter></div>)
}
export default App

Login.js组件代码(使用编程式导航三步走)

//   跳转到关于
//   1. 导入一个 useNavigate 钩子函数
import { useNavigate } from "react-router-dom"function Login () {// 2. 执行 useNavigate 函数 得到 跳转函数const navigate = useNavigate()function goabout () {navigate('/about')}return (<div>login{/* 3. 在事件中执行跳转函数完成路由跳转 */}<button onClick={goabout}>点击跳转关于页面</button></div>)
}
export default Login

功能就是:在浏览器地址栏手动跳转login组件,点击按钮能跳转到about组件,在login.js里面使用的是编程式导航
在这里插入图片描述
在这里有个问题,当点击登录后跳转到about界面了,但是点击浏览器的后退还是能会到登录页,解决方式如下

navigate('/about',{replact:true}) //将参数二的replace的值改为true以后,会以替换形式跳转而不是叠加跳转

跳转携带参数(两种方式)

场景:在跳转是需要携带参数的情况

1.searchParams传参(在跳转前的组件传参)

import { useNavigate } from "react-router-dom"
//下面两个代码要写到组件内
const navigate = useNavigate()
navigate('/about?id=1001')
// 取参(在跳转的目标组件取参)
import { useSearchParams } from "react-router-dom"
let [params] = useSearchParams()
let id = params.get('id')

上案例(还是以上面的demo为主)

Login代码为

//   跳转到关于
//   1. 导入一个 useNavigate 钩子函数
import { useNavigate } from "react-router-dom"function Login () {// 2. 执行 useNavigate 函数 得到 跳转函数const navigate = useNavigate()function goabout () {navigate('/about?id=1001', { replace: true })}return (<div>login{/* 3. 在事件中执行跳转函数完成路由跳转 */}<button onClick={goabout}>点击跳转关于页面</button></div>)
}
export default Login

About代码为

// 取参
import { useSearchParams } from "react-router-dom"
function About () {// 这里解构的params的包裹符号为中扩号,useSearchParams()是一个数组const [params] = useSearchParams()// params是一个对象,对象里面有一个get方法用来获取对应的参数,把参数名作为get方法的实参传过来const id = params.get('id') //多参数的话,需要得到哪个参数这里的id就换成什么参数,参数名有重复的话只能获取最靠前的参数的属性return (<div>about得到的参数id为----{id}</div>)
}export default About

2.params传参

这种方式需要在配置路由使用占位符,在App.js页面的将如下代码进行修改

修改前
<Route path='/about/:id' element={<About></About>}></Route>
修改后
<Route path='/about/:id' element={<About></About>}></Route>
// 传参
import { useNavigate } from "react-router-dom"
//下面两个代码要写到组件内
const navigate = useNavigate()
navigate('/about/1001')
// 取参
import { useParams } from "react-router-dom"
let params = useParams()
let id = params.id

上案例(还是以上面的demo为主)
Login.js

//   跳转到关于
//   1. 导入一个 useNavigate 钩子函数
import { useNavigate } from "react-router-dom"function Login () {// 2. 执行 useNavigate 函数 得到 跳转函数const navigate = useNavigate()function goabout () {navigate('/about/1001', { replace: true })}return (<div>login{/* 3. 在事件中执行跳转函数完成路由跳转 */}<button onClick={goabout}>点击跳转关于页面</button></div>)
}
export default Login

About.js

// 取参
import { useSearchParams,useParams } from "react-router-dom"
function About () {const params = useParams()const id = params.idreturn (<div>about得到的参数id为----{id}</div>)
}export default About

嵌套路由

基础配置

这里以App.js和Layou.js代码为例,其余引入的组件代码和layout.js一样,只是没有二级出口

App.js


// 进行路由配置
import { BrowserRouter, Link, Routes, Route } from 'react-router-dom'// 引入Login和Login组件
import Layout from './Layout'
import Login from './Login'
// 引入二级路由组件
import Article from './Article'
import Board from './Board'function App () {return (<div><BrowserRouter><Link to="/">首页</Link><Routes>{/* 一级路由 */}<Route path='/' element={<Layout></Layout>}>{/* 二级路由 */}{/* 这里可以写好多个子路由 */}<Route path="board" element={<Board></Board>}></Route></Route>{/* 一级路由 */}<Route path='/login' element={<Login></Login>}>{/* 二级路由 */}<Route path="article" element={<Article></Article>}></Route></Route></Routes></BrowserRouter></div>)
}
export default App

Layout.js

import { Outlet } from "react-router-dom"function Layout () {return (<div>Layout{/* 定义二级路由出口 */}<Outlet></Outlet></div>)
}export default Layout

效果图

在这里插入图片描述

默认二级路由设置

	   <Routes>{/* 一级路由 */}<Route path='/' element={<Layout></Layout>}>{/* 二级路由 */}{/* 这里可以写好多个子路由 */}//<Route path="board" element={<Board></Board>}></Route>//将上面的path属性去掉,给Route加上index属性就是默认显示的子路由<Route index element={<Board></Board>}></Route></Route></Routes>

在这里插入图片描述

404页配置

场景:当所有路径都没有匹配的时候显示
语法说明:在各级路由的最后添加*路由作为兜底

//这个要放到所有路由后面,当没有路由匹配时显示此路由
<Route path="*" element={<NotFound></NotFound>}></Route>

Mobx

简介

全局状态管理,同redux

优势:
1.简单:编写无模板的的极简代码来精确描述你的意图(原生js)
2.轻松实现最优渲染,依赖自动追踪最小渲染优化
3.架构自由,可移植,可测试

环境配置

配置说明
Mobx是一个独立的响应式的库,可以独立于任何UI框架而存在,但是通常人们把它和react来绑定使用,用mobx来做响应式数据建模,React作为UI视图框架渲染内容

所以配置方面需要三个部分

1.一个通过create-react-app创建好的react项目环境
2.mobx本身
3.一个连接mobx和react的中间部件(因为他们两个都是独立的)

# 创建项目
npx create-react-app react-mobx-app# 安装mobx和中间件工具 mobx-react-lite  只能函数组件中使用
npm install mobx mobx-react-lite

编写第一个Mobx的store

需求:
使用Mobx实现计数器案例,点击加号按钮实现递增运算

项目结构
在这里插入图片描述

初始化Mobx

实现步骤

1.定义数据状态(state)
2.数据响应式处理
3.定义action函数(修改数据)
4.实例化并导出实例

在counter.js里面加入Mobx代码

// 编写第一个mobx store小案例
// 使用Mobx实现计数器案例,点击加号按钮实现递增运算
import { makeAutoObservable } from "mobx"
class CounterStore {// 1.定义数据count = 0constructor(){// 2.把数据变成响应式makeAutoObservable(this)}// 3.定义action函数(修改数据)addCount=()=>{this.count++}
}// 4.实例化,然后导出给react使用
const counterStore = new CounterStore()
export {counterStore}

连接react

实现步骤

1.导入store实例
2.使用store数据
3.修改store中的数据
4.让组件视图响应数据变化

在App.js引入并完成数据的使用和响应式实现

// 1.导入countStore实例
import { counterStore } from "./store/counter"
// 2.导入中间件连接Mobx react完成响应式变化
import { observer } from "mobx-react-lite"
function App () {return (<div>{/* 把counter.js的count数据渲染一下 */}{counterStore.count}{/* 点击事件触发action函数修改count的值 */}<button onClick={counterStore.addCount}>+</button></div>)
}// 3.包裹App使其具有响应式
export default observer(App)

Mobx的计算属性computed

实现步骤

1.声明一个存在的数据
2.定义get计算属性
3.在makeAutoObservable方法中标记

还是以上面项目结构为例,在counter.js里面加入Mobx代码

import { computed, makeAutoObservable } from "mobx"
class CounterStore {// 1.定义一个原始数据 listlist = [1, 2, 3, 4, 5]constructor() {// 3.在makeAutoObservable方法中标记makeAutoObservable(this,{filterList:computed})}// 2.定义get计算属性get filterList(){return this.list.filter(item=>item>2)}// 定义方法修改listaddList=()=>{return this.list.push(7,8,9)}
}const counterStore = new CounterStore()
export { counterStore }

在App.js引入并完成数据的使用和响应式实现

import { counterStore } from "./store/counter2"
import { observer } from "mobx-react-lite"
function App () {return (<div>{/* 使用计算属性,数组不能直接在react展示,因此要拼接一下 */}{counterStore.filterList.join('-')}<button onClick={counterStore.addList}>修改数组</button></div>)
}
export default observer(App)

拆分子模块和创建根模块(模块化)

怎么做??

1.拆分Count和List模块,每个模块定义自己的独立的state/actions
2.在store/index.js中导入拆分之后的模块,进行模块组合
3.使用React的useCountext机制 导出useStore方法,供业务组件统一使用

项目目录

在这里插入图片描述

子模块一counter.store.js的代码如下

// 编写第一个mobx store小案例
// 使用Mobx实现计数器案例,点击加号按钮实现递增运算
import { computed, makeAutoObservable } from "mobx"
class CounterStore {// 定义数据count = 0// 定义一个原始数据 listlist = [1, 2, 3, 4, 5]constructor() {// 在makeAutoObservable方法中标记makeAutoObservable(this, {filterList: computed})}// 定义action函数(修改数据)addCount = () => {this.count++}// 定义get计算属性get filterList () {return this.list.filter(item => item > 2)}// 定义方法修改listaddList = () => {return this.list.push(7, 8, 9)}
}// 实例化,然后导出给react使用
export { CounterStore }

子模块二list.store.js代码如下

import { makeAutoObservable } from "mobx"class ListStore {list = ["react", "vue"]constructor() {makeAutoObservable(this)}addlist = () => {this.list.push("angular")console.log('this.list', this.list)}
}export { ListStore }

业务模块index.js代码如下

// 组合子模块
// 封装统一导出的供业务使用的方法
import React from "react";
// 引入子模块
import { CounterStore } from "./counter.store";
import { ListStore } from "./list.store";
// 1.声明一个rootStore
class RootStore{constructor(){// 对子模块进行实例化操作// 将来实例化根store的时候// 根store有两个属性 分别是counterStore和listStore// 各自对应的值 就是我们导入的子模块实例对象this.counterStore = new CounterStore()this.listStore=new ListStore()}
}// 实例化操作
// 使用react context机制 完成统一方法封装// 以下代码为固定代码,不用死记
// 通过new得到一个根实例对象
const rootStore = new RootStore()
// 使用react context机制,完成方法的统一封装
// Provider value = {传递的数据}
// 查找机制:useContext 优先从Provider value找,如果找不到就会找createContext方法1传递过来的默认参数
const context = React.createContext(rootStore)
// 这个方法作用:通过useContext拿到rootStore实例对象 然后返回
// 只要在这个业务组件中调用useStore()=>rootStore
const useStore = ()=>React.useContext(context)
// 再导出useStore方法,供组件通过调用该方法使用根实例
export {useStore}

App.js代码如下

// 1.导入业务组件
import { useStore } from "./store"
// 2.导入中间件连接Mobx react完成响应式变化
import { observer } from "mobx-react-lite"
function App () {const rootStore = useStore() //也可以解构赋值console.log('rootStore', rootStore)return (<div><div>第一个Store里面的数据操作</div>{/* 使用counterStore里面的数据 */}{rootStore.counterStore.count}{/* 调用counterStore里面的方法 */}<button onClick={rootStore.counterStore.addCount}>点击使count自增加1</button><br />{/* 调用counterStore里面的数组list */}{rootStore.counterStore.filterList.join('-')}{/* 调用counterStore里面的方法 */}<button onClick={rootStore.counterStore.addList}>点击给list加新值</button><br /><hr /><div>第二个Store里面的数据操作</div>{rootStore.listStore.list}<button onClick={rootStore.listStore.addlist}>点击给list加新值</button><br /><hr /></div>)
}// 3.包裹App使其具有响应式
export default observer(App)

App.js里面打印的结果图

在这里插入图片描述

Redux使用

转移到这篇文章看详细Redux学习
需要配好的Redux模板可以看这篇文章

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

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

相关文章

Golang代码漏洞扫描工具介绍——govulncheck

Golang Golang作为一款近年来最火热的服务端语言之一&#xff0c;深受广大程序员的喜爱&#xff0c;笔者最近也在用&#xff0c;特别是高并发的场景下&#xff0c;golang易用性的优势十分明显&#xff0c;但笔者这次想要介绍的并不是golang本身&#xff0c;而且golang代码的漏洞…

Linux网络编程

一.协议 1.1什么是协议 从应用的角度出发&#xff0c;协议可理解为“规则”&#xff0c;是数据传输和数据的解释的规则。 假设&#xff0c;A、B双方欲传输文件。规定: 第一次&#xff0c;传输文件名&#xff0c;接收方接收到文件名&#xff0c;应答OK给传输方; 第二次&#xff…

【每日一题】34. 在排序数组中查找元素的第一个和最后一个位置

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣&#xff08;LeetCode&#xff09; 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 […

vscode-server

1know_host清除 2 删除服务器里的home/user/.vscode-server&#xff08;不是根root下的vscode-server&#xff09;&#xff0c;删除时用户名保持一致。 3 ssh配置文件 /etc/ssh/sshd_config[想改变,使用root&#xff0c;修改文件权限] 4 删除修改后&#xff0c;重启Windows下…

打造生产级Llama大模型服务

对于任何想要尝试人工智能或本地LLM&#xff0c;又不想因为意外的云账单或 API 费用而感到震惊的人&#xff0c;我可以告诉你我自己的旅程是如何的&#xff0c;以及如何开始使用廉价的消费级硬件执行Llama2 推理 。 这个项目一直在以非常活跃的速度发展&#xff0c;这使得它非…

父域 Cookie实现sso单点登录

单点登录&#xff08;Single Sign On, SSO&#xff09;是指在同一帐号平台下的多个应用系统中&#xff0c;用户只需登录一次&#xff0c;即可访问所有相互信任的应用系统。Cookie 的作用域由 domain 属性和 path 属性共同决定。在 Tomcat 中&#xff0c;domain 属性默认为当前域…

Python浪漫星空

系列文章 序号文章目录直达链接1浪漫520表白代码https://want595.blog.csdn.net/article/details/1306668812满屏表白代码https://want595.blog.csdn.net/article/details/1297945183跳动的爱心https://want595.blog.csdn.net/article/details/1295031234漂浮爱心https://wan…

校园网web免认真,大量服务器

服务器加满了&#xff0c;没有几个人来&#xff0c;传点图片看实力 什么方法解web认证方式校园网&#xff1f; 一般的校园网是对学生免费开放的&#xff0c;假如你是学生输入学号密码上网就是了&#xff0c;假如你不是那就是想蹭网了&#xff0c;再假如你不想让管理员或上网行为…

数据分享|R语言逻辑回归、线性判别分析LDA、GAM、MARS、KNN、QDA、决策树、随机森林、SVM分类葡萄酒交叉验证ROC...

全文链接:http://tecdat.cn/?p27384 在本文中&#xff0c;数据包含有关葡萄牙“Vinho Verde”葡萄酒的信息&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 介绍 该数据集&#xff08;查看文末了解数据获取方式&#xff09;有1599个观测值和12个变量&#xf…

pdf添加水印

给pdf文件添加水印 引入依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.3</version></dependency>添加水印 package com.it2.pdfdemo02.util;import com.itextpdf.tex…

用 Pytest+Allure 生成漂亮的 HTML 图形化测试报告

本篇文章将介绍如何使用开源的测试报告生成框架 Allure 生成规范、格式统一、美观的测试报告。 通过这篇文章的介绍&#xff0c;你将能够&#xff1a; 将 Allure 与 Pytest 测试框架相结合&#xff1b; 如何定制化测试报告内容 执行测试之后&#xff0c;生成 Allure 格式的测…

免费和开源的机器翻译软件LibreTranslate

什么是 LibreTranslate &#xff1f; LibreTranslate 免费开源机器翻译 API&#xff0c;完全自托管。与其他 API 不同&#xff0c;它不依赖于 Google 或 Azure 等专有提供商来执行翻译。它的翻译引擎由开源 Argos Translate 库提供支持。 这个软件在 2022 年 3 月的时候折腾过&…

【图论】有向图的强连通分量

算法提高课笔记 文章目录 理论基础SCC板子 例题受欢迎的牛题意思路代码 学校网络题意思路代码 最大半连通子图题意思路代码 银河题意思路代码 理论基础 什么是连通分量&#xff1f; 对于一个有向图&#xff0c;分量中任意两点u&#xff0c;v&#xff0c;必然可以从u走到v&am…

Web安全与攻防

Web安全概述 在Internet大众化及Web技术飞速演变的今天&#xff0c;在线安全所面临的挑战日益严峻。伴随着在线信息和服务的可用性的提升&#xff0c;以及基于Web的攻击和破坏的增长&#xff0c;安全风险达到了前所未有的高度。Web安全可以从以下三个方面进行考虑&#xff1a;…

Jmeter系列-控制器Controllers的介绍(8)

Controllers 简介 JMeter是一款功能强大的性能测试工具&#xff0c;而控制器是JMeter中非常重要的一个组件。控制器用于控制测试计划的执行流程&#xff0c;可以根据需求来控制线程的启动、停止、循环等操作。 Jmeter有两种类型的控制器&#xff1a;Samplers&#xff08;取样…

【Linux】动静态库

目录 1.静态库2.动态库3.静态库的使用区别总结 1.静态库 我们在linux中已经帮我们下载好了C和C所需要的各种库&#xff0c;库也是文件&#xff0c;实际上就是各种接口的实现&#xff0c;我们在使用系统提供的譬如printf等函数时&#xff0c;就是使用系统中的库文件。使用一个库…

驱动开发,IO多路复用实现过程,epoll方式

1.框架图 被称为当前时代最好用的io多路复用方式&#xff1b; 核心操作&#xff1a;一棵树&#xff08;红黑树&#xff09;、一张表&#xff08;内核链表&#xff09;以及三个接口&#xff1b; 思想&#xff1a;&#xff08;fd代表文件描述符&#xff09; epoll要把检测的事件…

Vue Router入门:为Vue.js应用添加导航

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

【Unity】ShaderGraph应用(浮动气泡)

【Unity】ShaderGraph应用(浮动气泡) 实现效果 一、实现的方法 1.使用节点介绍 Position&#xff1a;获取模型的顶点坐标 Simple Noise:简单的噪声&#xff0c;用于计算顶点抖动 Fresnel Effect&#xff1a;菲涅耳效应&#xff0c;用于实现气泡效果 计算用节点 Add&…

jmeter生成html格式接口自动化测试报告

jmeter自带执行结果查看的插件&#xff0c;但是需要在jmeter工具中才能查看&#xff0c;如果要向领导提交测试结果&#xff0c;不够方便直观。 笔者刚做了这方面的尝试&#xff0c;总结出来分享给大家。 这里需要用到ant来执行测试用例并生成HTML格式测试报告。 一、ant下载安…