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.pswp.cn/web/75946.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/75946.shtml
英文地址,請注明出處:http://en.pswp.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協議傳輸一些隱私信息也就非常不安全&…