『web前端开发』React快速入门
项目需要开发一个web平台,已有 HTML + CSS + JavaScript 的一定基础
本文主要是快速入门react的笔记,仅了解核心的概念和语法,足够后续更改开源项目即可。学习过程主要参考下面的视频和官方文档
基本概念
-
react是什么
我们平常想要在Web网站上展示数据的时候,需要
(1)发送请求获取数据
(2)处理数据(过滤、整理格式等)
(3)操作DOM呈现页面
在我们操作DOM呈现页面的时候,通常需要DOM-API来操作UI,繁琐、效率低
1
2
3document.querySelector('.btn')
document.getElementById('app')
document.getElementsByTagName('span')react就是帮助我们进行(3),可以更加方便的操作DOM,将数据渲染为HTML视图
-
react的作用
原生 JavaScript 的缺点
- 原生JavaScript通过DOM-API操作DOM,繁琐且效率低
- 使用JavaScript直接操作DOM,浏览器会进行大量的重绘重排。
- 原生JavaScript没有组件化编码方案,代码复用率低。
react:
- 组件化模式、声明式编码,提高开发效率及组件复用率
- react native可以通过react语法进行移动端开发
- 使用虚拟DOM和Diffing算法,尽量减少与真实DOM的交互,防止了重绘重排,实现视图的高效更新
虚拟DOM的理解:
尚硅谷React教程 16:45-25:26
-
jsx
react中定义的一种JavaScript 的扩展语法(即react独有的模版语法),将HTML标签和JS代码混合使用。
例如:
1
2
3const element = <h1 className="foo">Hello, world</h1>;
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(element); -
状态
状态是组件中用于存储和控制数据的对象,当状态发生变化时,组件会重新渲染以反映新的状态。这使得React组件可以动态地响应用户交互和其他事件。
即产生了数据的更新、交互
jsx基本语法
-
组件
1)定义
React 应用程序是由 组件 组成的。一个组件是 UI(用户界面)的一部分,它拥有自己的逻辑和外观。组件可以小到一个按钮,也可以大到整个页面。
React 组件在形式上表现为:返回标签的 JavaScript 函数
- React 组件必须以大写字母开头
- React 组件都是以函数形式表示的
2)示例
自定义 MyButton 组件
1
2
3
4
5function MyButton() {
return (
<button>I'm a button</button>
);
}所定义的组件可以嵌套到其他的组件,注意这个应用方法
<MyButton />
,进行了自闭和JSX 比 HTML 更加严格,必须闭合标签,如
<br />
。1
2
3
4
5
6
7
8export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}- 父组件:这里可以认为
MyApp
是一个 父组件,将每个MyButton
渲染为一个“孩子”。这是 React 的神奇之处:你可以只定义组件一次,然后按需多处和多次使用。
3)注意事项
- 不能嵌套组件的定义,即在一个组件中不能直接定义另一个组件
- 当子组件需要使用父组件的数据时,你需要 通过 props 的形式进行传递
-
每一个组件只能一个根元素
1)原因
- 不管是vue还是react,模板都将被编译为render函数,而函数的返回值只能是一个,所以如果不用单独的根节点包住,就会并列返回多个返回值,这在js中是不允许的。
- 除了这一点,还有一个主要是原因是,react和vue都将把模板的内容转换为对应的元素,最后建立起虚拟dom树,而树状结构只能有唯一的根节点,这样在后续的虚拟dom数据有变化时,可以检查到具体更改的位置。如果有多个根节点,则不能明确到底要在哪个树上查找更新。
2)如何返回多个元素?使用Fragment将它们包裹到一个共享的父级中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26function App(){
const list = [
{id : 1, name : 'a'},
{id : 2, name : 'b'},
{id : 3, name : 'c'}
]
// 像这样在组件中并列返回多个根元素是不可行的
// const listConent = list.map(item => (
// <li key = {item.id}>{item.name}</li>
// <li>----------------------</li>
// ))
const listConent = list.map(item => (
<Fragment key = {item.id}>
<li key = {item.id}>{item.name}</li>
<li>----------------------</li>
</Fragment>
))
return(
<ul>{listConent}</ul>
)
}
export default App;Fragment 的详细用法:React中文文档 | Fragment
-
标签属性设置
- 使用
className
来设置 CSS 的class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import image from './logo.svg'
function App() {
const imgData = {
className: 'small',
style: {
width: 200,
height: 200,
backgroundColor: 'grey'
}
}
return (
<div>
<img
src={image}
alt=""
{...imgData}
/>
</div>
)
}
export default App; - 使用
react hook
-
基本概念
react的思想在于将组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。
react Hooks 是一些可以让你在函数组件中“钩入” React 状态和生命周期功能的特殊函数。它们可以解决一些 class 组件中常见的问题,并且使代码更加简洁和可读。
因为所有的钩子都是为函数引入外部功能,所以 React 约定,钩子一律使用
use
前缀命名,便于识别。你要使用 xxx 功能,钩子就命名为 usexxx。react hook的功能在某些方面比较类似于库函数,对一些特定的功能进行了封装,可以更加方便地重复使用。但是不同的是库函数式独立的,不依赖于特定的上下文或框架,例如,数学库函数(如
Math.sqrt
)可以在任何 JavaScript 代码中使用。但是react hook只能在React 函数组件或自定义 Hook 中使用。 -
React 内置 Hook
1)useState()
-
定义:用于在函数组件中进行状态变更
-
语法:
-
content:需要渲染的内容
-
setContent:用于更新状态的函数, 后续调用该函数进行状态更新操作。
注意在对对象进行更新的时候,一定要把所有的键值对都写上去,即使没有改变也要写,因为这里的逻辑是用新的对象直接覆盖掉原有的对象,如果没有将不变的键值对写上去会直接失去这些键值对。
将所有属性都写上去又比较繁琐,所以我们可以在前面加上
...content
,表示将content的所有属性先写在这里,后面再写上需要改变的键值对来覆盖掉前面的值。 -
useState()内接收状态的初始值。
这里的初始内容可以为变量、对象的形式
1
2
3
4
5
6
7
8
9
10
11
12
13
14const [content, setContent] = useState("初始内容");
const [content,setContent] = useState({
title: '默认标题',
content: '默认内容'
})
// setContent后面为更新后的内容
setContent('新内容')
function handClick(){
setContent({
...content,
content: '新内容'
})
} -
-
示例:
1
2
3
4
5
6
7
8
9
10
11import React, { useState } from "react";
export default function Button() {
const [buttonText, setButtonText] = useState("Click me, please");
function handleClick() {
return setButtonText("Thanks, been clicked!");
}
return <button onClick={handleClick}>{buttonText}</button>;
}
-
初始化应用
-
环境要求
-
安装 Node.js。Node 包括 npm(Node 程序包管理器)和 npx(Node 程序包运行器)
- 也可以使用Yarn作为npm的替代方案
-
设置npm的镜像源
-
查看:
1
npm config get registry
-
更改为淘宝源
1
npm config set registry https://registry.npmmirror.com
-
-
-
初始化应用
1
npx create-react-app reacttest
-
输入命令后会在 reacttest 文件夹下面构建好应用程序的基础架构
注意项目名字只能是小写,不能大小写混合
-
处理完成之后,可以 cd 到
reacttest
文件夹下,然后键入npm start
命令并回车,先前由 create-react-app 创建的脚本会启动一个本地服务 localhost:3000,并打开你的默认浏览器来访问这个服务。成功启动浏览器的话,你的浏览器上会显示如下画面,表示初始化成功
-
-
文件结构
-
src文件夹: 存放React 应用源码的目录。
可以保留该目录下的这两个文件,其他文件都不重要,可以直接全部删除
-
index.js
:入口文件 -
APP.js
:根组件文件react中的一个重要思想:其包括两种组件形式
- 函数形式(主流)
- 类形式
-
-
-
APP.js组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import logo from "./logo.svg";
import "./App.css";
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer">
Learn React
</a>
</header>
</div>
);
}
export default App;可以看到组件文件由三部分组成:import语句、APP组件函数和底部的export语句
-
import语句:允许在此脚本中使用其他文件中的代码
在这里引入我们需要的本地文件,包括css、图片等
-
APP组件函数
返回一个JSX表达式,该表达式定义了浏览器最终需要渲染的DOM
-
export语句:在
App.js
文件的最底部export default App
语句使得App
组件能被其他模块使用。
**插值的实现:**通过括号 + 变量名称。可以在标签内容和属性部分进行插值
- 注意使用{}进行插值的方式
- 注意jsx的语法,在给divContent赋值的时候并不需要使用引号,直接写标签即可
-
-
index.js入口文件
渲染方式
-
条件渲染
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function App() {
const divTitle = '标签标题'
const flag = true
let divContent = null
if (flag){
divContent = <span>flag为true</span>
} else{
divContent = <p>flag为false</p>
}
return (
<div title = {divTitle}>{divContent}</div>
);
}
export default App; -
数组渲染
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function App(){
const list = [
{id : 1, name : 'a'},
{id : 2, name : 'b'},
{id : 3, name : 'c'}
]
const listConent = list.map(item => (
<li key = {item.id}>{item.name}</li>
// 下面这样会报warning,没有key,没有唯一性
//<li>{item.name}</li>
))
return(
<ul>{listConent}</ul>
)
}
export default App;-
在react中遍历数据时,推荐在组件中使用 key 属性。这里的数组最好要设置一个key,保证当前元素的唯一性(即上面的id,一般在后端设置好这样的形态再返回到前端)
这样处理的原因是可以适配diff算法,更高效地创建react元素树以更新UI
详情可以参考:React总结:一文知React
-
-
响应事件与状态更新
注意要使用 useState 进行状态的更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import { useState } from 'react';
function App(){
const [content,setContent] = useState("初始内容")
function handClick(){
setContent("新内容")
}
return (
<>
<div>{content}</div>
<button onClick={handClick}>按钮</button>
</>
)
}
export default App;
组件间数据共享:props
父组件传递数据给子组件
-
定义:React 组件使用 props 来互相通信。每个父组件都可以提供 props 给它的子组件,从而将一些信息传递给它
-
语法:
-
预定义 props 后将其传递给子组件
1
2
3
4
5
6
7
8
9
10// 父组件Profile将person和size传递给子组件Avatar
export default function Profile() {
return (
<Avatar
// 这里为什么使用双括号:第一个括号表示传递,第二个括号是表示对象
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
} -
在子组件中读取 props
1
2
3
4
5
6
7
8
9
10
11
12// 在子组件中读取 person 和 size
function Avatar({ person, size = 100 }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}size = 100
:设置Prop的默认值
-
-
将 JSX 作为子组件传递(组件插槽)
-
可以直接将JSX传递,父组件将在名为
children
的 prop 中接收到该内容。使用标签头和标签尾来闭合 -
如果除了 children 中的 JSX ,还想要传递一些预定义的 prop的话,也可以设置自闭和传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35function List({ children, title, footer = <div>默认底部</div> }) {
return (
<>
<h2>{title}</h2>
<ul>
{children}
</ul>
{footer}
</>
);
}
export default function App() {
return (
<>
<List
title="列表1"
footer={<p>这是底部内容1</p>}
>
<li>内容1</li>
<li>内容2</li>
<li>内容3</li>
</List>
<List
title="列表2"
footer={<p>这是底部内容2</p>}
>
<li>内容A</li>
<li>内容B</li>
<li>内容C</li>
</List>
</>
);
} -
子组件传递数据给父组件
这里想要在子组件中status的值发生变化后,回传status给父组件
1 | import { useState } from "react"; |