react 基本概念解析
关于 react 你必须要知道的事情~
react 阻止默认事件
在 react 阻止默认行为 必须使用
e.preventDefault();
1 | function handleClick(e) { |
在 JSX 回调中你必须注意 this 的指向。 在 JavaScript 中,类方法默认没有 绑定 的。如果你忘记绑定 this.handleClick 并将其传递给 onClick,那么在直接调用该函数时,this 会是 undefined 。
这不是 React 特有的行为;这是 JavaScript 中的函数如何工作的一部分。 一般情况下,如果你引用一个后面没跟 () 的方法,例如 onClick={this.handleClick} ,那你就应该 绑定 (bind) 该方法。
1 | class Toggle extends React.Component { |
解决方式
1 | class LoggingButton extends React.Component { |
或者
1 | class LoggingButton extends React.Component { |
如果需要参数传递给事件处理程序
1 | <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> |
使用逻辑 && 操作符的内联 if 用法 来进行条件渲染
1 | function Mailbox(props) { |
使用条件操作符的内联 if-else
另一个用于条件渲染元素的内联方法是使用 JavaScript 的条件操作符 condition ? true : false 。
1 | render() { |
防止组件渲染 返回 null
1 | function WarningBanner(props) { |
react 和 key
键(key)
键 (Keys) 帮助 React 标识哪个项被修改、添加或者移除了。数组中的每一个元素都应该有一个唯一不变的键 (Keys) 来标识:
1 | const numbers = [1, 2, 3, 4, 5]; |
如何指定 key?
如果你提取 一个 ListItem 组件,应该把 key 放置在数组处理的
1 | function ListItem(props) { |
组合 继承
react 拥有一个强大的组合模型 建议使用组合而不是继承以实现代码的重用
包含 如果你不确定要使用什么组件 在 弹层等通用的容器中比较常见
建议这种组件使用特别的 children props 来直接传递
1 | function FancyBorder(props) { |
1 | function Dialog(props) { |
自定义组件写法
用户自定义的组件必须以大写字母开头
但一个元素类型以小写字母开头 他表示引用一个类似于
以大写字母开头的类型,类似于
1 | import React from 'react'; |
字符串字面量
1 | <MyComponent message="hello world" /> |
props (属性) 默认为 true
1 | <MyTextBox autocomplete /> |
jsx 中的 children
字符串变量
1 | <MyComponent>Hello world!</MyComponent> |
JSX 会删除每行开头和结尾的空格,并且也会删除空行。邻接标签的空行也会被移除,字符串之间的空格会被压缩成一个空格,因此下面的渲染效果都是相同的
1 | <div>Hello World</div> |
Booleans, Null, 和 Undefined 被忽略
false,null,undefined,和 true 都是有效的的 children (子元素) 。但是并不会被渲染,下面的 JSX 表达式渲染效果是相同的:
1 | <div /> |
在有条件渲染的时候非常有用 如果 showheader 为 true 的时候
1 | <div> |
如果在输出中想要渲染 false true null 或者 undefined 必须先将其转化为字符串
1 | <div> |
使用 propType 进行类型检查
可以通过赋值特定的 default 属性为 pros 定义默认值
1 | class Greeting extends React.Component { |
ES6 声明 class 组件
1 | class Greeting extends React.Component { |
设置初始化组件
1 | class Counter extends React.Component { |
一致性比较(diff 算法的实现)
1 | 使用react的时候 任何一个单点时刻可以认为 render()函数的作用是创建react元素树 |
react 基于以下两个假设实现了时间复杂度为 o(n)的算法
- 不同类型的两个元素将会产生不同的树。
- 开发人员可以使用一个 key prop 来指示在不同的渲染中那个那些元素可以保持稳定
diff 算法
当比较不同的树 react 会首先比较两个根元素 根据根的类型不同 它有不同的行为
元素类型不相同时
无论什么时候 当根元素类型不同时,react 将会销毁原先的树并重写构建新的树
当销毁原先的树时 之前的 dom 节点将销毁,实例组件执行 componentWillUnmount () 。当构建一个新的树 新的 dom 节点将会插入 dom 中 组件将会执行 componentWillMount () 以及 componentDidMount () 。与之前旧的树相关的 state 都会丢失。
dom 元素类型相同时
当元素类型相同 react 会比较检查它们的属性(attributes)保留相同的底层 dom 节点 只更新发生改变的属性
1 | <div className="before" title="stuff" /> |
通过比较两个元素,React 会仅修改底层 DOM 节点的 className 属性。
在处理完当前 DOM 节点后,React 会递归处理子节点。
相同类型的组件
当一个组件更新的时候 组件实例保持不变 以便在渲染中保持 state react 会更新组件实例的属性来匹配新的元素 并在组件实例中调用 componentWillReceiveProps () 和 componentWillUpdate ()。接下来, render () 方法会被调用并且 diff 算法对上一次的结果和新的结果进行递归。
子元素递归
1 | <ul> |
1 | <ul> |
怎么解决类似的问题
为了解决这个问题 react 支持一个 key 属性 当子节点有了 key react 使用这个 key 去比较原来的树的子节点和之后的子节点
1 | <ul> |
片段
解决痛点
1 | class Table extends React.Component { |
1 | 使用方式 |
插槽
Portals 提供了一种很好的方法,将子节点渲染到父组件 DOM 层次结构之外的 DOM 节点。
ReactDOM.createPortal(child, container)
1 | 第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 片段(fragment)。 |
1 | render() { |
错误边界
部分 UI 中的 JavaScript 错误不应该破坏整个应用程序,错误边界是 React 组件,它可以在子组件树的任何位置捕获 JavaScript 错误,记录这些错误,并显示一个备用 UI ** ,而不是使整个组件树崩溃。错误边界 (Error Boundaries) 在渲染,生命周期方法以及整个组件树下的构造函数中捕获错误。
1 | class ErrorBoundary extends React.Component { |
注意错误边界 (Error Boundaries) 仅可以捕获其子组件的错误。错误边界无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会向上冒泡至最接近的错误边界。这也类似于 JavaScript 中 catch {} 的工作机制
https://codepen.io/anon/pen/VdyBrE?editors=0010
未捕获错误的新行为
这一改变有非常重要的意义。自 React 16 开始,任何未被错误边界捕获的错误将会卸载整个 React 组件树。
时间处理器如何处理
错误边界无法捕获事件处理器内部的错误。
因为时间处理器不会在渲染周期内触发
因此若他们抛出异常 react 仍然能够知道需要在屏幕中显示什么
如果你需要在事件处理器内部捕获错误,使用普通的 JavaScript try /catch 语句:
1 | class MyComponent extends React.Component { |
代码拆分
1 | import { add } from './math'; |
以后
1 | import("./math").then(math => { |
当 Webpack 遇到这个语法时,它会自动启动 代码拆分 来拆分你的应用程序。
react component
Mounting (装载)
当组件实例被创建并且将其插入 dom 时 z 和谐方法将会被调用
1 | constructor() |
Updating (更新)
改变 props 或者 state 可以触发更新事件,在重新渲染组件时将会调用
1 | componentWillReceiveProps() |
Unmounting (卸载)
当一个组件从 Dom 中删除时 将会调用
1 | componentWillUnmount() |
render () 方法
render () 函数应该是纯函数,这意味着它不会修改组件状态,每次调用它时返回相同的结果 如果 shouldComponentUpdate () 方法返回 false ,render () 不会被调用。
当被调用时,它会检查 this.props 和 this.state 并返回其中一个类型
1 | react元素 |
你可以返回 null 或 false 来表示你不想要渲染任何东西 当返回 null 或 false 时,ReactDOM.findDOMNode (this) 将返回 null
当你想返回多个元素时 用数组操作
1 | render() { |
constructor () 方法
组件被装载前调用,当实现 react.component
时子类的 constructor (构造函数) 时 应该最先调用 super(props)
否则将找不到 this 报错
可以在构造函数中初始化 state 采用
this.state={} 的方式而不是使用 setState () 构造函数也经常用于将事件处理程序绑定到类实例 (目前可以用箭头函数解决此类问题)
如果 state 需要根据 props 的值来初始化,就是你需要考虑变量提升的时候
如果你没有初始化 状态 (state) ,并且没有绑定方法,不用实现一个构造函数
componentWillMount()
在组件装载之前立即被调用的方法 在 render 之前渲染 所以 不会触发 render
也是服务端渲染调用的唯一的生命周期的
钩子
componentDidMount()
组件装载之后立即被调用
初始化需要的 dom 节点或从远程加载数据的地方
在这里调用 setState()
会触 render ()
但会在浏览器更新屏幕之前发生。在这种情况下,即使 render () 会被调用两次, 也可以保证用户不会看到中间状态
componentWillReceiveProps()
在已装载组件接收新 props 之前被调用,
可以在此方法中比较 this.props 和 nextProps 并使用 this.setState () 执行状态转换。
shouldComponentUpdate(nextProps, nextState)
让 React 知道组件的输出是否不受 state 或 props 当前变化的影响,默认行为是在每次 state 更改时重新渲染,并且在绝大多数情况下,你应该依赖于默认行为。
当接收到新的 props 或 state 时,shouldComponentUpdate () 在渲染之前被调用。 默认返回 true ,对于初始 (第一次) 渲染,不调用此方法,返回 false 不会阻止子组件在 state 更改时重新渲染 但是componentWillUpdate()
,render()
和 componentDidUpdate()
将不会被调用
componentWillUpdate()
当接收到 state 或者 props,它会在渲染之前被调用
componentDidUpdate()
在更新发生之立即被调用,当组件已经更新时,也可以在这里操作 dom 也可以做网络请求
componentWillUnmount()
当组件被卸载和销毁之前 被调用 可以在此执行必要的清理 计时器 网络请求 或者在 didmount 创建的元素
1 | componentDidMount() { |
componentDidCatch()
错误边界 可以在其_子组件树 _中的任何位置捕获 js 错误 显示备用 ui
错误边界在渲染过程中,在生命周期方法中,以及整个树下的构造函数中捕获错误。
setState()
setState () 总是会导致重新渲染,除非 shouldComponentUpdate () 返回 false
setState 是作为一个请求而不是立即命令来更新组件 为了性能考虑 react 可能会批量 或 延迟到后面更新它 然后合并多个 setState () 更新多个组件
React 不保证 state 更新就立即应用 (重新渲染)。所以更新之后立即读取可能会有问题
你可以使用 componentDidUpdate 或者 setState 回调 (setState(updater, callback))
1 | 例如,假设我们想通过 props.step 在 state 中增加一个值: |
传递一个更新函数允许你在更新中访问当前的状态值。由于 setState 调用是批处理的,这允许你链式更新并确保它们建立在彼此之上,而不是产生冲突:
1 | incrementCount() { |
ReactDOMServer
ReactDOMServer 对象允许您在服务器上渲染组件。
renderToString()
renderToStaticMarkup()
渲染流
renderToNodeStream()
renderToStaticNodeStream()
渲染流可以减小第一个字节 (TTFB) 渲染时间,在文档的下一个部分生成之前,将文档的开头向下发送到浏览器。所有主流浏览器都会在服务器以这种方式流出内容时开始解析和呈现文档。
节流
是阻止函数在给定时间内被多次调用
Debounce(防抖)
防抖确保函数上次执行后的一段时间内,不会再次执行。
操作 css
1 | render() { |
Virtual DOM and Internals
什么是虚拟 dom
虚拟 dom 是一种编程概念,是指虚拟的视图被保留在内存中 通过如 reactDom 这样的库与 “真实” 的 dom 保持同步
虚拟 DOM 是由 JavaScript 库在浏览器 API 之上实现的一种概念
总结
本文为 react 概况,意在对 react 有个整理认知,其中的任意一条都值得深入研究,日后可能会针对某一个知识点单开文章~