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.js
    const 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 可以实现路由链接的高亮,通过 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

工作流程

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
      }
    }
    
  • 在 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 中
    // 引入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))
    
    2).创建 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)
      }
    }
    
    3).异步任务有结果后,分发一个同步的 action 去真正操作数据。
  • 备注:异步 action 不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步 action。

react-redux

所有的 UI 组件都应该包裹一个容器组件,他们是父子关系
容器组件是真正和 redux 打交道的,里面可以随意的使用 redux 的 api
UI 组件中不能使用任何 redux 的 api
容器组件会通过 props 传递给 UI 组件 1.redux 中所保存的状态 2.用于操作状态的方法
react-redux

基本使用

  • 明确两个概念:
    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)))
    
posted @   提莫一米五呀  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示