@feng3d/reactivity 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.cjs +103 -0
- package/dist/index.umd.cjs.map +1 -0
- package/lib/index.d.ts +19 -0
- package/lib/index.d.ts.map +1 -0
- package/package.json +66 -0
- package/src/index.ts +201 -0
- package/tsconfig.json +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 feng
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @feng3d/reactivity
|
|
2
|
+
|
|
3
|
+
feng3d的响应式库。
|
|
4
|
+
|
|
5
|
+
源码:https://gitee.com/feng3d/reactivity
|
|
6
|
+
|
|
7
|
+
文档:https://feng3d.com/reactivity
|
|
8
|
+
|
|
9
|
+
## 网站
|
|
10
|
+
|
|
11
|
+
https://feng3d.com/reactivity
|
|
12
|
+
|
|
13
|
+
## 安装
|
|
14
|
+
```
|
|
15
|
+
npm install @feng3d/reactivity
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 示例
|
|
19
|
+
|
|
20
|
+
### 监听对象属性的变化
|
|
21
|
+
```
|
|
22
|
+
import { computed, ref } from "@feng3d/reactivity";
|
|
23
|
+
|
|
24
|
+
const result = { time: undefined, values: [] };
|
|
25
|
+
|
|
26
|
+
const b = ref(2);
|
|
27
|
+
|
|
28
|
+
function 递归(depth = 10)
|
|
29
|
+
{
|
|
30
|
+
if (depth <= 0) return computed(() =>
|
|
31
|
+
{
|
|
32
|
+
return b.value
|
|
33
|
+
}).value;
|
|
34
|
+
|
|
35
|
+
return computed(() =>
|
|
36
|
+
{
|
|
37
|
+
return 递归(depth - 1) + 递归(depth - 2);
|
|
38
|
+
}).value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const cb = computed(() =>
|
|
42
|
+
{
|
|
43
|
+
return 递归(16);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const count = 10000;
|
|
47
|
+
|
|
48
|
+
b.value++;
|
|
49
|
+
cb.value;
|
|
50
|
+
|
|
51
|
+
const start = performance.now();
|
|
52
|
+
for (let i = 0; i < count; i++)
|
|
53
|
+
{
|
|
54
|
+
ref(1).value++; // 添加此行代码将会导致 @vue/reactivity 版本的性能下降,而 @feng3d/reactivity 版本的性能保持不变
|
|
55
|
+
|
|
56
|
+
cb.value;
|
|
57
|
+
}
|
|
58
|
+
result.time = performance.now() - start;
|
|
59
|
+
|
|
60
|
+
result.values.push(cb.value);
|
|
61
|
+
```
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
function ref(value) {
|
|
2
|
+
return new ValueReactiveNode(value);
|
|
3
|
+
}
|
|
4
|
+
function computed(func) {
|
|
5
|
+
return new FunctionReactiveNode(func);
|
|
6
|
+
}
|
|
7
|
+
let activeReactiveNode;
|
|
8
|
+
class ReactiveNode {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.parents = /* @__PURE__ */ new Set();
|
|
11
|
+
this.dirty = true;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 当前节点值。
|
|
15
|
+
*/
|
|
16
|
+
get value() {
|
|
17
|
+
this.run();
|
|
18
|
+
return this._value;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 执行当前节点。
|
|
22
|
+
*/
|
|
23
|
+
run() {
|
|
24
|
+
const parentReactiveNode = activeReactiveNode;
|
|
25
|
+
activeReactiveNode = this;
|
|
26
|
+
let node = this.invalidChildren;
|
|
27
|
+
while (node) {
|
|
28
|
+
if (node.value !== node.node.value) {
|
|
29
|
+
this.markDirty();
|
|
30
|
+
}
|
|
31
|
+
node = node.next;
|
|
32
|
+
}
|
|
33
|
+
this.invalidChildren = void 0;
|
|
34
|
+
if (this.dirty) {
|
|
35
|
+
this._value = this._runSelf();
|
|
36
|
+
if (parentReactiveNode) {
|
|
37
|
+
this.parents.add(parentReactiveNode);
|
|
38
|
+
}
|
|
39
|
+
this.dirty = false;
|
|
40
|
+
}
|
|
41
|
+
activeReactiveNode = parentReactiveNode;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 标记为脏,触发更新。
|
|
45
|
+
*/
|
|
46
|
+
markDirty() {
|
|
47
|
+
if (this.dirty) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this.dirty = true;
|
|
51
|
+
this.invalidate();
|
|
52
|
+
this.parents.clear();
|
|
53
|
+
}
|
|
54
|
+
invalidate() {
|
|
55
|
+
if (this.parents.size > 0) {
|
|
56
|
+
this.parents.forEach((parent) => {
|
|
57
|
+
const node = { node: this, value: this._value, next: parent.invalidChildren };
|
|
58
|
+
parent.invalidChildren = node;
|
|
59
|
+
parent.invalidate();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 执行当前节点自身。
|
|
65
|
+
*/
|
|
66
|
+
_runSelf() {
|
|
67
|
+
return this._value;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
class FunctionReactiveNode extends ReactiveNode {
|
|
71
|
+
constructor(func) {
|
|
72
|
+
super();
|
|
73
|
+
this.func = func;
|
|
74
|
+
}
|
|
75
|
+
_runSelf() {
|
|
76
|
+
return this.func();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
class ValueReactiveNode extends ReactiveNode {
|
|
80
|
+
get value() {
|
|
81
|
+
this.run();
|
|
82
|
+
return this._value;
|
|
83
|
+
}
|
|
84
|
+
set value(v) {
|
|
85
|
+
if (this._value === v)
|
|
86
|
+
return;
|
|
87
|
+
this.markDirty();
|
|
88
|
+
this._value = v;
|
|
89
|
+
}
|
|
90
|
+
constructor(value) {
|
|
91
|
+
super();
|
|
92
|
+
this._value = value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export {
|
|
96
|
+
computed,
|
|
97
|
+
ref
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\r\n * 创建响应式值的引用。\r\n *\r\n * @param value\r\n * @returns\r\n */\r\nexport function ref<T>(value: T): { value: T }\r\n{\r\n return new ValueReactiveNode<T>(value);\r\n}\r\n\r\n/**\r\n * 创建计算属性的函数\r\n * 计算属性的值会根据其依赖的响应式数据自动更新\r\n * @param func 计算属性的 getter 函数\r\n * @returns 包含 value 属性的对象,用于获取计算属性的值\r\n */\r\nexport function computed<T>(func: () => T): { value: T }\r\n{\r\n return new FunctionReactiveNode(func);\r\n}\r\n\r\n/**\r\n * 当前正在执行的反应式节点。\r\n */\r\nlet activeReactiveNode: ReactiveNode;\r\n\r\ntype ReactiveNodeLink = { node: ReactiveNode, value: any, next: ReactiveNodeLink };\r\n\r\n/**\r\n * 反应式节点。\r\n */\r\nclass ReactiveNode<T = any>\r\n{\r\n /**\r\n * 父反应节点。\r\n *\r\n * 记录了哪些节点调用了当前节点。\r\n */\r\n parents = new Set<ReactiveNode>();\r\n\r\n /**\r\n * 是否脏,是否需要重新计算。\r\n */\r\n dirty = true;\r\n\r\n /**\r\n * 失效的子节点,需要在执行时检查子节点值是否发生变化。\r\n */\r\n invalidChildren: ReactiveNodeLink;\r\n\r\n /**\r\n * 当前节点值。\r\n */\r\n get value()\r\n {\r\n this.run();\r\n\r\n return this._value;\r\n }\r\n protected _value: T;\r\n\r\n constructor()\r\n {\r\n }\r\n\r\n /**\r\n * 执行当前节点。\r\n */\r\n run()\r\n {\r\n const parentReactiveNode = activeReactiveNode;\r\n activeReactiveNode = this;\r\n\r\n let node = this.invalidChildren;\r\n while (node)\r\n {\r\n if (node.value !== node.node.value)\r\n {\r\n this.markDirty();\r\n }\r\n\r\n node = node.next;\r\n }\r\n this.invalidChildren = undefined as any;\r\n\r\n //\r\n // 保存当前节点作为父节点。\r\n // 设置当前节点为父节点。\r\n if (this.dirty)\r\n {\r\n this._value = this._runSelf();\r\n\r\n // 连接父节点和子节点。\r\n if (parentReactiveNode)\r\n {\r\n this.parents.add(parentReactiveNode);\r\n }\r\n //\r\n this.dirty = false;\r\n }\r\n\r\n // 执行完毕后恢复父节点。\r\n activeReactiveNode = parentReactiveNode;\r\n }\r\n\r\n /**\r\n * 标记为脏,触发更新。\r\n */\r\n markDirty()\r\n {\r\n if (this.dirty)\r\n {\r\n return;\r\n }\r\n this.dirty = true;\r\n\r\n this.invalidate();\r\n\r\n //\r\n this.parents.clear();\r\n }\r\n\r\n invalidate()\r\n {\r\n //\r\n if (this.parents.size > 0)\r\n {\r\n this.parents.forEach((parent) =>\r\n {\r\n const node: ReactiveNodeLink = { node: this, value: this._value, next: parent.invalidChildren };\r\n parent.invalidChildren = node;\r\n parent.invalidate();\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 执行当前节点自身。\r\n */\r\n protected _runSelf(): T\r\n {\r\n return this._value;\r\n }\r\n}\r\n\r\n/**\r\n * 反应式函数节点。\r\n *\r\n * 当使用 computed 函数时,会创建一个 ReactiveFunctionNode 对象。\r\n *\r\n * 当获取value值时,会执行func函数,返回结果。\r\n */\r\nclass FunctionReactiveNode<T> extends ReactiveNode\r\n{\r\n /**\r\n * 监听的函数。\r\n */\r\n func: () => T;\r\n\r\n constructor(func: () => T)\r\n {\r\n super();\r\n this.func = func;\r\n }\r\n\r\n protected _runSelf()\r\n {\r\n return this.func();\r\n }\r\n}\r\n\r\n/**\r\n * 属性值反应式节点。\r\n *\r\n * 当使用 reactive 函数创建一个反应式对象后,访问该对象的属性时,会创建一个 ReactiveGetValueNode 对象。\r\n *\r\n * 当设置反应式对象对应属性值时,会触发该节点。\r\n */\r\nclass ValueReactiveNode<V> extends ReactiveNode<V>\r\n{\r\n get value()\r\n {\r\n this.run();\r\n\r\n return this._value;\r\n }\r\n set value(v: V)\r\n {\r\n if (this._value === v) return;\r\n this.markDirty();\r\n this._value = v;\r\n }\r\n\r\n constructor(value: V)\r\n {\r\n super();\r\n this._value = value;\r\n }\r\n}\r\n\r\n"],"names":[],"mappings":"AAMO,SAAS,IAAO,OACvB;AACW,SAAA,IAAI,kBAAqB,KAAK;AACzC;AAQO,SAAS,SAAY,MAC5B;AACW,SAAA,IAAI,qBAAqB,IAAI;AACxC;AAKA,IAAI;AAOJ,MAAM,aACN;AAAA,EA6BI,cACA;AAxBA,SAAA,8BAAc;AAKN,SAAA,QAAA;AAAA,EAoBR;AAAA;AAAA;AAAA;AAAA,EAVA,IAAI,QACJ;AACI,SAAK,IAAI;AAET,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAUA,MACA;AACI,UAAM,qBAAqB;AACN,yBAAA;AAErB,QAAI,OAAO,KAAK;AAChB,WAAO,MACP;AACI,UAAI,KAAK,UAAU,KAAK,KAAK,OAC7B;AACI,aAAK,UAAU;AAAA,MACnB;AAEA,aAAO,KAAK;AAAA,IAChB;AACA,SAAK,kBAAkB;AAKvB,QAAI,KAAK,OACT;AACS,WAAA,SAAS,KAAK;AAGnB,UAAI,oBACJ;AACS,aAAA,QAAQ,IAAI,kBAAkB;AAAA,MACvC;AAEA,WAAK,QAAQ;AAAA,IACjB;AAGqB,yBAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,YACA;AACI,QAAI,KAAK,OACT;AACI;AAAA,IACJ;AACA,SAAK,QAAQ;AAEb,SAAK,WAAW;AAGhB,SAAK,QAAQ;EACjB;AAAA,EAEA,aACA;AAEQ,QAAA,KAAK,QAAQ,OAAO,GACxB;AACS,WAAA,QAAQ,QAAQ,CAAC,WACtB;AACU,cAAA,OAAyB,EAAE,MAAM,MAAM,OAAO,KAAK,QAAQ,MAAM,OAAO;AAC9E,eAAO,kBAAkB;AACzB,eAAO,WAAW;AAAA,MAAA,CACrB;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKU,WACV;AACI,WAAO,KAAK;AAAA,EAChB;AACJ;AASA,MAAM,6BAAgC,aACtC;AAAA,EAMI,YAAY,MACZ;AACU;AACN,SAAK,OAAO;AAAA,EAChB;AAAA,EAEU,WACV;AACI,WAAO,KAAK;EAChB;AACJ;AASA,MAAM,0BAA6B,aACnC;AAAA,EACI,IAAI,QACJ;AACI,SAAK,IAAI;AAET,WAAO,KAAK;AAAA,EAChB;AAAA,EACA,IAAI,MAAM,GACV;AACI,QAAI,KAAK,WAAW;AAAG;AACvB,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAClB;AAAA,EAEA,YAAY,OACZ;AACU;AACN,SAAK,SAAS;AAAA,EAClB;AACJ;"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
(function(global, factory) {
|
|
2
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.feng3d = {}));
|
|
3
|
+
})(this, function(exports2) {
|
|
4
|
+
"use strict";
|
|
5
|
+
function ref(value) {
|
|
6
|
+
return new ValueReactiveNode(value);
|
|
7
|
+
}
|
|
8
|
+
function computed(func) {
|
|
9
|
+
return new FunctionReactiveNode(func);
|
|
10
|
+
}
|
|
11
|
+
let activeReactiveNode;
|
|
12
|
+
class ReactiveNode {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.parents = /* @__PURE__ */ new Set();
|
|
15
|
+
this.dirty = true;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 当前节点值。
|
|
19
|
+
*/
|
|
20
|
+
get value() {
|
|
21
|
+
this.run();
|
|
22
|
+
return this._value;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 执行当前节点。
|
|
26
|
+
*/
|
|
27
|
+
run() {
|
|
28
|
+
const parentReactiveNode = activeReactiveNode;
|
|
29
|
+
activeReactiveNode = this;
|
|
30
|
+
let node = this.invalidChildren;
|
|
31
|
+
while (node) {
|
|
32
|
+
if (node.value !== node.node.value) {
|
|
33
|
+
this.markDirty();
|
|
34
|
+
}
|
|
35
|
+
node = node.next;
|
|
36
|
+
}
|
|
37
|
+
this.invalidChildren = void 0;
|
|
38
|
+
if (this.dirty) {
|
|
39
|
+
this._value = this._runSelf();
|
|
40
|
+
if (parentReactiveNode) {
|
|
41
|
+
this.parents.add(parentReactiveNode);
|
|
42
|
+
}
|
|
43
|
+
this.dirty = false;
|
|
44
|
+
}
|
|
45
|
+
activeReactiveNode = parentReactiveNode;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 标记为脏,触发更新。
|
|
49
|
+
*/
|
|
50
|
+
markDirty() {
|
|
51
|
+
if (this.dirty) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.dirty = true;
|
|
55
|
+
this.invalidate();
|
|
56
|
+
this.parents.clear();
|
|
57
|
+
}
|
|
58
|
+
invalidate() {
|
|
59
|
+
if (this.parents.size > 0) {
|
|
60
|
+
this.parents.forEach((parent) => {
|
|
61
|
+
const node = { node: this, value: this._value, next: parent.invalidChildren };
|
|
62
|
+
parent.invalidChildren = node;
|
|
63
|
+
parent.invalidate();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 执行当前节点自身。
|
|
69
|
+
*/
|
|
70
|
+
_runSelf() {
|
|
71
|
+
return this._value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
class FunctionReactiveNode extends ReactiveNode {
|
|
75
|
+
constructor(func) {
|
|
76
|
+
super();
|
|
77
|
+
this.func = func;
|
|
78
|
+
}
|
|
79
|
+
_runSelf() {
|
|
80
|
+
return this.func();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class ValueReactiveNode extends ReactiveNode {
|
|
84
|
+
get value() {
|
|
85
|
+
this.run();
|
|
86
|
+
return this._value;
|
|
87
|
+
}
|
|
88
|
+
set value(v) {
|
|
89
|
+
if (this._value === v)
|
|
90
|
+
return;
|
|
91
|
+
this.markDirty();
|
|
92
|
+
this._value = v;
|
|
93
|
+
}
|
|
94
|
+
constructor(value) {
|
|
95
|
+
super();
|
|
96
|
+
this._value = value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports2.computed = computed;
|
|
100
|
+
exports2.ref = ref;
|
|
101
|
+
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=index.umd.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.cjs","sources":["../src/index.ts"],"sourcesContent":["/**\r\n * 创建响应式值的引用。\r\n *\r\n * @param value\r\n * @returns\r\n */\r\nexport function ref<T>(value: T): { value: T }\r\n{\r\n return new ValueReactiveNode<T>(value);\r\n}\r\n\r\n/**\r\n * 创建计算属性的函数\r\n * 计算属性的值会根据其依赖的响应式数据自动更新\r\n * @param func 计算属性的 getter 函数\r\n * @returns 包含 value 属性的对象,用于获取计算属性的值\r\n */\r\nexport function computed<T>(func: () => T): { value: T }\r\n{\r\n return new FunctionReactiveNode(func);\r\n}\r\n\r\n/**\r\n * 当前正在执行的反应式节点。\r\n */\r\nlet activeReactiveNode: ReactiveNode;\r\n\r\ntype ReactiveNodeLink = { node: ReactiveNode, value: any, next: ReactiveNodeLink };\r\n\r\n/**\r\n * 反应式节点。\r\n */\r\nclass ReactiveNode<T = any>\r\n{\r\n /**\r\n * 父反应节点。\r\n *\r\n * 记录了哪些节点调用了当前节点。\r\n */\r\n parents = new Set<ReactiveNode>();\r\n\r\n /**\r\n * 是否脏,是否需要重新计算。\r\n */\r\n dirty = true;\r\n\r\n /**\r\n * 失效的子节点,需要在执行时检查子节点值是否发生变化。\r\n */\r\n invalidChildren: ReactiveNodeLink;\r\n\r\n /**\r\n * 当前节点值。\r\n */\r\n get value()\r\n {\r\n this.run();\r\n\r\n return this._value;\r\n }\r\n protected _value: T;\r\n\r\n constructor()\r\n {\r\n }\r\n\r\n /**\r\n * 执行当前节点。\r\n */\r\n run()\r\n {\r\n const parentReactiveNode = activeReactiveNode;\r\n activeReactiveNode = this;\r\n\r\n let node = this.invalidChildren;\r\n while (node)\r\n {\r\n if (node.value !== node.node.value)\r\n {\r\n this.markDirty();\r\n }\r\n\r\n node = node.next;\r\n }\r\n this.invalidChildren = undefined as any;\r\n\r\n //\r\n // 保存当前节点作为父节点。\r\n // 设置当前节点为父节点。\r\n if (this.dirty)\r\n {\r\n this._value = this._runSelf();\r\n\r\n // 连接父节点和子节点。\r\n if (parentReactiveNode)\r\n {\r\n this.parents.add(parentReactiveNode);\r\n }\r\n //\r\n this.dirty = false;\r\n }\r\n\r\n // 执行完毕后恢复父节点。\r\n activeReactiveNode = parentReactiveNode;\r\n }\r\n\r\n /**\r\n * 标记为脏,触发更新。\r\n */\r\n markDirty()\r\n {\r\n if (this.dirty)\r\n {\r\n return;\r\n }\r\n this.dirty = true;\r\n\r\n this.invalidate();\r\n\r\n //\r\n this.parents.clear();\r\n }\r\n\r\n invalidate()\r\n {\r\n //\r\n if (this.parents.size > 0)\r\n {\r\n this.parents.forEach((parent) =>\r\n {\r\n const node: ReactiveNodeLink = { node: this, value: this._value, next: parent.invalidChildren };\r\n parent.invalidChildren = node;\r\n parent.invalidate();\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 执行当前节点自身。\r\n */\r\n protected _runSelf(): T\r\n {\r\n return this._value;\r\n }\r\n}\r\n\r\n/**\r\n * 反应式函数节点。\r\n *\r\n * 当使用 computed 函数时,会创建一个 ReactiveFunctionNode 对象。\r\n *\r\n * 当获取value值时,会执行func函数,返回结果。\r\n */\r\nclass FunctionReactiveNode<T> extends ReactiveNode\r\n{\r\n /**\r\n * 监听的函数。\r\n */\r\n func: () => T;\r\n\r\n constructor(func: () => T)\r\n {\r\n super();\r\n this.func = func;\r\n }\r\n\r\n protected _runSelf()\r\n {\r\n return this.func();\r\n }\r\n}\r\n\r\n/**\r\n * 属性值反应式节点。\r\n *\r\n * 当使用 reactive 函数创建一个反应式对象后,访问该对象的属性时,会创建一个 ReactiveGetValueNode 对象。\r\n *\r\n * 当设置反应式对象对应属性值时,会触发该节点。\r\n */\r\nclass ValueReactiveNode<V> extends ReactiveNode<V>\r\n{\r\n get value()\r\n {\r\n this.run();\r\n\r\n return this._value;\r\n }\r\n set value(v: V)\r\n {\r\n if (this._value === v) return;\r\n this.markDirty();\r\n this._value = v;\r\n }\r\n\r\n constructor(value: V)\r\n {\r\n super();\r\n this._value = value;\r\n }\r\n}\r\n\r\n"],"names":[],"mappings":";;;;AAMO,WAAS,IAAO,OACvB;AACW,WAAA,IAAI,kBAAqB,KAAK;AAAA,EACzC;AAQO,WAAS,SAAY,MAC5B;AACW,WAAA,IAAI,qBAAqB,IAAI;AAAA,EACxC;AAKA,MAAI;AAAA,EAOJ,MAAM,aACN;AAAA,IA6BI,cACA;AAxBA,WAAA,8BAAc;AAKN,WAAA,QAAA;AAAA,IAoBR;AAAA;AAAA;AAAA;AAAA,IAVA,IAAI,QACJ;AACI,WAAK,IAAI;AAET,aAAO,KAAK;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA,IAUA,MACA;AACI,YAAM,qBAAqB;AACN,2BAAA;AAErB,UAAI,OAAO,KAAK;AAChB,aAAO,MACP;AACI,YAAI,KAAK,UAAU,KAAK,KAAK,OAC7B;AACI,eAAK,UAAU;AAAA,QACnB;AAEA,eAAO,KAAK;AAAA,MAChB;AACA,WAAK,kBAAkB;AAKvB,UAAI,KAAK,OACT;AACS,aAAA,SAAS,KAAK;AAGnB,YAAI,oBACJ;AACS,eAAA,QAAQ,IAAI,kBAAkB;AAAA,QACvC;AAEA,aAAK,QAAQ;AAAA,MACjB;AAGqB,2BAAA;AAAA,IACzB;AAAA;AAAA;AAAA;AAAA,IAKA,YACA;AACI,UAAI,KAAK,OACT;AACI;AAAA,MACJ;AACA,WAAK,QAAQ;AAEb,WAAK,WAAW;AAGhB,WAAK,QAAQ;IACjB;AAAA,IAEA,aACA;AAEQ,UAAA,KAAK,QAAQ,OAAO,GACxB;AACS,aAAA,QAAQ,QAAQ,CAAC,WACtB;AACU,gBAAA,OAAyB,EAAE,MAAM,MAAM,OAAO,KAAK,QAAQ,MAAM,OAAO;AAC9E,iBAAO,kBAAkB;AACzB,iBAAO,WAAW;AAAA,QAAA,CACrB;AAAA,MACL;AAAA,IACJ;AAAA;AAAA;AAAA;AAAA,IAKU,WACV;AACI,aAAO,KAAK;AAAA,IAChB;AAAA,EACJ;AAAA,EASA,MAAM,6BAAgC,aACtC;AAAA,IAMI,YAAY,MACZ;AACU;AACN,WAAK,OAAO;AAAA,IAChB;AAAA,IAEU,WACV;AACI,aAAO,KAAK;IAChB;AAAA,EACJ;AAAA,EASA,MAAM,0BAA6B,aACnC;AAAA,IACI,IAAI,QACJ;AACI,WAAK,IAAI;AAET,aAAO,KAAK;AAAA,IAChB;AAAA,IACA,IAAI,MAAM,GACV;AACI,UAAI,KAAK,WAAW;AAAG;AACvB,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IAClB;AAAA,IAEA,YAAY,OACZ;AACU;AACN,WAAK,SAAS;AAAA,IAClB;AAAA,EACJ;;;;;"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 创建响应式值的引用。
|
|
3
|
+
*
|
|
4
|
+
* @param value
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
7
|
+
export declare function ref<T>(value: T): {
|
|
8
|
+
value: T;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* 创建计算属性的函数
|
|
12
|
+
* 计算属性的值会根据其依赖的响应式数据自动更新
|
|
13
|
+
* @param func 计算属性的 getter 函数
|
|
14
|
+
* @returns 包含 value 属性的对象,用于获取计算属性的值
|
|
15
|
+
*/
|
|
16
|
+
export declare function computed<T>(func: () => T): {
|
|
17
|
+
value: T;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,CAG7C;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,CAGvD"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@feng3d/reactivity",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "反应式",
|
|
6
|
+
"homepage": "https://feng3d.com/reactivity",
|
|
7
|
+
"main": "./dist/index.umd.cjs",
|
|
8
|
+
"types": "./lib/index.d.ts",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"author": "feng",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.umd.cjs",
|
|
16
|
+
"types": "./lib/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"clean": "rimraf \"{lib,dist,public}\"",
|
|
21
|
+
"build": "vite build",
|
|
22
|
+
"test": "vitest",
|
|
23
|
+
"test:ui": "vitest --ui",
|
|
24
|
+
"types": "tsc",
|
|
25
|
+
"watch": "tsc -w",
|
|
26
|
+
"lint": "eslint --ext .js --ext .ts src test --ignore-path .gitignore --max-warnings 0",
|
|
27
|
+
"lintfix": "npm run lint -- --fix",
|
|
28
|
+
"docs": "typedoc",
|
|
29
|
+
"upload_oss": "npm run docs && feng3d-cli oss_upload_dir",
|
|
30
|
+
"release": "npm run clean && npm run lint && npm run build && npm run docs && npm run types && npm publish",
|
|
31
|
+
"prepublishOnly": "node scripts/prepublish.js",
|
|
32
|
+
"postpublish": "node scripts/postpublish.js"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://gitee.com/feng3d/reactivity.git"
|
|
37
|
+
},
|
|
38
|
+
"workspaces": [
|
|
39
|
+
".",
|
|
40
|
+
"./examples"
|
|
41
|
+
],
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist/",
|
|
47
|
+
"lib",
|
|
48
|
+
"src",
|
|
49
|
+
"readme",
|
|
50
|
+
"tsconfig.json"
|
|
51
|
+
],
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@feng3d/cli": "^0.0.19",
|
|
54
|
+
"@typescript-eslint/eslint-plugin": "5.17.0",
|
|
55
|
+
"@typescript-eslint/parser": "5.17.0",
|
|
56
|
+
"@vitest/ui": "^0.32.2",
|
|
57
|
+
"cross-env": "7.0.3",
|
|
58
|
+
"eslint": "8.12.0",
|
|
59
|
+
"rimraf": "3.0.2",
|
|
60
|
+
"tslib": "^2.4.0",
|
|
61
|
+
"typedoc": "^0.24.8",
|
|
62
|
+
"typescript": "5.1.3",
|
|
63
|
+
"vite": "^4.3.9",
|
|
64
|
+
"vitest": "^0.32.2"
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 创建响应式值的引用。
|
|
3
|
+
*
|
|
4
|
+
* @param value
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
7
|
+
export function ref<T>(value: T): { value: T }
|
|
8
|
+
{
|
|
9
|
+
return new ValueReactiveNode<T>(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 创建计算属性的函数
|
|
14
|
+
* 计算属性的值会根据其依赖的响应式数据自动更新
|
|
15
|
+
* @param func 计算属性的 getter 函数
|
|
16
|
+
* @returns 包含 value 属性的对象,用于获取计算属性的值
|
|
17
|
+
*/
|
|
18
|
+
export function computed<T>(func: () => T): { value: T }
|
|
19
|
+
{
|
|
20
|
+
return new FunctionReactiveNode(func);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 当前正在执行的反应式节点。
|
|
25
|
+
*/
|
|
26
|
+
let activeReactiveNode: ReactiveNode;
|
|
27
|
+
|
|
28
|
+
type ReactiveNodeLink = { node: ReactiveNode, value: any, next: ReactiveNodeLink };
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 反应式节点。
|
|
32
|
+
*/
|
|
33
|
+
class ReactiveNode<T = any>
|
|
34
|
+
{
|
|
35
|
+
/**
|
|
36
|
+
* 父反应节点。
|
|
37
|
+
*
|
|
38
|
+
* 记录了哪些节点调用了当前节点。
|
|
39
|
+
*/
|
|
40
|
+
parents = new Set<ReactiveNode>();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 是否脏,是否需要重新计算。
|
|
44
|
+
*/
|
|
45
|
+
dirty = true;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 失效的子节点,需要在执行时检查子节点值是否发生变化。
|
|
49
|
+
*/
|
|
50
|
+
invalidChildren: ReactiveNodeLink;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 当前节点值。
|
|
54
|
+
*/
|
|
55
|
+
get value()
|
|
56
|
+
{
|
|
57
|
+
this.run();
|
|
58
|
+
|
|
59
|
+
return this._value;
|
|
60
|
+
}
|
|
61
|
+
protected _value: T;
|
|
62
|
+
|
|
63
|
+
constructor()
|
|
64
|
+
{
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 执行当前节点。
|
|
69
|
+
*/
|
|
70
|
+
run()
|
|
71
|
+
{
|
|
72
|
+
const parentReactiveNode = activeReactiveNode;
|
|
73
|
+
activeReactiveNode = this;
|
|
74
|
+
|
|
75
|
+
let node = this.invalidChildren;
|
|
76
|
+
while (node)
|
|
77
|
+
{
|
|
78
|
+
if (node.value !== node.node.value)
|
|
79
|
+
{
|
|
80
|
+
this.markDirty();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
node = node.next;
|
|
84
|
+
}
|
|
85
|
+
this.invalidChildren = undefined as any;
|
|
86
|
+
|
|
87
|
+
//
|
|
88
|
+
// 保存当前节点作为父节点。
|
|
89
|
+
// 设置当前节点为父节点。
|
|
90
|
+
if (this.dirty)
|
|
91
|
+
{
|
|
92
|
+
this._value = this._runSelf();
|
|
93
|
+
|
|
94
|
+
// 连接父节点和子节点。
|
|
95
|
+
if (parentReactiveNode)
|
|
96
|
+
{
|
|
97
|
+
this.parents.add(parentReactiveNode);
|
|
98
|
+
}
|
|
99
|
+
//
|
|
100
|
+
this.dirty = false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 执行完毕后恢复父节点。
|
|
104
|
+
activeReactiveNode = parentReactiveNode;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 标记为脏,触发更新。
|
|
109
|
+
*/
|
|
110
|
+
markDirty()
|
|
111
|
+
{
|
|
112
|
+
if (this.dirty)
|
|
113
|
+
{
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.dirty = true;
|
|
117
|
+
|
|
118
|
+
this.invalidate();
|
|
119
|
+
|
|
120
|
+
//
|
|
121
|
+
this.parents.clear();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
invalidate()
|
|
125
|
+
{
|
|
126
|
+
//
|
|
127
|
+
if (this.parents.size > 0)
|
|
128
|
+
{
|
|
129
|
+
this.parents.forEach((parent) =>
|
|
130
|
+
{
|
|
131
|
+
const node: ReactiveNodeLink = { node: this, value: this._value, next: parent.invalidChildren };
|
|
132
|
+
parent.invalidChildren = node;
|
|
133
|
+
parent.invalidate();
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 执行当前节点自身。
|
|
140
|
+
*/
|
|
141
|
+
protected _runSelf(): T
|
|
142
|
+
{
|
|
143
|
+
return this._value;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 反应式函数节点。
|
|
149
|
+
*
|
|
150
|
+
* 当使用 computed 函数时,会创建一个 ReactiveFunctionNode 对象。
|
|
151
|
+
*
|
|
152
|
+
* 当获取value值时,会执行func函数,返回结果。
|
|
153
|
+
*/
|
|
154
|
+
class FunctionReactiveNode<T> extends ReactiveNode
|
|
155
|
+
{
|
|
156
|
+
/**
|
|
157
|
+
* 监听的函数。
|
|
158
|
+
*/
|
|
159
|
+
func: () => T;
|
|
160
|
+
|
|
161
|
+
constructor(func: () => T)
|
|
162
|
+
{
|
|
163
|
+
super();
|
|
164
|
+
this.func = func;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
protected _runSelf()
|
|
168
|
+
{
|
|
169
|
+
return this.func();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 属性值反应式节点。
|
|
175
|
+
*
|
|
176
|
+
* 当使用 reactive 函数创建一个反应式对象后,访问该对象的属性时,会创建一个 ReactiveGetValueNode 对象。
|
|
177
|
+
*
|
|
178
|
+
* 当设置反应式对象对应属性值时,会触发该节点。
|
|
179
|
+
*/
|
|
180
|
+
class ValueReactiveNode<V> extends ReactiveNode<V>
|
|
181
|
+
{
|
|
182
|
+
get value()
|
|
183
|
+
{
|
|
184
|
+
this.run();
|
|
185
|
+
|
|
186
|
+
return this._value;
|
|
187
|
+
}
|
|
188
|
+
set value(v: V)
|
|
189
|
+
{
|
|
190
|
+
if (this._value === v) return;
|
|
191
|
+
this.markDirty();
|
|
192
|
+
this._value = v;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
constructor(value: V)
|
|
196
|
+
{
|
|
197
|
+
super();
|
|
198
|
+
this._value = value;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES5",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"noImplicitAny": false,
|
|
6
|
+
"sourceMap": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"experimentalDecorators": true,
|
|
10
|
+
"emitDeclarationOnly": true,
|
|
11
|
+
"lib": [
|
|
12
|
+
"ES2015",
|
|
13
|
+
"DOM",
|
|
14
|
+
],
|
|
15
|
+
"outDir": "lib"
|
|
16
|
+
},
|
|
17
|
+
"include": [
|
|
18
|
+
"src/**/*.ts"
|
|
19
|
+
]
|
|
20
|
+
}
|