从头创建您自己的vue.js——第4部分(构建反应性)
什么是状态反应?
状态反应是当应用程序(一组变量)的状态发生变化时,我们做某事(反应)。我们分两步来完成:
- 创建一个“反应依赖项”(当变量发生变化时,我们会得到通知)
- 创建“反应状态”(基本上是依赖变量的集合
函数监视更改
要做到这一点,我们首先需要一个在反应性依赖项发生变化时执行的函数。在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) {}
}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()
}
订阅者需要是一个集合,因此我们不会重复订阅相同的函数。
订阅一个函数
这里,我们需要订阅一个新函数作为依赖项的观察者。我们称之为依赖。
depend() {
if (activeEffect) this.subscribers.add(activeEffect)
}
activeEffect是在watchEffect中设置的一个临时变量,本教程稍后将对此进行解释。
将依赖项更改通知订阅方
当值发生变化时,我们调用这个函数,以便在依赖项值发生变化时通知所有订阅者。
notify() {
this.subscribers.forEach((subscriber) => subscriber())
}
我们这里所做的是执行每个订阅者。记住:这是一个订户是一个函数。
Getter
在依赖项的getter中,我们需要将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 1
const 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 value
setTimeout(() => {
count.value++
}, 1000)
setTimeout(() => {
count.value++
}, 2000)
setTimeout(() => {
count.value++
}, 3000)
在控制台日志中,你应该可以看到这样的东西:
? value changed 1
? value changed 2
? value changed 3
? value changed 4
你可以找到完整的代码依赖?Github。
2. 构建反应状态
这只是谜团的第一部分,也是更好地理解接下来会发生什么的主要必要条件。
总结一下:我们有一个反应性依赖项和一个监视函数,它们让我们能够在变量(依赖项)发生变化时执行一个函数。这已经很酷了。但我们想更进一步,创建一个状态。
而不是像这样:
const count = Dep(1)
const name = Dep('Marc')
id.value = 2
name.value = 'Johnny'
我们想这样做:
const state = reactive({
count: 1,
name: 'Marc',
})
state.count = 2
state.name = 'Johnny'
为了实现这一点,我们需要对我们的代码做一些改变:
添加反应函数。这样就创建了“state”对象。
将getter和setter移到状态,而不是依赖项(因为这是发生变化的地方)
因此,依赖关系(Dep)将只起到这样的作用。只是依赖部分,不包含任何值。值存储在状态中。
reactive 函数
reactive()函数可以看作是状态的初始化。我们将带有初始值的对象传递给它,然后将其转换为依赖项。
对于每个对象属性,必须完成以下操作:
定义依赖关系(Dep)
定义者getter
定义赋值
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 John
- Android6.0源码分析之menu键弹出popupwindow菜单流程分析
- Android中初步自定义view
- Android中View研究自学之路 Android6.0源码分析之View(一)Android6.0源码分析之View(二)
- Android蓝牙配对弹出框过程分析 Android蓝牙配对弹出框过程分析
- Android6.0之修改或者查看系统属性值
- linux下Android7.0多用户编译问题
- 带你解锁蓝牙skill(0)
- WiFiAp探究实录--功能实现与源码分析
- Android7.1.1系统设置默认值大全
- Androidstudio编译c/c++jni方法
- 带你解锁蓝牙skill(三)
- 带你解锁蓝牙skill(二)
- 带你解锁蓝牙skill(一)
- Android四大组件完全解析(二)---Service
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android 中利用 ksoap2 调用 WebService的示例代码
- Android自定义View展示Wifi信号强弱指示方法示例
- android事件分发机制的实现原理
- Android 在子线程中更新UI的几种方法示例
- WebView设置WebViewClient的方法
- Android星级评分条的实现代码
- Android模仿美团顶部的滑动菜单实例代码
- android实现按钮获取焦点延迟加载
- Devtools 老师傅养成[1] - Chrome Devtools介绍
- Android4.4开发之电池低电量告警提示原理与实现方法分析
- 浅谈android性能优化之启动过程(冷启动和热启动)
- Android拖动条的实现代码
- Devtools 老师傅养成[3] - Console 面板
- Android开发中数据库升级且表添加新列的方法
- Android冷启动实现app秒开的实现代码