当前位置: 首页 > news >正文

React 18 选择 State 结构

参考文章

选择 State 结构

构建良好的 state 可以让组件变得易于修改和调试,而不会经常出错。以下是在构建 state 时应该考虑的一些建议。

构建 state 的原则

当编写一个存有 state 的组件时,需要选择使用多少个 state 变量以及它们都是怎样的数据格式。尽管选择次优的 state 结构下也可以编写正确的程序,但有几个原则可以指导做出更好的决策:

  1. 合并关联的 state。如果总是同时更新两个或更多的 state 变量,请考虑将它们合并为一个单独的 state 变量。
  2. 避免互相矛盾的 state。当 state 结构中存在多个相互矛盾或“不一致”的 state 时,就可能为此会留下隐患。应尽量避免这种情况。
  3. 避免冗余的 state。如果能在渲染期间从组件的 props 或其现有的 state 变量中计算出一些信息,则不应将这些信息放入该组件的 state 中。
  4. 避免重复的 state。当同一数据在多个 state 变量之间或在多个嵌套对象中重复时,这会很难保持它们同步。应尽可能减少重复。
  5. 避免深度嵌套的 state。深度分层的 state 更新起来不是很方便。如果可能的话,最好以扁平化方式构建 state。

这些原则背后的目标是 使 state 易于更新而不引入错误。从 state 中删除冗余和重复数据有助于确保所有部分保持同步。这类似于数据库工程师想要 “规范化”数据库结构,以减少出现错误的机会。用爱因斯坦的话说,“让你的状态尽可能简单,但不要过于简单。”

现在让我们来看看这些原则在实际中是如何应用的。

合并关联的 state

有时候可能会不确定是使用单个 state 变量还是多个 state 变量。

你会像下面这样做吗?

const [x, setX] = useState(0);
const [y, setY] = useState(0);

或这样?

const [position, setPosition] = useState({ x: 0, y: 0 });

从技术上讲,可以使用其中任何一种方法。但是,如果某两个 state 变量总是一起变化,则将它们统一成一个 state 变量可能更好。这样就不会忘记让它们始终保持同步,就像下面这个例子中,移动光标会同时更新红点的两个坐标:

import { useState } from 'react';export default function MovingDot() {const [position, setPosition] = useState({x: 0,y: 0});return (<divonPointerMove={e => {setPosition({x: e.clientX,y: e.clientY});}}style={{position: 'relative',width: '100vw',height: '100vh',}}><div style={{position: 'absolute',backgroundColor: 'red',borderRadius: '50%',transform: `translate(${position.x}px, ${position.y}px)`,left: -10,top: -10,width: 20,height: 20,}} /></div>)
}

另一种情况是,将数据整合到一个对象或一个数组中时,不知道需要多少个 state 片段。例如,当有一个用户可以添加自定义字段的表单时,这将会很有帮助。

注意:如果 state 变量是一个对象时,请记住,不能只更新其中的一个字段 而不显式复制其他字段。例如,在上面的例子中,不能写成 setPosition({ x: 100 }),因为它根本就没有 y 属性! 相反,如果想要仅设置 x,则可执行 setPosition({ ...position, x: 100 }),或将它们分成两个 state 变量,并执行 setX(100)

避免矛盾的 state

下面是带有 isSendingisSent 两个 state 变量的酒店反馈表单:

import { useState } from 'react';export default function FeedbackForm() {const [text, setText] = useState('');const [isSending, setIsSending] = useState(false);const [isSent, setIsSent] = useState(false);async function handleSubmit(e) {e.preventDefault();setIsSending(true);await sendMessage(text);setIsSending(false);setIsSent(true);}if (isSent) {return <h1>Thanks for feedback!</h1>}return (<form onSubmit={handleSubmit}><p>How was your stay at The Prancing Pony?</p><textareadisabled={isSending}value={text}onChange={e => setText(e.target.value)}/><br /><buttondisabled={isSending}type="submit">Send</button>{isSending && <p>Sending...</p>}</form>);
}// 假装发送一条消息。
function sendMessage(text) {return new Promise(resolve => {setTimeout(resolve, 2000);});
}

尽管这段代码是有效的,但也会让一些 state “极难处理”。例如,如果忘记同时调用 setIsSentsetIsSending,则可能会出现 isSendingisSent 同时为 true 的情况。组件越复杂,就越难理解发生了什么。

因为 isSendingisSent 不应同时为 true,所以最好用一个 status 变量来代替它们,这个 state 变量可以采取三种有效状态其中之一'typing' (初始), 'sending', 和 'sent':

import { useState } from 'react';export default function FeedbackForm() {const [text, setText] = useState('');const [status, setStatus] = useState('typing');async function handleSubmit(e) {e.preventDefault();setStatus('sending');await sendMessage(text);setStatus('sent');}const isSending = status === 'sending';const isSent = status === 'sent';if (isSent) {return <h1>Thanks for feedback!</h1>}return (<form onSubmit={handleSubmit}><p>How was your stay at The Prancing Pony?</p><textareadisabled={isSending}value={text}onChange={e => setText(e.target.value)}/><br /><buttondisabled={isSending}type="submit">Send</button>{isSending && <p>Sending...</p>}</form>);
}// 假装发送一条消息。
function sendMessage(text) {return new Promise(resolve => {setTimeout(resolve, 2000);});
}

仍然可以声明一些常量,以提高可读性:

const isSending = status === 'sending';
const isSent = status === 'sent';

但它们不是 state 变量,所以不必担心它们彼此失去同步。

避免冗余的 state

如果能在渲染期间从组件的 props 或其现有的 state 变量中计算出一些信息,则不应该把这些信息放到该组件的 state 中。

例如,以这个表单为例。它可以运行,但你能找到其中任何冗余的 state 吗?

import { useState } from 'react';export default function Form() {const [firstName, setFirstName] = useState('');const [lastName, setLastName] = useState('');const [fullName, setFullName] = useState('');function handleFirstNameChange(e) {setFirstName(e.target.value);setFullName(e.target.value + ' ' + lastName);}function handleLastNameChange(e) {setLastName(e.target.value);setFullName(firstName + ' ' + e.target.value);}return (<><h2>Let’s check you in</h2><label>First name:{' '}<inputvalue={firstName}onChange={handleFirstNameChange}/></label><label>Last name:{' '}<inputvalue={lastName}onChange={handleLastNameChange}/></label><p>Your ticket will be issued to: <b>{fullName}</b></p></>);
}

这个表单有三个 state 变量:firstNamelastNamefullName。然而,fullName 是多余的。在渲染期间,始终可以从 firstNamelastName 中计算出 fullName,因此需要把它从 state 中删除。

可以这样做:

import { useState } from 'react';export default function Form() {const [firstName, setFirstName] = useState('');const [lastName, setLastName] = useState('');const fullName = firstName + ' ' + lastName;function handleFirstNameChange(e) {setFirstName(e.target.value);}function handleLastNameChange(e) {setLastName(e.target.value);}return (<><h2>Let’s check you in</h2><label>First name:{' '}<inputvalue={firstName}onChange={handleFirstNameChange}/></label><label>Last name:{' '}<inputvalue={lastName}onChange={handleLastNameChange}/></label><p>Your ticket will be issued to: <b>{fullName}</b></p></>);
}

这里的 fullName 不是 一个 state 变量。相反,它是在渲染期间中计算出的:

const fullName = firstName + ' ' + lastName;

因此,更改处理程序不需要做任何特殊操作来更新它。当调用 setFirstNamesetLastName 时,会触发一次重新渲染,然后下一个 fullName 将从新数据中计算出来。

避免重复的 state

下面这个菜单列表组件可以让你在多种旅行小吃中选择一个:

import { useState } from 'react';const initialItems = [{ title: 'pretzels', id: 0 },{ title: 'crispy seaweed', id: 1 },{ title: 'granola bar', id: 2 },
];export default function Menu() {const [items, setItems] = useState(initialItems);const [selectedItem, setSelectedItem] = useState(items[0]);return (<><h2>What's your travel snack?</h2><ul>{items.map(item => (<li key={item.id}>{item.title}{' '}<button onClick={() => {setSelectedItem(item);}}>Choose</button></li>))}</ul><p>You picked {selectedItem.title}.</p></>);
}

当前,它将所选元素作为对象存储在 selectedItem state 变量中。然而,这并不好:selectedItem 的内容与 items 列表中的某个项是同一个对象。 这意味着关于该项本身的信息在两个地方产生了重复。

为什么这是个问题?让我们使每个项目都可以编辑:

import { useState } from 'react';const initialItems = [{ title: 'pretzels', id: 0 },{ title: 'crispy seaweed', id: 1 },{ title: 'granola bar', id: 2 },
];export default function Menu() {const [items, setItems] = useState(initialItems);const [selectedItem, setSelectedItem] = useState(items[0]);function handleItemChange(id, e) {setItems(items.map(item => {if (item.id === id) {return {...item,title: e.target.value,};} else {return item;}}));}return (<><h2>What's your travel snack?</h2> <ul>{items.map((item, index) => (<li key={item.id}><inputvalue={item.title}onChange={e => {handleItemChange(item.id, e)}}/>{' '}<button onClick={() => {setSelectedItem(item);}}>Choose</button></li>))}</ul><p>You picked {selectedItem.title}.</p></>);
}

请注意,如果首先单击菜单上的“Choose” 然后 编辑它,输入会更新,但底部的标签不会反映编辑内容。 这是因为有重复的 state,并且忘记更新了 selectedItem

尽管也可以更新 selectedItem,但更简单的解决方法是消除重复项。在下面这个例子中,将 selectedId 保存在 state 中,而不是在 selectedItem 对象中(它创建了一个与 items 内重复的对象),然后 通过搜索 items 数组中具有该 ID 的项,以此获取 selectedItem

import { useState } from 'react';const initialItems = [{ title: 'pretzels', id: 0 },{ title: 'crispy seaweed', id: 1 },{ title: 'granola bar', id: 2 },
];export default function Menu() {const [items, setItems] = useState(initialItems);const [selectedId, setSelectedId] = useState(0);const selectedItem = items.find(item =>item.id === selectedId);function handleItemChange(id, e) {setItems(items.map(item => {if (item.id === id) {return {...item,title: e.target.value,};} else {return item;}}));}return (<><h2>What's your travel snack?</h2><ul>{items.map((item, index) => (<li key={item.id}><inputvalue={item.title}onChange={e => {handleItemChange(item.id, e)}}/>{' '}<button onClick={() => {setSelectedId(item.id);}}>Choose</button></li>))}</ul><p>You picked {selectedItem.title}.</p></>);
}

(或者,可以将所选索引保持在 state 中。)

state 过去常常是这样复制的:

  • items = [{ id: 0, title: 'pretzels'}, ...]
  • selectedItem = {id: 0, title: 'pretzels'}

改了之后是这样的:

  • items = [{ id: 0, title: 'pretzels'}, ...]
  • selectedId = 0

重复的 state 没有了,只保留了必要的 state!

现在,如果编辑 selected 元素,下面的消息将立即更新。这是因为 setItems 会触发重新渲染,而 items.find(...) 会找到带有更新文本的元素。不需要在 state 中保存 选定的元素,因为只有 选定的 ID 是必要的。其余的可以在渲染期间计算。

避免深度嵌套的 state

想象一下,一个由行星、大陆和国家组成的旅行计划。可能会尝试使用嵌套对象和数组来构建它的 state,就像下面这个例子:

import { useState } from 'react';
import { initialTravelPlan } from './places.js';function PlaceTree({ place }) {const childPlaces = place.childPlaces;return (<li>{place.title}{childPlaces.length > 0 && (<ol>{childPlaces.map(place => (<PlaceTree key={place.id} place={place} />))}</ol>)}</li>);
}export default function TravelPlan() {const [plan, setPlan] = useState(initialTravelPlan);const planets = plan.childPlaces;return (<><h2>Places to visit</h2><ol>{planets.map(place => (<PlaceTree key={place.id} place={place} />))}</ol></>);
}
// places.js
export const initialTravelPlan = {id: 0,title: '(Root)',childPlaces: [{id: 1,title: 'Earth',childPlaces: [{id: 2,title: 'Africa',childPlaces: [{id: 3,title: 'Botswana',childPlaces: []}, {id: 4,title: 'Egypt',childPlaces: []}]}, {id: 10,title: 'Americas',childPlaces: [{id: 18,title: 'Venezuela',childPlaces: []}]}, {id: 19,title: 'Asia',childPlaces: [{id: 20,title: 'China',childPlaces: []}]}, {id: 26,title: 'Europe',childPlaces: [{id: 27,title: 'Croatia',childPlaces: [],}, {id: 33,title: 'Turkey',childPlaces: [],}]}, {id: 34,title: 'Oceania',childPlaces: [{id: 35,title: 'Australia',childPlaces: [],}]}]}, {id: 42,title: 'Moon',childPlaces: [{id: 43,title: 'Rheita',childPlaces: []}]}]
};

现在,假设想添加一个按钮来删除一个已经去过的地方。会怎么做呢?更新嵌套的 state 需要从更改部分一直向上复制对象。删除一个深度嵌套的地点将涉及复制其整个父级地点链。这样的代码可能非常冗长。

如果 state 嵌套太深,难以轻松更新,可以考虑将其“扁平化”。 这里有一个方法可以重构上面这个数据。不同于树状结构,每个节点的 place 都是一个包含 其子节点 的数组,可以让每个节点的 place 作为数组保存 其子节点的 ID。然后存储一个节点 ID 与相应节点的映射关系。

这个数据重组可能会让你想起看到一个数据库表:

import { useState } from 'react';
import { initialTravelPlan } from './places.js';function PlaceTree({ id, placesById }) {const place = placesById[id];const childIds = place.childIds;return (<li>{place.title}{childIds.length > 0 && (<ol>{childIds.map(childId => (<PlaceTreekey={childId}id={childId}placesById={placesById}/>))}</ol>)}</li>);
}export default function TravelPlan() {const [plan, setPlan] = useState(initialTravelPlan);const root = plan[0];const planetIds = root.childIds;return (<><h2>Places to visit</h2><ol>{planetIds.map(id => (<PlaceTreekey={id}id={id}placesById={plan}/>))}</ol></>);
}
// places.js
export const initialTravelPlan = {0: {id: 0,title: '(Root)',childIds: [1, 42],},1: {id: 1,title: 'Earth',childIds: [2, 10, 19, 26, 34]},2: {id: 2,title: 'Africa',childIds: [3, 4]}, 3: {id: 3,title: 'Botswana',childIds: []},4: {id: 4,title: 'Egypt',childIds: []},10: {id: 10,title: 'Americas',childIds: [18],   },18: {id: 18,title: 'Venezuela',childIds: []},19: {id: 19,title: 'Asia',childIds: [20],   },20: {id: 20,title: 'China',childIds: []},26: {id: 26,title: 'Europe',childIds: [27, 33],   },27: {id: 27,title: 'Croatia',childIds: []},33: {id: 33,title: 'Turkey',childIds: []},34: {id: 34,title: 'Oceania',childIds: [35],   },35: {id: 35,title: 'Australia',childIds: []},42: {id: 42,title: 'Moon',childIds: [43]},43: {id: 43,title: 'Rheita',childIds: []},
};

现在 state 已经“扁平化”(也称为“规范化”),更新嵌套项会变得更加容易。

现在要删除一个地点,只需要更新两个 state 级别:

  • 父级 地点的更新版本应该从其 childIds 数组中排除已删除的 ID。
  • 其根级“表”对象的更新版本应包括父级地点的更新版本。

下面是展示如何处理它的一个示例:

import { useState } from 'react';
import { initialTravelPlan } from './places.js';export default function TravelPlan() {const [plan, setPlan] = useState(initialTravelPlan);function handleComplete(parentId, childId) {const parent = plan[parentId];// 创建一个其父级地点的新版本// 但不包括子级 ID。const nextParent = {...parent,childIds: parent.childIds.filter(id => id !== childId)};// 更新根 state 对象...setPlan({...plan,// ...以便它拥有更新的父级。[parentId]: nextParent});}const root = plan[0];const planetIds = root.childIds;return (<><h2>Places to visit</h2><ol>{planetIds.map(id => (<PlaceTreekey={id}id={id}parentId={0}placesById={plan}onComplete={handleComplete}/>))}</ol></>);
}function PlaceTree({ id, parentId, placesById, onComplete }) {const place = placesById[id];const childIds = place.childIds;return (<li>{place.title}<button onClick={() => {onComplete(parentId, id);}}>Complete</button>{childIds.length > 0 &&<ol>{childIds.map(childId => (<PlaceTreekey={childId}id={childId}parentId={id}placesById={placesById}onComplete={onComplete}/>))}</ol>}</li>);
}

确实可以随心所欲地嵌套 state,但是将其“扁平化”可以解决许多问题。这使得 state 更容易更新,并且有助于确保在嵌套对象的不同部分中没有重复。

有时候,也可以通过将一些嵌套 state 移动到子组件中来减少 state 的嵌套。这对于不需要保存的短暂 UI 状态非常有效,比如一个选项是否被悬停。

摘要

  • 如果两个 state 变量总是一起更新,请考虑将它们合并为一个。
  • 仔细选择 state 变量,以避免创建“极难处理”的 state。
  • 用一种减少出错更新的机会的方式来构建 state。
  • 避免冗余和重复的 state,这样就不需要保持同步。
  • 除非特别想防止更新,否则不要将 props 放入 state 中。
  • 对于选择类型的 UI 模式,请在 state 中保存 ID 或索引而不是对象本身。
  • 如果深度嵌套 state 更新很复杂,请尝试将其展开扁平化。

相关文章:

React 18 选择 State 结构

参考文章 选择 State 结构 构建良好的 state 可以让组件变得易于修改和调试&#xff0c;而不会经常出错。以下是在构建 state 时应该考虑的一些建议。 构建 state 的原则 当编写一个存有 state 的组件时&#xff0c;需要选择使用多少个 state 变量以及它们都是怎样的数据格…...

LNMT与动静分离

目录 一、LNMT 一、部署tomcat 二、部署nginx 三、部署mariadb 四、配置nginx 二、操作流程及步骤 一、在第一台机器上进入 vim /etc/nginx/nginx.conf 更改配置文件 二、并查看端口是否成功启动 三、验证 四、再次来到网页验证 五、动静分离&#xff08;修改配置…...

【java】LinkedList 和 ArrayList的简介与对比

Java LinkedList和 ArrayList 在使用上&#xff0c;几乎是一样的。由于LinkedList是基于双向链表的&#xff0c;会多出list.getFirst();获取头部元素等方法 链表&#xff08;Linked list&#xff09;是一种常见的基础数据结构&#xff0c;是一种线性表&#xff0c;但是并不会按…...

机器学习基础14-算法调参(基于印第安糖尿病Pima数据集)

机器学习的模型都是参数化的&#xff0c;可以通过调参来提高模型的准确度。 模型有很多参数&#xff0c;如何找到最佳的参数组合&#xff0c;可以把它当作一个查询问题来处理&#xff0c;但是调整参数到何时为止呢&#xff1f;应该遵循偏差和方差协调的原则。 接下来将介绍在 s…...

ASUS华硕天选4笔记本电脑FA507XV原厂Windows11系统22H2

天选四FA507X原装系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件 华硕电脑管家、奥创控制中心等预装程序&#xff0c;恢复出厂状态W11 链接&#xff1a;https://pan.baidu.com/s/1SPoFW7wR5KawGu-yMckNzg?pwdayxd 提取码&#xff1a;ayxd...

IET独立出版 | EI检索 | 2023年第三届机械、航空航天与汽车工程国际会议

会议简介 Brief Introduction 2023年第三届机械、航空航天与汽车工程国际会议&#xff08;CMAAE 2023&#xff09; 会议时间&#xff1a;2023年12月8 -10日 召开地点&#xff1a;中国南京 大会官网&#xff1a;www.cmaae.org 航天是当今世界最具挑战性和广泛带动性的高技术领域…...

【Pytorch】CUDA error: no kernel image is available for execution on the device

记录一下pytorch安装的cuda版本和GPU cuda不一致的解决。 RuntimeError: CUDA error: no kernel image is available for execution on the device 一般就是pytorch和cuda安装的不匹配。 如果我安装的torch配的cuda信息如下&#xff0c; torch.__version__: 1.8.1cu102 tor…...

dolphinschedule配置企微告警服务(WeChat群组)

一、前置说明 ds配置好工作流后&#xff0c;比较重要的一个就是上线后的监控报警服务&#xff0c;如果你是基于企微作为协同办公的&#xff0c;WeChat群组预警必须是要安排上的&#xff0c;文章基于自建应用配合群组方式构建预警群&#xff0c;接入后&#xff0c;任务成功或者…...

Git中smart Checkout与force checkout

Git中smart Checkout与force checkout 使用git进行代码版本管理,当我们切换分支有时会遇到这样的问题&#xff1a; 这是因为在当前分支修改了代码&#xff0c;但是没有commit,所以在切换到其他分支的时候会弹出这个窗口&#xff0c; 提示你选force checkout或者smart checko…...

Java“牵手”1688商品跨境属性数据,1688API接口申请指南

1688平台商品详情跨境属性数据接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取1688商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片&#xff0c;重量&#xff0c;详情描述等详细信息 。 获取商品详情接口API是一种用于…...

Win解答 | 解决键盘中 字母+空格 导致的输入法弹窗导致的一系列问题

近三个月来&#xff0c;一直都有一个键盘组合键的问题影响我的电脑使用&#xff0c;不管是打字还是打游戏&#xff0c;都会出现按键盘的 字母空格 弹出一个特殊符号的候选框&#xff0c;如下图所示 图片中为 S空格 所出现的弹窗 一个看似方便&#xff0c;实则难受的功能 其实打…...

WPF读取dicom序列:实现上一帧、下一帧、自动播放、暂停

一、整体设计概况 创建WPF程序使用.Net Framework4.8定义Image控件展示图像增加标签展示dcm文件信息规划按钮触发对应的事件:上一帧、下一帧、自动播放、暂停、缩放、播放速率二、页面展示 三、代码逻辑分析 Windows窗体加载Loaded事件:生成初始图像信息Windows窗体加载Mous…...

homeassistant ubuntu自启动 网络设置

命令行安装virtualbox 或者安装包 hass官网下载 haos_ova-10.4.vdi virtualbox 装hass 最少2G内存 其他省略 自启动&#xff1a; gnome-session-properties 添加 VBoxManage startvm hass --type headless hass为自己的虚拟机名字 网络配置如下&#xff1a; 要全部打开...

生成式AI背景下编程工作者的技术和高级软考理论的演进融合之路

思考背景 近两次软考&#xff0c;我都参与了&#xff0c;2022年11月参加的是系统架构师的考试&#xff0c;2023年5月参加的是系统分析师的考试&#xff0c;去年参加系统架构是考试是完全的裸考和第一次考&#xff0c;成绩是选择题39&#xff0c;综合题46和论文48分&#xff0c…...

RabbitMQ的镜像队列

镜像队列 如果 RabbitMQ 集群中只有一个 Broker 节点&#xff0c;那么该节点的失效将导致整体服务的临时性不可用&#xff0c;并且也可能会导致消息的丢失。可以将所有消息都设置为持久化&#xff0c;并且对应队列的durable 属性也设置为 true &#xff0c;但是这样仍然无法…...

【Spring Boot】数据库持久层框架MyBatis — Spring Boot构建MyBatis应用程序

Spring Boot构建MyBatis应用程序 Spring Boot是用于快速构建Spring应用程序的框架。MyBatis是一种Java持久化框架&#xff0c;可以帮助开发人员轻松地管理数据库。将Spring Boot与MyBatis结合使用可以使开发人员更容易地创建和管理数据库应用程序。 以下是使用Spring Boot构建…...

【校招VIP】专业课考点之session cookie

考点介绍&#xff1a; 测试工作中我们经常会听到这两个词&#xff0c;作为测试一定要理解这两个概念&#xff0c;对于测试应用的接口、业务理解很有帮助。需要了解Cookie和Session的作用、原理和两者的区别。 『专业课考点之session cookie』相关题目及解析内容可点击文章末尾…...

IDEA集成Git相关操作知识(pull、push、clone)

一&#xff1a;集成git 1&#xff1a;初始化git&#xff08;新版本默认初始化&#xff09; 老版本若没有&#xff0c;点击VCS&#xff0c;选中import into Version Controller中的Create git Repository(创建git仓库)&#xff0c;同理即可出现git符号。 也可查看源文件夹有没有…...

Springboot 整合 Redis配置

RedisService接口 import java.util.List; import java.util.Map; import java.util.Set;/*** Redis操作Service* Created by macro on 2020/3/3.*/ public interface RedisService {/*** 保存属性*/void set(String key, Object value, long time);/*** 保存属性*/void set(St…...

Spark 启动时,报JAVA_HOME is not set

文章目录 1、报错内容2、解决方式3、再次启动Spark集群 1、报错内容 Spark启动时报错&#xff1a; hadoop104: JAVA_HOME is not set2、解决方式 解决方式&#xff1a; 打开启动配置文件 cd /opt/module/spark-standalone/sbin/ vim spark-config.sh配置Java的环境变量 …...

运用Python解析HTML页面获取资料

在网络爬虫的应用中&#xff0c;我们经常需要从HTML页面中提取图片、音频和文字资源。本文将介绍如何使用Python的requests库和BeautifulSoup解析HTML页面&#xff0c;获取这些资源。 一、环境准备 首先&#xff0c;确保您已经安装了Python环境。接下来&#xff0c;我们需要安…...

硬件名词--单片机,开发板和芯片

这里写自定义目录标题 初学硬件&#xff0c;一脸懵逼。整理了半天还是没太懂。只能说我不学习&#xff0c;我是知识点的搬运工。 1.半导体&#xff08;semiconductor&#xff09;指常温下导电性能介于导体与绝缘体之间的材料&#xff0c;常见材料有硅&#xff0c;锗、砷化镓等。…...

Ubuntu20.04下安装搜狗输入法Linux版

Ubuntu20.04下安装搜狗输入法Linux版 参考搜狗输入法的官网安装指南&#xff1b; 第一步&#xff1a;打开搜狗输入法官网&#xff1b; https://shurufa.sogou.com/ 点击X86_64后将会自动跳转到搜狗输入法的安装指南中&#xff1b; 安装指南 Ubuntu搜狗输入法安装指南 搜狗…...

springboot RabbitMQ客户端连接故障恢复

最近做RabbitMQ故障演练发现RabbitMQ服务器停止后&#xff0c;基于springboot的消费端不可以自动的恢复&#xff0c;队列的消费者消失&#xff0c;消息一直积压到队列中&#xff0c;这种情况肯定是不可接收的&#xff1b;通过研究源代码找到了解决方案。 一、添加自动恢复配置a…...

centos下配置SFTP且限制用户访问目录

一、SFTP使用场景 ftp是大多数网站的文件传输选择工具&#xff0c;但ftp并不是非常安全&#xff0c;并且在centos上搭建的vsftpd也非常的不稳定&#xff0c;偶尔会出现权限问题&#xff0c;例如500、或是账号密码不正确等等。 而SFTP是基于默认的22端口&#xff0c;是ssh内含…...

A - 最短路径

给出一张包含 n 个节点、 m 条边的无向图&#xff0c;请你求出图上两点 s,t 间的最短路径长度。 (请大家自行处理重边和自环) Input 第一行两个数 n,m &#xff0c;分别表示节点数和边数&#xff0c;以空格隔开&#xff0c;其中1≤n≤500,1≤m≤50000 &#xff1b; 之后 m 行…...

自然语言处理(三):基于跳元模型的word2vec实现

跳元模型 回顾一下第一节讲过的跳元模型 跳元模型&#xff08;Skip-gram Model&#xff09;是一种用于学习词向量的模型&#xff0c;属于Word2Vec算法中的一种。它的目标是通过给定一个中心词语来预测其周围的上下文词语。 这节我们以跳元模型为例&#xff0c;讲解word2vec的…...

1.1 数据库系统简介

思维导图&#xff1a; 1.1.数据库系统简介 前言&#xff1a; 数据库系统是一个软件系统&#xff0c;用于管理和操作数据库。它提供了一个组织良好、高效并能够方便存取的数据存储机制&#xff0c;并且能够支持各种数据操作、事务管理、并发控制和恢复功能。以下是数据库系统的…...

WebGL 绘制圆点

前言 这篇文章不说WebGL相关概念了&#xff0c;初学者先到网上看看WebGL相关概念。这里着重写一下在vue3前端框架下&#xff0c;如何通过webGL绘制圆点。 着色器代码(画点) 画点相关的着色器代码有顶点着色器和片元着色器&#xff0c;代码如下&#xff1a; 顶点着色器&…...

迅为RK3588开发板Android12 设置系统默认不锁屏

修改 frameworks/base/packages/SettingsProvider/res/values/defaults.xml 文件&#xff0c;修改为如下 所示&#xff1a; - <bool name"def_lockscreen_disabled">false</bool> <bool name"def_lockscreen_disabled">true</bool&…...