react学习笔记(复习)

news2024/11/27 4:23:16

React简介

react是什么?

React用于构建用户界面的JS库。是一个将数据渲染为HTML视图的开源JS库。

为什么学?

1.原生JS操作DOM繁琐,效率低

2.使用JS直接操作DOM,浏览器会进行大量的重绘重排

3.原生JS没有组件化编码方案,代码复用低

在学习之前最好看一下关于npm的知识:下面是我在网上看见的一个写的还不错的npm的文章

npm

React入门

React 基础案例

1.先倒入三个包:

【先引入react.development.js,后引入react-dom.development.js】

react.development.js
react-dom.development.js
babel.min.js 

2.创建一个容器

3.创建虚拟DOM,渲染到容器中

<body>
    <!-- 准备好容器 -->
    <div id="test">

    </div>
</body>
<!-- 引入依赖 ,引入的时候,必须就按照这个步骤-->
<script src="../js/react.development.js" type="text/javascript"></script>
<script src="../js/react-dom.development.js" type="text/javascript"></script>

<script src="../js/babel.min.js" type="text/javascript"></script>

<!--这里使用了babel用来解析jsx语法-->
<script type="text/babel">
        // 1.创建虚拟DOM
        const VDOM = <h1>Hello</h1>  //这个地方使用的是JSX语法,不需要加""
        // 2.渲染,如果有多个渲染同一个容器,后面的会将前面的覆盖掉
        ReactDOM.render(VDOM,document.getElementById("test"));        
</script>
</html>

这样,就会在页面中的这个div容器上添加这个h1.

JSX基础语法

1.定义虚拟DOM,不能使用“”

2.标签中混入JS表达式的时候使用{}

3.样式的类名指定不要使用class,使用className

4.内敛样式要使用双大括号包裹

5.不能有多个根标签,只能有一个跟标签

6.标签必须闭合

7.如果小写字母开头,就将标签转化为html同名元素,如果html中无该标签对应的元素,就报错;如果是大写字母开头,react就去渲染对应的组件,如果没有就报错

关于JS表达式和JS语句:

JS表达式:返回一个值,可以放在任何一个需要值的地方 a a+b demo(a) arr.map() function text(){}
JS语句:if(){} for(){} while(){} swith(){} 不会返回一个值

实例如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .sss{
            color: red;
        }
    </style>
</head>
<body>
    <!-- 准备好容器 -->
    <div id="test">
        
    </div>
</body>
<!-- 引入依赖 ,引入的时候,必须就按照这个步骤-->
<script src="../js/react.development.js" type="text/javascript"></script>
<script src="../js/react-dom.development.js" type="text/javascript"></script>

<script src="../js/babel.min.js"></script>
<!--这里使用了js来创建虚拟DOM-->
<script type="text/babel">
        const MyId = "title";
        const MyData = "Cyk";
        // 1.创建虚拟DOM
        const VDOM = (
            <h1 id = {MyId.toLocaleUpperCase()}>
                <span className = "sss" style = {{fontSize:'50px'}}>sss</span>
            </h1>
        )
        // 2.渲染,如果有多个渲染同一个容器,后面的会将前面的覆盖掉
        ReactDOM.render(VDOM,document.getElementById("test"));
</script>

</html>

两种创建虚拟DOM的方式

1.使用JSX创建虚拟DOM

 const VDOM = (
            <h1 id = {MyId.toLocaleUpperCase()}>
                <span className = "sss" style = {{fontSize:'50px'}}>sss</span>
            </h1>
        )

这个在上面的案例中已经演示过了 ,下面看看另外一种创建虚拟DOM的方式

2.使用JS创建虚拟DOM

// 1.创建虚拟DOM[在这使用了js的语法]React.createElement(标签,标签属性,内容)
const VDOM = React.createElement('h1',{id:"title"},"nihao")

使用JS和JSX都可以创建虚拟DOM,但是可以看出JS创建虚拟DOM比较繁琐,尤其是标签如果很多的情况下,所以还是比较推荐使用JSX来创建。

组件

当应用是以多组件的方式实现,这个应用就是一个组件化的应用

注意: 组件名称必须以大写字母开头。

React 会将以小写字母开头的组件视为原生 DOM 标签。例如,< div />代表 HTML 的 div 标签,而< Weclome /> 则代表一个组件,并且需在作用域内使用 Welcome

传递的参数,不能在组件中改动

函数式组件

//1.先创建函数,函数可以有参数,也可以没有,但是必须要有返回值 返回一个虚拟DOM
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
//2.进行渲染
ReactDOM.Render(<Welcom name = "ss" />,document.getElementById("div"));

让我们来回顾一下这个例子中发生了什么:

  1. 我们调用 ReactDOM.render() 函数,并传入 <Welcome name="Sara" /> 作为参数。
  2. React 调用 Welcome 组件,并将 {name: 'Sara'} 作为 props 传入。
  3. Welcome 组件将 Hello, Sara 元素作为返回值。
  4. React DOM 将 DOM 高效地更新为 Hello, Sara

Class组件

//必须继承React.Component
//然后重写Render()方法,该方法一定要有返回值,返回一个虚拟DOM
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
//渲染 【这个跟之前也是一样的】
ReactDOM.Render(<Welcom name = "ss" />,document.getElementById("div"));

执行过程:

​ 1.React解析组件标签,找到相应的组件

​ 2.发现组件是类定义的,随后new出来的类的实例,并通过该实例调用到原型上的render方法

​ 3.将render返回的虚拟DOM转化为真实的DOM,随后呈现在页面中

组件案例

下面,我们通过一个案例更好的理解组件:【只关注与核心代码】

我们发现组件是可以包含中使用的, 而且如果创建的数组,必须要代一个key。数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值

<script type="text/babel">

        //创建一个组件<li>
        function GetLi(props){      
            return <li>{props.value}</li>
        };

        // 1.创建类式组件<ul>
        class MyComponent extends React.Component{
            render(){
                console.log(this.props.arr);
                let com = this.props.arr.map((item,index)=>
                     //在这个地方包含了GetLi这个组件,【注意不能用{}】
                     //因为这个是一个列表,所以必须传递一个key【独一无二的Key】
                     //key 帮助 React 识别哪些元素改变了,比如被添加或删除。
                        <GetLi value={item} key = {index} />
                    );
                console.log(com);
                return <ul>{com}</ul>
            }
        }
        
        let num = [1,2,3,4]
        //2.渲染组件
        ReactDOM.render(<MyComponent  arr={num}/>,document.getElementById("test");
</script>

组件实例的三大属性

state

我们都说React是一个状态机,体现是什么地方呢,就是体现在state上,通过与用户的交互,实现不同的状态,然后去渲染UI,这样就让用户的数据和界面保持一致了。state是组件的私有属性。

在React中,更新组件的state,结果就会重新渲染用户界面(不需要操作DOM),一句话就是说,用户的界面会随着状态的改变而改变。

state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)

案例

1.需求:页面显示【今天天气很炎热】,鼠标点击文字的时候,页面更改为【今天天气很凉爽】

核心代码如下:

<body>
    <!-- 准备好容器 -->
    <div id="test">
        
    </div>
</body>
<!-- 引入依赖 ,引入的时候,必须就按照这个步骤-->
<script src="../js/react.development.js" type="text/javascript"></script>
<script src="../js/react-dom.development.js" type="text/javascript"></script>

<script src="../js/babel.min.js"></script>
<!--这里使用了js来创建虚拟DOM-->
<script type="text/babel">
        //1.创建组件
        class St extends React.Component{
            constructor(props){
                super(props);
                //先给state赋值
                this.state = {isHot:true,win:"ss"};
                //找到原型的dem,根据dem函数创建了一个dem1的函数,并且将实例对象的this赋值过去
                this.dem1 = this.dem.bind(this);
            }
            //render会调用1+n次【1就是初始化的时候调用的,n就是每一次修改state的时候调用的】
            render(){ //这个This也是实例对象
                //如果加dem(),就是将函数的回调值放入这个地方
                //this.dem这里面加入this,并不是调用,只不过是找到了dem这个函数,在调用的时候相当于直接调用,并不是实例对象的调用
                return <h1 onClick = {this.dem1}>今天天气很{this.state.isHot?"炎热":"凉爽"}</h1>    
            }
            //通过state的实例调用dem的时候,this就是实例对象
            dem(){
                const state =  this.state.isHot;
                 //状态中的属性不能直接进行更改,需要借助API
                // this.state.isHot = !isHot; 错误
                //必须使用setState对其进行修改,并且这是一个合并
                this.setState({isHot:!state});
            }
        }
        // 2.渲染,如果有多个渲染同一个容器,后面的会将前面的覆盖掉
        ReactDOM.render(<St />,document.getElementById("test"));
</script>

需要注意的是:

1.组件的构造函数,必须要传递一个props参数

2.特别关注this【重点】,类中所有的方法局部都开启了严格模式,如果直接进行调用,this就是undefined

3.想要改变state,需要使用setState进行修改,如果只是修改state的部分属性,则不会影响其他的属性,这个只是合并并不是覆盖。

this.setState(),该方法接收两种参数:对象或函数。

  1. 对象:即想要修改的state
  2. 函数:接收两个函数,第一个函数接受两个参数,第一个是当前state,第二个是当前props,该函数返回一个对象,和直接传递对象参数是一样的,就是要修改的state;第二个函数参数是state改变后触发的回调

在此还需要注意的是,setState有异步更新和同步更新两种形式,那么什么时候会同步更新,什么时候会异步更新呢?

React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等

大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。

//1.创建组件
class St extends React.Component{
    //可以直接对其进行赋值
    state = {isHot:10};
    render(){ //这个This也是实例对象
        return <h1 onClick = {this.dem}>点击事件</h1> 
    }
//箭头函数 [自定义方法--->要用赋值语句的形式+箭头函数]
    dem = () =>{
        //修改isHot
        this.setState({ isHot: this.state.isHot + 1})
        console.log(this.state.isHot);
    }
}

上面的案例中预期setState使得isHot变成了11,输出也应该是11。然而在控制台打印的却是10,也就是并没有对其进行更新。这是因为异步的进行了处理,在输出的时候还没有对其进行处理。

componentDidMount(){
    document.getElementById("test").addEventListener("click",()=>{
        this.setState({isHot: this.state.isHot + 1});
        console.log(this.state.isHot);
    })
}

但是通过这个原生JS的,可以发现,控制台打印的就是11,也就是已经对其进行了处理。也就是进行了同步的更新。

React怎么调用同步或者异步的呢?

在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列中延时更新,而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;但是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改为 true,而当 React 在调用事件处理函数之前就会先调用这个 batchedUpdates将isBatchingUpdates修改为true,这样由 React 控制的事件处理过程 setState 不会同步更新 this.state。

如果是同步更新,每一个setState对调用一个render,并且如果多次调用setState会以最后调用的为准,前面的将会作废;如果是异步更新,多个setSate会统一调用一次render

dem = () =>{
    this.setState({
        isHot:  1,
        cont:444
    })
    this.setState({
    	isHot: this.state.isHot + 1

    })
    this.setState({
        isHot:  888,
        cont:888
    })
}

上面的最后会输出:isHot是888,cont是888

 dem = () =>{
                
                this.setState({
                    isHot: this.state.isHot + 1,
                    
                })
                this.setState({
                    isHot: this.state.isHot + 1,
                    
                })
                this.setState({
                    isHot: this.state.isHot + 888
                })
            }

初始isHot为10,最后isHot输出为898,也就是前面两个都没有执行。

**注意!!这是异步更新才有的,如果同步更新,每一次都会调用render,这样每一次更新都会 **

简化版本:

1.state的赋值可以不再构造函数中进行

2.使用了箭头函数,将this进行了改变

<body>
    <!-- 准备好容器 -->
    <div id="test">
        
    </div>
</body>
<!-- 引入依赖 ,引入的时候,必须就按照这个步骤-->
<script src="../js/react.development.js" type="text/javascript"></script>
<script src="../js/react-dom.development.js" type="text/javascript"></script>

<script src="../js/babel.min.js"></script>
<script type="text/babel">
        class St extends React.Component{
            //可以直接对其进行赋值
            state = {isHot:true};
            render(){ //这个This也是实例对象
                return <h1 onClick = {this.dem}>今天天气很{this.state.isHot?"炎热":"凉爽"}</h1>    
                //或者使用{()=>this.dem()也是可以的}
            }
            //箭头函数 [自定义方法--->要用赋值语句的形式+箭头函数]
            dem = () =>{
                console.log(this);
                const state =  this.state.isHot;
                this.setState({isHot:!state});
            }
        }
        ReactDOM.render(<St />,document.getElementById("test"));       
</script>

如果想要在调用方法的时候传递参数,有两个方法:

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

上述两种方式是等价的,分别通过箭头函数和 Function.prototype.bind 来实现。

在这两种情况下,React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。

Props

Props主要用来传递数据,比如组件之间进行传值

基本使用:

<body>
    <div id = "div">

    </div>

</body>
<script type="text/babel">
    class Person extends React.Component{
        render(){
            return (
                <ul>
                    //接受数据并显示
                    <li>{this.props.name}</li>
                    <li>{this.props.age}</li>
                    <li>{this.props.sex}</li>
                </ul>
            )
        }
    }
    //传递数据
    ReactDOM.render(<Person name="tom" age = "41" sex="男"/>,document.getElementById("div"));
</script>

如果传递的数据是一个对象,可以更加简便的使用

<script type="text/babel">
    class Person extends React.Component{
        render(){
            return (
                <ul>
                    <li>{this.props.name}</li>
                    <li>{this.props.age}</li>
                    <li>{this.props.sex}</li>
                </ul>
            )
        }
    }
    const p = {name:"张三",age:"18",sex:"女"}
   ReactDOM.render(<Person {...p}/>,document.getElementById("div"));
</script>

... 这个符号恐怕都不陌生,这个是一个展开运算符,主要用来展开数组,如下面这个例子:

arr = [1,2,3];
arr1 = [4,5,6];
arr2 = [...arr,...arr1];  //arr2 = [1,2,,3,4,5,6]

但是他还有其他的用法:

1.复制一个对象给另一个对象{...对象名}。此时这两个对象并没有什么联系了

const p1 = {name:"张三",age:"18",sex:"女"}
const p2 = {...p1};
p1.name = "sss";
console.log(p2)  //{name:"张三",age:"18",sex:"女"}

2.在复制的时候,合并其中的属性

 const p1 = {name:"张三",age:"18",sex:"女"}
 const p2 = {...p1,name : "111",hua:"ss"};
 p1.name = "sss";
 console.log(p2)  //{name: "111", age: "18", sex: "女",hua:"ss"}

注意!! {...P}并不能展开一个对象

props传递一个对象,是因为babel+react使得{..p}可以展开对象,但是只有在标签中才能使用

对于props限制

很多时候都想要传递的参数进行相应的限制,比如:限制传递参数的类型,参数的默认值等等

react对此提供了相应的解决方法:

  • propTypes:类型检查,还可以限制不能为空
  • defaultProps:默认值
<script type="text/babel">

    
    class Person extends React.Component{
        render(){
            //props是只读的
            return (
                <ul>
                    <li>{this.props.name}</li>
                    <li>{this.props.age}</li>
                    <li>{this.props.sex}</li>
                </ul>
            )
        }
        //对组件的属性对其进行限制
        static propTypes = {
            name:PropTypes.string.isRequired, //限定name是string类型,并且必须要传递
            sex:PropTypes.string,  //限定sex是string类型
            speak:PropTypes.func   //限定speak是function类型
        }
        //指定默认的标签属性
        static defaultProps = {
            sex:"不男不女",
            age:18
        }   
        
    }
    //在js中可以使用{...p}来复制一个对象,但是这个地方并不是复制对象,而是babel+react通过展开运算符,展开了一个对象
    //但是只能在标签中进行使用
    //const p = {name:"张三",age:"18",sex:"女"}   {14}就代表的是数值
    //ReactDOM.render(<Person {...p}/>,document.getElementById("div"));
    ReactDOM.render(<Person name="sss" age = {14} speak="8"/>,document.getElementById("div"));
    

    function speak(){
        console.log("这个是一个函数")
    }

</script>
</html>

函数式组件的使用

函数在使用props的时候,是作为参数进行使用的(props);

function Person(props){
          return (
                <ul>
                    <li>{props.name}</li>
                    <li>{props.age}</li>
                    <li>{props.sex}</li>
                </ul>
            )
    }

Refs

Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。

Refs主要提供了三种方式:

1.字符串形式

在想要获取到一个DOM节点,可以直接在这个节点上添加ref属性。利用该属性进行获取该节点的值。

案例:给需要的节点添加ref属性,此时该实例对象的refs上就会有这个值。就可以利用实例对象的refs获取已经添加节点的值

<input ref="dian" type="text" placeholder="点击弹出" />

 inputBlur = () =>{
            alert(this.refs.shiqu.value);
        }

2.回调形式

回调形式会在ref属性中添加一个回调函数。将该DOM作为参数传递过去。

如:ref里面就是一个回调函数,self就是该input标签。然后在将该DOM元素赋值给实例对象中的一个属性

<input ref={self =>{ this.dian = self;console.log(self)}}  placeholder="点击弹出" />

也可以将函数提取出来,在ref中进行调用

isRef = (self) =>{
            this.dian = self;
            console.log(self)
        }

<input ref={this.isRef} type="text" placeholder="点击弹出" />

3.API形式

React其实已经给我们提供了一个相应的API,他会自动的将该DOM元素放入实例对象中

如下:依旧先在DOM元素中添加一个ref元素

{/*<input ref={this.容器名称} type="text" placeholder="点击弹出" />*/}
<input ref={this.MyRef} type="text" placeholder="点击弹出" />
<input ref={this.MyRef1} type="text" placeholder="点击弹出" />

通过API,创建React的容器,相当于省略了回调的中间环节。但是这个容器是专门专用的,所以每一个ref都需要创建这个。该API会将DOM元素赋值给实例对象的名称为容器名称的属性的current【这个current是固定的】

{/*容器名称 = React.createRef()*/}
MyRef = React.createRef();
MyRef1 = React.createRef();

然后就可以使用了

btnOnClick = () =>{
    //创建之后,将自身节点,传入current中
    console.log(this.MyRef.current.value);
}

官方提示我们不要过度的使用ref,如果发生时间的元素刚好是需要操作的元素,就可以使用事件去替代。

React事件

React的事件是通过onXxx属性指定事件处理函数

​ React使用的都是自定义的时间,而不是原生的事件

​ React中的事件是通过事件委托方式处理的

​ 事件中必须返回的是函数

​ 通过event.target得到发生事件的Dom元素对象

比如:

先声明一个事件,然后在根据事件创建相应的函数,根据事件的event参数,将DOM元素获取到。

<input onChange={this.saveName} type = "text" name ="username"/>

saveName = (event) =>{
            this.setState({name:event.target.value});
        }

受控和非受控组件

先来说说受控组件:

使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

saveName = (event) =>{
    this.setState({name:event.target.value});
}

savePwd = (event) => {
    this.setState({pwd:event.target.value});
}

render() {
    return (
        <form action="http://www.baidu.com" onSubmit={this.login}>
            用户名:<input value={this.state.name} onChange={this.saveName} type = "text" />
            密码<input value={this.state.pwd} onChange={this.savePwd} type = "password"/>
            <button>登录</button>
        </form>
    )
}

由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。由于 onchange 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。

对于受控组件来说,输入的值始终由 React 的 state 驱动。

非受控组件:

非受控组件其实就是表单元素的值不会更新state。输入数据都是现用现取的。

如下:下面并没有使用state来控制属性,使用的是事件来控制表单的属性值。

class Login extends React.Component{

    login = (event) =>{
        event.preventDefault(); //阻止表单提交
            console.log(this.name.value);
            console.log(this.pwd.value);
        }
        render() {
            return (
                <form action="http://www.baidu.com" onSubmit={this.login}>
                用户名:<input ref = {self => this.name =self } type = "text" name ="username"/>
                密码:<input ref = {self => this.pwd =self } type = "password" name ="password"/>
                <button>登录</button>
                </form>
            )
    }
}

高级函数

1.如果函数的参数是函数

2.如果函数返回一个函数

函数的珂里化

通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

如下,我们将上面的案例简化,创建高级函数:

 class Login extends React.Component{
 
        state = {name:"",pwd:""};
		
		//返回一个函数
        saveType = (type) =>{
            return (event) => {
                this.setState({[type]:event.target.value});
            }
        }

        //因为事件中必须是一个函数,所以返回的也是一个函数,这样就符合规范了
        render() {
            return (
                <form>
      				<input onChange = {this.saveType('name')} type = "text"/>
                    <button>登录</button>
                </form>
            )
        }
    }

    ReactDOM.render(<Login />,document.getElementById("div"));

生命周期

(旧)

组件从创建到死亡,会经过一些特定的阶段

​ React组件中包含一系列钩子函数{生命周期回调函数},会在特定的时刻调用

​ 我们在定义组件的时候,会在特定的声明周期回调函数中,做特定的工作

如下图是旧生命周期的结构图:

我们通过一个案例更详细的了解这个生命周期:

 class A extends React.Component{

        constructor(props){
            console.log("A --- constructor")
            super(props);
            this.state = {num:1}
        }

        add = () => {
            let {num} = this.state;
            this.setState({num:num+1});
            //强制更新
            //this.forceUpdate();
        }

       render(){
           console.log("A --- render");
            return (
                <div>
                    <h1>这个是第{this.state.num}个</h1>
                    <B name = {this.state.num}/>
                    <button onClick = {this.add}>点击加一</button>
                </div>
            )
       }

       //在render之前执行
       componentWillMount(){
            console.log("A --- componentWillMount");
       }

       //在render之后执行
       componentDidMount(){
        console.log("A --- componenetDidMount");
       }

       //更新操作 setState之后执行,判断是否可以更新(true可以,false不可以)
       shouldComponentUpdate(){
            console.log("A --- shouldComponentUpdate");
            return true;
       }
       // 组件将要更新之前
       componentWillUpdate(){
            console.log("A --- componentWillUpdate");
       }
       //组件更新之后,该函数可以接受相应的参数
       componentDidUpdate(){
            console.log("A --- componentDidUpdate");
       }

       //卸载组件之后
       componentWillUnmonut(){
            console.log("A --- componentWillUnmonut");
       }
     
   }
   class B extends React.Component{
       render(){
           return(   
                <div>
                    <h1>这个是B组件,传递过来的是:{this.props.name}</h1>
                </div>
           )
       }
       //父组件进行了更新,子组件先执行这个【注意,第一次传递数据的时候,并不执行】
       componentWillReceiveProps(){
        console.log("A --- componentWillReceiveProps");
       }
   }
    ReactDOM.render(<A   />,document.getElementById("div"));

我们在控制台看一下:

当我们刚刚打开控制台的时候,组件第一次加载:

当我们点击按钮更新sate的时候:

(新)

在最新的react版本中,有些生命周期钩子被抛弃了,在官网中是这样说的:

我们得到最重要的经验是,过时的组件生命周期往往会带来不安全的编码实践,具体函数如下:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

这些生命周期方法经常被误解和滥用;此外,我们预计,在异步渲染中,它们潜在的误用问题可能更大。我们将在即将发布的版本中为这些生命周期添加 “UNSAFE_” 前缀。(这里的 “unsafe” 不是指安全性,而是表示使用这些生命周期的代码在 React 的未来版本中更有可能出现 bug,尤其是在启用异步渲染之后。)

由此可见,新版本中并不推荐持有这三个函数,取而代之的是带有UNSAFE_ 前缀的三个函数,比如: UNSAFE_ componentWillMount。即便如此,其实React官方还是不推荐大家去使用,在以后版本中有可能会去除这几个函数。

如下图是新的生命周期:

从图上可以看出,新生命周期和旧生命周期的区别主要有:

1.抛弃了上面所说的三个钩子函数【其实还可以使用】

2.新添加了两个钩子函数

现在重点说一下,新添加的钩子函数

static getDerivedStateFromProps(props, state)

首先,该函数会调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用;该函数必须是静态的;给组件传递的数据(props)以及组件状态(state),会作为参数到这个函数中;该函数也必须有返回值,返回一个Null或者state对象。因为初始化和后续更新都会执行这个方法,因此在这个方法返回state对象,就相当于将原来的state进行了覆盖,所以倒是修改状态不起作用。

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递componentDidUpdate()

补充一下:componentDidUpdate(prevProps, prevState, snapshot)

该生命周期函数,可以有三个参数:原始传过来的参数,最开始的状态,getSnapshotBeforeUpdate传递的值

关于更多关于生命周期的介绍,可以参考官方文档:

React.Component – React

以上就是两个新添加的钩子函数,但是在现实开发中可能并不常用这两个。

**案例:在一个区域内,定时的输出以行话,如果内容大小超过了区域大小,就出现滚动条,但是内容不进行移动 **

如上面的动图:区域内部的内容展现没有变化,但是可以看见滚动条在变化,也就是说上面依旧有内容在输出,只不过不在这个区域内部展现。

实现:

【一些css样式,就不在这展示了】

1.首先我们先实现定时输出内容

我们可以使用state状态,改变新闻后面的值,但是为了同时显示这些内容,我们应该为state的属性定义一个数组。并在创建组件之后开启一个定时器,不断的进行更新state。更新渲染组件

 class New extends React.Component{

        state = {num:[]};

        //在组件创建之后,开启一个定时任务
        componentDidMount(){
            setInterval(()=>{
                let {num} = this.state;
                const news = (num.length+1);
                this.setState({num:[news,...num]});
            },2000);
        }

        render(){
            return (

                <div ref = "list" className = "list">{
                    this.state.num.map((n,index)=>{
                    return <div className="news" key={index} >新闻{n}</div>
                    })
                }</div>
            )
        }
  }
  ReactDOM.render(<New />,document.getElementById("div"));

2.接下来就是控制滚动条了

我们在组件渲染到DOM之前获取组件的高度,然后用组件渲染之后的高度减去之前的高度就是一条新的内容的高度,这样在不断的累加到滚动条位置上。

getSnapshotBeforeUpdate(){
	return this.refs.list.scrollHeight;
}

componentDidUpdate(preProps,preState,height){
	this.refs.list.scrollTop += (this.refs.list.scrollHeight - height);
}

这样就实现了这个功能。

Diff算法

提到这个算法,就必须说一下关于Key的事情了。

其实每个组件中的每个标签都会有一个key,只不过有的必须显示的指定,有的可以隐藏。

如果生成的render出来后就不会改变里面的内容,那么你不需要指定key(不指定key时,React也会生成一个默认的标识),或者将index作为key,只要key不重复即可。

但是如果你的标签是动态的,是有可能刷新的,就必须显示的指定key。必须上面案使用map进行便利的时候就必须指定Key:

this.state.num.map((n,index)=>{
	return <div className="news" key={index} >新闻{n}</div>
})

这个地方虽然显示的指定了key,但是官网并不推荐使用Index作为Key去使用

这样会很有可能会有效率上的问题

举个例子:

在一个组件中,我们先创建了两个对象,通过循环的方式放入< li>标签中,此时key使用的是index。

person:[
    {id:1,name:"张三",age:18},
    {id:2,name:"李四",age:19}
]

this.state.person.map((preson,index)=>{
  return  <li key = {index}>{preson.name}</li>
})

如下图展现在页面中:

此时,我们想在点击按钮之后动态的添加一个对象,并且放入到li标签中,在重新渲染到页面中。

我们通过修改State来控制对象的添加。

<button onClick={this.addObject}>点击增加对象</button>
addObject = () =>{
    let {person} = this.state;
    const p = {id:(person.length+1),name:"王五",age:20};
    this.setState({person:[p,...person]});
}

如下动图所示:

这样看,虽然完成了功能。但是其实存在效率上的问题, 我们先来看一下两个前后组件状态的变化:

我们发现,组件第一个变成了王五,张三和李四都移下去了。因为我们使用Index作为Key,这三个标签的key也就发生了改变【张三原本的key是0,现在变成了1,李四的key原本是1,现在变成了2,王五变成了0】在组件更新状态重新渲染的时候,就出现了问题:

因为react是通过key来比较组件标签是否一致的,拿这个案例来说:

首先,状态更新导致组件标签更新,react根据Key,判断旧的虚拟DOM和新的虚拟DOM是否一致

key = 0 的时候 旧的虚拟DOM 内容是张三 新的虚拟DOM为王五 ,react认为内容改变,从而重新创建新的真实DOM.

key = 1 的时候 旧的虚拟DOM 内容是李四,新的虚拟DOM为张三,react认为内容改变,从而重新创建新的真实DOM

key = 2 的时候 旧的虚拟DOM没有,创建新的真实DOM

这样原本有两个虚拟DOM可以复用,但都没有进行复用,完完全全的都是新创建的;这就导致效率极大的降低。

其实这是因为我们将新创建的对象放在了首位,如果放在最后其实是没有问题的,但是因为官方并不推荐使用Index作为key值,我们推荐使用id作为key值。从而完全避免这样的情况。

用index作为key可能会引发的问题:

1。若对数据进行:逆序添加、逆序删除等破坏顺序操作:

​ 会产生没有必要的真实DOM更新 界面效果没问题,但效率低。

2.如果结构中还包含输入类的DOM:

​ 会产生错误DOM更新 界面有问题。

3,注意! 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

开发如何选择key?

最好使用每一条数据的唯一标识作为key 比如id,手机号,身份证号

如果确定只是简单的展示数据,用Index也是可以的

而这个判断key的比较规则就是Diff算法

Diff算法其实就是react生成的新虚拟DOM和以前的旧虚拟DOM的比较规则:

  • 如果旧的虚拟DOM中找到了与新虚拟DOM相同的key:

    • 如果内容没有变化,就直接只用之前旧的真实DOM
    • 如果内容发生了变化,就生成新的真实DOM
  • 如果旧的虚拟DOM中没有找到了与新虚拟DOM相同的key:

    • 根据数据创建新的真实的DOM,随后渲染到页面上

React脚手架

react提供了一个用于创建react项目的脚手架库:create-react-app

创建项目并启动

1.全局安装:npm i -g create-react-app

2.创建项目:create-react-app 项目名

在这一步,有可能会出现:

这样可以直接使用:npx create-react-app 项目名

3.等待下载完成,进入项目文件夹,运行一下

比如,我这的项目名称是hello,就先进入hello文件夹

cd hello

npm start //启动这个项目

这个时会自动的打开浏览器,展现这个项目:

项目的目录结构

我们先来看一下public这个目录下面的结构:

这里面最主要的还是这个Index.html文件:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <!--%PUBLIC_URL%表示public文件夹的路径-->
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <!--用于开启理想视口,用于移动端页面的适配-->
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!--用于配置浏览器地址栏的颜色(仅支持安卓手机浏览器)-->
    <meta name="theme-color" content="#000000" />
    <!--描述网页信息的-->
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <!--用于指定网页添加到手机主屏幕后的图标(仅仅支持ios)-->
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
 
    <!--应用加壳时候的配置文件 -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
  
    <title>React App</title>
  </head>
  <body>
    <!-- 浏览器不支持JS的运行的时候展现 -->
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

src文件:

这里面其实最主要的就是App.js以及index.js,一个是组件,一个是将组件渲染到页面中的。

第一个脚手架应用

1.我们保持public中的Index.html不变

2.修改src下面的APP.js以及index.js文件

App.js: 【注意:创建好的组件一定要暴露出去】

//创建外壳组件APP
import React from 'react'

class App extends React.Component{
    render(){
        return (
            <div>Hello word</div>
        )
    }
}

export default App

index.js: 【主要的作用其实就是将App这个组件渲染到页面上】

//引入核心库
import React from 'react'
import ReactDOM from 'react-dom'
//引入组件
import App from './App'

ReactDOM.render(<App />,document.getElementById("root"))

这样在重新启动应用,就成功了。

我们也不建议这样直接将内容放入App组件中,尽量还是用内部组件。

我们在顶一个Hello组件:

import React,{Componet} from 'react'

export default class Hello extends Componet{
    render() {
        return (
            <h1>Hello</h1>
        )
    }
}

在App组件中,进行使用

class App extends Component{
    render(){
        return (
            <div>
                <Hello />
            </div>
        )
    }
}

这样的结果和前面是一样的。

但是由于普通的Js和组件都是js,所一最好组件使用jsx去展示。

样式冲突

当组件逐渐增多起来的时候,我们发现,组件的样式也是越来越丰富,这样就很有可能产生两个组件中样式名称有可能会冲突,这样会根据引入App这个组件的先后顺序,后面的会覆盖前面的,

为了避免这样的样式冲突,我们采用下面的形式:

1.将css文件名修改: hello.css --- >hello.module.css

2.引入并使用的时候改变方式:

import React,{Component}from 'react'
import hello from './hello.module.css'  //引入的时候给一个名称

export default class Hello extends Component{
    render() {
        return (
            <h1 className={hello.title}>Hello</h1>   //通过大括号进行调用
        )
    }
}

功能界面的组件化编码流程

1.拆分组件:拆分界面,抽取组件

2.实现静态组件

3.实现动态组件

  • 动态的显示初始化数据
    • 数据类型
    • 数据名称
    • 保存在哪个组件
  • 交互

注意事项:

1.拆分组件、实现静态组件。注意className、style的写法

2.动态初始化列表,如何确定将数据放在哪个组件的state中?

  • 某个组件使用:放在自身的state中
  • 某些组件使用:放在他们共同的父组件中【状态提升】

3.关于父子组件之间的通信

  • 父组件给子组件传递数据:通过props传递
  • 子组件给父组件传递数据:通过props传递,要求父组件提前给子组件传递一个函数

4.注意defaultChecked 和checked区别,defalutChecked只是在初始化的时候执行一次,checked没有这个限制,但是必须添加onChange方法类似的还有:defaultValue 和value

5.状态在哪里,操作状态的方法就在哪里

react ajax

React本身只关注与页面,并不包含发送ajax请求的代码,所以一般都是集成第三方的一些库,或者自己进行封装。

推荐使用axios。

在使用的过程中很有可能会出现跨域的问题,这样就应该配置代理。

所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port), 当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域 。

那么react通过代理解决跨域问题呢

方法一

在package.json中追加如下配置

"proxy":"请求的地址"      "proxy":"http://localhost:5000"  

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。
  2. 缺点:不能配置多个代理。
  3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)

方法二

  1. 第一步:创建代理配置文件

    在src下创建配置文件:src/setupProxy.js
    
  2. 编写setupProxy.js配置具体代理规则:

    const proxy = require('http-proxy-middleware')
    
    module.exports = function(app) {
      app.use(
        proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
          target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
          changeOrigin: true, //控制服务器接收到的请求头中host字段的值
          /*
          	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
          	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
          	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
          */
          pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
        }),
        proxy('/api2', { 
          target: 'http://localhost:5001',
          changeOrigin: true,
          pathRewrite: {'^/api2': ''}
        })
      )
    }
    

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
  2. 缺点:配置繁琐,前端请求资源时必须加前缀。

兄弟之间进行通信

这就要借助消息订阅和发布机制。

举个例子来说就是张三想要跟李四进行通信,张三就需要订阅一个消息【比如A消息】,李四想要给张三数据,就必须发布一个A消息,在发布的同时将数据放入消息中,因为张三订阅了名称为A的消息,此时就能接受到李四发布的消息,从而获取到数据。

这就有点类似于看报纸,甲想要知道每天都发生什么事情,于是订阅了每天日报,乙每天都会发布这个每天日报,因为甲订阅了,于是乙就会每天就给甲方推送,甲方从而获取数据。

在消息订阅和发布中,我们可以使用PubSubJs进行通信:

引入PubSubJs:

import PubSub from 'pubsub-js'

订阅消息:

PubSub.subscribe("getSate",(_,data)=>{
            console.log(data)
        })
PubSub.subscribe("订阅的消息名称",回调函数,第一个参数是消息名称,可以使用_来占位,第二个是传递的数据
        })

发布消息:

PubSub.publish("getSate",{isFrist:false,isLoad:true})
PubSub.publish("订阅的消息名称",传递的数据)

async和await

async:

该关键字是放在函数之前的,使得函数成为一个异步函数,他最大的特点就是将函数回封装成Promise,也就是被他修饰的函数的返回值都是Promise对象。而这个Promise对象的状态则是由函数执行的返回值决定的。

如果返回的是一个非promise对象,该函数将返回一个成功的Promise,成功的值则是返回的值;

如果返回的是一个promise对象,则该函数返回的就是该promise对应的状态。

await

await右边是一个表达式,如果该表达式返回的是一个Promise对象,则左边接收的结果就是该Promise对象成功的结果,如果该Promise对象失败了,就必须使用try..catch来捕获。如果该表达式返回的是是一个不是promise对象,则左边接受的就是该表达式的返回值。

当 await 关键字与异步函数一起使用时,它的真正优势就变得明显了 —— 事实上, await 只在异步函数里面才起作用。它可以放在任何异步的,基于 promise 的函数之前。它会暂停代码在该行上,直到 promise 完成,然后返回结果值。在暂停的同时,其他正在等待执行的代码就有机会执行了。

举个例子:

 f1 = () =>{
        return new Promise((resolve,reject)=>{
            // resolve(1);
            reject("错误")
        })
    }

    async function test(){
        try{
           const p =  await f1();
           console.log(p)
        }catch(error){
            console.error(error)
        }
    }
    test();

fetch

以前发送请求,使用ajax或者axios,现在还可以使用fetch。这个是window自带的,和xhr是一个级别的。

可以查看这个文章,写的真的不错:

fetch

React路由

SPA

单页Web应用(single page web application,SPA)。整个应用只有一个完整的页面。

点击页面中的链接不会刷新页面,只会做页面的局部更新。

数据都需要通过ajax请求获取,并在前端异步展现

什么是路由

一个路由其实就是一个映射关系(k:v)

key为路径,value可能是function 或者是 component

后端路由:

value是function,用来处理客户端提交的请求

注册路由:router.get(path,function(req,res))

工作过程:当node接收一个请求的时候,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应的数据

前端路由:

浏览器端路由,value是Component,用于展示页面内容

注册路由:< Route path="/test" component={Test}>

工作过程:当浏览器的path变为/test的时候,当前路由组件就会变成Test组件

前端路由的原理

这个主要是依靠于history,也就是浏览器的历史记录。

浏览器上的记录其实就是一个栈,前进一次就是入栈,后退一次就是出栈。

并且历史记录上有一个监听的方法,可以时时刻刻监听记录的变化。从而判断是否改变路径

History

react-router-dom

react的路由有三类:

web【主要适用于前端】,native【主要适用于本地】,anywhere【任何地方】

在这主要使用web也就是这个标题 react-router-dom

基本的使用:

导航中的a标签改写成Link标签

< Link to="/路径" >xxx< /Link>

展示区写Route标签进行路径的匹配

< Route path = '/路径' component={组件名称}>

< App>最外侧包裹了一个< BrowserRouter>或者< HashRouter>

<div className="list-group">
    <Link className="list-group-item"  to="/about">About</Link>
    <Link className="list-group-item"  to="/home">Home</Link>
</div>

<div className="panel-body">
    {/* 注册路由,也就是写对应的关系 */}
    <Route path="/about"component={About}/>
    <Route path="/home"component={Home}/>
</div>

index.js:
ReactDOM.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>
    ,document.getElementById("root"))

那么使用Link代替a标签之后,在页面上会是什么呢,我们发现其实页面上也是把link转化为了a标签

路由组件以及一般组件

1.写法不一样

一般组件:< Demo>

路由组件:< Route path="/demo" component ={Demo}/>

2.存放的位置一般不同

一般组件:components

路由组件:pages

3.接收的内容【props】

一般组件:写组件标签的时候传递什么,就能收到什么

路由组件:接收到三个固定的属性【history,location,match】

history:
    go: ƒ go(n)
    goBack: ƒ goBack()
    goForward: ƒ goForward()
    push: ƒ push(path, state)
    replace: ƒ replace(path, state)
location:
    pathname: "/about"
    search: ""
    state: undefined

match:
    params: {}
    path: "/about"
    url: "/about"

NavLink

因为Link不能够改变标签体,因此只适合用于一些写死的标签。而如果想要有一些点击的效果,使用NavLink.

如下代码,就写了ctiveClassName,当点击的时候就会触发这个class的样式

{/*NavLink在点击的时候就会去找activeClassName="ss"所指定的class的值,如果不添加默认是active
 这是因为Link相当于是把标签写死了,不能去改变什么。*/}

<NavLink  ctiveClassName="ss" className="list-group-item"  to="/about">About</NavLink>
<NavLink className="list-group-item"  to="/home">Home</NavLink> 

但是可能一个导航又很多标签,如果这样重复的写NavLink也会造成很多的重复性的代码问题。

因此可以自定义一个NavLink:

 // 通过{...对象}的形式解析对象,相当于将对象中的属性全部展开
 //<NavLink  to = {this.props.to} children = {this.props.children}/>
<NavLink className="list-group-item" {...this.props}/>

​ 在使用的时候:直接写每个标签中不一样的部分就行,比如路径和名称

{/*将NavLink进行封装,成为MyNavLink,通过props进行传参数,标签体内容props是特殊的一个属性,叫做children */}
<MyNavLink to = "/about" >About</MyNavLink>

样式错误

拿上面的案例来说:

这里面会有一个样式:

此时,加载该样式的路径为:

但是在写路由的时候,有的时候就会出现多级目录,

<MyNavLink to = "/cyk/about" >About</MyNavLink>

<Route path="/cyk/about"component={About}/>

这个时候就在刷新页面,就会出现问题:

样式因为路径问题加载失败,此时页面返回public下面的Index.html

解决这个问题,有三个方法:

1.样式加载使用绝对位置

 <link href="/css/bootstrap.css" rel="stylesheet"> 

2.使用 %PUBLIC_URL%

 <link href="%PUBLIC_URL%/css/bootstrap.css" rel="stylesheet">

3.使用HashRouter

因为HashRouter会添加#,默认不会处理#后面的路径,所以也是可以解决的

模糊匹配和精准匹配

react默认是开启模糊匹配的。

比如:

<MyNavLink to = "/home/a/b" >Home</MyNavLink>

此时该标签匹配的路由,分为三个部分 home a b;将会根据这个先后顺序匹配路由。

如下就可以匹配到相应的路由:

<Route path="/home"component={Home}/>

但是如果是下面这个就会失败,也就是说他是根据路径一级一级查询的,可以包含前面那一部分,但并不是只包含部分就可以。

<Route path="/a" component={Home}/>

当然也可以使用这个精确的匹配 exact={true}

如以下:这样就精确的匹配/home,则上面的/home/a/b就不行了

<Route exact={true}  path="/home" component={Home}/>
或者
<Route exact path="/home" component={Home}/>

初始化路由

在配置好路由,最开始打开页面的时候,应该是不会匹配到任意一个组件。这个时候页面就显得极其不合适,此时应该默认的匹配到一个组件。

此时就需要使用Redirect进行默认匹配了。如下的代码就是默认匹配/home路径所到的组件

<Switch>
    <Route path="/about"component={About}/>
    {/* exact={true}:开启严格匹配的模式,路径必须一致 */}
    <Route   path="/home" component={Home}/>
    {/* Redirect:如果上面的都没有匹配到,就匹配到这个路径下面 */}
    <Redirect  to = "/home"/>
</Switch>

就可以做到如下的效果:

嵌套路由

简单来说就是在一个路由组件中又使用了一个路由,就形成了嵌套路由。

举个例子来说:

我们在home这个路由组件中又添加两个组件:

APP.jsx:
<Route   path="/home" component={Home}/>
Home.jsx:
<div>
    <ul className="nav nav-tabs">
    <li>
    	<MyNavLink to = "/home/news">News</MyNavLink>
    </li>
    <li>
    	<MyNavLink  to = "/home/message">Message</MyNavLink>
    </li>
    </ul>
    
    <Switch>
        <Route path = "/home/news" component={News} />
        <Route path = "/home/message" component={Message} />
        <Redirect to="/home/message"/>
    </Switch>
</div>

react中路由的注册是有顺序的,因此在匹配的时候也是按照这个顺序进行的,因此会先匹配父组件中的路由

比如上面的 /home/news的路由处理过程:

1.因为父组件home的路由是先注册的,因此在匹配的时候先去找home的路由,也就是根据/home/news先模糊匹配到/home

2.在去Home组件里面去匹配相应的路由,从而找到了/home/news进行匹配,因此找到了News组件。

但是如果开启精确匹配,就会在第一步的时候卡住,这个时候就走不下去了。因此不要轻易的使用精确匹配

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

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

相关文章

Linux-ionde(软硬件链接)剖析

概述 文件是存储在硬盘上的&#xff0c;硬盘的最小存储单位叫做扇区sector&#xff0c;每个扇区存储512字节。操作系统读取硬盘的时候&#xff0c;不会一个个扇区地读取&#xff0c;这样效率太低&#xff0c;而是一次性连续读取多个扇区&#xff0c;即一次性读取一个块block。这…

OpenAI的DALL·e2生成的AI图像有时会带有偏见或NSFW

专家警告说&#xff0c;OpenAI的图像生成模型DALLe2可能玩起来很有趣&#xff0c;但它生成的图片可能会带有刻板印象和偏见&#xff0c;甚至是生成NSFW图片&#xff0c;因此在现实世界中部署会有风险。 OpenAI公司承认“如果没有足够的防护措施&#xff0c;像DALLe2这样的模型…

k8s节点RouteCreated为false

出现该情况后&#xff0c;一般是初始化节点失败。因此&#xff0c;需要把节点从集群中移除&#xff0c;再加入到集群中&#xff0c;即可解决。 通常出现这个状况后&#xff0c;该节点上是没有被分配pod ip的&#xff0c;可以通过命令查看&#xff1a; # 发现没有PodCIDR、PodC…

DHCP原理

目录 DHCP应用场景DHCP基本原理DHCP报文类型地址池DHCP工作原理DHCP租期更新DHCP重绑定IP地址释放 DHCP应用场景 在大型企业网络中&#xff0c;会有大量的主机或设备需要获取IP地址等网络参数。如果采用手工配置&#xff0c;工作量大且不好管理&#xff0c;如果有用户擅自修改网…

vue3前端开发,子组件向父组件传递数据练习

vue3前端开发,子组件向父组件传递数据练习&#xff01; <script setup> import Child from ./Child.vue const getMsg (msg)>{console.log(msg); } </script> <template><h3>Parent</h3><!--绑定事件--><Child get-Msg"getM…

回调地狱与解决方案

什么是回调地狱&#xff1f; 简单理解就是回调函数嵌套回调 示例&#xff1a; setTimeout(() > {console.log(1);setTimeout(() > {console.log(2);setTimeout(() > {console.log(3);}, 1000);}, 2000)}, 3000)如上代码所示&#xff0c;回调函数嵌套回调&#xff0c;就…

QoS简介

QoS产生的背景 网络的普及和业务的多样化使得互联网流量激增&#xff0c;从而产生网络拥塞&#xff0c;增加转发时延&#xff0c;严重时还会产生丢包&#xff0c;导致业务质量下降甚至不可用。所以&#xff0c;要在网络上开展这些实时性业务&#xff0c;就必须解决网络拥塞问题…

电商系统设计到开发02 单机性能压测

一、前言 接着上篇文章&#xff1a;电商系统设计到开发&#xff08;第一版&#xff09;-CSDN博客 已经开发的代码&#xff0c;今天我们对上面开发的代码进行压力测试&#xff0c;看看单机部署的情况下&#xff0c;性能表现如何。代码地址&#xff1a;风萧萧兮/concurrency-en…

Linux中关于head命令详解

head的作用 head用于查看文件的开头部分的内容。 head的参数 -q隐藏文件名-v 显示文件名-c<数目>显示的字节数-n<数目>显示的行数 head的案例 # 查看yum.log前五行内容 head -5 yum.log

对于随机生成图片接口浏览器走缓存的问题

前提场景 目前有一个api 他可以随机生成一张图片&#xff0c;我通过v-for循环一个Array渲染出来几个img 并且都调用了该接口&#xff0c;但是每个img都是一样的图片 具体代码如下 <div class"icon-group-box" v-for"item in groupList" :key"item…

Oracle篇—参数文件在11gRAC或12cRAC的启动位置介绍

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣…

让Mac与Windows合二为一:Microsoft Remote Desktop for Mac的魅力

在数字时代&#xff0c;远程连接已成为工作、学习和生活的必备工具。而Microsoft Remote Desktop for Mac正是这样一款能够让你随时随地&#xff0c;轻松连接到Windows系统的强大工具。 Microsoft Remote Desktop for Mac不仅提供了高效、稳定的远程访问体验&#xff0c;更凭借…

黑马程序员-瑞吉外卖-day4

实现账号的启动禁止 EmployeeController PutMappingpublic R<String> update(RequestBody Employee employee){employeeService.updateById(employee);return R.success("员工信息修改成功");} 出错 解决 common目录下 引入JacksonObjectMapper package com…

Sentinel 降级、限流、熔断

前言 在现代分布式系统中&#xff0c;如何有效地保护系统免受突发流量和故障的影响&#xff0c;是每个开发人员和架构师都需要思考的重要问题。在这样的背景下&#xff0c;Sentinel作为一个强大的系统保护和控制组件&#xff0c;为我们提供了降级、限流、熔断等多种策略&#…

宝塔 ftp 服务器发回了不可路由的地址/读取目录列表失败

ftp连接不上&#xff1a; 1.注意内网IP和外网IP 2.检查ftp服务是否启动 &#xff08;面板首页即可看到&#xff09; 3.检查防火墙20端口 ftp 21端口及被动端口39000 - 40000是否放行 &#xff08;如是腾讯云/阿里云等还需检查安全组&#xff09; 4.是否主动/被动模式都不能连接…

Java学习笔记(七)——操作数组工具类Arrays

文章目录 ArraysArrays.toString()Arrays.binarySearch()Arrays.copyOf()Arrays.copyOfRange()Arrays.fill()Arrays.sort()升序排序降序排序 Arrays 操作数组的工具类。 Arrays.toString() import java.util.Arrays;public class test40 {public static void main(String[] a…

SAP S/4HANA 2023 Fully-Activated Appliance 虚拟机版介绍

注&#xff1a;市面上所有在售虚拟机均为拷贝本人所作的虚拟机&#xff0c;存在各种技术问题&#xff0c;请知悉。 SAP S4HANA 2023 FAA版本内置了四个Client&#xff1a; 1、000&#xff1a;SAP初始Client&#xff0c;原则上不能动&#xff1b; 2、100&#xff1a;只激活了US…

自己是如何使用单元测试

前言 自己是如何使用单元测试 进行单元测试能够让我们在编写方法的具体实现代码后&#xff0c;能清晰地看到其是否能实现预期的功能&#xff0c;有助于我们及时修正自己方法中存在的bug&#xff0c;以免在后续使用到某方法时出现意想不到的错误。 一、引入单元测试所使用的依赖…

MySQL基础笔记(9)事务

一.简介 所谓事务&#xff0c;是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求&#xff0c;即&#xff0c;这些操作要么同时成功&#xff0c;或者同时失败——OS中有原语不可分割的概念&…

网工每日一练(1月18日)

1.在下面关于VLAN的描述中&#xff0c;不正确的是 &#xff08; C &#xff09; 。 A、VLAN把交换机划分成多个逻辑上独立的交换机 B、主干链路&#xff08;Trunk&#xff09;可以提供多个VLAN之间通信的公共通道 C、由于包含了多个交换机&#xff0c;所以VLAN扩大了冲突域 D、…