React核心思维模型(一)

一、数据和视图分离,数据改变驱动视图更新

<div>Tom</div>

如果我们想修改上述div盒子中的Tom为Jerry,应该怎样修改呢 

在jquery中我们直接把界面元素抓过来修改

document.getElementsByTagName('div').item(0) = 'Jerry'

但在react中,当需要更改用户界面时,在绝大多数情况下,我们都是去操作数据,而不是直接更改界面元素(DOM 节点)。当数据变动以后,相应的界面元素会自动更改。

<div>{state}</div>
const [state, setState] = useState('Tom')
setState('Jerry')

注意:在用 React 写界面时,先写一些 HTML 作为页面的静态结构,再用大括号标记,加入动态的、可交互的元素,记住,永远只关注数据!!

拓展:受控组件和非受控组件

当一个 input 关联了一个数据项(比如 state),我们称之为受控组件。其他可交互的 HTML 元素跟 input 情况类似,比如 select 和 textarea。

与受控组件相对应,另有一种组件被称为非受控组件,也就是说其状态不受 React 控制。

绝大多数情况下,我们都使用受控组件,非受控组件一般用于 state 无法表达控件状态的情况,比如,支持文件上传框:<input type="file" />

二、声明式、命令式和响应式

1.命令式:

传统的Web编程方式,比如jquery和浏览器API,需要我们一步一步下达明确的指令,考虑实现细节,在脑中跟踪系统内部状态,什么时候创建DOM元素,调用哪个Web API等等

示例打开对话框步骤

1.对话框是否存在?

2.如果不存在就创建对话框div,添加button

3.调整样式为display:block

2.声明式:

新生代Web框架,比如React、Vue.js以及SQL语言和CSS,我们只需要描述想要的结果,具体实现细节交给react本身,可以专注于业务逻辑

//绑定动态数据

const [visible ,setVisible] = useState(false)

//打开对话框

setVisible(true)

{visible && <div>对话框</div>}

注意:声明式其实是命令式的一种抽象,react底层本身也肯定调用了很多命令式的DOM API,所有声明式语言或者框架都是以某种命令式的实现为基础的

3.响应式

声明式和响应式是react的基本特征

当数据发生变化时,react将自动对相关DOM元素做相应的调整,看起来就像是DOM响应了数据变化的号召而自发地做出地更改,所以声明式是响应式的基础

react不算完全意义上的响应式,因为还需要我们调用set函数进行更改,而在Vue中,我们确实可以做到直接修改变量的值实现界面的改动

react的这种方式虽然会相对繁琐一点,但是在查找bug更方便,因为可以一直沿着函数调用栈追溯到肇事的代码                

三、JSX详解

1.JSX理解

JSX是伪装成HTML的JavaScript代码,JSX 标签实际上是一个函数调用

在发送到浏览器执行之前, React 开发工具将 JSX 标签自动转换为相应的 JavaScript 代码。

例子一

<div>Tom</div>

跟下面的代码是等效的

import {jsx as _jsx} from 'react/jsx-runtime'
function App() {return _jsx("div", { children: "Tom" })
}

在App函数里,我们调用了_jsx函数并且返回其结果,这个函数接收了两个参数:"div"和{children:"Tom" }

 例子二

<div className="mr-wall" />//等价于_jsx("div", { className: "mr-wall" })

这个函数调用仍然有两个参数,第一个参数是元素的类型,第二个参数则是包含了所有属性的一个对象。

例子三

<div><button />
</div>//等价于_jsx('div', { children: _jsx('button') })

children 是一个特殊的属性,包含了嵌套在 div 里的标签,或者说是 div 的“孩子”。而 button 同时也是一个 jsx 标签,所以 children 的值是再一次调用 _jsx 的结果。

例子四

<div className="container"><div className="mr-wall"> {alan} </div><button />
</div>

转换成 JavaScript:

_jsx('div', { className: "container", children: [_jsx("div", { className: "mr-wall", children: [" ", alan, " "] }), _jsx('button')
]})

2.JSX返回值

_jsx 函数所创建并返回了一个简单的 JavaScript 对象,这个对象的正式名称是 React 元素

function App() {const result = _jsx("input")console.log(result) // 打印到控制台return result
}

上述代码的打印结果是

Object {type: "input", key: null, ref: null, props: Object, _owner: null}

既然是函数调用,JSX 标签就是一个 JavaScript 表达式,可以写在任何能容纳表达式的地方。可以将JSX 标签赋值给一个变量,或者作为参数在调用函数时传过去,或者打印在控制台上。

3.JSX 和 HTML 的区别

举例比较

// JSX
<input style={{ minWidth: 200 }} />// HTML
<input style="min-width: 200px" />

为什么有两层大括号呢?

  • 外层大括号让 JSX 识别这是 JavaScript
  • 内层大括号定义了样式对象,minWidth: 200是一个键值对

 再看一个例子,HTML 的按钮是这样写的:

<button onclick="alert('OK')">OK</button>

当用户点击按钮时,onclick 属性中的代码会被执行,alert('OK') 作为一个字符串被当作 JavaScript 代码执行。

而 JSX 版本则是这样的:

<button onClick={() => alert("OK")}>OK</button>

 改成JavaScript:

_jsx('button', { onClick: () => alert('OK')}, "OK")

当用户点击这个按钮时,会触发 onClick 属性中定义的函数,() => alert('OK') 作为一个函数被当作 JavaScript 代码执行。

其他区别:

特性JSXHTML
语法类似于 JavaScript,允许使用表达式标准的标记语言,不支持 JavaScript 表达式
组件支持组件(如 <MyComponent />只支持 HTML 元素,没有组件概念
属性命名使用 camelCase(如 className使用小写字母(如 class
注释使用 {/* 注释内容 */}使用 <!-- 注释内容 -->
自闭合标签必须自闭合(如 <img />可以有自闭合形式(如 <img>
事件处理通过 camelCase 属性(如 onClick使用小写字母(如 onclick
表达式支持 JavaScript 表达式(如 {value}仅支持固定文本,不支持 JavaScript
风格写法使用对象语法(如 style={{ color: 'red' }}使用字符串(如 style="color: red;"
条件渲染使用三元运算符或逻辑与(如 {condition ? <Component /> : null}不支持条件渲染,需用 JavaScript 控制逻辑
版本控制使用 Babel 转换为 JavaScript直接由浏览器解析

4.JSX的参数

(1)属性值,如: <input value={text} />

如果作为属性值,那么这个“洞”里就可以放任何表达式,只要相应的组件能够处理,比如变量、函数、函数调用(JSX标签)

<div>{<input type="text" />}</div>     

可以这样书写,不过大括号有点多余

(2)标签的嵌套内容,如:<div>{content}</div>

如果是作为标签的嵌套内容,那么该表达式的值就不能是一个对象(React 元素除外)。例如

function App() {const content = { name: '艾伦', age: 25 }return <div>{content}</div>
}

浏览器上就会出现如下错误:

Error
Objects are not valid as a React child (found: object with keys {name, age}). If you meant to

举例:map遍历标签数组

以下两种写法是等价的,当大括号内表达式为数组时,JSX会把数组内容解释为标签的“孩子”

// 1
<select>{[<option>艾伦</option>, <option>汉堡</option>]}
</select>// 2
<select><option>艾伦</option><option>汉堡</option>
</select>

因此 

function App() {const snacks = ['艾伦', '汉堡']return (<select>{ snacks.map(snack => <option>{snack}</option>) }</select>)
}

四、组件渲染可以看成手翻书

React 的工作过程就像播放手翻书动画。每调用一次组件函数,组件就返回手翻书的一页,调用多次,装订起来,快速一翻就形成了完整的交互界面。

代码演示

function App() {console.log("新一帧") // 加了这一句方便跟踪程序运行状况const [isDialogVisible, setIsDialogVisible] = React.useState(false)const dialog = (<div style={styles.dialogBackdrop}><div style={styles.dialogContainer}><div>消息对话框</div><button onClick={() => { setIsDialogVisible(false) }}>关闭</button></div></div>);return (<div style={styles.app}><div>观察者窗口主控界面</div><button onClick={() => { setIsDialogVisible(true) }}>打开消息对话框</button>{ isDialogVisible && dialog }</div>);
}

 如果用户点击打开消息对话框一次,并点击关闭,JavaScript 控制台上会出现几行 新一帧

三行

第一行:APP首次渲染

第二行:点击打开消息对话框后再次渲染

第三行:点击关闭后再次渲染

五、react数据的不可变特性   

State 内的数据、Prop 和 React 元素都是不可变的 

 例如我们尝试修改和添加元素:

const [snacks, setSnacks] = useState([])
//添加新元素
snacks.push('芒果干')
//修改元素
snacks[5] = '巧克力'
//调用设置器
setSnacks(snacks)

上述修改是不起作用的,React 只要看到还是同一个数组,就会认为 State  中的数据没有发生任何变化。

正确方法是准备一个新数组:

// 添加新元素
const newSnacks = [...snacks, '芒果干']
setSnacks(newSnacks)// 替换 index 为 4 的元素
const newSnacks2 = [...snacks.slice(0, 4), '巧克力', ...snacks.slice(5)]
setSnacks(newSnacks2)

不可变特性同样也适用于对象、字符串和其他数据类型

为什么要不可变?

就单次操作来说,重建确实比直接修改慢,但是,从总体上来说,这种采用重建来修改状态的机制性能更优

在绝大多数的用户界面里,发生最普遍频繁的,不是对程序状态的修改,而是对状态数据的读取比对。如果我们约定所有的状态数据值都是不可变的,那么对其的比对操作的性能上就会有至少一个数量级的提升

相反,如果数据结构是不可变的,判断起来就容易很多:只需要做一个“浅层比较”,看看是不是同一个数组或对象就行了(下图)

所以,在最开始的例子里,即使我们替换了数组 snacks 内的元素、并调用 setSnacks(snacks),由于 snacks 是同一个数组,React 会认为 State 并未发生改变,从而置之不理。

React 的适用场景:如果整个界面和程序状态大致不变、而需要非常频繁地读取比对系统状态

第三方库immer

背景:

我们通常使用展开操作符 ... 来复制并修改数组或对象,这种方法在数据结构简单时还算方便好用,然而有多层嵌套的对象或数组时,就会显得累赘

作用:

immer 会为我们生成一个新的实例,并保证它符合不可变约定,只需要set修改后的数据即可

使用方法:

npm install -S immer
import produce from 'immer'
...
function App() {const [todos, setTodos] = useState({...})...setTodos(produce(draft => {draft[1].status.isComplete = true
}))...
}

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

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

相关文章

DataSophon集成ApacheImpala的过程

注意: 本次安装操作系统环境为Anolis8.9(Centos7和Centos8应该也一样) DataSophon版本为DDP-1.2.1 整合的安装包我放网盘了: 通过网盘分享的文件&#xff1a;impala-4.4.1.tar.gz等2个文件 链接: https://pan.baidu.com/s/18KfkO_BEFa5gVcc16I-Yew?pwdza4k 提取码: za4k 1…

使用 Kibana 将地理空间数据导入 Elasticsearch 以供 ES|QL 使用

作者&#xff1a;来自 Elastic Craig Taverner 如何使用 Kibana 和 csv 采集处理器将地理空间数据采集到 Elasticsearch 中&#xff0c;以便在 Elasticsearch 查询语言 (ES|QL) 中进行搜索。Elasticsearch 具有强大的地理空间搜索功能&#xff0c;现在 ES|QL 也具备这些功能&am…

ffmpeg视频滤镜:定向模糊-dblur

滤镜简述 dblur 官网链接 > https://ffmpeg.org/ffmpeg-filters.html#dblur 有一个模糊滤镜&#xff0c;我试了一下&#xff0c;没有感觉到它的特殊之处, 这里简单介绍一下。 滤镜使用 滤镜的参数 angle <float> ..FV.....T. set angle (from 0 t…

浏览器HTTP缓存解读(HTTP Status:200 304)

为什么要有浏览器缓存&#xff1f; 浏览器缓存(Brower Caching)是浏览器对之前请求过的文件进行缓存&#xff0c;以便下一次访问时重复使用&#xff0c;节省带宽&#xff0c;提高访问速度&#xff0c;降低服务器压力 http缓存机制主要在http响应头中设定&#xff0c;响应头中…

Facebook登录崩溃?别急,神奇秘籍拯救你丨出海笔记

今天文章很短&#xff0c;但非常实用&#xff01; 相信运营同学或多或少都会在登录facebook的时候遇到: 您所关注的链接可能已损坏&#xff0c;或页面可能已被移除。 This page isnt available, The link you followed may be broken, or the page may have been removed. 然…

Redmi Note 12 Turbo 1TB root教程

文章目录 通过手机下载完整的系统包通过payload-dumper-go 提取boot.img把下载的安装包直接拉到payload-dumper-go.exe通过第三步 找到boot.img把boot.img 拉到手机Download&#xff0c;通过magisk修补boot修补完成后找到这个文件&#xff0c;通过adb刷入 连接不上adb 通过手机…

Xcode 16.1 (16B40) 发布下载 - Apple 平台 IDE

Xcode 16.1 (16B40) 发布下载 - Apple 平台 IDE IDE for iOS/iPadOS/macOS/watchOS/tvOS/visonOS 发布日期&#xff1a;2024 年 10 月 28 日 Xcode 16.1 包含适用于 iOS 18.1、iPadOS 18.1、Apple tvOS 18.1、watchOS 11.1、macOS Sequoia 15.1 和 visionOS 2.1 的 SDK。Xco…

Docker-微服务项目部署

环境准备 1.微服务项目 参考&#xff1a;通过网盘分享的文件&#xff1a;wolf2w_cloud.zip 链接: https://pan.baidu.com/s/1Lr4k6LPIJ59gVNA_DgKM_Q?pwdkjxt 提取码: kjxt 前端项目&#xff1a;trip-mgrsite-ui&#xff0c;trip-website-ui&#xff0c;trip-wenda-ui 服务项…

【Redis】常见基本全局命令

一、Redis俩大核心命令 由于Redis是以键值对的形式进行数据存取&#xff0c;自然就离不开不断的存储和获取&#xff0c;而其所对应的命令则是set和get&#xff0c;如此说来二者为Redis的核心基础命令也不为过。 作用&#xff1a;用于存储Stirng类型的数据 返回&#xff1a;当…

Ubuntu下安装和配置MySQL5.7教程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 在ubuntu下安装MySQL数据库 查看操作系统版本 ​编辑 添加 MySQL APT 源 访问下载页面并下载发布包 安装发布包 安装MySQL 查看MySQL状态 开启自启动 登…

普通人适合做大模型吗?过程中会发生什么潜在的挑战?

对于普通人来说&#xff0c;直接进行大模型的研发和训练可能存在一定的挑战&#xff0c;因为这通常需要以下资源和知识&#xff1a; 专业知识&#xff1a; 大模型的开发需要深入理解机器学习、深度学习、神经网络等领域的知识。 计算资源&#xff1a; 大模型的训练需要高性能的…

(蓝桥杯C/C++)——常用库函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、 二分查找 1.二分查找的前提 2.binary_ search函数 3.lower_bound和upper_bound 二、排序 1.sort概念 2.sort的用法 3.自定义比较函数 三、全排列 1.next p…

qt配置https请求

qt应用版本 windows 32位 先说下心理路程&#xff0c;你能遇到的我都遇到了&#xff0c;你能想到的我都想到了&#xff0c;怎么解决看这一篇就够了&#xff0c;从上午12点到晚上12点几乎没离开电脑&#xff08;除了吃饭&#xff09;&#xff0c;对于openssl这种用的时候无感&am…

HTML练习题:彼岸的花(web)

展示效果: 代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>彼岸の花</title><style…

学STM32选标准库还是HAL库?

我学STM32的时候&#xff0c;stm32cubeMX还没推出&#xff0c;HAL库还没出来&#xff0c;主要以标准库为准。 下面讲下我学习STM32的经历&#xff0c;从陌生到应用到项目大概花了2-3周&#xff0c;相信99%的人都能通过这种方法&#xff0c;快速把STM32玩起来。 我是项目需要&am…

池化层笔记

池化层 文章目录 池化层二维池化层超参数池化层的分类代码实现填充和步幅 多个通道 总结 卷积对位置敏感&#xff0c;可以检测垂直边缘。需要有一定程度的平移不变性&#xff0c;而在平时图片的拍摄&#xff0c;会因为图片的照明&#xff0c;物体位置&#xff0c;比例&#xff…

以太网交换安全:DHCP Snooping

一、DHCP Snooping的概念及功能 DHCP Snooping是一种用于增强网络中DHCP服务安全性的技术。以下是对以太网交换安全中的DHCP Snooping进行详细的介绍&#xff1a; 基本概述 定义目的&#xff1a;DHCP Snooping是一种网络安全技术&#xff0c;旨在防止未经授权的DHCP服务器在网…

【问题记录】解决VMware虚拟机中鼠标侧键无法使用的问题

前言 有项目需要在Linux系统中开发&#xff0c;因为要测试Linux中相关功能&#xff0c;要用到shell&#xff0c;在Windows中开发太麻烦了&#xff0c;因此我选择使用UbuntuXfce4桌面来开发&#xff0c;这里我用到了Linux版本的IDEA&#xff0c;除了快捷键经常和系统快捷键冲突…

[JAVAEE] 面试题(二) - CAS 和 原子类

目录 一. CAS的实现原理 1.1 伪代码分析 1.2 底层实现 二. CAS 操作示例 三. ABA问题 四. 原子类 4.1 使用原子类的目的 4.2 原子类的使用示例 五. 总结 一. CAS的实现原理 CAS(compare and swap 比较和交换)是一种用于实现无锁并发的技术. 1.1 伪代码分析 // 伪代…

Ubuntu 20.04 安装 OpenCV 和 OpenCV_contrib 教程

Ubuntu 20.04 安装 OpenCV 和 OpenCV_contrib 教程 Ubuntu 20.04 安装 OpenCV 和 OpenCV_contrib 教程前言 OpenCV概述核心功能优势特点应用领域安装与使用 OpenCV_contrib概述核心功能具体模块 安装与使用一、准备工作二、下载OpenCV和OpenCV_contrib三、编译和安装OpenCV四、…