type
status
date
slug
summary
tags
category
icon
password

概念

index.js:
  • 项目入口
  • react和react-dom/client:项目必要的两个核心包
  • 导入项目根组件,将根组件渲染到id为root的节点上,也就是将项目的根组件渲染到public的index.html的id为root的div节点上

JSX

基础语法

JSX:JavaScript+XML(HTML)
  • 表示在JS代码中编写HTML模板结构,是React中编写UI模板的方式
  • HTML声明式的模板写法+JS的可编程能力,JSX 会让你把标签放到 JavaScript 中。而大括号会让你 “回到” JavaScript 中
  • 本质:JS的语法扩展,浏览器本身不能识别,需要通过解析工具做解析之后才能在浏览器中运行
  • 使用JS表达式:大括号语法,里面仅支持表达式,不支持语句(if、变量声明之类的)
  • 组件不能返回多个JSX标签,必须将其包裹到一个共享的父级中,哪怕是用空标签包含:<>内容</>

列表渲染/条件渲染

  • 列表渲染:
    • 用原生js的map方法
    • key值:独一无二的字符串或者number id。是react框架内部使用,用来提升更新性能的
    • v-for中key的作用有点像:key属性是dom元素的唯一标识,如果没有这个东西的话,在dom里面添加/删除一个元素就必须整体进行重绘,但如果有了kitemey,重绘之前vue会检查哪些是需要重绘的新元素,如果没有的话就直接用之前的,避免资源浪费。
    • 提高虚拟dom的更新性能
    • 若不设置key,可能触发一些bug:比如说只更新第二行,因为没有key导致其他的元素也更新了,或者最后的结果不是更新,而是做成了多一行其他的
    • 为了触发过渡的效果
  • 基础条件渲染
    • 复杂条件渲染
      • 自定义函数(返回不同JSX模板)+if判断语句

    事件绑定

    语法:on+事件名称={ 事件处理程序(回调函数) }
    获取事件对象:在事件回调函数里面写一个形参
    传递自定义参数:箭头函数
    notion image

    组件

    一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI,渲染组件就是把组件当成标签书写
    使用class的方式来创建组件:
    • this的指向:
      • 在类组件中,this指向当前组件实例
      • this.props可以访问当前组件的props,而这个props是从父组件传进来的

      获取组件的引用

      函数形式:
      • 当组件挂载完成后,react会调用ref属性里面对应的函数
      • react会把这个组件的实例作为参数传给这个函数
      • 比如说像表单校验的时候,就可以用这个引用来调用这个组件有的函数/方法:
        类组件创建:
        • 使用React.createRef()创建一个应用
        • 通过ref={this.formRef}将该引用绑定到表单上
        使用useRef创建:类组件不能用Hook
        useRef() 是 React Hook,只能在函数组件或自定义 Hook 中使用,不能在类组件中使用,类组件用React.createRef()创建引用而函数组件用useRef()
        原因:函数式组件没有this,没办法用this.form = xxx这种形式来为引用赋值,React 提供了 useRef() 这个 Hook,用于在函数组件中“存储跨渲染的引用”:

        父子组件通信

        单向数据流

        react采用的是单向数据流,而不是双向数据绑定,即数据只能从父组件流到子组件:
        原因:
        • 这种设计模式使得应用程序的结构更加清晰、易于理解和调试。相比之下,双向数据绑定会导致数据流动方向不确定,增加了代码的复杂度和难度
        • 在双向数据绑定中,当数据发生变化时,系统需要同时更新视图和数据模型,这会导致性能瓶颈。而在单向数据流中,数据的更新只会从父组件向子组件进行,这样可以避免不必要的视图更新,提高了应用程序的性能表现
        • 在双向数据绑定中,由于数据的修改可能来自于多个组件,造成了数据的不可预测性。而在单向数据流中,数据的修改只能由父组件或本身进行,这样可以更好地控制应用程序的逻辑,减少了错误发生的概率
        • React支持服务端渲染,这对于SEO和性能都非常重要。在双向数据绑定中,由于数据的修改可能来自于客户端,这使得服务端渲染变得更加困难。而在单向数据流中,由于数据的修改只能来自于服务端或本身,这样可以更方便地实现服务端渲染
        具体来说,React应用程序中的数据分为两种类型:
        1. Props:由父组件传递给子组件的只读属性,不能在子组件中修改;
          1. 由于Props属性是只读的,因此子组件不能直接修改它们的值。如果需要更新Props属性的值,那么必须由父组件进行更新并重新传递给子组件。
        1. State:组件内部维护的可变状态,可以通过setState()方法进行修改。
          1. State状态是可变的,可以在组件内部通过setState()方法进行修改。当状态发生变化时,React会自动重新渲染并将新的状态值传递给子组件,从而更新子组件的界面
        总之,react的数据只能由父组件流向子组件,要改变父组件也只能由组件自身发起

        组件间共享数据

        这段代码中两个按钮的计数器都是独立更新的,现在我想让两个按钮使用同一个计数:
        notion image
         
        也就是要将count变量的状态移动到MyButton组件的上一级:MyApp ,然后使用大括号的写法将MyApp 中的count的状态传给每个子组件:
        使用这种方式传递的信息被称作 prop
        修改子组件以获取从父组件传过来的count和onClick:
        当你点击按钮时,onClick 处理程序会启动。每个按钮的 onClick prop 会被设置为 MyApp 内的 handleClick 函数,所以函数内的代码会被执行。该代码会调用 setCount(count + 1),使得 state 变量 count 递增。新的 count 值会被作为 prop 传递给每个按钮,因此它们每次展示的都是最新的值。这被称为“状态提升”。通过向上移动 state,我们实现了在组件间共享它。

        父传子

        使用类的方式来创建组件:
        • 子组件:
          • 在组件的constructor里面用props接收父组件传来的数据
          • 用state来定义组件内部维护的可变状态(将这个组件所有要用的变量都装在这个里面),代码中用this.setState({ count: this.state.count + 1 }) 来修改count的值而other保持不变:
            • this.setState({ count: this.state.count + 1 })只更新了 countother 的值 不会丢失,仍然是 'test'
            • React 会对this.state进行浅合并(shallow merge),而不是直接替换整个 state。
            • 可以写接口来规定props和state需具备的属性:
        • 父组件:
          • Counter组件中声明了一个Props属性initialCount,并在构造函数中使用props参数来初始化状态变量。这样,当App组件传递initialCount属性时,Counter组件就能够正确地使用该属性
        使用函数的方式创建组件:也是一样的

        子传父

        用回调函数:
        • 父组件:写一个回调函数传给子组件,这个函数可以修改父组件里面的state
          • 子组件:调用父组件传来的回调函数,以此来给父组件传值

            父组件调用子组件的方法

            子组价将方法绑定到ref上,父组件获取子组件的ref来调用里面的方法
            • 子组件:
              • 使用React.forwardRef包装子组件
              • 将子组件的方法绑定到ref
            • 父组件:将ref(useRef返回的)传递给子组件

              使用Hook

              • use 开头的函数被称为 Hook,useState是内置的一个hook,也可以自己编写hook
              • React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。 React Hooks 就是那些钩子
              • 逻辑复用+响应式:组件复用的话样式也要复用,函数复用的话没有响应式
              有什么用?
              • 可以在不编写 class 的情况下使用 state 及其他 react 的特性
              • 可以将组建逻辑提取到可重用的函数中

              useState()

              useState是一个React Hook函数,它允许我们向组件添加一个状态变量,从而影响组件的渲染结果
              类似于toRef
              notion image
              计数器:
              官网例子:实现两个独立的点击+次数的按钮
              修改状态的规则:
              • 在React中,状态被认为是只读的,我们应该始终替换它而不是修改它,直接修改状态不能引发视图更新
              • 相当于用setCount,传入一个新值,这个方法在内部可以实现视图更新
              修改对象状态:
              • 规则:对于对象类型的状态变量,应该始终传给set方法一个全新的对象来进行修改
                • 在 React 中,直接修改状态对象不是推荐的做法。比如这段写法就不好:
                  正确做法是创建一个新对象,并用 setForm() 替换掉原来的:
                  这样 React 才能检测到状态变化,触发组件重新渲染。
              当你点击按钮时,它会:
              1. 调用 setForm 更新 form 的值;
              1. 使用展开运算符 ...form 复制 form 原有的属性;
              1. 然后用 name: 'lucy' 覆盖 form 中的 name 字段。
              setXXX里面传一个函数比较好:
              • 直接设置可能有问题:

                useContext()

                如果需要在组件之间共享状态,可以使用useContext()
                • 使用createContext 在组件外部建立一个Context
                  • 将需要共享上下文的组件封装在里面:
                    • 子组件通过useContext获取上下文中的属性:

                      useEffect()

                      useEffect()接受两个参数。第一个参数是一个函数,异步操作的代码放在里面。第二个参数是一个数组,用于给出 Effect 的依赖项,只要这个数组发生变化,useEffect()就会执行。第二个参数可以省略,这时每次组件渲染时,就会执行useEffect()
                      返回值:

                      useRef()

                      用于创建一个可变的引用对象,读写都通过.current来访问:
                      • 访问dom元素
                        • 存储可变值并且不会触发重新渲染:这跟vue的ref不一样,这样声明的变量不是响应式的
                          • 保存前一个值

                            useCallback

                            允许你在多次渲染中缓存函数的 React Hook
                            The useCallback Hook only runs when one of its dependencies update. 但是useCallBack的本质工作不是在依赖不变的情况下阻止函数创建,而是在依赖不变的情况下不返回新的函数地址而返回旧的函数地址。不论是否使用useCallBack都无法阻止组件render时函数的重新创建
                            使用场景:
                            • 使用React.memo的时候:

                              Hook 到底是如何工作的

                              useState

                              主要是采用了闭包的思想,,闭包通过返回一个函数,可以在函数外部获取到函数内部的变量值,同时也可以保存函数内部的变量值。
                              简单理解:
                              • 这样可以实现在函数 useState 外部也能访问到 _val 的值
                              • 并且使用 setState 后,返回的 _val 仍然是最新值
                              • 但是这样返回的是一个 getter 函数,跟真正的 useState 有出入
                              • 直接返回 _val 的话,外部的 state 就会被固定为 initial value 而不是 setState 之后的最新值
                              改进:
                              • 闭包里面再返回一个闭包
                              • useState 也是一个闭包,这样返回的 _val 就始终是 setState 这里传入的最新值,并且是一个值而不是一个 getter 函数
                              • 使用:

                                useEffect

                                简单实现:

                                多个 hook ⇒ Array

                                • 这样也可以看出来为什么 hook 只能在组件顶层使用,因为必须要保证每次渲染 hook 调用的顺序都是一致的
                                • So the basic intuition is having an array of hooks and an index that just increments as each hook is called and reset as the component is rendered.

                                创建自己的hook

                                封装上面的代码:
                                然后在一个组件里面调用这个钩子:
                                那这样的 hook 跟直接创建一个普通的函数有什么区别呢?
                                • 可以在自定义 hook 中调用别的 hook,但是最好不要在普通的非顶层函数中调用 hook,文档是这样说的:
                                  • 不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确
                                    • 即最好只在 React 的组件函数中调用 hook,但是自定义 hook 中可以调用其他 hook
                                  • React 内部使用链表结构来存储每个组件的 Hook 状态,每个 Hook 按照调用顺序在链表中占据一个固定位置,如果中间有哪一个 hook 没被调用,后续的 hook 调用就会出问题
                                    • react 判断哪个 state 对应哪个 useState 靠的是 hook 调用的顺序,只要 hook 的调用顺序在多次渲染中保持一致,react 就能将 state 正确地和对应的 hook 进行关联
                                    • 因此不能将 hook 放在分支中,如果需要条件判断也应该在 hook 内部进行
                                  • 在普通函数当中,其就是一个模块,因此其数据是在所有调用者中共享的,因此我们可以通过一个全局的数据结构来存储数据,这样就可以实现数据的复用
                                  • 自定义 hook 仍然算顶层,React 内部会维护一个调用栈来追踪当前正在执行的组件,在组件的内部 hook 链表中可以找到对应的位置
                                    • 自定义 hook 也是 hook,每次保持在这个组件中的调用顺序,那它内部的 hook 顺序也不变。无论嵌套多少层,只要是在组件渲染期间、按照固定顺序调用的,就是合法的

                                  动画

                                  • 创建动画
                                    • 创建了一个名为fadeAnimAnimated.Value,并放在state
                                  • 执行动画
                                    • 将动画跟 View 标签的属性绑定在一起,这个标签的这个属性就是由这个动画控制的
                                      • 在这个例子中:View的透明度是和这个值绑定的

                                    生命周期

                                    Class Component 生命周期:
                                    • 挂载阶段:组件实例被创建和插入 dom 树的过程
                                      • CompontWillMount :组件将要渲染,一般不怎么使用了,因为 React 推出了异步渲染导致挂载前的阶段都可以被打断重来
                                      • CompontDidMount:组件渲染完毕,在渲染过程中执行一次,一般用于监听事件/请求后台接口
                                    • 更新阶段:主要做状态更新操作
                                      • CompontWillReceiveProps :组件将要接收props数据,一般不用了
                                      • ShouldComponentUpdate(nextProps, nextState):组件接收到新的state或者props,判断是否更新,返回布尔值
                                        • 一般用法:通过手动对比 props 和 nextProps、state 和 nextState 来确定是否需要重新渲染子组件。通过实现 shouldComponentUpdate,可以手动控制是否需要重新渲染子组件,从而优化性能。如果不实现该方法,React 会默认返回 true,也就是说每次父组件更新时,子组件都会重新渲染
                                      • CompontWillUpdate :组件将要更新,一般不用了
                                      • ComponentDidUpdate :组件已经更新,此时组件已经重新渲染了,在这里可以对组件中的 dom 进行操作,一般用于判断参数是否变化,变化就调用请求函数重新请求数据
                                    • 卸载阶段
                                      • ComponentwillUnmount :组件将要卸载
                                    Function Component 生命周期:
                                    Function Component 是更彻底的状态驱动抽象,甚至没有 Class Component 生命周期的概念,只有一个状态,而 React 负责同步到 DOM。
                                    在 Class Component 中,发请求一般在 ComponentDidMount、ComponentDidUpdate和 ComponentWillUnmount 阶段,但是在函数式组件中,却没有这些,发请求就用 useEffect:它跟 class 组件中的 componentDidMount 、 componentDidUpdate  和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API
                                    • 使用 useEffect 相当于告诉 React 组件需要在渲染后执行某些操作,React 将在执行 DOM 更新之后调用它。
                                    • React 保证了每次运行 useEffect 的时候,DOM 已经更新完毕。

                                    其他

                                    Native Bridge 原生桥接

                                    原生桥接是 React Native 中 JavaScript 代码和原生代码(iOS/Android)之间通信的机制
                                    工厂函数确保每次都能创建新的组件实例,原生桥接负责 JavaScript 和原生代码之间的通信,原生端管理应用的生命周期和系统级功能

                                    闭包陷阱

                                    useState 中的闭包陷阱:
                                    • 主要原因:在 useState 中使用闭包,主要是因为 useState 的参数只会在组件挂载时执行一次。如果我们在 useState 中使用闭包,那么闭包中的变量值会被缓存,这意味着当我们在组件中更新状态时,闭包中的变量值不会随之更新
                                    • 解决:setState 也可以接收一个回调函数作为参数,这个回调函数会接受当前的 state 值作为参数,这样就可以在闭包中使用这个值而不用担心它被缓存
                                    useEffect 中的闭包陷阱:
                                    • 主要原因:在useEffect中使用闭包的问题则是因为useEffect中的函数是在每次组件更新时都会执行一次。如果我们在useEffect中使用闭包,那么这个闭包中的变量值也会被缓存
                                    • 解决:添加依赖数组

                                    React Fiber

                                    解决 CPU 瓶颈和 IO 瓶颈:
                                    • CPU 瓶颈:GUI 渲染线程和 JS 线程是互斥的,这样会导致如果在一帧内 js 脚本执行的时间太长,就没有时间去执行样式布局和样式绘制了,这就导致了掉帧。一般来说,解决掉帧,就是让浏览器在每一帧的时间中都预留一部分时间给 js 执行,然后将控制权交还给渲染线程,让其有足够的时间执行。当预留的时间不够用时,react 会先把控制权给渲染线程,然后在下一帧继续没有完成的工作。
                                      • 因此要解决 CPU 瓶颈主要是要实现时间片,时间片的关键是将同步更新转换为可中断的异步更新。
                                    • IO瓶颈:IO 的瓶颈主要来自于网络延迟,react 15 及其以下版本中,是使用的一种被称为 Stack Reconciler 的调度算法,当组件的更新任务被调度之后,它会一直执行到更新任务完成,期间不允许其他的任务干扰,这就会导致UI界面可能被卡死
                                    react fiber:
                                    • 可以被理解成一个执行单元/数据结构
                                    • 里面包含了组件的 tagkeytypestateNode 等相关信息,用于表示组件树上的每个节点和它们的关系
                                    • V16版支持可中断异步更新,任务支持时间切片
                                      • 我们知道React在数据更新时会有diff的操作,此时diff的过程是被分成一小段一小段的,Fiber节点保存了每一阶段任务的工作进度,js会比较一小部分虚拟dom,然后让出主线程,交给浏览器去做其他操作,然后继续比较,如此循环往复,直至完成diff,然后一次性更新到视图上

                                    lodash

                                    排序:
                                    • orderby(对象数组, ‘按某字段排序’, ‘排序方式(esc/desc)’)
                                    • 返回一个新的数组,不会修改原数组

                                    classnames

                                    可以非常方便地通过条件动态控制class类名的显示
                                    notion image
                                    可以用key: value的形式:
                                    • key表示要显示的类名
                                    • value表示控制类名显示的条件
                                     
                                    Vue的动态绑定类名和样式:
                                    script:用来控制是否加上active这个类名

                                    redux状态管理

                                    1. State 是整个应用在某一时刻的数据快照(由 Redux Store 管理)
                                    1. 视图(React)展示 State
                                    1. 用户操作或某些事件触发 dispatch(action)
                                    1. Redux 的 reducer 根据 action 和旧的 state 生成新的 state
                                    1. Redux Store 通知 UI 状态变化
                                    1. 视图基于新 state 重新渲染
                                    使用步骤:
                                    1. 创建 store,即全局状态容器,里面管理着全局 state
                                      1. Redux Store = 管理全局状态 + 处理 dispatch + 触发 subscribe() 监听更新
                                    1. provider提供全局状态
                                      1. 这个 ReduxProvider(来自 react-redux)把 store 放到 React 的上下文中,这样组件就可以通过 connect()useSelector() 访问到全局状态了
                                    1. 组件订阅 state + 发出 action
                                      1. 这个组件通过 connect() 绑定:
                                        • state.counter.num 映射到组件的 props.value
                                        • dispatch(INCREMENT) 映射到组件的 props.onIncrement(点按钮就能 dispatch)
                                        当组件触发 onIncrement(),就向 Redux 发出:
                                    并发/并行/进程/线程gns3的配置
                                    • Giscus