react
react
入门
相关 js 库
- react.js: React 的核心库
- react-dom.js: 提供操作 DOM 的 react 扩展库
- babel.min.js: 解析 JSX 语法代码转为纯 JS 语法代码的库
两种创建虚拟 dom 方法
- js
React.createElement('h1', {id: 'title'}, 'hello,react')
- jsx
<script type="text/javascript" src="../js/react-16.0.8/react.development.js"></script> <!-- 引入react-dom,用于支持react操作DOM --> <script type="text/javascript" src="../js/react-16.0.8/react-dom.development.js"></script> <script type="text/javascript" src="../js/react-16.0.8/babel.min.js"></script> <script type="text/babel"> // 必须声明babel // 创建虚拟DOM元素 const vDOM = (<h1>Hello world</h1>) // 渲染虚拟DOM到页面真实DOM容器中 ReactDOM.render(vDOM, document.getElementById('test'))
react 面向组件编程
自定义组件两种方式
- 函数式组件
function MyComponent () { return <h2>工厂函数组件(简单组件)</h2> } ReactDOM.render(<MyComponent />, document.getElementById('example1')) // ReactDOM.unmountComponentAtNode(<MyComponent />, document.getElementById('example1'))
- 类组件(复杂组件)
class MyComponent2 extends React.Component { render () { console.log(this) // MyComponent2的实例对象 return <h2>ES6类组件(复杂组件)</h2> } } ReactDOM.render(<MyComponent />, document.getElementById('example1'))
组件三大属性(复杂组件)
state
- 完整组件写法
class Weather extends React.Component { constructor(props) { super(props); this.state = { isHot: true }; // 解决changeWeather中this指向问题 this.changeWeather = this.changeWeather.bind(this) } render() { const { isHot } = this.state; return <h3 id='title' onClick={this.changeWeather}>今天天气很{isHot ? "hot" : "cool"}</h3>; } changeWeather(){ // changeWeather放在哪里? -Weather的原型对象上,供实例使用 // 由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用 // 类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined console.log(this) const isHot = this.state.isHot this.setState({isHot: !isHot}) } } ReactDOM.render(<Weather />, document.getElementById("test"));
- 简写
class Weather extends React.Component { // 初始化状态 state ={isHot: true} render() { const { isHot } = this.state; return <h3 id='title' onClick={this.changeWeather}>今天天气很{isHot ? "hot" : "cool"}</h3>; } // 自定义方法: 1.赋值语句 2.箭头函数 changeWeather = () => { const isHot = this.state.isHot this.setState({isHot: !isHot}) } } ReactDOM.render(<Weather />, document.getElementById("test"))
注意:
- 组件中 render 方法中的 this 为组件实例对像
- 组件自定义的方法中的 this 为 undefined
- 可以强制绑定 this: 通过函数对象的 bind()
- 可以使用箭头函数
- 状态数据不能直接修改或更新
props
- 基本使用 批量
class Person extends React.Component{ render(){ return ( <ul> <li>name: {this.props.name}</li> <li>age: {this.props.age}</li> </ul> ) } } // ReactDOM.render(<Person name='tom' age='18' />, document.getElementById("test")) // 批量传递 const person = { name: 'tom', age: 18} ReactDOM.render(<Person {...person} />, document.getElementById("test"))
- 约束和默认值
// react15.5.x npm install --save prop-types
// 对标签属性进行类型、必要性的限制 Person.propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number, speak: PropTypes.func // 限制speak为函数 } // 指定默认标签默认值 Person.defaultProps = { sex: 'no sex', age: 18 }
// 对属性进行类型、必要性的限制 static propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number, speak: PropTypes.func, // 限制speak为函数 }; // 指定属性默认值 static defaultProps = { sex: "no sex", age: 18, };
- 类式组件中构造器和 props
constructor(props) { // 构造器是否接受props以及是否传递给super, 取决于是否希望在构造器中通过this访问props super(props) console.log('constructor', this.props) }
- 函数式组件使用 props
function Person(props) { const { name, age } = props; }
refs
- 字符串形式 refs
render() { return ( <div> <input ref="input" onBlur={this.blur} type="text" placeholder="失去焦点提示数据" /> </div> ); } blur= () =>{ alert(this.refs.input.value) }
- 回调函数形式 refs
render() { return ( <div> <input ref={c => this.input = c} onBlur={this.blur} type="text" placeholder="失去焦点提示数据" /> </div> ); } blur= () =>{ alert(this.input.value) }
render() { return ( <div> <input ref={this.saveInput} onBlur={this.blur} type="text" placeholder="失去焦点提示数据" /> </div> ); } saveInput = (c) => { this.input = c } blur = () => { alert(this.input.value) }
- creatRef
// React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点 // 该容器是‘专人专用’的 myRef = React.createRef() showInfo = () => { console.log(this.myRef.current.value); } render() { return ( <div> <input ref={this.myRef} type="text" placeholder="点击按钮提示数据" /> </div> ); }
受控和非受控组件
- 受控组件: 表单项输入数据能自动收集成状态
- 非受控组件: 需要时才手动读取表单输入框中的数据(用 refs)
函数的柯里化
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
组件生命周期
- 16.x
- 17.x
// 若state的值在任何时候都取决于props,那么可以使用 static getDerivedStateFromProps(props, state) { return props } getSnapshotBeforeUpdate(prevProps, prevState) { return 'snapshot' } componentDidUpdate(prevProps, prevState, snapshot) {}
react 脚手架
html 标签
<!-- 用于配置浏览器页签+地址栏的颜色(仅支持安卓手机浏览器) -->
<meta name="theme-color" content="red" />
<!-- 用于指定网页添加到手机主屏幕后的图标 -->
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.png" />
<!-- 应用加壳时的配置文件 -->
<link rel="manifest" href="%PULIC_URL%/manifest.json" />
样式模块化
import hello from 'index.module.css'
export default class Hello extends Component {
render() {
return (
<h2 className={ hello.title }>Hello</h2>
)
}
}
命令行
npm install -g create-react-app
create-react-app hello
proxy
- 方法一
在 package.json 中追加如下配置,当请求了3000不存在的资源时,那么该请求会转发到5000"proxy": "http://localhost:5000"
- 方法二
创建代理配置文件,在 src 下创建配置文件: src/setupProxy.jsconst proxy = require("http-proxy-middleware"); module.exports = function (app) { app.use( proxy("/api1", { // 遇见/api1前缀的请求就会触发该代理配置 target: "http://localhost:5000", // 请求转发给谁 changeOrigin: true, // 控制服务器收到的请求头中Host的值 pathRewrite: { "^/api1": "" }, // 重写请求路径 }), proxy("/api2", { target: "http://localhost:5001", changeOrigin: true, pathRewrite: { "^/api2": "" }, }) ); };
路由
路由原理
- history
- hash (锚点) 兼容性好
基本使用(版本5)
npm install react-router-dom
import {Link, Route, BrowserRouter, HashRouter} from 'react-router-dom'
//1.导航区的a标签改为Link标签
<Link to="/xx">demo</Link>
//2.展示区写Route标签进行路径的匹配
<Switch>
<Route path="/xx" component={Demo} />
</Switch>
//3.<App />的最外侧包裹了一个<BrowserRouter> 或<HashRouter>
路由组件和一般组件
- 写法不同
一般组件:<Demo />
路由组件:<Route path="/demo" component={Demo} />
- 存放位置不同
一般组件:components
路由组件:pages - 接收到的 props 不同
一般组件:写组件标签时传递了什么就能接收到什么
路由组件:接收到三个固定的属性history
location
match
NavLink 与封装 NavLink
- NavLink 可以实现路由链接的高亮,通过 activeClassName 指定样式名
- 标签体内容是一个特殊的标签属性
this.props.children
Switch
解决多级路径刷新页面样式丢失的问题
- public/index.html 中引入样式时不写./ 写 /
- public/index.html 中引入样式时不写./ 写 %PUBLIC_URL%
- 使用 HashRouter
严格匹配和模糊匹配
- 默认使用的是模糊匹配,(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
- 开启严格匹配:
<Route exact={true} path="/at/about" component={About} />
- 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
Redirect 的使用
- 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到 Redirect 指定的路由
- 具体编码:
<Switch> <Route path="/about" component={About} /> <Redirect to='/home' /> </Switch>
嵌套路由
向路由组件传递参数
- params 参数
路由链接(携带参数):<Link to={
/home/message/detail/id/title}>{item.title}</Link>
注册路由(声明接收):<Route path="/home/message/detail/:id/:title" component={Detail} />
接受参数 :const { id, title } = this.props.match.params;
- search 参数
路由链接(携带参数):<Link to={'/home/message/detail?id=${item.id}&title=${item.title}'}>{item.title}</Link>
注册路由(无需声明接收):<Route path="/home/message/detail" component={Detail} />
接受参数 :const {search} = this.props.location
备注 : 获取到的 search 是 urlencode 编码字符串 需要借助 qs 解析 - state参数
路由链接(携带参数):<Link to={{pathname:'/home/message/detail',state:{id:item.id,title:item.tile}}}>{item.title}</Link>
注册路由(无需声明接收):<Route path="/home/message/detail" component={Detail} />
接受参数 :const {id,title} = this.props.location.state
备注 : 刷新也可以保留住参数
编程式路由导航
借助 this.props.history 对象上的 api 对操作路由跳转、前进、后退
withRouter
可以加工一般组件,让一般组件具备路由组件所特有的 API,返回值是一个新组件
import {withRouter} from 'react-router-dom'
class Header extends Component {}
export default withRouter(Header)
BrowserRouter 与 HashRouter 的区别
- 底层原理不一样:
BrowserRouter 使用的是 H5 的 history API,不兼容 IE9 及以下版本
HashRouter 使用的是 URL 的哈希值。 - path 表现形式不一样
BrowserRouter 的路径中没有#,例如:localhost:3000/demo/test
HashRouter 的路径包含#,例如:localhost:3000/#/demo/test - 刷新后对路由 state 参数的影响
BrowserRouter 没有任何影响,因为 state 保存在 history 对象中
HashRouter 刷新后会导致路由 state 参数的丢失!!! - 备注:HashRouter 可以用于解决一些路径错误相关的问题。
antd
基本使用
按需引入 css
自定义主题
redux
工作流程
三个核心概念
- action
- store
- reducer
求和案例_redux 精简版
- 去除 Count 组件自身的状态
- src下建立:
-redux
-store.js
-count_reducer.js - store.js:
1).引入 redux 中的 createStore 函数,创建一个 store
2).createstore 调用时要传入一个为其服务的 reducer
3).记得暴露 store 对象- count_reducer.js:
1).reducer 的本质是一个函数,接收:preState,action,返回加工后的状态
2).reducer 有两个作用:初始化状态,加工状态
3).reducer 被第一次调用时,是 store 自动触发的,传递的 preState 是 undefined
// 创建一个为Count组件服务的reducer,reducer的本质就是一个函数 // 参数:之前的状态 动作对象 import {INCREMENT, DECREMENT} from './constant' const INIT_STATE = 0 export default function countReducer(pre = INIT_STATE, action) { // if (pre === undefined) pre = 0 const { type, data } = action; switch (type) { case INCREMENT: return pre + data; case DECREMENT: return pre - data; default: return pre } }
- count_reducer.js:
- 在 index.js 中检测 store 中状态的改变,一旦发生改变重新渲染
store.subscribe(() => { ReactDOM.render(<App />, document.getElementById('root')) })
- 备注:redux 只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
求和案例_redux 完整版
新增文件:
1.count_action.js 专门用于创建 action 对象
2.constant.js 放置由于编码疏忽写错 action 中的 type
求和案例_redux 异步 action 版
- 明确:延迟的动作不想交给组件自身,想交给 action
- 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回(非必须)
- 具体编码:
1).yarn add redux-thunk
,并配置在 store 中
2).创建 action 的函数不再返回一般对象,而是一个函数,该函数中写异步任务。// 引入createStore 专门用于创建redux中最为核心的store对象 import {createStore, applyMiddleware} from 'redux' // 引入为Count组件服务的reducer import countReducer from './count_reducer' import thunk from 'redux-thunk' export default createStore(countReducer,applyMiddleware(thunk))
3).异步任务有结果后,分发一个同步的 action 去真正操作数据。// 专门为Count组件生成action对象 import {INCREMENT, DECREMENT} from './constant' // 同步action 就是指action的值为一般对象 export const createIncrementAction = data => ({type: INCREMENT, data}) export const createDecrementAction = data => ({type: DECREMENT, data}) // 异步action 就是指action的值为函数,异步action中一般都会调用同步action export const createIncrementAsyncAction = (data,t) => { return (dispatch) => { setTimeout(() => { dispatch(createIncrementAction(data)) },t) } }
- 备注:异步 action 不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步 action。
react-redux
所有的 UI 组件都应该包裹一个容器组件,他们是父子关系
容器组件是真正和 redux 打交道的,里面可以随意的使用 redux 的 api
UI 组件中不能使用任何 redux 的 api
容器组件会通过 props 传递给 UI 组件 1.redux 中所保存的状态 2.用于操作状态的方法
基本使用
- 明确两个概念:
UI 组件:不能使用任何 redux 的 api,只负责页面的呈现、交互等。
容器组件:负责和 redux 通信,将结果交给 UI 组件。 - 如何创建一个容器组件 -靠 react-redux 的 connect 函数
connect(mapStateToProps,mapDispatchToProps)(UI组件)
-mapStateToProps:映射状态,返回值是一个对象
-mapDispatchToProps:映射操作状态的方法,返回值是一个对象 - 备注:容器组件中的 store 是靠 props 传进去的,而不是在容器组件中直接引入
优化
- 容器组件和 UI 组件整合成一个文件
- 无需自己给容器组件传递 store,给
<App/>
包裹一个<Provider store={store}></Provider>
即可。import { Provider } from 'react-redux' ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
- 使用了 react-redux 后也不用再自己检测 redux 中状态的改变了,容器组件可以自动完成这个工作。
- mapDispatchToProps 也可以简单的写成一个对象
- 一个组件要和 redux “打交道”要经过那几步?
- 定义好 UI 组件--不暴露
- 引入 connect 生成一个容器组件,并暴露,写法如下:
connect( state =>({key:value}), {key:xxxxxAction} )(UI组件)
- 在UI组件中通过 this.props.xxxxxxx 读取和操作状态
数据共享
- 定义一个 Pserson 组件,和 Count 组件通过 redux 共享数据。
- 为 Person 组件编写:reducer、action,配置 constant 常量。
- 重点:Person 的 reducer 和 Count 的 Reducer 要使用 combineReducers 进行合并,合并后的总状态是一个对象!
// 引入createStore 专门用于创建redux中最为核心的store对象 import {createStore, applyMiddleware,combineReducers} from 'redux' // 引入为Count组件服务的reducer import countReducer from './reducers/count' import personReducer from './reducers/person' import thunk from 'redux-thunk' // 汇总所有的reducer const reducers = combineReducers({ count: countReducer, persons: personReducer }) export default createStore(reducers,applyMiddleware(thunk))
- 交给 store 的是总 reducer,最后注意在组件中取出状态的时候,记得“取到位”。
- 备注:redux 的 reducer 必须是一个纯函数
react-redux 开发者工具
yarn add redux-devtools-extension
- store中进行配置
import {composeWithDevTools} from 'redux-devtools-extension' const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验