React 类组件你不知道的细节+案例

news2024/11/19 8:49:24

React基础-组件-类组件

1.组件概述

目标:了解React组件的作用和创建组件的方式


  • 什么是组件
  • 组件的设计思想

1.what is 组件啊?

在前端开发中组件就是用户界面当中一块独立的区域,在组件内部会包含这块区域中的视图代码,样式代码以及逻辑代码

React是采用组件的方式来构建用户界面,通过将多个组件进行组合形成完成的用户界面,就好比搭乐高一样

2.组件设计思想

组件的核心思想之一就是重复使用用,定义一次就可以在任何地方进行使用

组件可以用来封装用户界面中的重复区块,复用重复区块

组件的第二个核心思想就是解耦。

在传统的 web页面开发中,一个html文件就是一个页面,就是说当前页面中的所有代码都被写在同一个文件中,这就很容易导致代码的冲突

在组件化开发中,每个组件都有自己的作用域,组件与组件之间的代码不会发生任何冲突m从而避免在传统开发的模式中经常出现改了A,B却挂了的问题


2.创建组件

目标: 掌握创建类组件的方式

import ReactDOM from "react-dom/client";
import {Component} from "react"
​
//1.组件名称首字母大写
//2.只有继承了 Component类才是React 组件
//3.类中必须是包含render方法用于渲染用户界面, render方法的名字是固定的,渲染用户界面时返回 jsx,不渲染任何界面时返回 null;
class App extends Component {
  render() {
    return <div>头部组件</div>}
}
​
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App></App>) 

1.组件单独存放

在实际的React项目开发中,组件作为独立的个体一般都会被放置在单独的文件中方便维护

//src/App.js
import {Component} from "react";
//约定:组件文件的名称和组件名称保持一致
class App extends Component {
  render() {
    return <div>头部组件</div>}
}
//导出组件,以便在其他地方导入并使用组件
export default App; 
//src/index.js
import ReactDOM from "react-dom/client";
import App from "./App";
​
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<App></App>) 

3.组件状态

1.组件状态什么玩意?

用一个例子来比喻就是说现实生活中的状态指同一个事物有不同形式,比如水,当水达到沸点那么就会变成水蒸气,如果水在零摄氏度以下就会结成冰

在web 应用中用户界面也是有状态的,比如有一块将要展示用户列表的区域,用户列表数据需要从服务端获取,该区域将会有以下这几种状态

状态解释
空闲在没有发出请求时该区域为空闲状态
加载中请求在发出后没有得到响应前该区域为加载中状态
加载成功当请求得到响应用户列表渲染成功后该区域为成功状态
加载失败当请求未得到正确的响应时该区域为失败状态
结束不论请求成功与失败请求都结束了该区域为结束状态

再比如导航链接,它具有默认状态和选中状态.下拉框具有收缩状态和展开状态

那么如何在程序中表示用户界面的状态呢?在程序中可以通过声明变量进行状态的记录

//idle:空闲状态
//loading:加载中
//success:加载成功
//error:加载失败
//finish:结束
let status = "idle" 
let navLink = "白色";
let activeNavLink = "绿色" 

React应用程序使用组件构建用户界面,所以用户界面的状态需要被声明在组件中,被声明在组件中的状态被叫做组件状态。

2.操作组件状态

目标:掌握操作组件状态的方法

在这类组件中组件状态必须声明在类的state属性中,state属性的名字时固定的,对象类型.

在render方法中通过this关键获取 state属性中的状态。

//目标:实现计数器案例,即声明组件状态 count用于存储数值,点击按钮让数值+1
class App extends Component {
  //state 对象用于存储组件状态
  state = {
    //状态count,初始值为 0
    count:0,};
  render() {
    //render 方法中的this指向组件的实例对象
    //state 属性时类的实例属性
    //所以通过this 是可以获取到state对象的
    return <button>{this.state.count}</button>}
} 

类组件中的组件状态必须通过类实例对象下的 setState 方法进行修改,只有这样才能触发视图更新.

当前组件实例下并没有setState 方法,setState方法是父类 Component提供的。

class App extends Component {
  state = {
    count:0};
  render() {
    return (
  <button onClick={()=> {
          //1.更新状态,接收对象作为参数,改哪一个状态就传递哪一个状态即可.react内部会帮助我们进行状态合并操作
          //2.更新视图
          this.setState({count:this.state.count + 1})
      }}>{this.state.count}</button>
  )}
} 

可能🤔得点:那就是之前说过事件处理函数中的this指向undefined,为什么此处它又指向了组件实例对象呢?

因为此处事件处理函数是➡️箭头函数,箭头函数不绑定this,箭头函数中的this指向了箭头函数定义处的this,由于当前的事件处理函数是在render方法中定义的,而render方法中的this指向了组件实例对象,所以该事件处理函数中的this指向了组件实例对象。

⚠️的点:在state对象存储多个状态的情况下,使用setState更新状态时只传递需要更新的状态即可,React会先接收我们传递给setState方法的状态,再使用它和原有状态进行合并,从而产生新状态。

import {Component} from "react";
​
export default class App extends Component {
  constrouct() {
    super()
    this.state = {
      count:0,
      name:"张三"
  }}
  render() {
    return <div><button onClick={()=>this.setState({count:this.state.count + 1})}>{this.state.count}</button> <span>{this.state.name}</span></div>}
} 

3.更改事件函数 this 指向

目标:掌握React中事件处理函数this关键字的指向如何更改

大多数情况下,我们都希望时吧事件处理函数的this关键字指向组件实例对象。

以下有三种情况可以解决这个问题

class App extends Component {
  onClickHandler() {
    console.log(this)}
  render() {
    //使用render函数中的this关键字调用真正的事件处理函数,使其内部指向当前组件实例对象
    //小问题:组件状态发生更改后要更新视图,也就是render方法会重新执行,当每次render方法重新执行时 javascript 执行引擎都会创建新的行为匿名箭头函数,都会为元素绑定新的行内匿名箭头函数,性能有一丢丢损失
    return <button onClick={()=>this.onClickHandler()}>button</button>}
}
​
//为什么改写成箭头函数以后可以解决this问题
//简写语法
class Person {
  fn = () => {}
}
​
class Person {
  constrouct() {
     // 由于箭头函数不绑定 this, 所以 fn 函数在被调用后, 函数内部的 this 实际上用的是 constructor 构建函数中的 this
    // 而构造函数中的 this 指向了组件实例对象, 所以 fn 函数中的 this 就指向了实例对象
    this.fn = () => {}}
} 
class App extends Component {
  onClickHandler() {
    console.log(this)}
  render() {
    //通过bind方法将事件处理函数中的this指向组件实例对象,bind方法返回一个新的被更改了this指向的函数作为事件处理函数
    //问题:render 方法每次重新执行都会为元素绑定新的bind方法返回的事件处理函数,性能有一丢丢损失
    return <button onClick={this.onClickHandler.bind(this)}></button>}
} 
class App extends Component {
  constrouct() {
    super() {
      // 在构造函数中将事件处理函数更改为组件对象
      // 构造函数中的 this 指向的是组件的实例对象
      // 这样即保证了事件处理函数为原型方法, 又确保了 this 指向的更改只执行一次
      // 所以从性能角度考虑, 这种方式是最为理想的
      // 推荐: ⭐️⭐️⭐️⭐️⭐️
      this.onClickHandler = this.onClickHandler.bind(this);
  }
    onClickHandler() {
      console.log(this)
  }
    render() {
      return <button onClickHandler={this.onClickHandler}></button>
  }
    }
} 

4.状态不可变

目标: 理解React中状态不可变理念

React中关于状态有一个核心的思想理念就是状态不可变,该理念是需要被时刻严格遵守

状态不可变是指在更新组件状态时不能直接操作现有的状态,而是要基于现有状态值产生新状态值。

state = {
  count:0,
  list:[1,2,3],
  person: {
    name:"张三",
    age:18}
} 
//🙅写法
this.state.count = 200;
this.state.count-- 
this.state.list.push
this.state.person.name = "王武" 
//正确写法
this.setState({
  //数组的删除写法
  list:[...this.state.list.slice(0,1),"p",...this.state.list.slice(2)],
  
}) 

如何理解 React 状态不可变?

因为 React 在更新真实 DOM 对象之前,要对新旧状态对象 state 进行对比找出要更新的部分,所以当前状态也就是旧状态是不能被直接更改的。


4.非受控表单组件

目标:掌握非受控表单的使用方式

在HTML中表单控件可以维护自身的状态

用户在表单控件中输入的值就是表单控件要维护的状态,表单控件会实时将状态存储到表单控件对应的DOM对象的value属性中

非受控表单组件是指表单组件的状态由自己进行维护

在非受控组件中开发者腰获取表单控件需要先获取表单控件DOM对象,再通过value属性去获取表单控件状态

import {Component,createRef} from "react";
​
class App extends Component {
  constrouct() {
    super();
    //设置this指向实例对象
    this.onClickHandler = this.onClickHandler.bind(this)}
  //createRef:创建元素的引用对象
  inputRef = createRef();
  //按钮点击事件的事件处理函数
  onClickHandler() {
    //通过文本框的元素引用对象获取文本框状态
    console.log(this.inputRef.current.value);}
  render() {
    return (
  <>
    {/*为元素引用对象绑定元素*/}<input type="text" ref={this.inputRef} />{/*点击按钮时获取文本框控件自身管理的状态*/}<button onCLick={this.onClickHandler}></button>
      </>
  )}
} 

5.受控表单组件

目标:掌握受控组件的使用方式,理解受控组件的执行过程

1.受控表单组件的使用方式

受控表单组件是指表单的状态由组件状态管理,就是将表单的状态和组件状态进行映射。

通过表单控件的value属性绑定组件状态,通过onChange事件调用setState更新组件状态

class App extends Component {
  state = {
    text:"默认值"}
  onChangeHandler(event) {
    //调用setState方法更新组件状态
    //将组件状态的值更新为用户在文本框输入的内容
    this.setState({
      text:event.target.value
  })}
  render() {
    return (
  <input type="text" 
      {/* 将组件状态和文本框的 value 属性进行绑定 */}
        value={this.state.text}
      {/* 为文本框绑定 change 事件, 当用户在文本框中输入时更新组件状态 */}
        onChange={this.onChangeHandler}
      />
  )}
} 

在只为表单控件添加value属性而没添加onChange事件的情况下,浏览器的控制会报错警告

<input type="text" value={this.state.text} />
​
<input type="text" defaultValue={this.state.text} /> 

你为表单控件供了 value 属性但是没有提供 onChange 事件的事件处理函数,这将渲染一个只读的表单控件。如果你只是想为文本框设置一个默认值,你应该使用 defaultValue,否则,你要么添加 onChange 事件的事件处理函数要么添加 readOnly 属性。

  • 添加只读属性是为了告诉 react 你就是要渲染一个只读的表单控件。警告消失。

  • 添加 onChange 事件处理函数是为了完成组件状态与表单控件的映射功能。警告消失。

  • 在使用非受控表单的情况下如果要为表单控制设置默认值可以使用 defaultValue 属性。警告消失。

  • 总结:在受控组件中,表单控件身上必须同时具有 value 属性和 onChange 事件。


2.受控表单组件执行过程

(1) 用户触发表单控件的 onChange 事件,执行 onChange 事件的事件处理函数

(2) 在事件处理函数中通过事件对象获取到表单控件的最新状态并使用最新状态更新组件状态

(3) 组件状态更新完成后 render 方法重新执行,在 render 方法中通过最新的组件状态渲染视图


3.onChange 事件说明

在 React 中使用的 onChange 事件并不是原生 JS 中的 onChange 事件,原生 JS 中的 onChange 事件是在表单离开焦点后触发的,而 React 中的 onChange 事件是实时触发的。

在原生 JS 中,表单控件的实时改变事件是 input,但并不是所有的表单控件都有该事件,比如 select。所以 React 为了方便开发者实现受控组件,重新封装了 onChange 事件,让 onChange 事件变成实时触发事件且其他表单控件也可以使用 onChange 事件。


6.综合案例-评论

1.准备案例的布局与样式

<!-- public/index.html -->
<!-- 收藏按钮字体图标 -->
<link href="https://at.alicdn.com/t/font_2998849_vtlo0vj7ryi.css" rel="stylesheet" /> 
// src/Comment.js
import React from "react";
import "./comment.css";
​
class Comment extends React.Component {
  render() {
    return (
      <div className="comments">
        <h3 className="comm-head">评论</h3>
        <div className="comm-input">
          <textarea placeholder="爱发评论的人,运气都很棒"></textarea>
          <div className="foot">
            <div className="word">0/100</div>
            <div className="btn">发表评论</div>
          </div>
        </div>
        <h3 className="comm-head">
        热门评论<sub>(5)</sub>
          <span className="active">默认</span>
    <span>时间</span>
        </h3>
        <ul className="comm-list">
          <li className="comm-item">
            <div className="avatar"></div>
            <div className="info">
              <p className="name vip">
              清风徐来
                <img
                  alt=""
                  src="https://gw.alicdn.com/tfs/TB1c5JFbGSs3KVjSZPiXXcsiVXa-48-48.png"
                />
              </p>
              <p className="time">
              2012-12-12
              {/* 未收藏: icon-collect 已收藏: icon-collect-sel */}
                <span className="iconfont icon-collect"></span>
                <span className="del">删除</span>
              </p>
              <p>
              这里是评论的内容!!!这里是评论的内容!!!这里是评论的内容!!!
              </p>
            </div>
          </li>
        </ul>
      </div>
  );}
}
​
export default Comment; 
/* src/comment.css */
body {
  margin: 0;
}
.comments {
  background-color: #121212;
  color: #eee;
  padding: 0 30px;
  width: 1000px;
  margin: 70px auto 0;
  overflow: hidden;
}
.comm-head {
  color: #eee;
  font-size: 24px;
  line-height: 24px;
  margin-bottom: 24px;
}
.comm-head sub {
  font-size: 14px;
  color: #666;
  margin-left: 6px;
  bottom: 0.2em;
  position: relative;
}
​
.comm-head span {
  display: inline-block;
  line-height: 1;
  padding: 5px 16px;
  font-size: 14px;
  font-weight: normal;
  border-radius: 12px;
  background-color: rgba(255, 255, 255, 0.1);
  color: #999;
  cursor: pointer;
  margin-left: 30px;
}
.comm-head span:hover,
.comm-head span.active {
  color: #61f6ff;
}
​
.comm-list {
  list-style: none;
  padding: 0;
}
.comm-item {
  display: flex;
  margin-bottom: 24px;
}
.comm-item .avatar {
  width: 48px;
  height: 48px;
  line-height: 48px;
  border-radius: 24px;
  display: inline-block;
  cursor: pointer;
  background-position: 50%;
  background-size: 100%;
  background-color: #eee;
}
.comm-item .info {
  padding-left: 16px;
}
.comm-item .info p {
  margin: 8px 0;
}
.comm-item .info p.name {
  color: #999;
}
.comm-item .info p.vip {
  color: #ebba73;
}
.comm-item .info p.vip img {
  width: 14px;
  vertical-align: baseline;
  margin-left: 5px;
}
.comm-item .info p.time {
  color: #666;
  font-size: 14px;
  display: flex;
  align-items: center;
}
​
.comm-item .info .iconfont {
  margin-left: 20px;
  position: relative;
  top: 1px;
  cursor: pointer;
}
.comm-item .info .iconfont.icon-collect-sel {
  color: #ff008c;
}
.comm-item .info .del {
  margin-left: 20px;
  cursor: pointer;
}
.comm-item .info .del:hover {
  color: #ccc;
}
​
.comm-input {
  border-radius: 6px;
  padding: 18px;
  background-color: #25252b;
}
.comm-input textarea {
  border: 0;
  outline: 0;
  resize: none;
  background: transparent;
  color: #999;
  width: 100%;
  font-family: inherit;
  height: auto;
  overflow: auto;
}
.comm-input .foot {
  display: flex;
  justify-content: flex-end;
  justify-items: center;
}
.comm-input .foot .word {
  line-height: 36px;
  margin-right: 10px;
  color: #999;
}
.comm-input .foot .btn {
  background-color: #ff008c;
  font-size: 14px;
  color: #fff;
  line-height: 36px;
  text-align: center;
  border-radius: 18px;
  padding: 0 24px;
  cursor: pointer;
  user-select: none;
} 
// src/index.js
import ReactDOM from "react-dom/client";
import Comment from "./Comment";
​
let root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Comment />); 

2.渲染评论列表

// src/Comment.js
import React from "react";
import "./comment.css";
import dateformat from "dateformat";
import classNames from "classnames";
​
export default class Comment extends React.Component {
  state = {
    //当前用户
    // 当前用户
    user: {
      name: "清风徐来",
      vip: true,
      avatar: "https://static.youku.com/lvip/img/avatar/310/6.png",
  },
    // 评论列表
    comments: [
    {
        id: 100,
        name: "__RichMan",
        avatar: "https://r1.ykimg.com/051000005BB36AF28B6EE4050F0E3BA6",
        content:
          "这阵容我喜欢😍靳东&闫妮,就这俩名字,我就知道是良心剧集...锁了🔒",
        time: new Date("2022-10-12T10:10:23"),
        vip: true,
        collect: false,
    },
    {
        id: 101,
        name: "糖蜜甜筒颖",
        avatar:
          "https://image.9xsecndns.cn/image/uicon/712b2bbec5b58d6066aff202c9402abc3370674052733b.jpg",
        content:
          "突围神仙阵容 人民的名义第三部来了 靳东陈晓闫妮秦岚等众多优秀演员实力派 守护人民的财产 再现国家企业发展历程",
        time: new Date("2022-09-23T15:12:44"),
        vip: false,
        collect: true,
    },
    {
        id: 102,
        name: "清风徐来",
        avatar: "https://static.youku.com/lvip/img/avatar/310/6.png",
        content:
          "第一集看的有点费力,投入不了,闫妮不太适合啊,职场的人哪有那么多表情,一点职场的感觉都没有",
        time: new Date("2022-07-01T00:30:51"),
        vip: true,
        collect: false,
    },
  ],};
  render() {
    return (
      <div className="comments">
        <h3 className="comm-head">评论</h3>
        <div className="comm-input">
          <textarea placeholder="爱发评论的人,运气都很棒"></textarea>
          <div className="foot">
            <div className="word">0/100</div>
            <div className="btn">发表评论</div>
          </div>
        </div>
        <h3 className="comm-head">
        热门评论<sub>({this.state.comments.length})</sub>
          <span className="active">默认</span>
          <span>时间</span>
        </h3>
        <ul className="comm-list">
        {this.state.comments.map((item, index) => (
            <li className="comm-item">
              <div
                className="avatar"
                style={{ backgroundImage: `url(${item.avatar})` }}
              ></div>
              <div className="info">
                <p className="name vip">
                {item.name}
                {item.vip ? (
                    <img
                      alt=""
                      src="https://gw.alicdn.com/tfs/TB1c5JFbGSs3KVjSZPiXXcsiVXa-48-48.png"
                    />
                ) : null}
                </p>
                <p className="time">
                {dateformat(item.time, "yyyy-mm-dd")}
                {/* 未收藏: icon-collect 已收藏: icon-collect-sel */}
                  <span
                    className={classNames([
                      "iconfont",
                    {
                        "icon-collect-sel": item.collect,
                        "icon-collect": !item.collect,
                    },
                  ])}
                  ></span>
                {item.name === this.state.user.name ? (
                    <span className="del">删除</span>
                ) : null}
                </p>
                <p>{item.content}</p>
              </div>
            </li>
        ))}
        </ul>
      </div>
  );}
}
​ 

3. 发表评论

目标:完成发表评论功能,对用户输入的评论内容字数进行限制。

(1) 将文本域组件更改为受控组件

// src/Comment.js
class Comment extends Component {
  // 构造函数
  constructor() {
    super();
    // 更改事件处理函数的 this 指向
    this.updateContent = this.updateContent.bind(this);}  // 组件状态
  state = {
    // 用户输入的评论内容
    content: "",};
​
  // 同步用户在文本域中输入的状态
  updateContent(event) {
    this.setState({ content: event.target.value});}
​
  render() {
    // 将 value 属性和组件状态 content 进行绑定
    // 添加 onChange 事件用于更新组件状态
    return <textarea value={this.state.content} onChange={this.updateContent}></textarea>;}
} 

(2) 展示用户输入的内容的数量并对数量进行限制

// src/Comment.js
class Comment extends Component {
  // 同步用户在文本域中输入的状态
  updateContent(event) {
    // 获取用户在文本域中输入的内容
    const value = event.target.value;
    // 如果内容长度大于100, 阻止程序继承执行
    if (value.length > 100) return;
    // 内容符合长度要求, 设置组件状态
    this.setState({ content: value });}
​
  render() {
    return <div className="word">{this.state.content.length}/100</div>}
} 

(3) 实现发表评论、清空文本域

import { Component } from "react";
import dateFormat from "dateformat";
import "./comment.css";
​
class Comment extends Component {
  constructor() {
    // 将事件处理函数中的 this 指向组件实例
    this.publishComment = this.publishComment.bind(this);}
  
  // 发表评论
  publishComment() {
    // 如果用户没有输入评论内容, 阻止程序继续执行
    if (this.state.content.length === 0) return;
    // 更新组件状态
    this.setState({
      // 更新评论列表
      comments: [
      {
          id: Math.random(),
          content: this.state.content,
          ...this.state.user,
          collect: false,
          time: new Date().toDateString(),
      },
        ...this.state.comments,
    ],
      // 更新用户在文本域中输入的内容
      content: "",
  });}
​
  render() {
    return <div onClick={this.publishComment}>发表评论</div>;}
} 

4. 删除评论

目标:实现删除评论功能

class Comment extends Component {
  constructor() {// 将事件处理函数中的 this 指向组件实例
    this.deleteComment = this.deleteComment.bind(this);}
​
  // 删除评论
  deleteComment(id) {
    this.setState({
      comments: this.state.comments.filter((item) => item.id !== id),
  });}
​
  render() {
    return <span onClick={() => this.deleteComment(item.id)}>删除</span>;}
} 

5. 收藏评论

目标:实现收藏评论功能

import { Component } from "react";
import dateFormat from "dateformat";
import "./comment.css";
​
class Comment extends Component {
  constructor() {// 将事件处理函数中的 this 指向组件实例
    this.collectComment = this.collectComment.bind(this);}
​
  // 收藏评论
  collectComment(id) {
    this.setState({
      comments: this.state.comments.map((item) =>
        item.id === id ? { ...item, collect: !item.collect } : item
    ),
  });}
​
  render() {
    return <span onClick={() => this.collectComment(item.id)}></span>;}
} 

6. 评论排序

目标:实现评论列表排序功能

npm i lodash 
import orderBy from "lodash/orderBy";
​
class Comment extends React.Component {
  state = {
    // 排序: 按照 id 和 time 进行排序
    sortField: "id",};render() {
    return (
      <>
        <span
          className={classNames({ active: this.state.sortField === "id" })}
          onClick={() =>
            this.setState({
              sortField: "id",
              comments: orderBy(this.state.comments, ["id"], ["asc"]),
          })
        }
        >
        默认
        </span>
        <span
          className={classNames({ active: this.state.sortField === "time" })}
          onClick={() =>
            this.setState({
              sortField: "time",
              comments: orderBy(this.state.comments, ["time"], ["asc"]),
          })
        }
        >
        时间
        </span>
      </>
  );}
} 

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

Cadence PCB仿真使用Allegro PCB SI配置差分对的方法图文教程

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,配置方法3,总结1,概述 本文简单介绍使用Allegro PCB SI配置差分对的方法。 2,配置方法 第1步:打开待仿真的PCB文件,并确认软件为Allegro PCB SI 如果,打开软件不是Allegro PCB SI则可这样切换 执行Fil…

天下苦“个人公众号认证”久矣,吾闻今可

大家好&#xff0c;我是小悟 一看到个人公众号可以认证&#xff0c;便以迅雷不及掩耳之势准备资料&#xff0c;一顿操作猛如虎后&#xff0c;我的号终于认证啦。 看到别人的个人公众号有认证的&#xff0c;这两天我就在想要怎么才能认证&#xff0c;于是就去搜索相关的内容&am…

电子采购系统的优势是什么 常用的电子采购系统介绍

采购是企业发展中的重要环节之一。在企业采购流程中&#xff0c;并不是简单的完成买和卖就行了&#xff0c;这其中还会涉及到各个方面。例如&#xff0c;在企业采购活动中&#xff0c;常常会遇到供应商数据维护难&#xff0c;采购成本把控难&#xff0c;供应商筛选难等问题。而…

面对集中式缓存实现上的挑战,Redis交出的是何种答卷?聊聊Redis在分布式方面的能力设计

大家好&#xff0c;又见面了。 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容&#xff0c;将会通过系列专题&#xff0c;讲清楚缓存的方方面面。如果感兴趣&#xff0c;欢迎关注以获取后续更新。 在本专栏前面的文章中&#xff0c;我们介绍了各种本地缓存框…

数据结构与算法1—线性表

1. 线性表的定义 线性表L是n&#xff08;n≥0&#xff09;个具有相同属性的数据元素a1&#xff0c;a2&#xff0c;a3&#xff0c;…&#xff0c;an组成的有限序列&#xff0c;其中序列中元素的个数n称为线性表的长度。当n0时称为空表&#xff0c;即不含有任何元素。常常将非空…

express接口

文章目录什么是接口创建 API 路由模块编写 GET 接口编写 POST 接口完整代码CORS 跨域资源共享使用 CORS 中间件解决跨域问题实现 JSONP 接口什么是接口 API (Application Programming Interface&#xff0c;应用程序编程接口 ) 是一些预先定义的函数&#xff0c;目的是提供应用…

TCP/IP 网络模型有哪几层

备注&#xff1a;本文参考小林coding相关内容&#xff0c;侵权请联系作者删除 1.应用层 最上层的&#xff0c;也是我们能直接接触到的就是应用层&#xff08;Application Layer&#xff09;&#xff0c;我们电脑或手机使用的应用软件都是在应用层实现。那么&#xff0c;当两个…

高并发系统设计 -- 登录系统设计

同源策略 同源策略是一种安全策略。是游览器最核心最基本的安全功能。防止XSS&#xff0c;CSFR等攻击具体表现是游览器在执行脚本之前&#xff0c;会判断脚本是否与打开的网页是同源的&#xff0c;也就是协议&#xff0c;域名&#xff0c;端口是否都相同&#xff0c;相同就是同…

记录--前端性能监控初步实战

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前言 在当下前后端分离的主流环境下&#xff0c;前端部分的优化变得越来越重要。为了提升前端的性能和用户体验&#xff0c;我觉得可能需要从三个维度采集数据进行分析。 前端埋点。通过埋点收集和统计…

SCRM与CRM的区别

当私域流量的概念兴起时&#xff0c;企业直接触达用户的场景也越来越丰富&#xff0c;SCRM形式的私域运营已然成为很多企业数字化转型布局的关键阵地。 前言 当私域流量的概念兴起时&#xff0c;企业直接触达用户的场景也越来越丰富&#xff0c;SCRM形式的私域运营已然成为很多…

服务器部署所有前后端分离项目

1、Linux服务器安装好jdk、mysql、redis、node 2、拉取最新代码 gitee仓库项目地址&#xff1a;https://gitee.com/y_project/RuoYi-Vue 拉取代码到本地 3、修改后端配置 3.1、修改系统内上传文件位置的配置&#xff1a; 默认是windows系统的配置&#xff0c;将此配置修改…

JMeter接口测试使用教程哪里有?

JMeter接口测试使用教程哪里有&#xff1f;接口测试是测试工程师的必备技能之一&#xff0c;运用JMeter工具一步步实现接口请求&#xff0c;数据参数化&#xff0c;断言等操作&#xff0c;通过常见的企业实际测试场景详解JMeter各项技术使用&#xff0c;最后结合Jenkins持续集成…

Django搭建个人博客Blog-Day01

创建虚拟环境虚拟环境相当于一个抽屉&#xff0c;在这个抽屉里面安装的任何软件&#xff0c;都不会影响到其他抽屉&#xff0c;所以利用虚拟环境就可以做到同时安装不同版本的第三方库&#xff0c;而互不影响。使用虚拟环境的目的是为了防止一些第三方库出现版本不兼容问题&…

c++11 标准模板(STL)(std::forward_list)(五)

定义于头文件 <forward_list> template< class T, class Allocator std::allocator<T> > class forward_list;(1)(C11 起)namespace pmr { template <class T> using forward_list std::forward_list<T, std::pmr::polymorphic_…

当我们在谈论DataOps时,我们到底在谈论什么

1. DataOps到底是什么&#xff1f; 伴随着全球数字化转型的高速发展&#xff0c;在云计算、物联网、5G、边缘计算、元宇宙等新技术的驱动下&#xff0c;数据爆炸的时代已经来临。IDC Global DataSphere显示&#xff0c;2021年&#xff0c;全球数据总量达到了84.5ZB&#xff0c…

Java多线程案例——阻塞队列(生产者消费者模型)

一&#xff0c;阻塞队列1.阻塞队列的概念和作用阻塞队列同数据结构中的队列一样都遵守“先进先出”的原则&#xff08;不了解队列相关知识的朋友请查看之前队列的博文&#xff1a;(6条消息) 栈和队列&#xff08;内附模拟实现代码&#xff09;_徐憨憨&#xff01;的博客-CSDN博…

功率放大模块如何选择(安泰功率放大器模块产品介绍)

功率放大器模块系列产品介绍 一、功率放大模块介绍 功率放大模块&#xff1a; 功率放大模块具有体积小&#xff0c;集成度高&#xff0c;使用方便&#xff0c;应用广泛等优点&#xff0c;凭借着输出频率广、输出电压高、输出功率大等特性&#xff0c;能够广泛应用在各种领域…

动态范围控制原理

DRC介绍 开门见山&#xff0c;动态范围的定义就是信号的最大幅值和最小幅值比值的对数(单位dB)&#xff0c; 动态范围会受到系统中各个环节的影响。例如同样是这段音乐&#xff0c;在一个40dB背景噪声的环境中播放&#xff0c;那么由于掩蔽效应等因素的影响&#xff0c;最终实际…

前端跳转第三方网页中间页

前端跳转安全提示 掘金跳转中间页背景介绍跳转过渡页的优点实现原理解析哈喽啊小伙伴们久等了 消失了有半年了 &#xff0c;因为个人工作原因没腾出时间给大家分享日常踩坑和特殊功能的讲解。不过这次我回来了就要好好分享了背景介绍 前端小伙伴一定知道CSDN 和 稀土掘金 两大…

Dev-C++下载安装详细教程

文章目录前言一、下载Dev-C二、安装Dev-C三、使用Dev-C打印HelloWorld总结前言 本文总结了关于Dev-C下载与安装的详细过程&#xff0c;并使用Dev-C打印了“Hello World!”。本篇博客面向C语言初学者&#xff0c;或者考研复试的学生使用&#xff0c;因为大部分学校的考研复试都使…