Skip to content

Commit

Permalink
refactor: try to cache computed styles at least within updates
Browse files Browse the repository at this point in the history
Here I added basic cache for computed styles which is busted every time
any style or instance is changed.

Though it computes each property only once between changes.
So property label, style section dots and style control reuse the same
computed piece.

This can potentially fix performance and memory usages. Though requires testing.
  • Loading branch information
TrySound committed Nov 22, 2024
1 parent c537a6a commit c4c6d18
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 5 deletions.
21 changes: 17 additions & 4 deletions apps/builder/app/builder/features/style-panel/shared/model.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,26 @@ export const $availableColorVariables = computed(
availableVariables.filter((value) => value.fallback?.type !== "unit")
);

// completely bust the cache when model is changed
let prevModel: undefined | StyleObjectModel;
// store cached computed declarations by following key
// instanceSelector + styleSourceId + state + property
let cache = new Map<string, ComputedStyleDecl>();

Check failure on line 298 in apps/builder/app/builder/features/style-panel/shared/model.tsx

View workflow job for this annotation

GitHub Actions / checks

'cache' is never reassigned. Use 'const' instead

const validateCache = (model: StyleObjectModel) => {
if (model !== prevModel) {
prevModel = model;
cache.clear();
}
};

export const createComputedStyleDeclStore = (property: StyleProperty) => {
return computed(
[$model, $instanceAndRootSelector, $selectedOrLastStyleSourceSelector],
(model, instanceSelector, styleSourceSelector) => {
validateCache(model);
return getComputedStyleDecl({
cache,
model,
instanceSelector,
styleSourceId: styleSourceSelector?.styleSourceId,
Expand All @@ -306,10 +321,6 @@ export const createComputedStyleDeclStore = (property: StyleProperty) => {
);
};

export const useStyleObjectModel = () => {
return useStore($model);
};

export const useComputedStyleDecl = (property: StyleProperty) => {
const $store = useMemo(
() => createComputedStyleDeclStore(property),
Expand All @@ -324,7 +335,9 @@ export const useParentComputedStyleDecl = (property: StyleProperty) => {
computed(
[$model, $instanceAndRootSelector],
(model, instanceSelector) => {
validateCache(model);
return getComputedStyleDecl({
cache,
model,
instanceSelector: instanceSelector?.slice(1),
property,
Expand Down
27 changes: 27 additions & 0 deletions apps/builder/app/shared/style-object-model.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1576,3 +1576,30 @@ describe("style value source", () => {
});
});
});

describe("cache", () => {
test("reuse computed values", () => {
const model = createModel({
css: `
bodyLocal {
color: red;
}
`,
jsx: <$.Body ws:id="body" ws:tag="body" class="bodyLocal"></$.Body>,
});
const cache = new Map();
const first = getComputedStyleDecl({
cache,
model,
instanceSelector: ["body"],
property: "color",
});
const second = getComputedStyleDecl({
cache,
model,
instanceSelector: ["body"],
property: "color",
});
expect(first).toBe(second);
});
});
23 changes: 22 additions & 1 deletion apps/builder/app/shared/style-object-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,15 @@ export type ComputedStyleDecl = {
* https://drafts.csswg.org/css-cascade-5/#value-stages
*/
export const getComputedStyleDecl = ({
cache = new Map(),
model,
instanceSelector = [],
styleSourceId,
state,
property,
customPropertiesGraph = new Map(),
}: {
cache?: Map<string, ComputedStyleDecl>;
model: StyleObjectModel;
instanceSelector?: InstanceSelector;
styleSourceId?: StyleDecl["styleSourceId"];
Expand All @@ -302,6 +304,17 @@ export const getComputedStyleDecl = ({
*/
customPropertiesGraph?: Map<Instance["id"], Set<Property>>;
}): ComputedStyleDecl => {
const cacheKey = JSON.stringify({
instanceSelector,
styleSourceId,
state,
property,
});
const cachedValue = cache.get(cacheKey);
if (cachedValue) {
return cachedValue;
}

const isCustomProperty = property.startsWith("--");
const propertyData = isCustomProperty
? customPropertyData
Expand Down Expand Up @@ -431,5 +444,13 @@ export const getComputedStyleDecl = ({
// fallback to inherited value
cascadedValue ??= computedValue;

return { property, source, cascadedValue, computedValue, usedValue };
const computedStyleDecl: ComputedStyleDecl = {
property,
source,
cascadedValue,
computedValue,
usedValue,
};
cache.set(cacheKey, computedStyleDecl);
return computedStyleDecl;
};

0 comments on commit c4c6d18

Please sign in to comment.