# react 相关
# 概念
在 react 应用程序由两大重要组成:元素和组件。
# 元素
React 元素是构成 React 应用的最小砖块,描述了屏幕上看到的内容(DOM)。React 元素开销极小,React 保证了 React 元素与 DOM 元素的一致。
React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。更新 UI 唯一的方式是创建一个全新的元素。
# JSX 语法
JSX 是 JavaScript 的扩展,将 UI 和 逻辑 结合起来,有利于描述页面应有的交互本质。React 负责将这些语法生成元素。 Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。React.createElement() 会预先执行一些检查,以帮助你编写无错代码,但实际上它创建了一个这样的对象:
// 注意:这是简化过的结构
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
2
3
4
5
6
7
8
这些对象被称为 “React 元素”。它们描述了你希望在屏幕上看到的内容。React 通过读取这些对象,然后使用它们来构建 DOM 以及保持随时更新。
React 认为渲染逻辑本质上与其他 UI 逻辑内在耦合,通过将元素和逻辑共同存放在 组件 中,以实现关注点分离。
关注点分离:日常生活中解决复杂问题的一种系统思维方法。思路是,先将复杂问题做合理的分解,再分别仔细研究问题的不同侧面 (关注点),最后综合各方面的结果,合成整体的解决方案。
JSX 本质上是一个表达式。
# JSX 能够防注入攻击
React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。
# 组件
从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
根据组件的定义方式可分为函数组件和类组件。
React 内部对两种组件的处理:
// 如果 Greeting 是一个函数
const result = Greeting(props); // <p>Hello</p>
// 如果 Greeting 是一个类
const instance = new Greeting(props); // Greeting {}
const result = instance.render(); // <p>Hello</p>
2
3
4
5
6
# 函数组件与类组件
从官方的观点,应该是更推荐使用 函数组件。
- Hook 避免了 class 需要的额外开支,像是创建类实例和在构造函数中绑定事件处理器的成本。
- 没有计划从 React 中移除 class……
- 我们准备让 Hook 覆盖所有 class 组件的使用场景,但是我们将继续为 class 组件提供支持。
# 纯组件
纯函数:不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。
所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。为了实现 UI 交互,组件可以维护自有的状态 state 。
state 的更新是异步的,因为 React 为了提升性能,会收集多个 setState 一次更新。
# 与元素的区别
一个 Element 是一个简单的对象,它描述了你希望在屏幕上以 DOM 节点或其他组件的形式呈现的内容。
而一个组件可以用多种不同方式声明。它可以是一个含有 render() 方法的类。或者,在简单的情况中,它可以定义为函数。无论哪种情况,它都将 props 作为输入,并返回一个 JSX 树作为输出。
# 生命周期
# 组合代替继承
# Context
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
Context 设计目的是为了共享那些对于一个组件树而言是 “全局” 的数据,例如当前认证的用户、主题或首选语言。
也有一些其它方案可以解决父组件向深嵌套的子组件传递 prop 的方案:
- 组件组合:把子组件定义在父组件内,然后传递子组件本身
- 使用 render prop 以加强父子组件间的渲染通信
有的时候在组件树中很多不同层级的组件需要访问同样的一批数据。Context 能让你将这些数据向组件树下所有的组件进行 “广播”,所有的组件都能访问到这些数据,也能访问到后续的数据更新。
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。
# HOOK
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性,是一些可以让你在函数组件里 “钩入” React state 及生命周期等特性的函数。
# 为什么使用 HOOK
- 在组件之间复用状态逻辑很难
React 没有提供将可复用性行为 “附加” 到组件的途径,你可以使用 Hook 从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑。 这使得在组件间或社区内共享 Hook 变得更便捷。
- 复杂组件变得难以理解
组件起初很简单,但是逐渐会被状态逻辑和副作用充斥。每个生命周期常常包含一些不相关的逻辑。
在多数情况下,不可能将组件拆分为更小的粒度,因为状态逻辑无处不在。
为了解决这个问题,Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分。
- 难以理解的 class
你必须去理解 JavaScript 中 this 的工作方式,这与其他语言存在巨大差异。还不能忘记绑定事件处理器。即便在有经验的 React 开发者之间,对于函数组件与 class 组件的差异也存在分歧,甚至还要区分两种组件的使用场景。
class 不能很好的压缩,并且会使热重载出现不稳定的情况。
为了解决这些问题,Hook 使你在非 class 的情况下可以使用更多的 React 特性。 从概念上讲,React 组件一直更像是函数。而 Hook 则拥抱了函数,同时也没有牺牲 React 的精神原则。Hook 提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术。
# 使用规则
- 只能在函数最顶层调用 Hook。不要在循环、条件判断或者子函数中调用。
- 如果需要条件判断,可以在 Hook 内部实现
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。
- Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。
# 使用多个 useState 时,React 怎么知道哪个是哪个
React 使用 Hook 函数的调用顺序来保证对应关系。因为通过上述规则,保证了在每次渲染中,Hook 的调用顺序都是相同的。
# 常用 HOOK
# useState
- 参数:初始 state
- 返回值:当前 state 以及更新 state 的函数
State 变量可以很好地存储对象和数组,因此,你仍然可以将相关数据分为一组。然而,不像 class 中的 this.setState,更新 state 变量总是替换它而不是合并它。
state 只在组件首次渲染的时候被创建。在下一次重新渲染时,useState 返回给我们当前的 state。否则它就不是 “state” 了!
# useEffect
副作用:数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。
副作用分为两种:需要清除的、不需要清除的。
当你调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的 “副作用” 函数。整合了 componentDidMount、componentDidUpdate 和 componentWillUnmount 生命周期函数。
有一些副作用,我们希望它在每次渲染之后执行 —— 但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方(componentDidMount、componentDidUpdate)调用它。
通过使用 Hook,你可以把组件内相关的副作用组织在一起(例如创建订阅及取消订阅),而不要把它们拆分到不同的生命周期函数里。
- 默认情况下,React 会在每次渲染后调用副作用函数 —— 包括第一次渲染的时候。
- 可以访问到组件的 props 和 state
- 通过返回一个函数来指定如何 “清除” 副作用
- React 会在执行当前 effect 之前对上一个 effect 进行清除。
- 目的是为了减少由于 prop 更新带来的数据不一致(effect 覆盖了 componentDidUpdate,却没法在 update 阶段手动更新 state)
注意
传递给 useEffect 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 count 的值(重新生成 effect 函数,产生新闭包,包进最新的 state),而不用担心其过期的原因。每次我们重新渲染,都会生成新的 effect,替换掉之前的。某种意义上讲,effect 更像是渲染结果的一部分 —— 每个 effect “属于” 一次特定的渲染。
与 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 useLayoutEffect Hook 供你使用,其 API 与 useEffect 相同。
进阶
可以使用多个 useEffect ,将不相关逻辑分离到不同的 effect 中,实现关注点分离。(又一个抛弃 class 组件的理由)React 将按照 effect 声明的顺序依次调用组件中的每一个 effect。
可以向 useEffect 传入第二个数组参数以优化渲染频次。数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量,否则你的代码会引用到先前渲染中的旧变量。
关于这种优化方式有一些 注意事项 (opens new window)。
# 自定义 HOOK
Hook 是一种复用状态逻辑的方式,它不复用 state 本身。
Hook 的每次调用都有一个完全独立的 state
你可以创建涵盖各种场景的自定义 Hook,如表单处理、动画、订阅声明、计时器,甚至可能还有更多我们没想到的场景。
// 自定义的例子,订阅用户状态
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 使用自定义 hook
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
2
3
4
5
6
7
8
9
约定
- 自定义 Hook 不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么
- 自定义 Hook 应当以 use 开头,以便 React 检查其是否符合 Hook 规则
- 自定义 Hook 中的 state 和 effect 都是完全隔离的
# useRedux
# 八股
# React 的特性
- 虚拟 DOM
- 服务端渲染
- 单向数据流、数据绑定
- 可复用/可组合的组件式 UI 开发视图
#
# 原理
# 最佳实践
# MVVM
- React 与 jQuery 的区别是什么?
- 组件完整的生命周期
- React 里 setState 方法是同步的还是异步的?
- 组件属性受控和不受控是什么意思?
- React 里组件间通信用什么方案?
- React 高阶组件(HOC)是什么,在什么场景使用?有什么副作用?
- 什么是 Stateless Component?
- React 如何做性能优化?shouldComponentUpdate
- 父组件如何改变内部子组件的渲染逻辑?
- this.props.children
- React.Children.map
- React.cloneElement
- 单页面应用:React-Router
- /1 跳转到 /2 时页面组件是否会被重新初始化渲染
- 如何实现类打点的全局需求,onEnter
- 页面 bundle 太大有什么解决方案
- React 服务端渲染
- Vue 双向绑定实现原理
- getter/setter + 劫持数组方法
- 还有哪些方案?脏检查/Proxy,三个方案优缺点
- vue 里在 data 上添加一个新属性 view 是否会得到响应?不会,为什么?怎么解决?
- Vue 2.0 引入 vdom 的原因是什么
- vdom 有哪些优势:diff 性能、标准化数据适配多端渲染