阅读 46

从头开始创建自己的Vue.js-第4部分(构建反应式)

作者: Marc Backes                                                   翻译:张全玉


这篇文章是从头开始创建Vue.js的系列文章的第四部分,我将在这里教您如何创建诸如Vue.js之类的反应式框架的基础。 要关注此博客文章,建议您首先阅读本系列的其他部分。

路线图🚘

  1. 简介

  2. 虚拟DOM基础

  3. 实现虚拟DOM与渲染

  4. 响应式

  5. 总结

什么是状态反应式

状态反应式是指当我们的应用程序(变量集)的状态改变时我们做某事(反应)的时间。 我们分两个步骤进行操作:

创建一个“反应性依赖项”(当变量更改时,我们会收到通知) 创建“反应状态”(基本上是依赖变量的集合)

1.建立反应性依赖

监视变化的功能 为此,我们首先需要一个在反应性依赖项更改时执行的函数。 在Vue中,这称为watchEffect; 我们也将其称为函数。

在我们的示例中,此函数如下所示:

function watchEffect(fn) {    activeEffect = fn    fn()    activeEffect = null}复制代码

全局变量activeEffect是一个临时变量,我们在其中存储传递给watchEffect的函数。 这是必要的,因此我们可以在函数本身读取引用该函数的依赖项时对其进行访问。

依赖类

我们可以将反应性依赖项看作一个变量,当其值更改时通知其订阅者。

可以使用初始值创建它,因此我们需要一个构造函数 我们需要订阅一个函数来更改依赖项。 我们称这个为depend() 当值更改时,我们需要一个通知订阅功能的依赖项。 我们称其为notify() 当值被读取或写入时,我们需要做一些事情,所以我们需要一个getter和setter 所以我们的骨架看起来像这样:

class Dep {    // Initialize the value of the reactive dependency    constructor(value) {}​    // Subscribe a new function as observer to the dependency    depend() {}​    // Notify subscribers of a value change    notify() {}​    // Getter of the dependency. Executed when a part of the software reads the value of it.    get value() {}​    // Setter of the dependency. Executed when the value changes    set value(newValue) {}}复制代码

该类具有两个字段:value(依赖项的值)和subscribers(订阅函数集)。

我们将逐步实施此步骤。

构造器

在构造函数中,我们初始化两个字段。

constructor(value) {    this._value = value // not `value` because we add getter/setter named value    this.subscribers = new Set()}复制代码

subscribers必须是一个Set,因此我们不会重复订阅相同的功能。

订阅功能 在这里,我们需要为观察者订阅一个新函数。 我们称之为依赖。

depend() {    if (activeEffect) this.subscribers.add(activeEffect)}复制代码

activeEffect是在watchEffect中设置的临时变量,本教程后面将对此进行说明。

通知订阅者依赖关系更改

当值更改时,我们将调用此函数,因此我们可以在相关性值更改时通知所有订阅者。

notify() {    this.subscribers.forEach((subscriber) => subscriber())}复制代码

我们在这里要做的是执行每个subscriber。 切记:这是一个subscriber是一项功能。

Geter

在依赖项的获取器中,我们需要将activeEffect(当依赖项发生更改时将执行的函数)添加到订阅服务器列表。 换句话说,使用我们前面定义的depend()方法。

结果,我们返回当前值。

get value() {    this.depend()    return this._value}复制代码

Setter

在依赖项的setter中,我们需要执行所有监视此依赖项的功能(订阅者)。 换句话说,使用我们前面定义的notify()方法。

set value(newValue) {    this._value = newValue    this.notify()}复制代码

试试看

依赖关系的实现已完成。 现在是时候尝试一下了。 为此,我们需要做三件事:

  • 定义一个依赖

  • 添加要在依赖项更改时执行的功能

  • 更改依赖项的值

// Create a reactive dependency with the value of 1const count = new Dep(1)​// Add a "watcher". This logs every change of the dependency to the console.watchEffect(() => {    console.log('👻 value changed', count.value)})​// Change valuesetTimeout(() => {    count.value++}, 1000)setTimeout(() => {    count.value++}, 2000)setTimeout(() => {    count.value++}, 3000)复制代码

在控制台日志中,您应该能够看到以下内容:

👻 value changed 1👻 value changed 2👻 value changed 3👻 value changed 4复制代码

2.建立反应状态

这只是难题的第一部分,主要是更好地了解接下来将要发生的事情。

回顾一下:我们有一个反应性依赖项和一个watch函数,它们使我们能够在变量(依赖项)发生变化时执行一个函数。 这已经很酷了。 但是我们想更进一步,创造一个状态。

而不是像这样的东西:

const count = Dep(1)const name = Dep('Marc')id.value = 2name.value = 'Johnny'复制代码

我们想做这样的事情:

const state = reactive({    count: 1,    name: 'Marc',})state.count = 2state.name = 'Johnny'复制代码

为此,我们需要对代码进行一些更改:

添加反应函数。 这创建了“状态”对象。 将getter和setter移至状态而不是依赖项(因为这是发生更改的地方) 因此,依存关系(Dep)仅会如此。 仅依赖项部分,不包含任何值。 值存储在状态中。

反应函数

reactive()函数可以看作是状态的初始化。我们将一个带有初始值的对象传递给它,然后将其转换为依赖项。

对于每个对象属性,必须执行以下操作:

  • 定义依赖项(Dep

  • 定义getter

  • 定义setter

function reactive(obj) {    Object.keys(obj).forEach((key) => {        const dep = new Dep()        let value = obj[key]        Object.defineProperty(obj, key, {            get() {                dep.depend()                return value            },            set(newValue) {                if (newValue !== value) {                    value = newValue                    dep.notify()                }            },        })    })    return obj}复制代码

依赖关系的变化

另外,我们需要从依赖项中删除getter和setter,因为我们现在是在反应状态下进行的:

class Dep {    subscribers = new Set()    depend() {        if (activeEffect) this.subscribers.add(activeEffect)    }    notify() {        this.subscribers.forEach((sub) => sub())    }}复制代码

watchEffect函数保持不变。

试用代码

而且我们已经完成了将依赖变量转换为反应状态的工作。 现在我们可以尝试代码:

const state = reactive({    count: 1,    name: 'Marc',})​watchEffect(() => {    console.log('👻 state changed', state.count, state.name)})​setTimeout(() => {    state.count++    state.name = 'Johnny'}, 1000)​setTimeout(() => {    state.count++}, 2000)setTimeout(() => {    state.count++}, 3000)复制代码

在控制台日志中,您应该看到如下所示:

👻 state changed 1 Marc👻 state changed 2 Marc👻 state changed 2 Johnny👻 state changed 3 Johnny👻 state changed 4 Johnny复制代码

总结✨

这就是本系列的这一部分。 我们做了以下工作:

  • 创建一个具有内部值的依赖项,该依赖项在值更改时通知订阅的函数。

  • 创建一个状态,在该状态中,将为每个值的更改调用预订的函数。



关注下面的标签,发现更多相似文章
评论