React
- react中文件名可以是js或jsx,文件名为tsx则表示应用了typescript
- 组件名称必须大写
- jsx或tsx中的( )代表其中为html
- jsx或tsx中的{ }代表其中为js
- export default可以写class或函数前面,也可以写页面底部
jsx或tsx中特殊的html属性
export default function App() {
const msg = "123"
return (
<div>
<label htmlFor="input">用户名</label>
<input type="text" id="input" />
<section className="hello"></section>
<h2 style={{ color: "pink" }}>{msg}</h2>
</div>
)
}
export default function App() {
const msg = "123"
return (
<div>
<label htmlFor="input">用户名</label>
<input type="text" id="input" />
<section className="hello"></section>
<h2 style={{ color: "pink" }}>{msg}</h2>
</div>
)
}
htmlForfor相当于html的for属性
<label for="input">用户名</label>
<label for="input">用户名</label>
className相当于html的class属性
<section class="hello"></section>
<section class="hello"></section>
style = 相当于html的style="",
<h2 style="color: pink;">{msg}</h2>
<h2 style="color: pink;">{msg}</h2>
至于为什么是双括号,单个括号代表使用js,双括号代表使用了一个js对象,这个对象控制样式
export default function App() {
const msg = "123"
const colorStyle = { color: "pink" }
return <h2 style={colorStyle}>{msg}</h2>
}
export default function App() {
const msg = "123"
const colorStyle = { color: "pink" }
return <h2 style={colorStyle}>{msg}</h2>
}
遍历数组渲染
只能使用map,因为forEach只会返回undefined
export default function App() {
const arr = ["1", "2", "3"]
return (
<>
<ul>
{arr.map((item, index) => {
return <li key={index}>{item}</li>
})}
</ul>
</>
)
}
export default function App() {
const arr = ["1", "2", "3"]
return (
<>
<ul>
{arr.map((item, index) => {
return <li key={index}>{item}</li>
})}
</ul>
</>
)
}
styled-components
样式组件,需要安装vscode插件vscode-styled-components来支持语法高亮和提示
import React from "react"
import styled from "styled-components"
function App() {
return (
<StyleContainer>
<div>App</div>
</StyleContainer>
)
}
const StyleContainer = styled.div`
color: pink;
`
export default App
import React from "react"
import styled from "styled-components"
function App() {
return (
<StyleContainer>
<div>App</div>
</StyleContainer>
)
}
const StyleContainer = styled.div`
color: pink;
`
export default App
Hook
Hook是React16.8.0起新增的特性,它可以在不编写 class 的情况下使用 state 以及其他的 React 特性
useState
useState
会返回一个数组
:数组的第一个参数是一个变量,第二个参数是一个更新这个变量的函数
import React, { useState } from 'react';
function Example() {
// 通过数组解构赋值的方式,声明变量和变量更新函数
const [count, setCount] = useState(0);
return (s
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
import React, { useState } from 'react';
function Example() {
// 通过数组解构赋值的方式,声明变量和变量更新函数
const [count, setCount] = useState(0);
return (s
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
声明多个state变量
const [age, setAge] = useState(42)
const [fruit, setFruit] = useState('banana')
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }])
const [age, setAge] = useState(42)
const [fruit, setFruit] = useState('banana')
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }])
useEffect
在 React 组件中执行过数据获取、订阅或者手动修改过 DOM。统一把这些操作称为“副作用”,或者简称为“作用”
useEffect
就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
具有相同的用途,只不过被合并成了一个 API
什么是函数副作用?
函数副作用是指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。副作用的函数不仅仅只是返回了一个值,而且还做了其他的事情,比如:
componentDidMount和componentDidUpdate
(1)初次渲染时执行 (2)每次页面更新之后都要执行
没有第二个参数代表监听所有的属性更新
useEffect(() => {
document.title = `You clicked ${count} times`
})
useEffect(() => {
document.title = `You clicked ${count} times`
})
监听多个属性的变化需要将属性作为数组传入第二个参数。
useEffect(() => {
console.log('n或者m变化了')}
},[n,m])
useEffect(() => {
console.log('n或者m变化了')}
},[n,m])
componentDidMount
初次渲染时执行 ,第二个参数为空数组
useEffect(() => {
document.title = `You clicked ${count} times`
}, [])
useEffect(() => {
document.title = `You clicked ${count} times`
}, [])
componentWillUnMount
useEffect( () => {
return () => { 清理函数 }
}, [] )
useEffect( () => {
return () => { 清理函数 }
}, [] )
严格模式React.StrictMode-渲染执行两次
什么是 React.StrictMode?
React.StrictMode 是在 2018 年的 16.3.0 版本中引入的组件。一开始,它只用在类组件中,而在 16.8.0 中,它对 hook 同样适用。 就像在版本说明中提及的一样:
React.StrictMode 是帮助应用适应异步渲染的组件
所以它应该用来帮助工程师避免常见的错误,并使他们的 React 应用抛弃过时的 API,从而逐步升级。 这些提示对于更好地调试是有帮助的,因为这个库正在向异步渲染时代迈进,所以大的改动时时发生。 很有用,对吧?
严格模式下函数组件每次渲染执行两次,使用hooks的话即useEffect
会触发两次,是因为React在开发模式下会刻意执行两次渲染,以防止组件内有什么side effect引起 bug,提前预防
关闭严格模式,注释掉React.StrictMode
标签即可
import React from "react"
import ReactDOM from "react-dom/client"
import "./index.css"
import App from "./App"
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
// <React.StrictMode>
<App />
// </React.StrictMode>
)
import React from "react"
import ReactDOM from "react-dom/client"
import "./index.css"
import App from "./App"
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
// <React.StrictMode>
<App />
// </React.StrictMode>
)
react-router-dom
npm i -s react-router-dom
npm i -s react-router-dom
Link控制路由切换,Routes与Route控制显示的子路由
import "./App.css"
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom"
function List() {
return <div>list</div>
}
function Home() {
return <div>home页面</div>
}
function App() {
return (
<div className="App">
<Router>
<Link to="/">首页</Link>
<Link to="/list">列表</Link>
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/list" element={<List />}></Route>
</Routes>
</Router>
</div>
)
}
export default App
import "./App.css"
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom"
function List() {
return <div>list</div>
}
function Home() {
return <div>home页面</div>
}
function App() {
return (
<div className="App">
<Router>
<Link to="/">首页</Link>
<Link to="/list">列表</Link>
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/list" element={<List />}></Route>
</Routes>
</Router>
</div>
)
}
export default App
父传子
父直接传
import {useState} from 'react'
import Son from './son'
const Father = ()=>{
const [count,setCount] = useState(0)
return (
<div>
<h1>父组件{count}</h1>
<button onClick={()=>setCount(count+1)}>点击</button>
<Son num={count}></Son>
</div>
)
}
export default Father
import {useState} from 'react'
import Son from './son'
const Father = ()=>{
const [count,setCount] = useState(0)
return (
<div>
<h1>父组件{count}</h1>
<button onClick={()=>setCount(count+1)}>点击</button>
<Son num={count}></Son>
</div>
)
}
export default Father
子在props中接收
const Son = (props)=>{
return (
<div>
<p>{props.num}</p>
</div>
)
}
export default Son
const Son = (props)=>{
return (
<div>
<p>{props.num}</p>
</div>
)
}
export default Son
子传父
父向子传递回调函数
import {useState} from 'react'
import Son from './son'
const Father = ()=>{
const [count,setCount] = useState(0)
return (
<div>
<h1>父组件{count}</h1>
<Son num={count} changeCount={(count)=>setCount(count)}></Son>
</div>
)
}
export default Father
import {useState} from 'react'
import Son from './son'
const Father = ()=>{
const [count,setCount] = useState(0)
return (
<div>
<h1>父组件{count}</h1>
<Son num={count} changeCount={(count)=>setCount(count)}></Son>
</div>
)
}
export default Father
子调用回调函数
const Son = (props)=>{
const {num,changeCount}= props
return (
<div>
<button onClick={()=>changeCount(num+1)}>点击</button>
</div>
)
}
export default Son
const Son = (props)=>{
const {num,changeCount}= props
return (
<div>
<button onClick={()=>changeCount(num+1)}>点击</button>
</div>
)
}
export default Son
useCallback
父组件useCallback
import {useState,useCallback} from 'react'
import Son from './son'
const Father = ()=>{
const [count,setCount] = useState(0)
const changeCount=useCallback(num=>setCount(num),[])
return (
<div>
<h1>父组件{count}</h1>
<Son num={count} add={changeCount}></Son>
</div>
)
}
export default Father
import {useState,useCallback} from 'react'
import Son from './son'
const Father = ()=>{
const [count,setCount] = useState(0)
const changeCount=useCallback(num=>setCount(num),[])
return (
<div>
<h1>父组件{count}</h1>
<Son num={count} add={changeCount}></Son>
</div>
)
}
export default Father
子组件useCallback
import {useCallback} from 'react'
const Son = (props)=>{
const {add,num} = props
const c = useCallback(()=>add(num+1),[num,add])
return (
<div>
<p>{num}</p>
<button onClick={c}>点击</button>
</div>
)
}
export default Son
import {useCallback} from 'react'
const Son = (props)=>{
const {add,num} = props
const c = useCallback(()=>add(num+1),[num,add])
return (
<div>
<p>{num}</p>
<button onClick={c}>点击</button>
</div>
)
}
export default Son
useContext
祖先中createContex
import {useState,createContext} from 'react'
import Son from './son'
export const Context = createContext(0)
const Father = ()=>{
const [count,setCount] = useState(0)
return (
<div>
<h1>父组件{count}</h1>
<Context.Provider value={count}>
<Son add={(num)=>setCount(num)}></Son>
</Context.Provider>
</div>
)
}
export default Father
import {useState,createContext} from 'react'
import Son from './son'
export const Context = createContext(0)
const Father = ()=>{
const [count,setCount] = useState(0)
return (
<div>
<h1>父组件{count}</h1>
<Context.Provider value={count}>
<Son add={(num)=>setCount(num)}></Son>
</Context.Provider>
</div>
)
}
export default Father
后代useContext
import {useContext} from 'react'
import { Context } from "./father"
const Son = (props)=>{
const count = useContext(Context)
const {add} = props
return (
<div>
<p>{count}</p>
<button onClick={()=>add(count+2)}>点击</button>
</div>
)
}
export default Son
import {useContext} from 'react'
import { Context } from "./father"
const Son = (props)=>{
const count = useContext(Context)
const {add} = props
return (
<div>
<p>{count}</p>
<button onClick={()=>add(count+2)}>点击</button>
</div>
)
}
export default Son
useMemo
用于减少每次组件重新渲染时重复进行复杂的计算,参数为一个函数和可选的依赖项数组,返回传入函数的调用结果
React.memo()
React.memo() 用于减少子组件的重新渲染
React.memo 会检查props
的变更,仅当传入的props
发生变化时组件才会重新渲染,对复杂对象只会做浅层对比,可以通过传入第二个参数来控制对比过程
第二个参数为一个接收重新渲染前后props的函数
父组件
import {useState} from 'react'
import Son from './son'
const Father = ()=>{
const [count,setCount] = useState(0)
const a = () => { setCount(count + 1)}
console.log('父组件渲染了');
return (
<div>
<h1 onClick={a}>父组件{count}</h1>
<Son></Son>
</div>
)
}
export default Father
import {useState} from 'react'
import Son from './son'
const Father = ()=>{
const [count,setCount] = useState(0)
const a = () => { setCount(count + 1)}
console.log('父组件渲染了');
return (
<div>
<h1 onClick={a}>父组件{count}</h1>
<Son></Son>
</div>
)
}
export default Father
子组件
import React from 'react';
const Son = ()=>{
console.log('子组件被渲染了')
return (
<div>
<p>子组件</p>
</div>
)
}
export default React.memo(Son)
import React from 'react';
const Son = ()=>{
console.log('子组件被渲染了')
return (
<div>
<p>子组件</p>
</div>
)
}
export default React.memo(Son)
子组件第二个参数
function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
}
export default React.memo(MyComponent, areEqual);
function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
}
export default React.memo(MyComponent, areEqual);
shouldComponentUpdate
Vue中没有shouldComponentUpdate检查来提高性能的原因
- react: 数据变化之后,用户通过 setState 主动通知框架更新视图
- vue:数据变化之后,框架通过对原生对象和数组类型变更方法的劫持自动发现更新的部分,刷新视图,用户没有手动过程
- react: diff dom,但是不 diff 数据
- vue: diff 数据(其实不能叫 diff,而是通过对更改的劫持,自动获得了 diff),也 diff dom(其实可以不 diff,它这个机制,其实用不到 vdom)
react实现v-if
{ this.state.istrue && JSX内容 }
{ this.state.istrue && JSX内容 }
react实现v-show
<h2 style={{display:data?'block':'none'}}>
<h2 style={{display:data?'block':'none'}}>
副作用
在React中,"副作用"指的是与组件渲染无关的操作,这些操作可以在组件的生命周期内执行。副作用通常包括但不限于以下内容:
- 数据获取:从外部API、数据库或服务器获取数据。
- 数据订阅:在组件订阅外部数据源的变化,例如WebSocket连接、Redux Store的变化等。
- 手动DOM操作:直接操作DOM元素,例如滚动到特定位置、焦点管理等。
- 设置定时器或计时器:创建定时器,以便在一段时间后执行某些操作。
- 清理操作:在组件卸载或更新之前清理资源,例如取消订阅、关闭WebSocket连接、清除定时器等。
为什么要关注副作用呢?因为React是一个声明式的UI库,它的核心思想是通过描述UI应该是什么样子来构建UI,而不是告诉它如何做。在React中,组件的渲染应该是纯粹的,即给定相同的输入,它应该产生相同的输出。然而,副作用会破坏这种纯粹性,因为它们可以在组件渲染之外改变应用程序的状态。
为了处理副作用,React引入了一些特殊的函数和Hook,例如useEffect
。useEffect
Hook允许你在函数式组件中执行副作用操作,并且可以确保这些操作在适当的时机执行,如组件挂载、更新或卸载时。这有助于将副作用隔离在特定的位置,以便更容易管理和调试。
例如,你可以使用useEffect
来执行数据获取、数据订阅和其他可能会影响组件状态的副作用操作,而不会破坏组件的纯粹性。这种方式有助于使组件更可预测和可维护。