Blog

useEffect的四种执行时机

Content #

useEffect 能够在下面四种时机去执行一个回调函数产生副作用:

  1. 每次 render 后执行:不提供第二个依赖项参数。比如

useEffect(() => {})。

  1. 仅第一次 render 后执行:提供一个空数组作为依赖项。比如

useEffect(() => {}, [])。

  1. 第一次以及依赖项发生变化后执行:提供依赖项数组。比如

useEffect(() => {}, [deps])。

  1. 组件 unmount 后执行:返回一个回调函数。比如

useEffect() => { return () => {} }, [])。

Viewpoints #

From #

03|内置 Hooks(1):如何保存组件状态和使用生命周期?

sub:useEffect

Content #

useEffect,用于执行一段副作用。

什么是副作用呢?通常来说,副作用是指一段和当前执行结果无关的代码。比如说要修改函数外部的某个变量,要发起一个请求,等等。也就是说,在函数组件的当次执行过程中,useEffect 中代码的执行是不影响渲染出来的 UI 的。

useEffect 可以接收两个参数,函数签名如下:

useEffect(callback, dependencies)

第一个为要执行的函数 callback,第二个是可选的依赖项数组 dependencies。其中依赖项是可选的,如果不指定,那么 callback 就会在每次函数组件执行完后都执行;如果指定了,那么只有依赖项中的值发生变化的时候,它才会执行。

callback函数必须是同步的,不能是异步的(不能使用async声明)。

显示Blog实例(useEffect, useState) useEffect的四种执行时机 定义依赖项的三个注意点 函数作为依赖项 三种常用的生命周期方法与useEffect

From #

03|内置 Hooks(1):如何保存组件状态和使用生命周期?

Hooks与Class在逻辑复用上的对比

Content #

Hooks 带来的最大好处:逻辑复用 #

在之前的 React 使用中,有一点经常被大家诟病,就是非常难以实现逻辑的复用,必须借助于高阶组件等复杂的设计模式。但是高阶组件会产生冗余的组件节点,让调试变得困难。不过这些问题可以通过 Hooks 得到很好的解决。所以如果有人问你 Hooks 有什么好处,那么最关键的答案就是简化了逻辑复用。

就以刚才我们提到的绑定窗口大小的场景为例。如果有多个组件需要在用户调整浏览器窗口大小时,重新调整布局,那么我们需要把这样的逻辑提取成一个公共的模块供多个组件使用。以 React 思想,在 JSX 中我们会根据 Size 大小来渲染不同的组件,例如:

function render() {
  if (size === "small") return <SmallComponent />;
  else return <LargeComponent />;
}

这段代码看上去简单,但如果在过去的 Class 组件中,我们甚至需要一个比较复杂的设计模式来解决,这就是高阶组件。

在 Class 组件的场景下,我们首先需要定义一个高阶组件,负责监听窗口大小变化,并将变化后的值作为 props 传给下一个组件。

const withWindowSize = Component => {
  // 产生一个高阶组件 WrappedComponent,只包含监听窗口大小的逻辑
  class WrappedComponent extends React.PureComponent {
    constructor(props) {
      super(props);
      this.state = {
        size: this.getSize()
      };
    }
    componentDidMount() {
      window.addEventListener("resize", this.handleResize);
    }
    componentWillUnmount() {
      window.removeEventListener("resize", this.handleResize);
    }
    getSize() {
      return window.innerWidth > 1000 ? "large" "small";
    }
    handleResize = ()=> {
      const currentSize = this.getSize();
      this.setState({
        size: this.getSize()
      });
    }
    render() {
      // 将窗口大小传递给真正的业务逻辑组件
      return <Component size={this.state.size} />;
    }
  }
  return WrappedComponent;
};

这样,在你的自定义组件中可以调用 withWindowSize 这样的函数来产生一个新组件,并自带 size 属性,例如:

...

Hooks的来历

Content #

如果我们想要让函数组件更有用,目标就是给函数组件加上状态。这看上去似乎并不是难事。

简单想一下,函数和对象不同,并没有一个实例的对象能够在多次执行之间保存状态,那势必需要一个函数之外的空间来保存这个状态,而且要能够检测其变化,从而能够触发函数组件的重新渲染。

再进一步想,那我们是不是就是需要这样一个机制,能够把一个外部的数据绑定到函数的执行。当数据变化时,函数能够自动重新执行。这样的话,任何会影响 UI 展现的外部数据,都可以通过这个机制绑定到 React 的函数组件。

在 React 中,这个机制就是 Hooks。

顾名思义,Hook 就是“钩子”的意思。在 React 中,Hooks 就是把某个目标结果钩到某个可能会变化的数据源或者事件源上,那么当被钩到的数据或事件发生变化时,产生这个目标结果的代码会重新执行,产生更新后的结果。

对于函数组件,这个结果是最终的 DOM 树;对于 useCallback、useMemo 这样与缓存相关的组件,则是在依赖项发生变化时去更新缓存。所以 Hooks 的结构可以如下图所示:

从图中可以看到,一个执行过程(Execution),例如是函数组件本身,可以绑定在(钩在)传统意义的 State,或者 URL,甚至可以是窗口的大小。这样当 State、URL、窗口大小发生变化时,都会重新执行某个函数,产生更新后的结果。

当然,既然我们的初衷是为了实现 UI 组件的渲染,那么在 React 中,其实所有的 Hooks 的最终结果都是导致 UI 的变化。但是正如 React 官方曾经提到过的,Hooks 的思想其实不仅可以用在 React,在其它一些场景也可以被利用。

Viewpoints #

From #

02|理解 Hooks:React 为什么要发明 Hooks?

LRU策略与扫描式单次查询

Content #

只看数据的访问时间,使用 LRU 策略在处理扫描式单次查询操作时,无法解决缓存污染。

扫描式单次查询操作,指应用对大量的数据进行一次全体读取,每个数据都会被读取,而且只会被读取一次。

此时,因为这些被查询的数据刚刚被访问过,所以 lru 字段值都很大。在使用 LRU 策略淘汰数据时,这些数据会留存在缓存中很长一段时间,造成缓存污染。如果查询的数据量很大,这些数据占满了缓存空间,却又不会服务新的缓存请求,此时,再有新数据要写入缓存的话,还是需要先把这些旧数据替换出缓存才行,这会影响缓存的性能。

From #

PCRE流派与POSIX流派的对比

Content #

在遵循 POSIX 规范的 UNIX/LINUX 系统上,按照 BRE 标准 实现的有 grep、 sed 和 vi/vim 等,而按照 ERE 标准 实现的有 egrep、awk 等。

在 UNIX/LINUX 系统里 PCRE 流派与 POSIX 流派的对比。

刚刚我们提到了工具对应的实现标准,其实有一些工具实现同时兼容多种正则标准,比如前面我们讲到的 grep 和 sed。如果在使用时加上 -E 选项,就是使用 ERE 标准;如果加上 -P 选项,就是使用 PCRE 标准。

# 使用 ERE 标准
grep -E '[[:digit:]]+' access.log

# 使用 PCRE 标准
grep -P '\d+' access.log

Viewpoints #

From #

07 | 正则有哪些常见的流派及其特性?

BRE与ERE的区别

Content #

POSIX 规范定义了正则表达式的两种标准:

  1. BRE 标准(Basic Regular Expression 基本正则表达式);
  2. ERE 标准(Extended Regular Expression 扩展正则表达式)。

早期 BRE 与 ERE 标准的区别主要在于,BRE 标准不支持量词问号和加号,也不支持多选分支结构管道符。BRE 标准在使用花括号,圆括号时要转义才能表示特殊含义。BRE 标准用起来这么不爽,于是有了 ERE 标准,在使用花括号,圆括号时不需要转义了,还支持了问号、加号 和 多选分支。

我们现在使用的 Linux 发行版,大多都集成了 GNU 套件。GNU 在实现 POSIX 标准时,做了一定的扩展,主要有以下三点扩展。

  1. GNU BRE 支持了 +、?,但转义了才表示特殊含义,即需要用\+、\?表示。
  2. GNU BRE 支持管道符多选分支结构,同样需要转义,即用 \|表示。
  3. GNU ERE 也支持使用反引用,和 BRE 一样,使用 \1、\2…\9 表示。

BRE 标准和 ERE 标准的详细区别,浅黄色背景是 BRE 和 ERE 不同的地方,三处天蓝色字体是 GNU 扩展。

总之,GNU BRE 和 GNU ERE 它们的功能特性并没有太大区别,区别是在于部分语法层面上,主要是一些字符要不要转义。

Viewpoints #

From #

07 | 正则有哪些常见的流派及其特性?

...

求向量子空间的基

Content #

该如何得到向量子空间的基呢?

只要通过三步操作,就能得到向量子空间 U 的基,

$$U=span[x_1, x_2, \cdots, x_m]$$
  1. 把向量 $x_1,x_2,\cdots,x_m$ 组合成矩阵 A 的列;
  2. 得到 A 的行阶梯形矩阵;
  3. 和主元列相关的向量就是 U 的一个基。

From #

霸王道杂之

Content #

刘询不愧是西汉的中兴之主,死后“中宗”的庙号当之无愧。他既能笼络儒家,又能抗衡儒家,也不惜杀戮儒臣,从而在王道和霸道之间圆润执中,汉朝的“建国”至此才算真正完成,汉家也臻于极盛之绚烂。就在这绚烂之中,隐约存在着某种危机。刘询的太子刘奭热衷儒学,曾在侍宴的时候,看父亲心情好,从容劝父亲要遵从王道,减少杀戮。没想到刘询对此很不满意,说了一段著名的话,这段话可以视为汉朝“建国”的内涵,也是“汉道”的宣示,更是王莽登上历史舞台的预言:汉家自有制度,本以霸王道杂之,奈何纯任德教,用周政乎!且俗儒不达时宜,好是古非今,使人眩于名实,不知所守,何足委任!乱我家者,太子也!

“霸王道杂之”,不能“纯用周政”,这一不可淆乱的“家法”就是汉朝“建国”的汉道。从刘邦和吕后立国,经过一百五十多年的探索、涵养,至此,汉朝的建国和建政大业终于都已完成,西汉的“德性”也在这时期臻于圆满 。当然,这一“家法”很难说是皇家有目的制作的若干制度,而是由几代汉帝层层累积形成的惯例、传统。如果说儒家提供了一种理论上的合法性,那么对皇室以及当时的汉朝人来说,汉家自秦沿袭而来的惯例、传统也具有合法性。两者都是刘氏家族拥有天下的必要因素。

然而,刘询鼓励各地报祥瑞,是为了烘托汉朝的伟大和自己的神圣,却做梦也没想到,祥瑞和灾异是“一体两面”,当朝野习惯了以灾异和祥瑞来窥探政事,那么一旦对政治不满,就满眼都是灾异;而所有的灾异都会指涉政治,从而侵蚀着汉朝统治的合法性。刘询用外戚来对抗儒臣,是因为外戚不必学习儒术即可从政,而且外戚一般只担任中朝官,与外朝的儒臣形成平衡。但他没有预料到,有一天,儿媳妇的侄子王莽身为外戚,居然儒家化了,甚至成为儒家的代表,从而得以收拢内外两朝,架空皇权。

From #

缓存写提交存的两个缺点

Content #

首先是在客户端发送 Commit 前,SQL 要被缓存起来,如果某个业务场景同时存在长事务和海量并发的特点,那么这个缓存就可能被撑爆或者成为瓶颈。

其次是客户端看到的 SQL 交互过程发生了变化,在 MySQL 中如果出现事务竞争,判断优先级的规则是 First Write Win,也就是对同一条记录先执行写操作的事务获胜。而 TiDB 因为缓存了所有写 SQL,所以就变成了 First Commit Win,也就是先提交的事务获胜。

Viewpoints #

From #

10 | 原子性:如何打破事务高延迟的魔咒?