React快速入门
一、React
1.JSX
JSX全称JavaScript XML,与HTML结构相似,大大降低学习成本。
1.1 前言
1 2 3 4 5
| npx create-react-app 项目名称
cd 项目名称 npm start
|
注意:
- React元素的属性名使用驼峰命名法
- 特殊属性名:class->className,for->htmlFor、tabindex->tabIndex
- 没有子节点的React元素可以用
/>
结束。
- 小括号包裹JSX语法
JSX嵌入JS表达式
- 数据存储在JS中
- 语法:{JS表达式}
- JSX也是合法的JS表达式
- 不能出现对象和语句
1.2 JSX条件渲染
1 2 3 4 5 6 7 8 9 10
| const isLoading = false const loadData=()=>{ if(isLoading) return <div>loading...</div> return <div>数据加载完成</div> } const title =( <h1> {loadData()} </h1> )
|
1.3 JSX列表渲染
- 要渲染一组数据,应该使用数组的map()方法
- 每一个列表都应该添加key属性且不可变保持唯一
1 2 3 4 5 6 7 8 9 10
| const songs=[ {id: 1,name: '我'}, {id: 2,name: '号'} ]
const list = ( <ul> {songs.map(item=> <li key={item.id}>{item.name}</li>)} </ul> )
|
1.4 JSX样式处理
1 2 3 4 5 6 7 8
| import './index.css';
const list=( <h1 className="title"> JSX样式处理 </h1> )
|
2.TSX
2.1 使用CRA创建支持TS的项目
1 2 3 4
| npx create-react-app my-app --template typescript
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
|
- 项目新增了
tsconfig.json
用来配置TS的编译选项
- 文件扩展名为
*.tsx
- 项目中
src
文件夹下增加了react-app-env.d.ts
,用来声明React的默认类型
2.2 函数组件类型
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
| type MyProps = { name: string; age?: number; }
const Hello = ({ name, age = 18 }: MyProps) => { return <div>My Name is {name} ,my age is {age}</div>; }
const App = () => { return ( <div> <Hello name="John" age={30} /> </div> ); }
const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <App /> </React.StrictMode> );
|
2.3 class组件类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| type State = { count: number }; type Props = {message?: string};
class App extends React.Component {}
class App2 extends React.Component<{}, State> {}
class App3 extends React.Component<Props> {}
class App4 extends React.Component<Props, State> {}
|
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
| type State = { count: number }; type Props = {name: string, age?: number};
class App extends React.Component<Props> { state: State = { count: 0 };
handleClick = () => { this.setState({ count: this.state.count + 1 }); }
render() { const { name, age = 18 } = this.props; return ( <div> <h1>Hello, {name}</h1> <h2>You are {age} years old</h2> <h3>{this.state.count}</h3> <button onClick={this.handleClick}>Click me</button> </div> ); } }
|
3.组件
组件是React核心
组件特点:可复用性、独立、可组合性
3.1创建组件
注意:
- 函数名必须大写字母开头
- 必须有返回值,表示该组件的结构
- 如果返回值为null表示不渲染任何内容
1 2 3 4 5 6 7 8 9 10
| function Hello(){ return <div>这个是我的函数组件</div> }
const Hello = ()=> <div>这个是我的函数组件</div>
ReactDom.render(<Hello />,document.getElementById('root'))
|
注意:
- 类名首字母大写
- 类组件应该继承React.Component父类
- 类组件必须提供render()方法
- render()方法必须有返回值
1 2 3 4 5 6 7 8 9
| class Hello extends React.Componet{ render(){ return <div>这个是我的类组件</div> } }
ReactDom.render(<Hello />,document.getElementById('root'))
|
每个组件都应该放在单独的文件中:
步骤:
- 创建组件文件
- 在文件中导入React
- 创建组件
- 导出组件
- 在需要使用的文件中导入该组件
1 2 3 4 5 6 7 8 9 10 11
| import React from 'react'
class Hello extends React.Componet{ render(){ return <div>这个是我的JS组件</div> } }
export default Hello
|
1 2 3 4 5 6
| import React from 'react' import ReactDOM from 'react-dom' import Hello from './Hello.js'
ReactDom.render(<Hello />,document.getElementById('root'))
|
3.2 事件处理
- 事件绑定语法:
on+事件名称={事件处理程序}
- 事件的名称采用驼峰命名法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class App extends React.Component { handleClick = () => { console.log('click'); } render() { return <button onClick={this.handleClick}>按钮</button>; } }
const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <App /> );
|
- 事件处理对象
- 可以通过事件处理程序的参数获得事件对象
- 该合成事件对象兼容所有浏览器
1 2 3 4 5 6 7 8 9 10
| class App extends React.Component { handleClick(e) { e.preventDefault(); console.log('click'); } render() { return <button onClick={this.handleClick}>按钮</button>; } }
|
3.3 有状态组件和无状态组件
- 函数组件叫做无状态组件,类组件叫做有状态组件
- 状态(state)即数据
- 函数组件没有自己的状态,只负责数据的展示(静)
- 类组件有自己的状态,负责更新UI,让组件动起来
- 状态(state)值是对象,是组件的私有数据
- 只能用
setState()
来修改state
数据
setState()
用来修改状态和更新UI
- 思想:数据驱动视图
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class App extends React.Component { state = { count: 0 } render() { return ( <div> <h1>计数器:{this.state.count}</h1> <button onClick={() => this.setState({count: this.state.count + 1})}>+1</button> </div> ); } }
|
逻辑代码独立出去后this指向问题处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class App extends React.Component { state = { count: 0 } onAddOne = () => { this.setState({ count: this.state.count + 1 }) } render() { return ( <div> <h1>计数器:{this.state.count}</h1> <button onClick={this.onAddOne}>+1</button> </div> ); } }
|
1 2 3 4
| constructor(){ super() this.onAddOne = this.onAddOne.bind(this) }
|
3.4 表单处理
步骤:
- 在state中添加一个状态,作为表单元素额value值
- 给表单元素绑定change事件,将表单元素的值 设置为state的值 (双向绑定)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class App extends React.Component { state = { txt: '', } onChange = (e) => { this.setState({ txt: e.target.value, }) } render() { return ( <div> <input type="text" value={this.state.txt} onChange={this.onChange} /> </div> ); } }
|
多表单元素统一处理
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
| class App extends React.Component { state = { txt: '', content: '', city: 'SZ', isChecked: false, } onChange = (e) => { const target = e.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name;
this.setState({ [name]: value }); } render() { return ( <div> <input type="text" name='txt' value={this.state.txt} onChange={this.onChange} /><br /> <textarea name='content' value={this.state.content} onChange={this.onChange} /><br /> <select name='city' value={this.state.city} onChange={this.onChange}> <option value="BJ">北京</option> <option value="SH">上海</option> <option value="SZ">深圳</option> </select><br /> <input type="checkbox" name='isChecked' checked={this.state.isChecked} onChange={this.onChange} /><br /> </div> ); } }
|
- 调用React.createRef()创建一个ref对象
- 将创建好的ref对象添加到文本框中
- 通过ref对象获取到文本框的值
3.5 小结案例–评论
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| class App extends React.Component { state = { comments: [ { id: 1, name: 'Pete Hunt', text: 'This is one comment' }, { id: 2, name: 'Jordan Walke', text: 'This is *another* comment' }, ], username: '', content: '', }
commentsList = () => { return (this.state.comments.length === 0 ?<div className='noComment'><strong>暂无评论,快去评论吧~</strong></div> :( <ul> {this.state.comments.map((comment) => { return ( <li key={comment.id}> <h3>{comment.name}:</h3> <p>{comment.text}</p> </li> ); })} </ul> ) ); }
onChange = (e) => { const { name, value } = e.target; this.setState({ [name]: value }); }
addComment = () => { const comment = { id: this.state.comments.length + 1, name: this.state.username, text: this.state.content, }; this.setState({ comments: [...this.state.comments, comment], }); }
render() { return ( <div className='app'> <div> <input type="text" name='username' value={this.state.username} className='user' placeholder='输入姓名' onChange={this.onChange}/><br/> <textarea name='content' className='text' value={this.state.content} placeholder='输入评论' onChange={this.onChange}/><br/> <button className='btn' onClick={this.addComment}>提交</button> </div> <div><h2>评论列表</h2></div> {this.commentsList()} </div> ); } }
const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <App /> );
|
4.组件通讯
4.1 组件接收数据
组件是独立且封闭的单元,默认情况下只能使用自己的数据,而对于一个复杂的项目通常需要拆分多个组件,而多个组件需要数据传递就是组件通讯。
props的作用:接收传递给组件的数据
- 传递数据:给组件标签添加属性
- 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class App extends React.Component { render() { return ( <div> <h1>{this.props.name}</h1> </div> ); } }
const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <App name='jack' age={19} colors={['read','green']} fn={()=> console.log('Hello')}/> );
|
注意:
- props无法对属性赋值,这是个只读属性
- 类组件在使用构造函数时,需要把props作为构造函数和super()的参数
4.2 父组件传递给子组件
- 父组件要提供传递的state数据
- 给子组件标签添加属性,值为state中的数据
- 子组件通过props接收父组件中传递的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Father extends React.Component { state = { name: '王五', } render() { return ( <div> <Child name={this.state.name} /> </div> ); } }
const Child = (props) => { return ( <h1>{props.name}</h1> ); }
|
4.3 子组件传递数据给父组件
- 父组件提供回调函数
- 将该函数作为属性值传递给子组件
- 子组件通过props调用回调函数
- 将子组件的数据作为参数传递给回调函数
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
| class Father extends React.Component { state = { msg: '' } say=(data)=>{ console.log('father say:', data); this.setState({ msg: data }) } render() { return ( <div> <Child say={this.say} /> </div> ); } } class Child extends React.Component { state = { data: 'hello' } handleClick=()=>{ this.props.say(this.state.data); } render() { return ( <div> <button onClick={this.handleClick}>click</button> </div> ); } }
|
4.4 兄弟组件通信
- 状态提升
- 将多个组件需要共享的状态提升到他们最近的父组件中
- 公共父组件职责:提供共享状态、提供操作共享状态的方法
- 要通讯的子组件只需通过props接收状态或操作状态的方法
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
| class Father extends React.Component { state = { count: 0, } add = () => { this.setState({ count: this.state.count + 1, }) } render() { return ( <div> <Child1 count={this.state.count} /> <Child2 add={this.add} /> </div> ); } } const Child1 = (props) => { return ( <div> <h1>计数器:{props.count}</h1> </div> ) } const Child2 = (props) => { return ( <div> <button onClick={()=> props.add()}>+1</button> </div> ) }
|
4.5 跨多个组件通信
- 生产者与消费者
- 调用React.createContext()创建Provider和Consumer两个组件
- 使用Provider在通信组件的最近父组件
- 设置value属性,表示要传递的数据
- 使用Consumer组件接收数据
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 35 36 37 38 39 40 41 42
| const {Provider, Consumer} = React.createContext();
class App extends React.Component { state = { count: 0, }; add = () => { this.setState({ count: this.state.count + 1, }); };
render() { return ( <Provider value={{count: this.state.count,add: this.add}}> <div> <App1 /> <App2 /> </div> </Provider> ); } }
const App1 = () => ( <div> <Consumer> { value => <h1>{value.count}</h1> } </Consumer> </div> );
const App2 = () => ( <Consumer> {value => { return ( <button onClick={ ()=>value.add() }>Click</button> ); }} </Consumer> )
|
5.组件进阶
5.1 静态类型检查
prop-types库提供props属性的类型校验
1 2 3 4 5 6 7 8 9
| App.propTypes={ a: PropTypes.number, fn: PropTypes.func.isRequired, tag: PropTypes.element, filter: PropTypes.shape({ area: PropTypes.string, price: PropTypes.number, }) }
|
prop-types库提供props属性默认值
1 2 3
| App.defaultProps={ a: 10, }
|
5.2 组件的生命周期
- 生命周期:组件从创建到挂载到页面中运行,再到组件不用时卸载的过程。
- 生命周期的每个阶段总是伴随着一些方法的调用,这些方法就是生命周期的钩子函数
- 钩子函数:为开发人员在不同阶段提供操作时机
- 只有类组件才有生命周期
创建时:
- 执行时间:组件创建时
- 执行顺序:
constructor()--->render()--->componentDidMount()
钩子函数 |
触发时间 |
作用 |
constructor |
组件创建时,最先执行 |
1.初始化state 2.为事件处理程序绑定this |
render |
每次渲染都会触发 |
渲染UI(不能调用setState) |
componentDidMount |
组件挂载(DOM渲染完成)后触发 |
1.发送网络请求 2.DOM操作 |
更新时:
- 执行时间:调用函数 1.
setState()
2.forceUpdate()
3.组件接收到新的props
- 执行顺序:
render()--->componentDidUpdate()
钩子函数 |
触发时间 |
作用 |
render |
每次渲染都会触发 |
渲染UI |
componentDidUpdate |
组件更新(DOM渲染完成)后触发 |
1.发送网络请求 2.DOM操作 |
卸载时:
钩子函数 |
触发时间 |
作用 |
componentWillUnmount |
组件卸载时触发 |
清理工作 |
新的生命周期函数:
钩子函数 |
触发时间 |
用法 |
getDerivedStateFromProps |
第一次初始化组件以及后续的更新前 |
静态函数,需要加static。 返回一个对象作为新的state,返回null则不需要更新state |
getSnapshotBeforeUpdate |
组件更新前 |
在更新前获得过去的快照 |
5.3 组件复用
props的children属性
- 表示了组件的子节点,当组件标签有子节点时,props就会有该属性
- 与普通props一样,值可以是任意值。
利用复用来处理相似功能的方法
两种方式:
- render props模式
- 高阶组件
步骤:
- 创建复用的组件,在复用组件中提供状态和操作状态的方法
- 将复用组件的状态作为props.render(state)方法的参数,暴露到组件外
- 外部使用render()的属性接收参数并使用
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
| class Mouse extends React.Component { state = { x: 0, y: 0 } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } componentDidMount() { document.addEventListener('mousemove', this.handleMouseMove); } render() { return this.props.render(this.state); } } class App extends React.Component { render() { return ( <div> <h1>Move the mouse around!</h1> <Mouse render={(mouse) => ( <p>The mouse position is ({mouse.x}, {mouse.y})</p> )} /> </div> ); } }
|
children属性 代替 render属性
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
| class Mouse extends React.Component { state = { x: 0, y: 0 } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } componentDidMount() { document.addEventListener('mousemove', this.handleMouseMove); } render() { return this.props.children(this.state); } } class App extends React.Component { render() { return ( <Mouse> {(mouse) => ( <div> <h1>Move the mouse around!</h1> <p>The current mouse position is ({mouse.x}, {mouse.y})</p> </div> )} </Mouse> ); } }
|
代码优化:
1 2 3 4 5 6 7 8
| componentWillUnmount() { document.removeEventListener('mousemove', this.handleMouseMove); }
Mouse.propTypes = { children: PropTypes.func.isRequired }
|
高阶组件采用包装(装饰)模式来实现状态逻辑复用
步骤:
- 创建一个函数,名称为with开头
- 指定函数参数,为大写字母开头
- 在函数内部创建一个类组件,提供复用的状态逻辑代码
- 在该组件中,渲染参数组件,同时将状态通过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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| function withMouse(WrappedComponent){ class Mouse extends React.Component { state = { x: 0, y: 0 } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } componentDidMount() { document.addEventListener('mousemove', this.handleMouseMove); } componentWillUnmount() { document.removeEventListener('mousemove', this.handleMouseMove); } render() { return <WrappedComponent {...this.state}/> } } Mouse.displayName = `withMouse(${getDisplayName(WrappedComponent)})`; return Mouse; }
function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component'; }
const Position = (props) => { return ( <p>The current mouse position is ({props.x}, {props.y})</p> ); }
const MousePosition = withMouse(Position);
class App extends React.Component { render() { return ( <div> <h1>Move the mouse around!</h1> <MousePosition/> </div> ); } }
|
高阶组件的props的传递丢失问题:
1 2 3 4
| render() { return <WrappedComponent {...this.state} {...this.props}/> }
|
5.4 setState函数
1 2 3 4 5
| this.setState({ x: event.clientX, y: event.clientY });
|
1 2 3 4 5 6 7 8 9 10
| this.setState((prevState,props) => { return { x: event.clientX, y: event.clientY }; },()=>{
});
|
5.5 组件性能优化
1 2 3 4 5 6 7 8
| shouldComponentUpdate(nextProps,nextSate){ return true return false }
|
类组件继承React.PureComponent
,自己已经实现shouldComponentUpdate
钩子函数,来对前后props和state进行比较,来决定是否进行本次渲染。
当要对比引用类型的值时,需要重新创建对象,才能更新组件。
5.6 虚拟DOM和Diff算法
执行过程:
- React初始state,创建虚拟DOM对象树
- 根据虚拟DOM生成真正的DOM,渲染到页面中
- 当数据变化后,重新根据新的数据,创建新的虚拟DOM对象树
- 与上一次得到的虚拟DOM,使用Diff算法进行对比,得到需要更新的内容
- 最终,React只将变化的内容更新到DOM中,重新渲染到页面。
6.Hooks
- 高阶组件复用代码复杂
- 生命周期复杂
- 函数组件是无状态组件
6.1 useState
接收一个初始值,返回一个状态值和一个更新函数
1
| const [state, setState] = useState(initialState);
|
更新函数通过接收一个新的值来更新状态值
Example
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
| import React,{useState} from 'react';
const {Provider, Consumer} = React.createContext();
const App = () => { const [count, setCount] = useState(0); const add = () => setCount(count + 1); return ( <Provider value={{count, add}}> <App1 /> <App2 /> </Provider> ); }
const App1 = () => ( <div> <Consumer> { value => <h1>{ value.count }</h1> } </Consumer> </div> );
const App2 = () => ( <Consumer> {value => { return ( <button onClick={ value.add }>Click</button> ); }} </Consumer> )
|
6.2 useEffect
函数组件没有生命周期
useEffect作用:通过使用这个 Hook,你告诉 React 你的组件需要在渲染后做一些事情。React 会记住你传递的函数(我们将它称为我们的“效果”),并在执行 DOM 更新后调用它。
useEffect运行时间:默认情况下,它在第一次渲染后和每次更新后运行。
1 2 3 4
| useEffect(() => { document.title = `You clicked ${count} times`; });
|
1 2 3 4
| useEffect(() => { document.title = `You clicked ${count} times`; },[count]);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const App = () => { const [count, setCount] = useState(0);
const add = () => setCount(count + 1); const remove = () => ReactDOM.unmountComponentAtNode(document.getElementById('root'));
useEffect(() => { const timer = setInterval(() => { setCount(count => count + 1); }, 1000); return () => { clearInterval(timer); }; },[]);
return ( <Provider value={{count, add, remove}}> <App1 /> <App2 /> </Provider> ); }
|
常用操作:
- 发起ajax请求
- 设置订阅/启动定时器
- 更改DOM
6.3 useContext
函数组件中处理跨多个组件通信的问题
单文件跨组件通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const Context = React.createContext();
const App = () => { const [count, setCount] = useState(0);
const add = () => setCount(count + 1);
return ( <Context.Provider value={{count, add}}> <App1 /> <App2 /> </Context.Provider> ); }
const App1 = () => { const {count} = useContext(Context); return <h1>{count}</h1>; }
const App2 = () => { const {add} = useContext(Context); return <button onClick={ add }>Add</button> }
|
多文件跨组件通信
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| import React, { useContext } from 'react';
const App2 = (props) => { const { add, remove } = useContext(props.context); return ( <> <button onClick={add}>Add</button> <button onClick={remove}>Remove</button> </> ); }
export default App2;
import React,{useContext} from 'react';
const App1 = (props) => { const {count} = useContext(props.context); return <h1>{count}</h1>; };
export default App1;
import React, { useEffect, useState } from 'react'; import App1 from './components/Show'; import App2 from './components/Add';
const Context = React.createContext();
const App = (props) => { const [count, setCount] = useState(0);
const add = () => setCount(count + 1); const remove = () => props.root.unmount();
useEffect(() => { const interval = setInterval(() => { setCount(count => count + 1); }, 1000); return () => clearInterval(interval); }, []);
return ( <Context.Provider value={{ count, add, remove }}> <App1 context={Context} /> <App2 context={Context} /> </Context.Provider> ); }
export default App;
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App.jsx';
const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App root={root}/> </React.StrictMode> );
|
7.外部常用库
7.1 axios请求数据
axios是基于promise对ajax的一种封装
发起GET请求
axios.get('url',{params:{(js对象)参数}}).then(callback)
发起POST请求
axios.post('url',{(js对象)参数}).then(callback)
其他方式
1 2 3 4 5 6 7
| axios({ method: '请求类型', url: '请求的URL地址', data: {数据}, params: {参数} }).then(result => result).catch(err => callback).
|
7.2 Router路由
现代前端应用大多都是SPA(单页应用程序),也就是只有一个HTML页面的应用程序,可以减轻服务器压力,用户体验好,为了有效使用单页面管理原来的多页面的功能,前端路由应运而生。
React Router官网:https://reactrouterdotcom.fly.dev/docs/en/v6
安装
1 2 3 4 5 6 7 8 9 10 11
| import { BrowserRouter } from 'react-router-dom'; import 'bootstrap/dist/css/bootstrap.min.css';
const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode> );
|
基础使用 Routes, Route, Link, Outlet
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import {Routes, Route, Link, Outlet} from 'react-router-dom';
const Header = () => ( <> <h1>Header</h1> <p> Outlet 标签为子元素占位符,为子元素占位,布局页面 </p> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/First">First</Link></li> <li><Link to="/Second">Second</Link></li> </ul> <hr /> <Outlet/> </> );
const Home = () => ( <> <h1>Home</h1> <p> This is the home page </p> </> );
const First = () => ( <> <h1>First</h1> <p> This is the first page </p> </> );
const NotFound = () => ( <> <h1>Not Found</h1> <p> This page is not found </p> </> );
const App = () => ( <Routes> <Route path="/" element={ <Header/> }> <Route index element={ <Home/> }/> <Route path="First" element={ <First/> }/> <Route path="*" element={ <NotFound/> }/> </Route> </Routes> );
|
7.3 Ant Design的UI库
1 2 3 4 5 6
| # 安装UI样式 npm install antd --save # 安装依赖修改webpack npm install @craco/craco npm install craco-less npm install less-loader less
|
修改 package.json
里的 scripts
属性
1 2 3 4 5
| "scripts": { "start": "craco start", "build": "craco build", "test": "craco test", }
|
然后在项目根目录创建一个 craco.config.js
用于修改默认配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const CracoLessPlugin = require('craco-less');
module.exports = { plugins: [ { plugin: CracoLessPlugin, options: { lessLoaderOptions: { lessOptions: { modifyVars: { '@primary-color': '#1DA57A' }, javascriptEnabled: true, }, }, }, }, ], };
|
7.4 Redux
专门用于状态管理的JS库,集中管理React组件的共享状态
从 UI 层完全抽离出来,只负责管理数据,让 React 只专注于 View 层的绘制。
中文文档:https://cn.redux.js.org/
英文文档: https://redux.js.org/
1 2 3
| # 安装 redux npm install @reduxjs/toolkit npm install react-redux
|
创建 Redux Store
1 2 3 4 5 6 7 8 9 10 11
| import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export default configureStore({ reducer: { counter: counterReducer } })
|
创建 Slice Reducer
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
| import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: state => { state.value += 1 }, decrement: state => { state.value -= 1 }, incrementByAmount: (state, action) => { state.value += action.payload } } })
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
|
React Counter 组件
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 35 36 37 38 39 40 41
| import React, { useState } from 'react' import { useSelector, useDispatch } from 'react-redux' import { decrement, increment, incrementByAmount, incrementAsync, selectCount } from './counterSlice' import styles from './Counter.module.css'
export function Counter() { const count = useSelector(selectCount) const dispatch = useDispatch() const [incrementAmount, setIncrementAmount] = useState('2')
return ( <div> <div className={styles.row}> <button className={styles.button} aria-label="Increment value" onClick={() => dispatch(increment())} // 调用方法 > + </button> <span className={styles.value}>{count}</span> <button className={styles.button} aria-label="Decrement value" onClick={() => dispatch(decrement())} > - </button> </div> </div> ) }
|