React 笔记—高级指引
本文基于官方 v18.1.0 文档。
Code-Splitting
You need to keep an eye on the code you are including in your bundle so that you don’t accidentally make it so large that your app takes a long time to load.
Code-Splitting is a feature supported by bundlers like Webpack, Rollup and Browserify (via factor-bundle) which can create multiple bundles that can be dynamically loaded at runtime.
Code-splitting your app can help you “lazy-load” just the things that are currently needed by the user, which can dramatically improve the performance of your app.
The best way to introduce code-splitting into your app is through the dynamic import()
syntax.
When Webpack comes across this syntax, it automatically starts code-splitting your app.
React 提供的一种异步动态加载机制。用
React.lazy
导入组件,然后在Suspense
组件中使用组件。
The fallback
prop accepts any React elements that you want to render while waiting for the component to load.
介绍了 React 18 中并发特性 Transition 的一种用法,使用
startTransition
避免显示 fallback。
一种基于路由拆分 bundle 的思路。用到了
React.lazy
和 React Router。
React.lazy
currently only supports default exports. If the module you want to import uses named exports, you can create an intermediate module that reexports it as the default.
Context
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Error Boundaries
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed.
Error boundaries do not catch errors for:
- Event handlers
- Asynchronous code (e.g.
setTimeout
orrequestAnimationFrame
callbacks) - Server side rendering
- Errors thrown in the error boundary itself (rather than its children)
A class component becomes an error boundary if it defines either (or both) of the lifecycle methods static getDerivedStateFromError()
or componentDidCatch()
. Use static getDerivedStateFromError()
to render a fallback UI after an error has been thrown. Use componentDidCatch()
to log error information.
Only class components can be error boundaries.
New Behavior for Uncaught Errors
As of React 16, errors that were not caught by any error boundary will result in unmounting of the whole React component tree.
React 16 起,未捕获错误会导致整个 React 组件树被卸载。
…it is worse to leave corrupted UI in place than to completely remove it.
try
/catch
只适用于命令式代码,而基于 jsx 的 React 是声明式的。
Error boundaries do not catch errors inside event handlers.
错误边界仅负责 React 组件本身的错误处理,比如 render 方法、其它生命周期方法。
Forwarding Refs
Ref forwarding is a technique for automatically passing a ref through a component to one of its children.
Ref forwarding is not limited to DOM components. You can forward refs to class component instances, too.
大致上就是,有时候组件使用者想获取组件内层某个 DOM 元素或子组件的 ref,因为 ref is not available in props,必须使用
React.forwardRef
创建组件,将 ref 作为函数参数传递到组件内部。即,正常创建的组件
<FancyButton ref={ref} />
,那 ref 一定是指向FancyButton
的;而如果此组件是通过React.forwardRef
创建,ref 就有可能指向内部元素。这似乎意义不大啊,虽然 ref 是保留字,我依然可以换用其它更有意义的 prop 名字通过普通组件达到相同的目的,比如
<FancyButton innerButtonRef={ref} />
。
而且 ref 转发只适用于函数组件,不能用于类组件。
似乎只是提供了一种可以不修改接口就可以改变行为的手段,对组件库开发者比较有用?
Fragments
Fragments let you group a list of children without adding extra nodes to the DOM.
Fragments declared with the explicit <React.Fragment>
syntax may have keys.
短语法不能有属性。
Higher-Order Components
A higher-order component is a function that takes a component and returns a new component.
Use HOCs For Cross-Cutting Concerns
横切关注点指的是一些具有横越多个模块的行为。
高阶组件用来解决多组具有同一类切面导致的重复代码的问题。可以将形成切面关系的两者通过另一个更高阶的组件封装,将横切关注点放在高阶组件内。
A HOC is a pure function with zero side-effects.
Don’t Mutate the Original Component. Use Composition.
Convention: Pass Unrelated Props Through to the Wrapped Component
HOCs add features to a component. They shouldn’t drastically alter its contract. It’s expected that the component returned from a HOC has a similar interface to the wrapped component.
Static Methods Must Be Copied Over
在 HOC 返回前必须把 wrappedComponent 的所有静态方法复制到容器组件中,否则用户通过高阶组件调用不到这些静态方法。
However, this requires you to know exactly which methods need to be copied. You can use hoist-non-react-statics to automatically copy all non-React static methods.
Integrating with Other Libraries
以 jQuery 和 backbone.js 为例,详述了如何将 React 同其它库集成。略过没看。
JSX In Depth
Specifying The React Element Type
Capitalized types indicate that the JSX tag is referring to a React component.
React 组件名称必须大写字母开头。
Since JSX compiles into calls to React.createElement
, the React
library must also always be in scope from your JSX code.
只要用到了 React 组件,就要
import React from 'react';
If you don’t use a JavaScript bundler and loaded React from a <script>
tag, it is already in scope as the React
global.
You cannot use a general expression as the React element type. If you do want to use a general expression to indicate the type of the element, just assign it to a capitalized variable first.
JavaScript Expressions as Props
if
statements and for
loops are not expressions in JavaScript, so they can’t be used in JSX directly.
When you pass a string literal, its value is HTML-unescaped.
即
<MyComponent message="<3" />
这种会先被转义。
If you pass no value for a prop
, it defaults to true.
In general, we don’t recommend not passing a value for a prop, because it can be confused with the ES6 object shorthand {foo}
which is short for {foo: foo}
rather than {foo: true}
.
You can also pick specific props that your component will consume while passing all other props using the spread syntax.
1 | const Button = props => { |
变量
other
的定义。
In JSX expressions that contain both an opening tag and a closing tag, the content between those tags is passed as a special prop: props.children
.
props.children
works just like any other prop in that it can pass any sort of data, not just the sorts that React knows how to render.
Booleans, Null, and Undefined Are Ignored
false
, null
, undefined
, and true
are valid children. They simply don’t render.
Optimizing Performance
使用一些第三方虚拟列表库:
In most cases, instead of writing shouldComponentUpdate()
by hand, you can inherit from React.PureComponent
. It is equivalent to implementing shouldComponentUpdate()
with a shallow comparison of current and previous props and state.
The Power Of Not Mutating Data
因为
React.PureComponent
做的是浅比较,所以要避免修改 object 类型的 state 或 prop 的值。
可以通过展开操作符或object.assign
等方法每次创建新对象赋值给 state / prop。
Portals
Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
1 | render() { |
Event Bubbling Through Portals
This includes event bubbling. An event fired from inside a portal will propagate to ancestors in the containing React tree, even if those elements are not ancestors in the DOM tree.
Profiler API
1 | function onRenderCallback( |
React Without ES6
略过。
React Without JSX
略过。
Reconciliation
React implements a heuristic O(n) algorithm based on two assumptions:
- Two elements of different types will produce different trees.
- The developer can hint at which child elements may be stable across different renders with a
key
prop.
Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch.
When comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes.
When updating style, React also knows to update only the properties that changed.
By default, when recursing on the children of a DOM node, React just iterates over both lists of children at the same time and generates a mutation whenever there’s a difference.
If you implement it naively, inserting an element at the beginning has worse performance.
如果没有
key
,React 就无脑地迭代比较每一子项——即用索引匹配比较项。所以如果在索引 0 处新插入了项,React 会认为自 0 开始的每一项都改变了,而不认为只是新加了一项。借此说明key
的必要性。
When children have keys, React uses the key to match children in the original tree with children in the subsequent tree.
key
只对 children 生效。
Reorders can also cause issues with component state when indexes are used as keys.
如果用索引做 key,重排序时比较项会错乱。
呼应开头的两个假设。为了能获得最好的性能,尽量使用同一种组件/元素类型;尽量使用稳定、可预测、唯一的 key 值。
Refs and the DOM
You may not use the ref
attribute on function components because they don’t have instances.
ref
updates happen before componentDidMount or componentDidUpdate lifecycle methods.
If you want to allow people to take a ref
to your function component, you can use forwardRef
.
承上文,
ref
不能指向函数组件。
ref
属性还支持接受一个函数作为参数值。函数接受一个指向当前组件/元素的参数。这时候就不需要createRef
了,用普通的成员变量保存参数即可。
If the ref
callback is defined as an inline function, it will get called twice during updates, first with null
and then again with the DOM element. This is because a new instance of the function is created with each render, so React needs to clear the old ref and set up the new one.
Render Props
The term “render prop” refers to a technique for sharing code between React components using a prop whose value is a function.
Use Render Props for Cross-Cutting Concerns
More concretely, a render prop is a function prop that a component uses to know what to render.
In fact, any prop that is a function that a component uses to know what to render is technically a “render prop”.
children 也是。但是 children 还比较特殊,支持把函数定义放在组件内部。
上边 3 句话逐渐接近 render prop 的本质。
Be careful when using Render Props with React.PureComponent
内联函数传递的通病——每次渲染都会认为是新创建的函数。解决方法也是一样,改为组件类的实例方法。
Static Type Checking
介绍了 Flow、TypeScript 等几种静态检查工具、语言。略。
Strict Mode
Strict mode checks are run in development mode only; they do not impact the production build.
StrictMode
currently helps with:
Warning about legacy string ref API usage
Warning about deprecated findDOMNode usage
Detecting unexpected side effects
Conceptually, React does work in two phases:
- The render phase determines what changes need to be made to e.g. the DOM. During this phase, React calls
render
and then compares the result to the previous render. - The commit phase is when React applies any changes. (In the case of React DOM, this is when React inserts, updates, and removes DOM nodes.) React also calls lifecycles like
componentDidMount
andcomponentDidUpdate
during this phase.
这章节信息量很大。列出了
render
阶段具体有哪些生命周期方法;v18 引入的并发模式有可能会导致多次调用render
阶段的生命周期方法;严格模式会故意地两次调用这些生命周期方法以帮助识别非幂等代码导致的问题等。
In the future, we’d like to add a feature that allows React to add and remove sections of the UI while preserving state.
为了支持这个 feature,React 要多次 mount、unmount 组件,
StrictMode
会人为地 unmount 然后重新 mount 组件以测试组件,发现问题。
Typechecking With PropTypes
To run typechecking on the props for a component, you can assign the special propTypes
property.
PropTypes
exports a range of validators that can be used to make sure the data you receive is valid.
When an invalid value is provided for a prop, a warning will be shown in the JavaScript console. For performance reasons, propTypes
is only checked in development mode.
1 | MyComponent.propTypes = { |
MyComponent
只能包含一个元素。
1 | Greeting.defaultProps = { |
name prop 的默认值是 Stranger。
必须在
export
之前定义 propTypes,所以要把 export default function 分开两句,中间定义 propTypes 属性。
Uncontrolled Components
Changing the value of defaultValue
attribute after a component has mounted will not cause any update of the value in the DOM.
In React, an <input type="file" />
is always an uncontrolled component because its value can only be set by a user, and not programmatically.
Web Components
React and Web Components are built to solve different problems. Web Components provide strong encapsulation for reusable components, while React provides a declarative library that keeps the DOM in sync with your data.