极客时间-笔记:React Hooks 核心原理与实战

认识react

  1. JSX 并不是一个新的模板语言,而可以认为是一个语法糖。
  2. React.createElement 作用就是创建一个组件的实例;参数:
  • 第一个参数表示组件的类型;
  • 第二个参数是传给组件的属性,也就是 props
  • 第三个以及后续所有的参数则是子组件。
React.createElement(
 "div",
 null,
 React.createElement(
   "button",
   { onClick: function onClick() {
       return setCount(count + 1);
     } },
   React.createElement(CountLabel, { count: count })
 )
);

理解hooks

  1. hooks的好处:简化了逻辑复用。

如果用class组件实现逻辑复用需要封装高阶组件;有以下缺点:

  1. 代码难理解,不直观,很多人甚至宁愿重复代码,也不愿用高阶组件;
  2. 会增加很多额外的组件节点。每一个高阶组件都会多一层节点,这就会给调试带来很大的负担。
  1. 在 Class 组件中,代码是从技术角度组织在一起的,例如在 componentDidMount 中都去做一些初始化的事情。而在函数组件中,代码是从业务角度组织在一起的,相关代码能够出现在集中的地方,从而更容易理解和维护。

内置 Hooks

  1. React 提供的 Hooks 其实非常少,一共只有 10 个,比如 useState、useEffect、useCallback、useMemo、useRef、useContext 等等。
  2. 副作用是指一段和当前执行结果无关的代码。
  3. useEffect 让我们能够在下面四种时机去执行一个回调函数产生副作用:
  • 每次 render 后执行:不提供第二个依赖项参数。比如useEffect(() => {})。
  • 仅第一次 render 后执行:提供一个空数组作为依赖项。比如useEffect(() => {}, [])。
  • 第一次以及依赖项发生变化后执行:提供依赖项数组。比如useEffect(() => {}, [deps])。
  • 组件 unmount 后执行:返回一个回调函数。比如useEffect() => { return () => {} }, [])。
  1. Hooks 的使用规则包括以下两个:
  • 只能在函数组件的顶级作用域使用;
  • 只能在函数组件或者其他 Hooks 中使用。
  1. useCallback解决事件处理函数的问题:
  • 不仅增加了系统的开销
  • 每次创建新函数的方式会让接收事件处理函数的组件,需要重新渲染。
  1. useCallback和useMemo做了同一件事情:建立了一个绑定某个结果到依赖数据的关系。只有当依赖变了,这个结果才需要被重新得到

用useMemo实现useCallback:
const myEventHandler = useMemo(() => {
// 返回一个函数作为缓存结果
return () => {
// 在这里进行事件处理
}
}, [dep1, dep2]);

问题:useCallback/useMemo 什么情况下使用?是所有情况?还是个别情况,例如计算量较大等情况?

  1. useRef:
  • 在多次渲染之间共享数据

使用 useRef 保存的数据一般是和 UI 的渲染无关的,因此当 ref 的值发生变化时,是不会触发组件的重新渲染的,这也是 useRef 区别于 useState 的地方。

  • 保存某个 DOM 节点的引用

function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// current 属性指向了真实的 input 这个 DOM 节点,从而可以调用 focus 方法
inputEl.current.focus();
};
return (
<>



);
}
ref 这个属性提供了获得 DOM 节点的能力,并利用 useRef 保存了这个节点的应用。这样的话,一旦 input 节点被渲染到界面上,那我们通过 inputEl.current 就能访问到真实的 DOM 节点的实例了。

  1. useContext 定义全局状态

和全局的变量去保存数据的区别?
就是为了能够进行数据的绑定。当这个 Context 的数据发生变化时,使用这个数据的组件就能够自动刷新。但如果没有 Context,而是使用一个简单的全局变量,就很难去实现了。

  1. useContext 的缺点:
  • 会让调试变得困难,因为你很难跟踪某个 Context 的变化究竟是如何产生的。
  • 让组件的复用变得困难,因为一个组件如果使用了某个 Context,它就必须确保被用到的地方一定有这个 Context 的 Provider 在其父组件的路径上。

所以在 React 的开发中,除了像 Theme、Language 等一目了然的需要全局设置的变量外,我们很少会使用 Context 来做太多数据的共享。需要再三强调的是,Context 更多的是提供了一个强大的机制,让 React 应用具备定义全局的响应式数据的能力。

自定义hooks的使用场景:

  • 抽取业务逻辑;
  • 封装通用逻辑;
  • 监听浏览器状态;
  • 拆分复杂组件。

全局状态管理:如何在函数组件中使用 Redux?:

  1. 理解 Redux 的三个基本概念:State、Action 和 Reducer。
  • 其中 State 即 Store,一般就是一个纯 JavaScript Object。
  • Action 也是一个 Object,用于描述发生的动作。
  • 而 Reducer 则是一个函数,接收 Action 和 State 并作为参数,通过计算得到新的 Store。

好处:1. 可预测性(Predictable):即给定一个初始状态和一系列的 Action,一定能得到一致的结果,同时这也让代码更容易测试。2. 易于调试:可以跟踪 Store 中数据的变化,甚至暂停和回放。因为每次 Action 产生的变化都会产生新的对象,而我们可以缓存这些对象用于调试。Redux 的基于浏览器插件的开发工具就是基于这个机制,非常有利于调试。

  1. React 和 Redux 共同使用时的单向数据流:
  2. 什么是异步action?Middleware 在 Action 真正到达 Reducer 之前提供的一个额外处理 Action 的机会:

Redux 中的 Action 不仅仅可以是一个 Object,它可以是任何东西,也可以是一个函数。利用这个机制,Redux 提供了 redux-thunk 这样一个中间件,它如果发现接受到的 action 是一个函数,那么就不会传递给 Reducer,而是执行这个函数,并把 dispatch 作为参数传给这个函数,从而在这个函数中你可以自由决定何时,如何发送 Action。

复杂状态处理:如何保证状态一致性?

  • 一个是状态最小化原则,也就是说要避免冗余的状态;
  • 另一个则是唯一数据源原则,避免中间状态。

函数组件设计模式:如何应对复杂条件渲染场景?

  1. 容器模式:实现按条件执行 Hooks
  • 把条件判断的结果放到两个组件之中,确保真正 render UI 的组件收到的所有属性都是有值的。
  • 还有一种做法,就是把判断条件放到 Hooks 中去。
  1. 使用 render props 模式重用 UI 逻辑


    逻辑复用,自定义hook亦可

事件处理:如何创建自定义事件?

  1. React 原生事件的原理:合成事件(Synthetic Events)

由于虚拟 DOM 的存在,在 React 中即使绑定一个事件到原生的 DOM 节点,事件也并不是绑定在对应的节点上,而是所有的事件都是绑定在根节点上。然后由 React 统一监听和管理,获取事件后再分发到具体的虚拟 DOM 节点上。
在 React 17 之前,所有的事件都是绑定在 document 上的,而从 React 17 开始,所有的事件都绑定在整个 App 上的根节点上,这主要是为了以后页面上可能存在多版本 React 的考虑。

  1. React 这么做的原因主要有两个:
  • 虚拟 DOM render 的时候, DOM 很可能还没有真实地 render 到页面上,所以无法绑定事件。
  • React 可以屏蔽底层事件的细节,避免浏览器的兼容性问题。同时呢,对于 React Native 这种不是通过浏览器 render 的运行时,也能提供一致的 API。
  1. 由于浏览器事件的冒泡模型。无论事件在哪个节点被触发, React 都可以通过事件的 srcElement这个属性,知道它是从哪个节点开始发出的,这样 React 就可以收集管理所有的事件,然后再以一致的 API 暴露出来。
  2. 虽然自定义事件和原生事件看上去类似,但是两者的机制是完全不一样的:
  • 原生事件是浏览器的机制;
  • 而自定义事件则是纯粹的组件自己的行为,本质是一种回调函数机制。

路由管理:为什么每一个前端应用都需要使用路由机制?

  1. React Router 管理


    react router示例

这里需要注意,React Router 不仅支持浏览器,还支持 React Native,以及一些用 Web 实现的移动 App,所以它提供了多个 npm 模块。

  1. BrowserRouter、Link、Route、Switch 等组件的用法及作用。
  • BrowserRouter:表示用标准的 URL 路径去管理路由,比如 /my-page1 这样的标准 URL 路径。除此之外,还有 MemoryRouter,表示通过内存管理路由;HashRouter,标识通过 hash 管理路由。我们自己实现的例子其实就是用的 hash 来实现路由。
  • Link:定义一个导航链接,点击时可以无刷新地改变页面 URL,从而实现 React Router 控制的导航。
  • Route: 定义一条路由规则,可以指定匹配的路径、要渲染的内容等等。
  • Switch:在默认情况下,所有匹配的 Route 节点都会被展示,但是 Switch 标记可以保证只有第一个匹配到的路由才会被渲染。
  1. 使用嵌套路由:实现二级导航页面所谓嵌套路由,也称为子路由,就是一个页面组件内部,还需要通过 URL 上的信息来决定组件内部某个区域该如何显示。
    嵌套路由示例
  2. 在 URL 中保存页面状态

一方面可以提升用户体验,另一方面也可以简化页面之间的交互

  1. 路由层面实现权限控制

我们完全可以利用前端路由的动态特性。你已经看到了,路由是通过 JSX 以声明式的方式去定义的,这就意味着路由的定义规则是可以根据条件进行变化的,也就是所谓的动态路由。

jsx声明routes

按需加载:如何提升应用打开速度?

  1. 如何实现按需加载?

使用 import 语句,定义按需加载的起始模块语法是 import(someModule)。

webpack分包

react-loadable按需加载

  1. 使用 service worker 缓存前端资源
  • 缓存永远不过期。你只要下载过一次,就永远不需要再重新下载,除非主动删除。
  • 永远不会访问过期的资源。换句话说,如果发布了一个新版本,那么你可以通过版本化的一些机制,来确保用户访问到的一定是最新的资源。

1.注册 Service Worker


注册service worker
  1. 在 Service Worker 安装之后初始化缓存机制


    安装
  2. 拦截请求
    拦截请求

    参考链接:cache mdn

你可能感兴趣的:(极客时间-笔记:React Hooks 核心原理与实战)