React
以下:携程前端训练营 感觉讲的一般般
React初学者指南
程序员操作虚拟DOM,React操作实际DOM
-
React重要性质:
- 组件可组合 组件的目的在于重用
- 一个组件独立于其他组件,修改一个组件的时候不相干的组件不会互相干扰
-
React项目的创建:
- Create React App
npx create-react-app my-app
- 使用bable和webpack编译和打包代码,可以使用ESLint来检测代码
- Vite
- 使用ESBuild代替webpack
- Next.js
- Create React App
-
npm和npx
npx是一个由Node.js官方提供的用于快速执行npm包中的可执行文件的工具。它可以帮助我们在不全局安装某些包的情况下,直接运行该包提供的命令行工具。npx会在执行时,检查本地项目中是否安装了对应的依赖,如果没有安装则会自动下载安装,并执行命令。如果本地已经存在该依赖,则直接执行命令。npx 不会像 npm 或 yarn 一样将包下载到本地的 node_modules 目录中。相反,它会在执行命令时,在本地缓存中寻找并下载包,然后执行该包中的命令。这样可以避免在开发过程中在全局安装大量的包,同时也可以确保使用的是最新版本的包。
npx会在执行完命令后删除下载的包。这是因为npx会在执行命令之前,将需要执行的包下载到一个临时目录中,并在执行完毕后删除该目录。这样可以避免在本地留下不必要的依赖包。如果需要保留依赖包,可以使用–no-cleanup选项来禁止删除下载的包。
-
React 常用功能:
- JSX
- Compoents
- Props and state
- Lists,keys,and events
- Core React Hooks(useState,useEffect)
- React Context 包括useContext
- 如何编写自定义React hook
- Rending and re-rending
- Pure functions
- Side effects
-
React常用类库
- 状态管理类库:Zustand
- 样式:TailwindCSS
- 路由:React Router
- 数据获取:React Query
- 对于表单:使用React Hook Form
-
每个js文件中的export default function都表示是该层的顶层组件,只能有一个
React 核心概念
React元素
我们使用JSX特性来编写React元素。因为JSX实际上只是JavaScript函数(而不是HTML),所以与HTML不同,单标记元素(如img元素)必须是自关闭的。它们必须以正斜杠/结尾:
1 |
|
JSX是构造React应用程序最常用的方式,但它不是React所必需的,React.createElement()函数和JSX语言是一一对应的
1 |
|
JSX不能被浏览器理解。它需要被编译成浏览器可以理解的普通JavaScript。最常用的JSX编译器称为Babel。
总结JSX:
JSX
JSX中可以嵌套React组件;react组件必须以大写字母开头,HTML标签必须是小写字母开头;组件必须返回JSX
- 什么是React组件?(见下)
- 如何实现嵌套?只要将React组件名嵌套到别的组件即可
1
2
3
4
5
6
7
8export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
React组件
React组件名必须是以大写字母开头,React组件时返回标签的JS函数,如下:
1
2
3
4
5function MyButton() {
return (
<button>I'm a button</button>
);
}
组件之间的值传递
React组件可以接受传递给它们的称为props的数据,props从父组件传递给子组件;JS中的任何值都可以作为props传递,包括其它元素和组件;有两种传递方式,一种是作为组建的属性传递,第二种是放在组件的开始标签和结束标签之间传递
1 |
|
条件渲染
- 使用if语句:
1
2
3
4
5
6
7
8
9
10
11let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
); - 使用三元运算符,直接在JSX部分使用
1
2
3
4
5
6
7<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>
列表渲染
使用for循环或者array的map()函数
1 |
|
JSX语法
JSX是构造React应用程序最常用的方式,但它不是React所必需的,React.createElement()函数和JSX语言是一一对应的
1 |
|
- 标签必须闭合,只有一个标签的
img input br
元素必须以正斜杠结尾;双标签的JSX元素必须要有结束标签或者尾随正斜杠结束 - 组件不能返回多个JSX标签,标签必须包裹在同一个共享的父亲级中,使用
<div>...</div>
或使用空的<>...</>
包裹 - JSX属性的命名和HTML属性不同,为了区分JS语法,一些可能出现在元素中的JS中的保留字必须改名,比如元素中的
class
属性要更改为className
- 在多行编写JSX元素,必须以括号括起来(不管JSX元素是一个变量还是函数返回值);JSX中注释用花括号括起来
- React元素内联样式:内联样式要使用两组花括号,内联样式不是普通字符串,是对象属性,即将样式整体作为一个有键值对的复杂对象处理
1
2
3
4
5
6
7
8
9<img
className="avatar"
src={user.imageUrl}
alt={'Photo of ' + user.name}
style={{
width: user.imageSize,
height: user.imageSize
}}
/> - JSX嵌入动态值:用花括号括起来
1
2
3return (
<h1>{user.name}</h1>
); - 元素的响应事件:响应事件可以直接在组件中声明事件处理函数来响应事件,直接将函数名以参数传递给事件
1
2
3
4
5
6
7
8
9
10
11function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}注意,react中如果元素响应事件需要传参,最好写成箭头函数
传递一个函数(正确) 调用一个函数(错误)
<button onClick={() => alert('...')}>
<button onClick={alert('...')}>
React应用
1. ReactDOM.render():通过将应用挂载到HTML元素上来渲染(显示)应用
2. JSX元素:称为“根节点”,因为它是应用程序的根节点。意思是,渲染它将渲染其中的所有子元素
3. HTML (DOM)元素:应用程序在HTML页面中插入的位置。元素通常是一个id为“root”的div,位于index.html文件中。
React上下文
props是级级传递的,为了避免某些不需要改变量的组件去作为“中间人”,使用React中的createContext函数创建一个组件。
const NameContext = createContext('');
生产者(父)使用NameContext.provider包裹,传递的值放在value中,如传递多值,则打包称为一个对象;消费者使用NameContext.Consumer包裹
1 |
|
react 事件处理
不能监听react组件上的事件,只能监听JSX元素上的事件,最基本的react事件是`onClick,onChange,onSubmit`
- onClick 处理JSX元素(按钮)上的单击事件
- onChange 处理键盘事件(即用户输入或文本区域)
- onSubmit 处理来自用户的表单提交
React钩子函数
以use开头的函数被称为Hook;Hook 比普通函数更为严格。只能在组件(或其他 Hook)的 顶层 调用 Hook。如果你想在一个条件或循环中使用 钩子函数,请提取一个新的组件并在组件内部使用它。
useState
定义钩子的时候,useState函数中传入的值就是初始值,一般会按照[something, setSomething]
这样命名
1 |
|
- 状态提升:如果想让拥有hook的两个组件共享数据,则需要将各个按钮的 state “向上” 移动到最接近包含所有按钮的组件之中。在最大的组件中声明好state,接着把对应的something和setSomething向下传递给子组件。
useEffect
- useEffect 是一个 React Hook,它允许你 将组件与外部系统同步。
useEffect(setup, dependencies?)
- setup 函数 ,其 setup 代码 用来连接到该系统。它应该返回一个 清理函数(cleanup),其 cleanup 代码 用来与该系统断开连接。
- 一个 依赖项列表,包括这些函数使用的每个组件内的值。
- 使用场景:参考
- 不传依赖项:useEffect在每次渲染后都执行
- 依赖项为空数组:希望只在第一次渲染之后执行
- 设置定时器,避免定时器每次渲染都重新计时
- 进行数据请求【可以调用Ajax】
- 从 URLSearchParams 获取数据,用于初始化 state。
- 依赖项为非空数组:在每次渲染后,非空数组上的 state 有更新时,就会执行 EffectCallback,即 useEffect 传入回调函数。
useRef
- useRef 是一个 React Hook,它能帮助引用一个不需要渲染的值。可以直接通过该hook访问JSX元素。
- 可以通过 ref 操作 DOM
当 React 创建 DOM 节点并将其渲染到屏幕时,React 将会把 DOM 节点设置为 ref 对象的 current 属性。当节点从屏幕上移除时,React 将把 current 属性设置回 null。1
2
3
4
5
6
7
8
9
10
11
12
13import { useRef } from "react";
function Header() {
const inputRef = useRef(); //参数为初始值=
window.addEventListener("keydown", (event) => {
if (event.code === "KeyK" && event.ctrlKey) {
event.preventDefault();
inputRef.current.focus();
}
});
//将 ref 对象作为 ref 属性传递给想要操作的 DOM 节点的 JSX:
return <input ref={inputRef} />
}
useContext
- useContext 是一个 React Hook,可以让你读取和订阅组件中的 context。
- 返回值:它被确定为传递给树中调用组件上方最近的 SomeContext.Provider 的 value。如果没有这样的 provider,那么返回值将会是为创建该 context 传递给 createContext 的 defaultValue。
- useContext() 总是在调用它的组件** 上面 寻找最近的 provider。它向上搜索, 不考虑 **调用 useContext() 的组件中的 provider。
- 应用场景:上下文:提供一种比标准context更为简单的方式
- provider还是老方式,consumer可以直接通过useContext(context名)来获取value
useCallback
- useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。防止重新创建函数影响应用性能。
- 用法:
useCallback(fn, dependencies)
- 其中dependencies:有关是否更新 fn 的所有响应式值的一个列表。响应式值包括 props、state,和所有在你组件内部直接声明的变量和函数。
- 使用场景:跳过多次渲染,提升性能:useCallback 在多次渲染中缓存一个函数,直至这个函数的依赖发生改变。
1
2
3
4
5
6
7
8
9
10import { useCallback } from 'react';
function ProductPage({ productId, referrer, theme }) {
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails,
});
}, [productId, referrer]);
//...
useMemo
- useMemo 是一个 React Hook,它在每次重新渲染的时候能够缓存计算的结果。
- 用法:
const cachedValue = useMemo(calculateValue, dependencies)
- calculateValue必须是一个没有任何参数的 calculation 函数,像这样 () =>,并且返回任何你想要的计算结果。
- 类似地,dependencies中记录所有在 calculateValue 函数中使用的响应式变量组成的数组。
自定义hook
react错误捕捉
React库
axios
发起请求 在useEffect里,一般放get,其他放单独函数里axios.put/post/delete.then
redux
状态管理库,提供了store用来管理大量数据
-
创建store需要使用
import { createStore } from 'redux';
Don’t create more than one store in an application!
-
createStore函数
接受三个参数:- 第一个参数是一个函数,通常被称为 reducer(必需的);该函数一般有两个参数 state和action
- 第二个参数是状态state的初始值(可选)(一般在reducer函数里初始化)
- 第三个参数是一个增强器enhancer,如果有的话,我们可以传递中间件(可选的)
1
2
3
4
5
6
7import { createStore } from 'redux';
const reducer = (state = 0, action) => {
console.log('reducer called');
return state;
};
const store = createStore(reducer); -
订阅store中的变化
使用store.subscribe
函数
一旦我们将reducer传递给createStore函数,它就会立即被调用。 -
改变store
更改存储的唯一方法是分派操作。如下:1
2
3
4
5
6
7
8store.dispatch({
type: 'INCREMENT'
})
store.dispatch({
type: 'INCREMENT',
payload: 1
});上述函数向store发送一个类型为INCREMENT的action。dispatch函数接受一个对象作为参数,这个对象被称为一个action。该action必须具有如上所示的type属性。如果你没有传递type属性,那么你会得到一个错误。type是自定义的,但是建议使用大写和下划线分隔。
- 注意,reducer中不能更改原来的state
- 还可以在dispatch函数中添加额外的信息作为action的一部分,可以添加payload,获取payload可以使用action.payload
Router
- React Router DOM是针对web应用的,React Router Native是针对使用React Native制作的移动应用的。
npm install react-router-dom
- 导入
import { BrowserRouter as Router } from 'react-router-dom';
导入BrowserRoute时,通常将其别名(重命名)为“Router”。如果我们想在整个应用程序中提供路由,它需要被包裹在整个组件树中。 - Route组件:每个路由提供至少两个属性,path和component(或render):
- path 属性指定一个路由位于应用程序的哪个路径上。
例如,对于一个about页面,我们可能希望该路由可以在路径’/about’中访问。 - render或component属性用于显示路径的特定组件。component属性只能接收到一个给定组件的引用,而render通常用于应用一些条件逻辑来呈现一个或另一个组件。对于render,你可以使用对组件的引用,也可以使用函数
- 也可以完全不要render和component属性,使用组件作为route的子组件
- 如果想让一个组件(比如导航栏)在每个页面上都可见,把它放在browser router中,要声明的路由的上方(或下方)
- path 属性指定一个路由位于应用程序的哪个路径上。
- Switch Component
- 使用excat解决同时出现两个页面的问题:
<Route exact path="/" component={Home} />
- 使用"react-router-dom"中的switch组件:switch组件查看它的所有子路由,并显示其路径与当前URL匹配的第一个子路由,一次显示一个页面。
- 使用excat解决同时出现两个页面的问题:
- 404 Route
<Route path="*" component={NotFound} />
- Link Component
一般在的Navbar中,创建link,指定to属性进行跳转,可以提供内联样式1
2
3
4
5
6
7
8function Navbar() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
)
} - NavLink Component
允许创建active link的样式,可以使用activeClassName 属性或者直接使用行内样式activeStyle - Redirect Component
重定向组件<Redirect to="/" />
- useHistory Hook
- 它们可以被作为普通的React hook调用,我们可以使用它们的值。在我们的任何组件顶部调用它,并获得与我们组件相关的数据。比如他们所在的pathname,以及query parameters等,所有的location数据都可以从
history.location
中访问。 - 可以使用
history.push
完成重定向
- 它们可以被作为普通的React hook调用,我们可以使用它们的值。在我们的任何组件顶部调用它,并获得与我们组件相关的数据。比如他们所在的pathname,以及query parameters等,所有的location数据都可以从
- useLocation Hook
- 如果你只想要location数据,那么你所需要做的就是调用useLocation来获取与
history. location
数据相同的对象
- 如果你只想要location数据,那么你所需要做的就是调用useLocation来获取与
- useParams Hook + Dynamic Routes
- 动态路由写法:在参数之前加上冒号作为前缀
/blog/:postSlug
- 使用useParams hook访问任何路由参数,useParams将返回一个对象,该对象将包含与我们的路由参数匹配的属性
- 动态路由写法:在参数之前加上冒号作为前缀
- useRouteMatch Hook
- 想知道给定的组件是否在某个页面上,可以使用useRouteMatch hook
React Native
- 架构:
- 旧:JSC环境中运行的rn业务代码 通过Bridge完成js代码和原生端API和组件的调用
- 新:JSI: JavaScript Interface 支持JS对象持有C++的引用,同步调用
- 核心组件:
基础
- 样式:推荐少用内联,多用
StyleSheet.create({})
- 布局:主要布局基于Flexbox,通过flexDirection(默认垂直排序),使用justifyContent和alignItems控制子元素在主轴和次轴的排列方式,RN支支持relative(默认)和absolute两种position设置;宽、高度可以设置自适应(默认)、百分比、固定值
- Props:组件的属性,父子组件之间的数据传输;
- props是不可变的,外部属性的变更也会引起组件的渲染
- state:和react一样
- 交互
- 触摸事件:
- 点击onPress.onLongPress onDoublePress、滑动等
- 事件处理与WEB开发中的事件处理非常相似
- 事件冒泡和捕获遵循DOM标准
- 以便使用Touchable系列组件响应事件
- 动画
- Animated:声明的形式定义动画的输入和输出;支持组合、跟踪状态值、跟踪手势
- LayoutAnimation:全局范围;更新flexbox布局
- 导航
- 官方导航库:
Reat Navigation
安装:npm install @react-navigation/native @react-navigation/stack
- 提供多种导航组件
- 官方导航库:
- 触摸事件:
- 网络请求
- 使用useEffect()中的fetch
1
2
3
4
5
6
7useEffect(() => {
fetch('https://reactnative.dev/movies.json')
.then((response) => response.json())
.then((json) => setData(json.movies))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
- 使用useEffect()中的fetch
- 生命周期
- 组件生命周期、响应式Effect生命周期、页面生命周期
- 组件挂在mounting(轻量初始化)->页面更新Updating->Unmounting
- 一般使用函数组件
- 对于整个页面生命周期:RN没有提供页面状态变化的处理(切后台等),解决方法:
import{useNavigation,useFocusEffect}from '@react-navigatio /native'
- 环境搭建:使用后Expo