Skip to content

Commit

Permalink
fix: computed 依赖优化
Browse files Browse the repository at this point in the history
  • Loading branch information
missannil committed Feb 13, 2024
1 parent d9c07f2 commit cff7833
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 45 deletions.
10 changes: 5 additions & 5 deletions jest/computed/arrMap/arrMap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ const parent = document.createElement("parent-wrapper"); // 创建挂载(父)节

comp.attach(parent); // attach 到父亲节点上,此时会触发自定义组件的 attached 钩子

test("计算属性函数中调用数组的方法时,方法不属于依赖", async () => {
test("计算属性函数中调用数组的方法时,方法不属于依赖", () => {
expect(comp.data.objFunAdd).toBe(2);

expect(comp.data.__computedCache__.objFunAdd.dependences[0].paths).toStrictEqual(["objFun"]);
expect(comp.data.__computedCache__.objFunAdd.dependences[0].paths).toStrictEqual(["objFun", "add"]);

expect(comp.data.__computedCache__.objFunAdd.dependences[1]).toBeUndefined();
expect(comp.data.__computedCache__.objFunAdd.dependences[1].paths).toStrictEqual(["objFun", "a"]);
});

test("计算属性函数中调用数组的方法时,方法不属于依赖-子组件", async () => {
test("计算属性函数中调用数组的方法时,方法不属于依赖-子组件", () => {
expect(comp.data._compA_listMap).toStrictEqual([3, 4, 5]);

expect(comp.data.__computedCache__._compA_listMap.dependences).toStrictEqual([{
Expand All @@ -26,7 +26,7 @@ test("计算属性函数中调用数组的方法时,方法不属于依赖-子组
}]);
});

test("计算属性函数中调用数组索引时,索引属于依赖", async () => {
test("计算属性函数中调用数组索引时,索引属于依赖", () => {
expect(comp.data.listMap).toBe(2);

expect(comp.data.__computedCache__.listMap.dependences).toStrictEqual([{ "paths": ["storeList", "0"], "val": 1 }]);
Expand Down
3 changes: 0 additions & 3 deletions jest/computed/arrMap/arrMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ const rootComponent = RootComponent()({
},
lifetimes: {
attached() {
// @ts-ignore
console.log(this.data.__computedCache__);

store.changedList([1, 2, 3]);
},
},
Expand Down
46 changes: 43 additions & 3 deletions jest/computed/uniqueDependences/uniqueDependences.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { load, render, sleep } from "miniprogram-simulate";
import path from "path";
import type { ComputedDependence } from "../../../src/api/DefineComponent/assignOptions/computedWatchHandle/computedUpdater";
import { uniqueDependences } from "../../../src/api/DefineComponent/assignOptions/computedWatchHandle/uniqueDependences";
import { user } from "../../common";

const id = load(path.resolve(__dirname, "uniqueDependences")); // 此处必须传入绝对路径
Expand All @@ -9,16 +11,54 @@ const parent = document.createElement("parent-wrapper"); // 创建挂载(父)节

comp.attach(parent); // attach 到父亲节点上,此时会触发自定义组件的 attached 钩子

test("计算属性函数中调用数组的方法时,方法不属于依赖", async () => {
test("相同依赖去重", () => {
const testDependences: ComputedDependence[] = [
{ paths: ["a", "b"], val: 1 },
{ paths: ["c", "d"], val: 1 },
{ paths: ["a", "b"], val: 1 },
{ paths: ["e", "f"], val: 1 },
{ paths: ["c", "d"], val: 1 },
{ paths: ["e", "f"], val: 1 },
];
const res = uniqueDependences(testDependences, [], "");

expect(res).toStrictEqual([{ paths: ["a", "b"], val: 1 }, { paths: ["c", "d"], val: 1 }, {
paths: ["e", "f"],
val: 1,
}]);
});

test("连续依赖去除", () => {
const testDependences: ComputedDependence[] = [
{ paths: ["a", "b"], val: 1 },
];
const res = uniqueDependences(testDependences, ["a", "b"], "c");

expect(res).toStrictEqual([]);
});

test("间隔依赖去除", () => {
// if(-2paths concat -1的val === 当前依赖) 去除-2依赖。 示例 this.data.obj[this.data.id].list 去除 obj

const testDependences: ComputedDependence[] = [
{ paths: ["a"], val: 1 },
{ paths: ["id"], val: "2" },
];
const res = uniqueDependences(testDependences, ["a"], "2");

expect(res).toStrictEqual([{ paths: ["id"], val: "2" }]);
});

test("依赖去重", async () => {
expect(comp.data.__computedCache__.list.dependences).toStrictEqual([
{ paths: ["obj"], val: { a: { list: [1, 2, 3] } } },
{ paths: ["id"], val: "0" },
{ paths: ["obj", "0"], val: undefined },
]);

await sleep(200);

expect(comp.data.__computedCache__.list.dependences).toStrictEqual([
{ paths: ["obj"], val: { a: { list: [1, 2, 3] } } },
{ paths: ["id"], val: "a" },
{ paths: ["obj", "a", "list"], val: [1, 2, 3] },
]);
});
3 changes: 2 additions & 1 deletion jest/computed/uniqueDependences/uniqueDependences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ const rootComponent = RootComponent()({
},
computed: {
list(): any[] {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { obj, id } = this.data;

return obj[id]?.list || [];
return this.data.obj[this.data.id]?.list || [];
},
},
lifetimes: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { deepProxy, getProxyOriginalValue } from "./data-tracer";
import { getPathsValue } from "./getPathsValue";

import { isEqual } from "./isEqual";
import { uniqueDependences } from "./uniqueDependences";
export type ComputedDependence = { paths: string[]; val: unknown };

export function computedUpdater(this: Instance, isUpdated = false): boolean {
Expand Down Expand Up @@ -35,7 +34,7 @@ export function computedUpdater(this: Instance, isUpdated = false): boolean {
isUpdated = true;

// 更新依赖,去重
this.data.__computedCache__[key].dependences = uniqueDependences(newDependences);
this.data.__computedCache__[key].dependences = newDependences;

// 有一个计算属性更新就重新更新所有计算互相,避免后置依赖导致前置依赖错误
return computedUpdater.call(this, isUpdated);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* eslint-disable complexity */
import { deepClone } from "../../../../utils/deepClone";
import type { ComputedDependence } from "./computedUpdater";
import { uniqueDependences } from "./uniqueDependences";

export function deepProxy(
data: object,
Expand All @@ -13,15 +15,14 @@ export function deepProxy(
}
const val = target[prop];

// val不为undefined(依赖还为初始化的其他计算属性)且非自身属性或函数不再返回代理。 (如 this.data.arr.slice(),slice不属于自身属性,小程序允许data子字段为函数的情况,也不代理)
if ((val !== undefined) && (!Object.prototype.hasOwnProperty.call(target, prop) || typeof val === "function")) {
// 如 this.data.arr.slice(),`slice`不属于自身属性不代理,不加入依赖。但不包含小程序data子字段为函数的情况)
if (typeof val === "function" && (!Object.prototype.hasOwnProperty.call(target, prop))) {
// val有不是函数的情况么?
return (val as Function).bind(target);
}
// 依赖长度不为0时径依赖去重(只留最后一个路径),比如 return this.data.obj.user.name 去重得到的是最后1个路径 ['obj','user','name'] , 不去重则3个依赖路径了 ['obj'], ['obj','user'],['obj','user','name'],在这里去重效率高些,外部去重时还要再次遍历,加"dependences[dependences.length - 1].paths.toString() === basePath.toString()"这判断是有可能遇到` this.data.obj[this.data.id].list`的情况,这种情况下,第一个依赖是obj,第二个依赖是id,因为依赖不是一个路径连续向下的,所以不能去重。
if (basePath.length !== 0 && dependences[dependences.length - 1].paths.toString() === basePath.toString()) {
dependences.pop();
}

dependences = uniqueDependences(dependences, basePath, prop);

const curPath = basePath.concat(prop);

dependences.push({ paths: curPath, val });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { FinalOptionsOfComponent } from "..";

import type { ComputedDependence } from "./computedUpdater";
import { deepProxy, getProxyOriginalValue } from "./data-tracer";
import { uniqueDependences } from "./uniqueDependences";

type ItemCache = {
dependences: ComputedDependence[];
Expand Down Expand Up @@ -65,7 +64,7 @@ export function initComputedAndGetCache(
initAllData[key] = initValue;

computedCache[key] = {
dependences: uniqueDependences(dependences),
dependences,
method: computedFunc,
value: initValue,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,98 @@
import type { ComputedDependence } from "./computedUpdater";

export /**
* 依赖去重
* @example
* ```ts
* const dependences = [{path:['a','b']}, {path:['a','b','c']}, {path:['d','e']}, {path:['d']}]
* const result = uniqueDependences(dependences)
* // result = [{path:['a','b'] } ,{ path:['d'] }]
* ```
*/
function uniqueDependences(dependences: ComputedDependence[]): ComputedDependence[] {
if (dependences.length === 1) return dependences;
// 相同依赖去重
function sameDependences(dependences: ComputedDependence[]) {
for (let f = 0; f < dependences.length; f++) {
const firstPath = dependences[f].paths.join(".") + ".";
const firstPath = dependences[f].paths.toString();
for (let i = f + 1; i < dependences.length; i++) {
const curPath = dependences[i].paths.join(".") + ".";
if (firstPath.startsWith(curPath)) {
// 例如 path[0] = 'a.b.c.',curPath = 'a.b.'
const curPath = dependences[i].paths.toString();
if (firstPath === curPath) {
dependences.splice(f, 1);

return uniqueDependences(dependences);
}
if (curPath.startsWith(firstPath)) {
// 例如 curPath = 'a.b.' path[0] = 'a.b.c.',
dependences.splice(i, 1);

return uniqueDependences(dependences);
return sameDependences(dependences);
}
}
}

return dependences;
}
// 当前依赖是上一个依赖的子依赖时,去除上一个依赖
function serialDependences(dependences: ComputedDependence[], basePath: string[]) {
const lastDependence = dependences[dependences.length - 1];
if (lastDependence.paths.toString() === basePath.toString()) {
dependences.pop();
}

return dependences;
}
// -2依赖 + -1val === 当前依赖 去除-2依赖
function separatedDependences(dependences: ComputedDependence[], basePath: string[], prop: string) {
const lastDependence = dependences[dependences.length - 1].val;
if (
typeof lastDependence === "string"
&& dependences[dependences.length - 2].paths.concat(lastDependence).toString()
=== basePath.concat(prop).toString()
) {
dependences.splice(dependences.length - 2, 1);
}

return dependences;
}

/**
* 依赖去重
*/
export function uniqueDependences(
dependences: ComputedDependence[],
basePath: string[],
prop: string,
): ComputedDependence[] {
if (dependences.length === 0) return dependences;
// 相同依赖去重 例如 [{path:['a','b']}, {path:['a','b']}] => [{path:['a','b'] }]
dependences = sameDependences(dependences);

// 连续依赖去除 当前依赖是上一个依赖的子依赖时,去除上一个依赖
dependences = serialDependences(dependences, basePath);

// -2依赖 + -1依赖 === 当前依赖 去除-2依赖
if (dependences.length >= 2) {
dependences = separatedDependences(dependences, basePath, prop);
}

return dependences;
}
// import type { ComputedDependence } from "./computedUpdater";
// export /**
// * 依赖去重
// * @example
// * ```ts
// * const dependences = [{path:['a','b']}, {path:['a','b','c']}, {path:['d','e']}, {path:['d']}]
// * const result = uniqueDependences(dependences)
// * // result = [{path:['a','b'] } ,{ path:['d'] }]
// * ```
// */
// function uniqueDependences(dependences: ComputedDependence[]): ComputedDependence[] {
// if (dependences.length === 1) return dependences;
// console.log("外部去重前", dependences);

// for (let f = 0; f < dependences.length; f++) {
// const firstPath = dependences[f].paths.join(".") + ".";
// for (let i = f + 1; i < dependences.length; i++) {
// const curPath = dependences[i].paths.join(".") + ".";
// if (firstPath.startsWith(curPath)) {
// // 例如 path[0] = 'a.b.c.',curPath = 'a.b.'
// dependences.splice(f, 1);

// return uniqueDependences(dependences);
// }
// if (curPath.startsWith(firstPath)) {
// // 例如 curPath = 'a.b.' path[0] = 'a.b.c.',
// dependences.splice(i, 1);

// return uniqueDependences(dependences);
// }
// }
// }
// console.log("外部去重后", dependences);

// return dependences;
// }

0 comments on commit cff7833

Please sign in to comment.