React基础
1、渐进式MVC框架,专注视图层,单向数据流
2、可以使用create-react-app脚手架进行搭建,内部集成webpack,使用npm eject可以暴露出配置,此过程不可逆
3、使用jsx语法进行页面的编写,react dom在渲染所有输入时,会将输入项进行转义,所有内容在渲染前都会被转义称为字符串,这样可以有效防止XSS(跨站脚本)攻击
4、babel会将jsx转译成为一个React.createElement()的函数调用
5、仅使用 React 构建的应用通常只有单一的根 DOM 节点。如果你在将 React 集成进一个已有应用,那么你可以在应用中包含任意多的独立根 DOM 节点。将所要渲染的元素传入ReactDOM.render()中
6、定义组件的方式有两种,一种是之前的通过class方式进行编写,一种是v16之后新增的函数编写,也就是React-Hook
* 在class组件中常用的方法
* constructor 此class组建的构造函数,常在这里进行数据的初始化,需要注意的是,如果使用props,必须在此处进行一项super(props)的操作,不然会报错
* render 此方法用来渲染组件的内容,该方法返回值就是这个class所要输出渲染的内容
* 在class组件中可以使用react的内置生命周期函数进行操作,常用的生命周期函数见下文
* 函数组件
* 函数组件在v16之后也可以进行状态管理,使用hook进行相关操作
* 在函数组件中不存在生命周期
* 写法较于class组件更简便
7、常用的生命周期函数 - class组件
* 挂载阶段 - 当组件实例被创建并插入DOM时,生命周期调用如下
* constructor()
* static getDerivedStateFromProps()
* render()
* componentDidMount()
* 更新阶段 - 当组件的state或者props发生变化时会触发组件更新
* static getDerivedStateFromProps()
* shouldComponentUpdate()
* render()
* static getSnapshotBeforeUpdate()
* componentDidUpdate()
* 卸载阶段 - 组件从dom中移除时触发
* componentWillUnmount
* 错误处理 - 当渲染过程、生命周期或者子组件的构造函数抛出错误的时候触发
* static getDerivedStateFromError()
* componentDidCatch()
* 生命周期函数详解
-- 常用 --
* render()
* 此函数是class组件中唯一必须要实现的方法
* 函数应该是纯函数,在state或者props不改变的情况下,每次调用应该返回相同的结果
* 当该方法被调用时,会检查state或者props的变化并作出相关的返回
* shouldComponentUpdate返回false的时候不会触发render方法的执行
* constructor(props)
* 如果不初始化state或者不进行方法的绑定,不需要为React组件实现构造函数
* 在挂载前执行,如果React.component子类实现构造函数时没有进行super(props)的调用,this.props在构造函数中会出现未绑定的bug
* 在构造函数使用this.state = {} 进行state的初始化,不可在此处进行setState的操作
* 避免在constructor中进行存在副作用的操作或者订阅,相关操作应该移到componentDidMount中进行操作
* 避免将this.props的值直接赋值给this.state
* componentDidMount()
* 组件挂载结束后调用
* 依赖于dom节点的操作应该在此处,或者ajax请求
* 在此处添加订阅函数,同时在componentWillUnMount进行订阅的取消
* componentDidUpdate(prevProps, prevState,snapshot)
* 在更新后会被立即执行,首次渲染不会执行
* 在该声明周期函数中使用setState需要注意的是必须在一个条件判断语句中,否则会死循环
* 如果组件实现了getSnapshotBeforeUpadte()生命周期--不常用,那么这个函数的返回值会作为第三个参数传递给componentDidUpdate,否则此参数为undefined
* componentWillUnmount()
* 组件卸载之前执行
* 通常在此处进行订阅的取消或者网络请求的关闭
* 不要在此处调用setState,因为组件被卸载,不会重新渲染
-- 不常用 --
* shouldComponentUpdate(nextProps, nextState)
* 根据该函数的返回值判断组件是否需要进行更新操作,会在props或者state变化时,在更新之前执行
* 首次渲染或者forceUpdate()时不会触发
* 该方法仅作为性能优化的方式,不要企图使用该方法阻止渲染
* 不要在该方法中进行深层比较或者JSON.stringfy(),这样非常影响效率
* 可以将 this.props 与 nextProps 以及 this.state 与nextState 进行比较,并返回 false 以告知 React 可以跳过更新。请注意,返回 false 并不会阻止子组件在 state 更改时重新渲染。
* static getDerivedStateFromProps(props, state)
* 在render之前会触发
* 返回一个对象更新state
* 常用于state的值在任何情况都依赖于props的情况下
* getSnapshotBeforeUpdate(prevProps, prevState)
* 在最近的一次渲染之前进行调用
* 在dom发生变化之前传回信息,此方法的任何返回值会传递给componentDidUpdate
* 常用于需要实时获取dom信息的场景,如滚动位置
* 应该返回snapshot值或者null
* Error boundaries
* 在子组件实例的任何位置捕获错误,展示降级UI
* 如果在class组件中使用componentDidCatch(error, info)或者static getDerivedStateFromError(error),那么这个组件就会成为一个error boundaries
* 不要用他来做流程控制
-- Unsafe,未来可能会被弃用的生命周期 --
* UNSAFE_componentWillMount()
* UNSAFE_componentWillReceiveProps(nextprops)
* UNSAFE_componentWillUpdate()
8、常用api
* setState(updater,[callback])
* 更新state的唯一方法
View Code
this.setState((state, props) => { return {counter: state.counter + props.step}; }, () => {});
* 不要再setState之后立即使用state数值,因为state的更新可能是异步的
* 在react的方法或者生命周期函数中表现为异步,在原生方法或者setTimeout中表现为同步
* React会将多次调用的setState合并
* forceUpdate()
* component.forceUpdate(callback)
* 默认情况下,当组件的 state 或 props 发生变化时,组件将重新渲染。如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件重新渲染。
* defaultProps
* 设置class接受的props初始值
View Code
class A extends React.Component{}
A.defaultProps = {something: init}
9、React的事件处理
* react使用小驼峰写法来定义事件
* 需要将事件函数传递给事件,而不是字符串
* 在React中无法使用return false;来阻止默认行为,必须显示的调用preventDefault()
* 事件绑定
* 通过 onClick = {this.clickFnc} 使用的方法必须要在constructor中进行绑定
View Code
this.clickFnc = this.clickFnc.bind(this)
* 使用箭头函数不需要绑定
View Code
onClick = {() => this.clickFnc()}
* 事件处理函数参数
View Code
onClick = {() => this.clickFnc(id, e)}; onClick = {this.clickFnc.bind(this, id)};
* 这两种情况是等价的,React会将事件对象作为第二个参数进行传递,不同的是使用箭头函数必须显示的进行传递,普通函数会被隐式传递
10、列表渲染
* 渲染列表时需要为每一项增加key值作为唯一标识
* key会告诉React那些组件发生了变化
* key要放在数组的就近上下文
11、导入导出
* 使用export default进行默认导出
* 使用export A {}进行批量导出
* 使用import A from ''获取默认导出的文件
* 使用import { A } from ''获取批量导出,获取到的是一个对象
* 可以使用React.lazy进行动态引入
View Code
const SelfComponent = React.lazy(() => import('...'))
* 使用React.lazy引入的文件必须要使用Suspense组件包裹,可以包裹多个懒加载组件
* 使用方法:<Suspense fallback={loading...}></Suspense>,fallback接受的是一个在加载阶段渲染的React元素
12、Context
* Context提供了一种全局的数据共享,而不是只能通过props层层传递
* 常见于用户的身份信息、网站主题、网站语言等
* 基本用法 - 仅展示class组件中的context用法,不涉及react-hook
View Code
const ThemeContext = React.createContext(...) // 参数为要传递的数值 <ThemeContext.Provider value="..."></ThemeContext.Provider> // 使用ThemeContext.Provider包裹要接受Context数值的组件 let value = this.context; // 指定 contextType 读取当前的 theme context。 <Button theme={value}></Button>
* createContext()会提供一个Proider跟一个Consumer属性
* 使用Provider包裹需要参数传递的组件
* 在子组件中使用Consumer进行组件包裹,Consumer接受一个函数{value => ...}
* value 值为父组件传递的context数值
* 返回一个渲染的reactDom
* Context.displayName
* 接受一个字符串作为参数,React.devtools使用该参数来确定context要显示的内容
13、Refs转发
* 在React中可以使用ref来获取dom元素,访问dom节点或者在render中创建的react元素
* 常见用法
View Code
const ref = React.createRef() // 父组件要引用子组件的元素,需要用到ref转发 <Test ref={ref}></Test> const Test = React.forwardRef((props, ref) => (<div ref={ref}></div>))
* 挂载结束后可以通过ref.current指向该dom元素
* 常用来管理焦点、触发动画、文本管理,媒体播放
14、Fragments
* 允许一个组件返回多个元素,但是不需要多余的节点
* <React.Fragments>....</React.Fragments>
* 渲染到页面的时候只会渲染内部的元素,不会渲染Fragments
* 可以简写成为<></>
* 常用于列表的渲染,但是不需要在列表的顶层加父级元素
15、高阶组件 -- HOC
* 指的是接收组件作为参数,返回新组件的函数
* 可用来扩展组件的功能,常用于组件的复用
* 对原组件不做修改,HOC是纯函数,不存在副作用
* 将不相关的props传递给被包裹组件而不是弃用用
* 在render方法中不要使用HOC,因为会每次创建一个全新的组件
* 复制静态方法
* 使用React.forwardRef进行ref的传递
16、性能优化
* 使用构建工具,create-react-app自带webpack构建
* 虚拟化长列表,例如react-window
* 使用shouldComponentUpdate(np,ns)进行渲染优化,默认总是返回true,可以使用React.PureComponent代替shouldComponentUpdate,它将当前与之前的props与state进行浅比较覆写了shouldComponentUpdate
17、Portals
* 提供了一种将子节点渲染到父组件之外的DOM节点的解决方案
* React.createPortal(child,container)
* child -- 任何可以渲染的dom节点
* container -- DOM元素
18、Profiler
* 用来测量React渲染的代价
* <Profiler id={string} onRender={callback}></Profiler>
* 存在额外的性能开销,不建议在生产环境使用
19、协调
* 设计动机
* 基于Dom树的变化进行比较,得出最小的更新操作次数
* 在以下的前提下提出一个O(n)的算法
* 不同的元素构建不同的树
* 通过设置key属性,得到哪些元素是修改过的从而不需要全局更新树
* Diff算法
* 首先比较两棵树的根节点
* 当根节点为不同类型的元素时,会摧毁之前的树,新建一个新的
* 比如当元素类型发生变化时,a->p,span->img等
* 当根节点是相同的元素时,React会保留节点,仅比较属性的变化
* 处理完当前节点后,会对子节点继续递归处理
* 引入key值帮助react发现需要更新的元素
* key建议使用id,如果使用index作为key的话,在列表顺序需要重新排序的时候,diff会变慢
* 需要具备稳定、可预测、列表内唯一的特性
20、Render Props
* 将渲染内容传递的一种方式
View Code
<A render={(data) => <B data={data} />}></A> A.render {this.props.render(this.state)} B.render {this.props.data}
* 任何被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”
* 可以使用Render prop实现大多数的HOC
* 由于是在render是定义了渲染函数,所以每次渲染的时候prop都是不同,因此不建议同PureComponent一起使用
21、静态类型检查
* PropTypes
View Code
import PropTypes from 'prop-types' class A extends React.Component{ ... this.props.name this.state.count ... } A.propTypes = { name: PropTypes.string, count: PropTypes.number }
* TypeScript -- 详情见TypeScript笔记
22、严格模式
* StrictMode是一个用来突出程序中潜在问题的工具,同Fragment一样,不会渲染任何可见的UI
* 仅在开发模式下运行,不会影响生产构建
* <React.StriceMode>...</React.StrictMode>
23、受控组件、非受控组件
* 受控组件即为可通过state进行控制的组件,数据通过react进行管理
* 非受控组件是数据不通过react进行管理,交由dom元素自身进行管理
* 非受控组件可以通过ref来获取dom节点的相关数据