Roselia-Blog

Roselia-Blog Hooks

06/11/2020 04:14:27

见习魔法师

Roselia-Blog Hooks

我们知道,React Hooks是一个令人称道的设计,它可以让使用者在不使用class的前提下使用state以及其他react特性,这个和Roselia-Blog的内嵌脚本十分相似,该脚本类似于tex,是文字与控制命令的结合。但是,该脚本没有类来存储状态,在这种情况下,我们可以将整个文章视为一个渲染函数,作者写出了一篇文章,恰巧,这篇文章里有部分内嵌脚本用于帮助渲染文章的内容。

在3.8.2中,Roselia-Blog支持了hooks API,这些API和React中的同名hook表现一致(虽然Roselia-Blog是用Vue实现的),当然,也有Vue-style的API以供使用。 当然,hooksAPI 在评论中是无法使用的,所有的改动也只针对文章内容。

defState

让我们先从最简单的一个例子来看看hooks是如何实现的:


渲染出来结果如下:

这个语句由两个部分组成,第一个是对defState的调用,第二个是对btn的调用,最后返回了btn的结果(逗号表达式,并不是生成元组)。

defState接受两个参数,第一个是变量名,第二个是初始值,可以是一个接受0个参数,返回初始值的函数。之后会在上下文中引入同名的响应式变量,这就是接下来能用c的原因。btn接受三个参数(都是可选的),第一个是按钮显示的文字,第二个是onClick的handler,最后一个是class的名字。这段代码会生成一个按钮,显示当前count,点击一下就将count + 1,最后就是这样的结果。

那么为什么count改变时,button上显示的文字也会改变呢?其实很简单,只要监听变量的改变,然后重新渲染即可。此时所有的script都会被执行,在第二次执行defState的时候,和第一次不同,将会直接引用之前的变量,也就是说,defState会根据传入的名字来检查cache中有没有该变量,若有,则直接返回,若没有,则初始化该值并返回,然后监听该值的变化,如果改变了,就重新渲染。其基本思想是依赖调用的时序和名字,保证每次调用对相同的名字返回相同的引用。

export class RoseliaScriptState {
  private readonly internalState: object
  public readonly state: object
  private updateCallback: (() => void) | undefined;
  constructor(callback?: () => void) {
    this.updateCallback = callback
    this.internalState = {}
    this.state = new Proxy(this.internalState, {
      set(target, key, value) {
        const result = Reflect.set(target, key, value)
        this.triggerCallback()
        return result
      }
    })
  }

  public triggerCallback() {
    this.updateCallback?.()
  }

  public defineState<S>(key: string, value: S | (() => S)) {
    if (key in this.internalState) return this.internalState[key];
    this.internalState[key] = value instanceof Function ? value() : value;
  }
}

可以看见,监听改变是通过Proxy实现的,对外只暴露被监听的state

useState

当然,有诸多React的拥趸怀念useState,不用担心,我特地准备了一份。useState只接受一个参数,即初始值,返回一个列表,第一个元素是值,第二个元素是设置该值的函数,该设置函数可以接受一个值,或者一个根据旧值生成新值的函数。

useState依赖于函数调用的顺序,即该函数在每次渲染时返回的是时序的,相同次序调用的函数返回相同的引用。关于其具体实现,可以看React hooks: not magic, just arrays 。因此,我们可以这样写一个计数器:


渲染结果如下:

注意,在测试时,同一篇文章中变量不要冲突,现在c是,x是。 因为所有的脚本是按顺序调用的,所以定义的变量的作用域是从定义到文章末尾,所以变量要先定义再声明,否则你可能会获得上次渲染的变量。

useEffect

useEffect是另一个hook,可以管理渲染中的副作用。该hook和Effect Hook表现一致,可以用于显示通知等其他用处。

这里有一个demo是利用useEffect实现一个可保存状态的todo list,其原理是通过监听某个变量的改动而执行effect,每次渲染时,如果值没有发生改变,则不执行。

Roselia-Hooks 了解情况:完成了 个,总共 个。

代码如下:








Roselia-Hooks 了解情况:完成了 个,总共  个。





例如,Roselia-Blog的额外显示设置,现在可以使用反应式状态来跟踪,从而实现动态更新的效果,目前Roselia-Blog支持的开关有:

在引入了hooksAPI之后,我们就不需要使用class-style component了,虽然我也从来没有过这个plan。hooks 的好处是可以在想要使用reactive状态的时候直接声明,将相同任务的代码放在一起,但是可能会有性能损失,因为所有的闭包都会重复创建,比如所有的帮助函数,都会重新创建一份,但是鉴于Roselia-Blog的文章都相对简短,现代计算机足以应对这种程度的重渲染,因此不需要再暴露dom API去手动管理dom了,也算是告别了刀耕火种的时代了。

做一个游戏

手速大挑战!

最高分:

当前分数: 剩余时间:

如果你迫不及待想要尝试hooks API,可以参见上篇文章中提到的使用编辑页面的的方法。