React 更新 state 中的数组

更新 state 中的数组

数组是另外一种可以存储在 state 中的 JavaScript 对象,它虽然是可变的,但是却应该被视为不可变。同对象一样,当你想要更新存储于 state 中的数组时,你需要创建一个新的数组(或者创建一份已有数组的拷贝值),并使用新数组设置 state。

开发环境:React+ts+antd
文中例子参考React官方文档教程,不同的是这里使用TypeScript 来写

学习内容

  • 如何添加、删除或者修改 React state 中的数组中的元素
  • 如何更新数组内部的对象
  • 如何通过 Immer 降低数组拷贝的重复度

在没有 mutation 的前提下更新数组

在 JavaScript 中,数组只是另一种对象。同对象一样,你需要将 React state 中的数组视为只读的。这意味着你不应该使用类似于 arr[0] = ‘bird’ 这样的方式来重新分配数组中的元素,也不应该使用会直接修改原始数组的方法,例如 push() 和 pop()。

相反,每次要更新一个数组时,你需要把一个新的数组传入 state 的 setting 方法中。为此,你可以通过使用像 filter() 和 map() 这样不会直接修改原始值的方法,从原始数组生成一个新的数组。然后你就可以将 state 设置为这个新生成的数组。

下面是常见数组操作的参考表。当你操作 React state 中的数组时,你需要避免使用左列的方法,而首选右列的方法:

避免使用 (会改变原始数组)推荐使用 (会返回一个新数组)
添加元素push,unshiftconcat,[…arr] 展开语法
删除元素pop,shift,splicefilter,slice
替换元素splice,arr[i] = … 赋值map
排序reverse,sort先将数组复制一份

或者,你可以使用 Immer ,这样你便可以使用表格中的所有方法了。

向数组中添加元素

创建一个 数组,其包含了原始数组的所有元素 以及 一个在末尾的新元素。这可以通过很多种方法实现,最简单的一种就是使用 … 数组展开 语法:

setArtists( // 替换 state[ // 是通过传入一个新数组实现的...artists, // 新数组包含原数组的所有元素{ id: nextId++, name: name } // 并在末尾添加了一个新的元素]
);

例子

import React from 'react';
import { useState } from 'react';
import { Input, Button } from 'antd';
// 定义 Artist 类型
type Artist = {id: number;name: string;
};
const App: React.FC = () => {let nextId = 0;const [name, setName] = useState<string>('');const [artists, setArtists] = useState<Artist[]>([]);return (<><h1>添加列表:</h1><Inputstyle={{ width: '200px', marginRight: '10px' }}value={name}onChange={e => setName(e.target.value)}/><Buttoncolor="blue"variant="solid"onClick={() => {setArtists([...artists,{id: nextId++, name: name}]);}}>添加</Button><ul>{artists.map(artist => (<li key={artist.id}>{artist.name}</li>))}</ul></>);
};export default App;

在这里插入图片描述
数组展开运算符还允许你把新添加的元素放在原始的 …artists 之前:

setArtists([{ id: nextId++, name: name },...artists // 将原数组中的元素放在末尾
]);

这样一来,展开操作就可以完成 push() 和 unshift() 的工作,将新元素添加到数组的末尾和开头。

从数组中删除元素

从数组中删除一个元素最简单的方法就是将它过滤出去。换句话说,你需要生成一个不包含该元素的新数组。这可以通过 filter 方法实现,例如:

import React from 'react';
import { useState } from 'react';
let initialArtists = [{ id: 0, name: 'Marta Colvin Andrade' },{ id: 1, name: 'Lamidi Olonade Fakeye'},{ id: 2, name: 'Louise Nevelson'},
];
const App: React.FC = () => {const [artists, setArtists] = useState(initialArtists);return (<><h1>删除列表:</h1><ul>{artists.map(artist => (<li key={artist.id}>{artist.name}{' '}<button onClick={() => {setArtists(artists.filter(a =>a.id !== artist.id));}}>删除</button></li>))}</ul></>);
};export default App;

在这里插入图片描述
点击“删除”按钮几次,并且查看按钮处理点击事件的代码。

setArtists(artists.filter(a => a.id !== artist.id)
);

这里,artists.filter(s => s.id !== artist.id) 表示“创建一个新的数组,该数组由那些 ID 与 artists.id 不同的 artists 组成”。换句话说,每个 artist 的“删除”按钮会把 那一个 artist 从原始数组中过滤掉,并使用过滤后的数组再次进行渲染。

注意,filter 并不会改变原始数组

转换数组

如果你想改变数组中的某些或全部元素,你可以用 map() 创建一个新数组。你传入 map 的函数决定了要根据每个元素的值或索引(或二者都要)对元素做何处理。

在下面的例子中,一个数组记录了两个圆形和一个正方形的坐标。当你点击按钮时,仅有两个圆形会向下移动 50 像素。这是通过使用 map() 生成一个新数组实现的。

import React from 'react';
import { useState } from 'react';
import {Button} from "antd";let initialShapes = [{ id: 0, type: 'circle', x: 0, y: 100 },{ id: 1, type: 'square', x: 100, y: 100 },{ id: 2, type: 'circle', x: 200, y: 100 },
];
const App: React.FC = () => {const [shapes, setShapes] = useState(initialShapes);const handleClick=()=> {const nextShapes = shapes.map(shape => {if (shape.type === 'square') {// 不作改变return shape;} else {// 返回一个新的圆形,位置在下方 50px 处return {...shape,y: shape.y + 50,};}});// 使用新的数组进行重渲染setShapes(nextShapes);}return (<><Button type='primary' onClick={handleClick}>所有圆形向下移动!</Button><div style={{position:"relative"}}>{shapes.map(shape => (<divkey={shape.id}style={{background: 'purple',position: 'absolute',left: shape.x,top: shape.y,borderRadius:shape.type === 'circle'? '50%' : '',width: 20,height: 20,}} />))}</div></>);
};export default App;

在这里插入图片描述

点击按钮后:
在这里插入图片描述

替换数组中的元素

想要替换数组中一个或多个元素是非常常见的。类似 arr[0] = ‘bird’ 这样的赋值语句会直接修改原始数组,所以在这种情况下,你也应该使用 map。

要替换一个元素,请使用 map 创建一个新数组。在你的 map 回调里,第二个参数是元素的索引。使用索引来判断最终是返回原始的元素(即回调的第一个参数)还是替换成其他值:

import React from 'react';
import { useState } from 'react';
import {Button} from "antd";let initialCounters = [0, 0, 0
];
const App: React.FC = () => {const [counters, setCounters] = useState(initialCounters);const handleIncrementClick=(index:number)=> {const nextCounters = counters.map((c, i) => {if (i === index) {// 递增被点击的计数器数值return c + 1;} else {// 其余部分不发生变化return c;}});setCounters(nextCounters);}return (<><ul>{counters.map((counter, i) => (<li key={i} style={{marginBottom:"20px"}}><span style={{marginRight:"20px"}}>{counter}</span><Button type="primary" onClick={() => {handleIncrementClick(i);}}>+1</Button></li>))}</ul></>);
};export default App;

在这里插入图片描述

向数组中插入元素

有时,你也许想向数组特定位置插入一个元素,这个位置既不在数组开头,也不在末尾。为此,你可以将数组展开运算符 … 和 slice() 方法一起使用。slice() 方法让你从数组中切出“一片”。为了将元素插入数组,你需要先展开原数组在插入点之前的切片,然后插入新元素,最后展开原数组中剩下的部分。

下面的例子中,插入按钮总是会将元素插入到数组中索引为 1 的位置。

import React from 'react';
import { useState } from 'react';
import {Button,Input} from "antd";let nextId = 3;
const initialArtists = [{ id: 0, name: '清明上河图密码' },{ id: 1, name: '雪迷宫'},{ id: 2, name: '白夜行'},
];
const App: React.FC = () => {const [name, setName] = useState('');const [artists, setArtists] = useState(initialArtists);const handleClick=()=> {const insertAt = 1; // 可能是任何索引const nextArtists = [// 插入点之前的元素:...artists.slice(0, insertAt),// 新的元素:{ id: nextId++, name: name },// 插入点之后的元素:...artists.slice(insertAt)];setArtists(nextArtists);setName('');}return (<><h1>添加列表:</h1><Inputstyle={{width:"200px",marginRight:"10px"}}value={name}onChange={e => setName(e.target.value)}/><Button type="primary" onClick={handleClick}>插入</Button><ul>{artists.map(artist => (<li key={artist.id}>{artist.name}</li>))}</ul></>);
};export default App;

在这里插入图片描述

其他改变数组的情况

总会有一些事,是你仅仅依靠展开运算符和 map() 或者 filter() 等不会直接修改原值的方法所无法做到的。例如,你可能想翻转数组,或是对数组排序。而 JavaScript 中的 reverse() 和 sort() 方法会改变原数组,所以你无法直接使用它们。

然而,你可以先拷贝这个数组,再改变这个拷贝后的值。

例如:

import React from 'react';
import { useState } from 'react';
import {Button} from "antd";const initialList = [{ id: 0, title: 'Big Bellies' },{ id: 1, title: 'Lunar Landscape' },{ id: 2, title: 'Terracotta Army' },
];
const App: React.FC = () => {const [list, setList] = useState(initialList);const handleClick=()=> {const nextList = [...list];nextList.reverse();setList(nextList);}return (<><Button type="primary" onClick={handleClick}>翻转</Button><ul>{list.map(artwork => (<li key={artwork.id}>{artwork.title}</li>))}</ul></>);
};export default App;

在这里插入图片描述

点击翻转后

在这里插入图片描述
在这段代码中,你先使用 […list] 展开运算符创建了一份数组的拷贝值。当你有了这个拷贝值后,你就可以使用像 nextList.reverse() 或 nextList.sort() 这样直接修改原数组的方法。你甚至可以通过 nextList[0] = “something” 这样的方式对数组中的特定元素进行赋值。

然而,即使你拷贝了数组,你还是不能直接修改其内部的元素。这是因为数组的拷贝是浅拷贝——新的数组中依然保留了与原始数组相同的元素。因此,如果你修改了拷贝数组内部的某个对象,其实你正在直接修改当前的 state。举个例子,像下面的代码就会带来问题。

const nextList = [...list];
nextList[0].seen = true; // 问题:直接修改了 list[0] 的值
setList(nextList);

虽然 nextList 和 list 是两个不同的数组,nextList[0] 和 list[0] 却指向了同一个对象。因此,通过改变 nextList[0].seen,list[0].seen 的值也被改变了。这是一种 state 的 mutation 操作,你应该避免这么做!你可以用类似于 更新嵌套的 JavaScript 对象 的方式解决这个问题——拷贝想要修改的特定元素,而不是直接修改它。下面是具体的操作。

更新数组内部的对象

对象并不是 真的 位于数组“内部”。可能他们在代码中看起来像是在数组“内部”,但其实数组中的每个对象都是这个数组“指向”的一个存储于其它位置的值。这就是当你在处理类似 list[0] 这样的嵌套字段时需要格外小心的原因。其他人的艺术品清单可能指向了数组的同一个元素!

当你更新一个嵌套的 state 时,你需要从想要更新的地方创建拷贝值,一直这样,直到顶层。 让我们看一下这该怎么做。

在下面的例子中,两个不同的艺术品清单有着相同的初始 state。他们本应该互不影响,但是因为一次 mutation,他们的 state 被意外地共享了,勾选一个清单中的事项会影响另外一个清单:

import React, { useState } from 'react';// 定义 Artwork 类型
type Artwork = {id: number;title: string;seen: boolean;
};// 定义初始列表
const initialList: Artwork[] = [{ id: 0, title: 'Big Bellies', seen: false },{ id: 1, title: 'Lunar Landscape', seen: false },{ id: 2, title: 'Terracotta Army', seen: true },
];// 定义 App 组件
const App: React.FC = () => {const [myList, setMyList] = useState<Artwork[]>(initialList);const [yourList, setYourList] = useState<Artwork[]>(initialList);const handleToggleMyList = (artworkId: number, nextSeen: boolean) => {const myNextList = [...myList];const artwork = myNextList.find(a => a.id === artworkId);if (artwork) {artwork.seen = nextSeen;setMyList(myNextList);}};const handleToggleYourList = (artworkId: number, nextSeen: boolean) => {const yourNextList = [...yourList];const artwork = yourNextList.find(a => a.id === artworkId);if (artwork) {artwork.seen = nextSeen;setYourList(yourNextList);}};return (<><h1>艺术愿望清单</h1><h2>我想看的艺术清单:</h2><ItemList artworks={myList} onToggle={handleToggleMyList} /><h2>你想看的艺术清单:</h2><ItemList artworks={yourList} onToggle={handleToggleYourList} /></>);
};// 定义 ItemList 组件的 props 类型
type ItemListProps = {artworks: Artwork[];onToggle: (artworkId: number, nextSeen: boolean) => void;
};// 定义 ItemList 组件
const ItemList: React.FC<ItemListProps> = ({ artworks, onToggle }) => {return (<ul>{artworks.map((artwork) => (<li key={artwork.id}><label><inputtype="checkbox"checked={artwork.seen}onChange={(e) => {onToggle(artwork.id, e.target.checked);}}/>{artwork.title}</label></li>))}</ul>);
};export default App;

在这里插入图片描述

问题出在下面这段代码中:

 const myNextList = [...myList];const artwork = myNextList.find(a => a.id === artworkId);if (artwork) {artwork.seen = nextSeen;setMyList(myNextList);}

虽然 myNextList 这个数组是新的,但是其内部的元素本身与原数组 myList 是相同的。因此,修改 artwork.seen,其实是在修改原始的 artwork 对象。而这个 artwork 对象也被 yourList 使用,这样就带来了 bug。这样的 bug 可能难以想到,但好在如果你避免直接修改 state,它们就会消失。

你可以使用 map 在没有 mutation 的前提下将一个旧的元素替换成更新的版本。

setMyList(myList.map(artwork => {if (artwork.id === artworkId) {// 创建包含变更的*新*对象return { ...artwork, seen: nextSeen };} else {// 没有变更return artwork;}
}));

此处的 … 是一个对象展开语法,被用来创建一个对象的拷贝.

通过这种方式,没有任何现有的 state 中的元素会被改变,bug 也就被修复了。

import React, { useState } from'react';// 定义 Artwork 类型,描述艺术作品的结构
type Artwork = {id: number;title: string;seen: boolean;
};// 定义初始艺术作品列表
const initialList: Artwork[] = [{ id: 0, title: 'Big Bellies', seen: false },{ id: 1, title: 'Lunar Landscape', seen: false },{ id: 2, title: 'Terracotta Army', seen: true },
];// 定义 BucketList 组件的类型
type BucketListProps = {};// BucketList 组件
const App: React.FC = (props: BucketListProps) => {const [myList, setMyList] = useState<Artwork[]>(initialList);const [yourList, setYourList] = useState<Artwork[]>(initialList);// 处理 myList 中艺术作品状态切换的函数const handleToggleMyList=(artworkId: number, nextSeen: boolean)=> {setMyList(myList.map((artwork: Artwork) => {if (artwork.id === artworkId) {// 创建包含变更的*新*对象return { ...artwork, seen: nextSeen };} else {// 没有变更return artwork;}}));}// 处理 yourList 中艺术作品状态切换的函数const handleToggleYourList=(artworkId: number, nextSeen: boolean)=> {setYourList(yourList.map((artwork: Artwork) => {if (artwork.id === artworkId) {// 创建包含变更的*新*对象return { ...artwork, seen: nextSeen };} else {// 没有变更return artwork;}}));}return (<><h1>艺术愿望清单</h1><h2>我想看的艺术清单:</h2><ItemListartworks={myList}onToggle={handleToggleMyList} /><h2>你想看的艺术清单:</h2><ItemListartworks={yourList}onToggle={handleToggleYourList} /></>);
}
export default App// 定义 ItemList 组件的 props 类型
type ItemListProps = {artworks: Artwork[];onToggle: (artworkId: number, nextSeen: boolean) => void;
};// ItemList 组件
const ItemList=(props: ItemListProps)=>{return (<ul>{props.artworks.map((artwork: Artwork) => (<li key={artwork.id}><label><inputtype="checkbox"checked={artwork.seen}onChange={(e: React.ChangeEvent<HTMLInputElement>) => {props.onToggle(artwork.id,e.target.checked);}}/>{artwork.title}</label></li>))}</ul>);
}

在这里插入图片描述

通常来讲,你应该只直接修改你刚刚创建的对象。如果你正在插入一个新的 artwork,你可以修改它,但是如果你想要改变的是 state 中已经存在的东西,你就需要先拷贝一份了。

使用 Immer 编写简洁的更新逻辑

在没有 mutation 的前提下更新嵌套数组可能会变得有点重复。就像对对象一样:

通常情况下,你应该不需要更新处于非常深层级的 state 。如果你有此类需求,你或许需要调整一下数据的结构,让数据变得扁平一些。
如果你不想改变 state 的数据结构,你也许会更喜欢使用 Immer ,它让你可以继续使用方便的,但会直接修改原值的语法,并负责为你生成拷贝值。
下面是我们用 Immer 来重写的艺术愿望清单的例子:

import React from "react";
import { useImmer } from 'use-immer';
// 定义 Artwork 类型
type Artwork = {id: number;title: string;seen: boolean;
};// 定义初始列表
const initialList: Artwork[] = [{ id: 0, title: 'Big Bellies', seen: false },{ id: 1, title: 'Lunar Landscape', seen: false },{ id: 2, title: 'Terracotta Army', seen: true },
];// 定义 BucketList 组件
const BucketList:React.FC=()=> {const [myList, updateMyList] = useImmer<Artwork[]>(initialList);const [yourList, updateYourList] = useImmer<Artwork[]>(initialList);// 处理我的列表中项目的切换const handleToggleMyList=(id: number, nextSeen: boolean)=> {updateMyList((draft) => {const artwork = draft.find((a) => a.id === id);if (artwork) {artwork.seen = nextSeen;}});}// 处理你的列表中项目的切换const handleToggleYourList=(artworkId: number, nextSeen: boolean)=> {updateYourList((draft) => {const artwork = draft.find((a) => a.id === artworkId);if (artwork) {artwork.seen = nextSeen;}});}return (<><h1>艺术愿望清单</h1><h2>我想看的艺术清单:</h2><ItemList artworks={myList} onToggle={handleToggleMyList} /><h2>你想看的艺术清单:</h2><ItemList artworks={yourList} onToggle={handleToggleYourList} /></>);
}export default BucketList// 定义 ItemList 组件的 props 类型
type ItemListProps = {artworks: Artwork[];onToggle: (id: number, nextSeen: boolean) => void;
};// 定义 ItemList 组件
const ItemList=({ artworks, onToggle }: ItemListProps) =>{return (<ul>{artworks.map((artwork: Artwork) => (<li key={artwork.id}><label><inputtype="checkbox"checked={artwork.seen}onChange={(e: React.ChangeEvent<HTMLInputElement>) => {onToggle(artwork.id, e.target.checked);}}/>{artwork.title}</label></li>))}</ul>);
}

在这里插入图片描述
请注意当使用 Immer 时,类似 artwork.seen = nextSeen 这种会产生 mutation 的语法不会再有任何问题了:

updateMyTodos(draft => {const artwork = draft.find(a => a.id === artworkId);artwork.seen = nextSeen;
});

这是因为你并不是在直接修改原始的 state,而是在修改 Immer 提供的一个特殊的 draft 对象。同理,你也可以为 draft 的内容使用 push() 和 pop() 这些会直接修改原值的方法。

在幕后,Immer 总是会根据你对 draft 的修改来从头开始构建下一个 state。这使得你的事件处理程序非常的简洁,同时也不会直接修改 state。

摘要

  • 你可以把数组放入 state 中,但你不应该直接修改它。
  • 不要直接修改数组,而是创建它的一份 新的 拷贝,然后使用新的数组来更新它的状态。
  • 你可以使用 […arr, newItem] 这样的数组展开语法来向数组中添加元素。
  • 你可以使用 filter() 和 map() 来创建一个经过过滤或者变换的数组。
  • 你可以使用 Immer 来保持代码简洁。

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

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

相关文章

java -jar与java -cp的区别

java -jar与java -cp 1、情景描述2、情景分析3、两者区别 通常情况下&#xff0c;我们会看到以下两种命令启动的Java程序&#xff1a; java -jar xxx.jar [args] java -cp xxx.jar mainclass [args]这两种用法有什么区别呢&#xff1f; 1、情景描述 1&#xff09;Java打包单个…

【Java】面向对象程序三板斧——如何优雅设计包、封装数据与优化代码块?

&#x1f381;个人主页&#xff1a;User_芊芊君子 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 &#x1f50d;系列专栏&#xff1a;【Java】内容概括 【前言】 在Java编程中&#xff0c;类和对象是面向对象编程的核心概念。而包&#xff08;Package&am…

玩转Docker | 使用Docker搭建Blog微博系统

玩转Docker | 使用Docker搭建Blog微博系统 前言一、Blog介绍项目简介主要特点二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署Blog服务下载镜像创建容器检查容器状态设置权限检查服务端口安全设置四、访问Blog系统访问Blog首页登录Blog五、总结前言 在数字…

用Java NIO模拟HTTPS

HTTPS流程 名词解释&#xff1a; R1:随机数1 R2:随机数2 R3:随机数3 publicKey:公钥 privateKey:私钥 要提供https服务&#xff0c;服务端需要安装数字证书&#xff0c;在&#xff08;TCP建立连接之后&#xff09;TLS握手时发给客户端&#xff0c;客户端验证证书&#x…

树莓派_利用Ubuntu搭建gitlab

树莓派_利用Ubuntu搭建gitlab 一、给树莓派3A搭建基本系统 1、下载系统镜像 https://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ 2、准备系统SD卡 二、给树莓派设备联网 1、串口后台登录 使用串口登录后台是最便捷的&#xff0c;因为前期网络可能不好直接成功 默…

Hook_Unfinished

#include <windows.h>// 假设这两个函数是存在的 void DoRD() {} void 改堆栈cal1() {} void 改回堆栈cal1() {}__declspec(naked) void HOOKcall() {__asm{pushadnop}__asm{popadmov eax, dword ptr [esi 8]sub eax, ecxretn} }int main() {// 第一个 Hook 操作DWORD H…

数据结构(六)——红黑树及模拟实现

目录 前言 红黑树的概念及性质 红黑树的效率 红黑树的结构 红黑树的插入 变色不旋转 单旋变色 双旋变色 插入代码如下所示&#xff1a; 红黑树的查找 红黑树的验证 红黑树代码如下所示&#xff1a; 小结 前言 在前面的文章我们介绍了AVL这一棵完全二叉搜索树&…

c# 数据结构 链表篇 有关双向链表的一切

本人能力有限,如有不足还请斧正 目录 0.双向链表的好处 1.双向链表的分类 2.不带头节点的标准双向链表 节点类:有头有尾 链表类:也可以有头有尾 也可以只有头 增 头插 尾插 删 查 改 遍历 全部代码 3.循环双向链表 节点类 链表类 增 头插 尾插 删 查 遍历…

Numba 从零基础到实战:解锁 Python 性能新境界

Numba 从零基础到实战&#xff1a;解锁 Python 性能新境界 一、引言 在 Python 的世界里&#xff0c;性能一直是一个备受关注的话题。Python 以其简洁易读的语法和丰富的库生态&#xff0c;深受开发者喜爱&#xff0c;但在处理一些计算密集型任务时&#xff0c;其执行速度往往…

单位门户网站被攻击后的安全防护策略

政府网站安全现状与挑战 近年来&#xff0c;随着数字化进程的加速&#xff0c;政府门户网站已成为政务公开和服务公众的重要窗口。然而&#xff0c;网络安全形势却日益严峻。国家互联网应急中心的数据显示&#xff0c;政府网站已成为黑客攻击的重点目标&#xff0c;被篡改和被…

Spring Boot 项目三种打印日志的方法详解。Logger,log,logger 解读。

目录 一. 打印日志的常见三种方法&#xff1f; 1.1 手动创建 Logger 对象&#xff08;基于SLF4J API&#xff09; 1.2 使用 Lombok 插件的 Slf4j 注解 1.3 使用 Spring 的 Log 接口&#xff08;使用频率较低&#xff09; 二. 常见的 Logger&#xff0c;logger&#xff0c;…

NI的LABVIEW工具安装及卸载步骤说明

一.介绍 最近接到个转交的项目&#xff0c;项目主要作为上位机工具开发&#xff0c;在对接下位机时&#xff0c;有用到NI的labview工具。labview软件是由美国国家仪器&#xff08;NI&#xff09;公司研制开发的一种程序开发环境&#xff0c;主要用于汽车测试、数据采集、芯片测…

cmd 终端输出乱码问题 |Visual Studio 控制台输出中文乱码解决

在网上下载&#xff0c;或者移植别人的代码到自己的电脑&#xff0c;使用VS运行后&#xff0c;控制台输出中文可能出现乱码。这是因为源代码的编码格式和控制台的编码格式不一致。 文章目录 查看源代码文件编码格式查看输出控制台编码格式修改编码格式修改终端代码页 补充总结 …

A009-基于pytest的网易云自动化测试

题 目 :基于pytest的网易云自动化测试 主要内容 综合应用所学的软件测试理论和方法,实现网易云的功能自动化测试。 (1)自动化测试介绍; (2)自动化功能测试框架介绍; (3)设计功能测试用例 (4)书写自动化测试脚本; (5)测试评价与结论。 任务要求 (1)能…

LVGL Video控件和Radiobtn控件详解

LVGL Video控件和Radiobtn控件详解 一、 Video控件详解1. 概述2. 创建和初始化3. 基本属性设置4. 视频控制5. 回调函数6. 高级功能7. 注意事项 二、Radiobtn控件详解1. 概述2. 创建和初始化3. 属性设置4. 状态控制5. 组管理6. 事件处理7. 样式设置8. 注意事项 三、效果展示四、…

AbortController:让异步操作随时说停就停

AbortController&#xff1a;让异步操作随时说停就停 一、什么是 AbortController&#xff1f; AbortController 是 JavaScript 在浏览器和部分 Node.js 环境中提供的全局类&#xff0c;用来中止正在进行或待完成的异步操作&#xff08;如 fetch() 请求、事件监听、可写流、数…

机器学习 从入门到精通 day_04

1. 决策树-分类 1.1 概念 1. 决策节点 通过条件判断而进行分支选择的节点。如&#xff1a;将某个样本中的属性值(特征值)与决策节点上的值进行比较&#xff0c;从而判断它的流向。 2. 叶子节点 没有子节点的节点&#xff0c;表示最终的决策结果。 3. 决策树的…

C++ Primer (第五版)-第十三章 拷贝控制

文章目录 概述13.1拷贝、赋值与销毁合成拷贝构造函数拷贝初始化参数和返回值拷贝初始化的限制编译器可以绕过拷贝构造函数拷贝运算符析构函数三/五原则使用default阻止拷贝合成的拷贝控制成员可能是删除的 private拷贝控制拷贝控制和资源管理行为像值的类类值拷贝赋值运算符定义…

Vue el-from的el-form-item v-for循环表单如何校验rules(一)

实际业务需求场景&#xff1a; 新增或编辑页面&#xff08;基础信息表单&#xff0c;一个数据列表的表单&#xff09;&#xff0c;数据列表里面的表单数是动态添加的。数据可新增、可删除&#xff0c;在表单保存前&#xff0c;常常需要做表单必填项的校验&#xff0c;校验通过以…

测试100问:http和https的区别是什么?

哈喽&#xff0c;大家好&#xff0c;我是十二&#xff0c;今天给大家分享的问题是&#xff1a;http和https的区别是什么&#xff1f; 首先我们要知道 HTTP 协议传播的数据都是未加密的&#xff0c;也就是明文的&#xff0c;因此呢使用 http协议传输一些隐私信息也就非常不安全&…