1. 渲染一个基本组件
以下一个小demo用来展示第一个用react语法来编写的组件
javascript
// main文件
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
// 父子组件单向传递
<App type="text"/>
);
typescript
function App({ type }: { type: string }){
const h1 = <h1>react后台管理</h1>;
const list = ["tom", "Jack", "Lucy", "Lily"];
const style = { color: "pink", fontSize: "16px" };
const value = "123";
const title = "标题1"
return (
<div className="APP">
{h1}
<div>
// 在JSX中使用可以使用大括号编辑javaScript
{list.map((item) => (
<span key={item} style={style}>
{item}
</span>
))}
</div>
<div>{title}</div>
<div>
<div>用户名称:{user.name}</div>
<div>用户性别:{user.gender}</div>
</div>
<input type={type} value={value}></input>
</div>
)
}
export default App;
2. 常见hooks的用法
2.1 useState
语法定义:
scss
const [state, dispatch] = useState(initData);
state: 定义的数据源,可视作一个函数组件内部的变量,但只在首次渲染被创造。
dispatch: 改变state的函数,推动函数渲染的渲染函数,即会进行触发=>渲染=>提交三步骤。dispacth有两种情况-非函数和函数。
initData: state的初始值,initData有两种情况-非函数和函数。
typescript
function App({ type }: { type: string }){
const h1 = <h1>react后台管理</h1>;
const list = ["tom", "Jack", "Lucy", "Lily"];
const style = { color: "pink", fontSize: "16px" };
const value = "123";
let condition = 2;
const [user, setUser] = useState({
name: "terrence",
gender: "male",
});
const [title, setTitle] = useState("标题1");
const changeTitle = () => {
setTitle("标题2");
};
const changeUserName = () => {
// 当要修改一个数组或对象中的某个元素或属性时,
// 需要使用展开运算符进行浅拷贝,最后修改需要修改的属性
setUser({ ...user, name: "Bob" });
};
const changeUser = () => {
setUser({ name: "Lily", gender: "female" });
};
return (
<div className="APP">
{h1}
<div>
// 在JSX中使用可以使用大括号编辑javaScript
{list.map((item) => (
<span key={item} style={style}>
{item}
</span>
))}
</div>
<div>{title}</div>
<div>
<div>用户名称:{user.name}</div>
<div>用户性别:{user.gender}</div>
</div>
<input type={type} value={value}></input>
<br />
<div style={{ marginTop: "10px" }}>
<button onClick={changeTitle}>修改标题</button>
<button onClick={changeUserName}>修改用户名称</button>
<button onClick={changeUser}>修改用户</button>
</div>
</div>
)
}
export default App;
如果要进行需要点击按钮后,数字进行加三的操作,则需要将set操作更改为函数形式,因为state如同一张快照,在下一次渲染时,相同的操作会合并为一次操作,因此,可以改变为操作改成函数形式,这样会将操作进行入队,在react下次渲染时,会依次执行队列中的操作。
javascript
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
// 每点击一次按钮只会触发一次加一操作
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
</>
)
}
scss
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
// 修改为函数形式即可变成递增操作
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
}}>+3</button>
</>
)
}
2.2 useEffect
语法定义:
scss
useEffect(setup, dependenices)
setup: 是用来处理Effect函数。setup函数会选择性返回一个清理函数。当组件首次被添加到DOM之前,会执行一次setup函数,在每次依赖项发生变化后,回显执行清理函数,在新DOM渲染时,会再执行一次setup函数。当组件被从DOM中移除,会执行最后一次清理函数。
dependenciese: setup代码中的依赖项。依赖项必须是的固定形式,如[dep1, dep2, dep3]
tips: useEffect是用来模拟了组件的更新这段生命周期。
其中有一些重要的区别如下:
- 传递依赖数组时,setup函数会在组件在渲染到DOM上时,会执行一次,当依赖项发生了变化时,会重新执行一次。
- 传递空数组时,setup函数只会在组件渲染到DOM上之前执行一次。
- 不传递依赖项时,setup函数会在组件每次渲染时,执行一次。
以下是使用useEffect来监听窗口变化大小
javascript
import { useState, useEffect } from "react";
export function useWindowSize() {
const [size, setSize] = useState({
// 整个屏幕的宽高
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
});
const handleResize = () => {
setSize({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
});
};
useEffect(() => {
// 注意凡是监听全局全局变量的改变的一定要卸载
// 防止内存泄漏
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return [size];
}
2.3 useRef
语法:
csharp
const ref = useRef(initialValue)
- initialValue:ref 对象的 current 属性的初始值。可以是任意类型的值。这个参数会首次渲染后被忽略。
返回值:
useRef 返回一个只有一个属性的对象:
- current:最初,它被设置为你传递的 initialValue。之后你可以把它设置为其他值。如果你把 ref 对象作为一个 JSX 节点的 ref 属性传递给 React,React 将为它设置 current 属性。
在后续的渲染中,useRef 将返回同一个对象。
用法
javascript
import { useRef, useState } from 'react'
import './App.css'
function App() {
const userRef = useRef<HTMLInputElement>(null)
const [val, setVal] = useState('')
const handleClick = () => {
userRef.current?.focus()
setVal(userRef.current?.value || '')
}
return (
<div className='App'>
<input type='text' ref={userRef} />
<button onClick={handleClick}>按钮</button>
<p>{val}</p>
</div>
)
}
export default App
2.4 useContext
语法:
ini
const value = useContext(SomeContext)
- SomeContext:先前用 createContext 创建的 context。context 本身不包含信息,它只代表你可以提供或从组件中读取的信息类型。
用法:
javascript
// App.tsx
const UserContext = React.createContext({ name: '' })
function App() {
return (
<UserContext.Provider value={{ name: 'jack' }}>
<div>
<p>欢迎学习React后台课程</p>
<Child1 />
</div>
</UserContext.Provider>
)
}
function Child1() {
return (
<div>
<p>
<span>Child1</span>
</p>
<p>
<Child2 />
</p>
</div>
)
}
function Child2() {
const { name } = useContext(UserContext)
return <span>Child2{name}</span>
}
2.5 useTransition
useTransition 是一个让你在不阻塞 UI 的情况下来更新状态的 React Hook。
语法:
scss
const [isPending, startTransition] = useTransition()
参数
useTransition 不需要任何参数。
返回值
useTransition 返回一个由两个元素组成的数组:
- isPending 标志,告诉你是否存在待处理的转换。
- startTransition函数 允许你将状态更新标记为转换状态。
javascript
function App(){
const [query, setQuery] = useState('');
const [sequence, setSequence] = useState([]);
const [isPending, startTransition] = useTransition();
const handleInputChange = (e: any) => {
setQuery(e.target.value)
startTransition(() => {
const arr = Array.from({ length: 1000 }).fill(1);
setSequence([...sequence, ...arr]);
})
}
return (
<div>transition的用法</div>
<input type="text" onChange={handleInputChange} value={query}></input>
<div>
{isPending ?
(<div>lOADING...</div>)
:
sequence.map((item: number, index: number) => {
return <div key={index}>{item}</div>
})
}
</div>
)
}
3. 与Vue.js的比较
3.1 大致的比较
3.2 diff算法实现的比较
vue中diff算法实现流程:
- 在内存中构建虚拟dom树
- 将内存中虚拟dom树渲染成真实dom结构
- 数据改变的时候,将之前的虚拟dom树结合新的数据生成新的虚拟dom树
- 将此次生成好的虚拟dom树和上一次的虚拟dom树进行一次比对(diff算法进行比对),来更新只需要被替换的DOM,而不是全部重绘。在Diff算法中,只平层的比较前后两棵DOM树的节点,没有进行深度的遍历。
- 会将对比出来的差异进行重新渲染。
react中diff算法实现流程:
- DOM结构发生改变-----直接卸载并重新create
- DOM结构一样-----不会卸载,但是会update变化的内容
- 所有同一层级的子节点.他们都可以通过key来区分-----同时遵循1.2两点
(其实这个key的存在与否只会影响diff算法的复杂度,换言之,你不加key的情况下,diff算法就会以暴力的方式去根据一二的策略更新,但是你加了key,diff算法会引入一些另外的操作)
3.3 Vue和React生命周期
Vue:
初始化阶段:
- beforeCreate()
- created():已经挂载了数据,但是dom节点没有渲染。此阶段可以发送一些ajax请求与初始化事件。
- beforeMount()
- mounted():数据已经挂载完毕,可以获取到真实的dom。
运行中的阶段:
- beforeUpdate():这个阶段获取到的数据是dom内容更新前的数据。此阶段应该避免对数据的更改,会造成死循环。
- updated():可以获取到更新过后的数据,当数据触发重新渲染时,可以在updated函数中获取真实的dom元素。
销毁阶段:
- beforeDestroy:这个钩子,可以在组件被销毁前,做一些清除工作,比如定时器,窗口大小发生变化的绑定事件。
- destroyed:这个阶段组件只有真实dom存在在页面中
其他:
- 被keep-alive包裹的组件会有active与deacive两个生命周期,因为组件不会被销毁,组件会在活动与失活之间的状态切换。
React:
react组件的生命周期主要分为四个阶段:
- 初始化阶段:初始化属性props和状态state。
- 挂载阶段:将组建挂载到页面上。
- 更新阶段:当组件中的props和state发生变化会进行更新。
- 卸载阶段:组件从页面中删除。
4. 参考资料
- react中文文档:react.docschina.org/learn
- react慕课网入门教程:coding.imooc.com/class/644.h...