Skip to content

Commit

Permalink
fix: dont download serialized styles (#578)
Browse files Browse the repository at this point in the history
fixes #544
  • Loading branch information
manucorporat authored Jun 8, 2022
1 parent a4a6b98 commit 6fd80b9
Show file tree
Hide file tree
Showing 13 changed files with 105 additions and 50 deletions.
11 changes: 3 additions & 8 deletions packages/qwik/src/core/component/component-ctx.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { assertDefined } from '../assert/assert';
import { appendStyle, RenderContext } from '../render/cursor';
import type { RenderContext } from '../render/cursor';
import { visitJsxNode } from '../render/render';
import { ComponentScopedStyles, QHostAttr, RenderEvent } from '../util/markers';
import { promiseAll, then } from '../util/promises';
import { styleContent, styleHost } from './qrl-styles';
import { isStyleTask, newInvokeContext } from '../use/use-core';
import { newInvokeContext } from '../use/use-core';
import { processNode } from '../render/jsx/jsx-runtime';
import { logDebug, logError } from '../util/log';
import type { ValueOrPromise } from '../util/types';
Expand Down Expand Up @@ -55,12 +55,7 @@ export const renderComponent = (rctx: RenderContext, ctx: QContext): ValueOrProm
(jsxNode) => {
rctx.hostElements.add(hostElement);
const waitOnPromise = promiseAll(waitOn);
return then(waitOnPromise, (waitOnResolved) => {
waitOnResolved.forEach((task) => {
if (isStyleTask(task)) {
appendStyle(rctx, hostElement, task);
}
});
return then(waitOnPromise, () => {
if (typeof jsxNode === 'function') {
ctx.dirty = false;
jsxNode = jsxNode();
Expand Down
31 changes: 18 additions & 13 deletions packages/qwik/src/core/component/component.public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { toQrlOrError } from '../import/qrl';
import { $, implicit$FirstArg, QRL } from '../import/qrl.public';
import { qPropWriteQRL } from '../props/props-on';
import type { JSXNode } from '../render/jsx/types/jsx-node';
import { StyleAppend, useWaitOn } from '../use/use-core';
import { useRenderContext, useWaitOn } from '../use/use-core';
import { useHostElement } from '../use/use-host-element.public';
import { ComponentScopedStyles, OnRenderProp } from '../util/markers';
import { styleKey } from './qrl-styles';
Expand All @@ -14,6 +14,7 @@ import { jsx } from '../render/jsx/jsx-runtime';
import { useSequentialScope } from '../use/use-store.public';
import { WatchDescriptor, WatchFlags } from '../watch/watch.public';
import type { MutableWrapper } from '../object/q-object';
import { appendStyle, hasStyle } from '../render/cursor';

// <docs markdown="../readme.md#useCleanup">
// !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
Expand Down Expand Up @@ -578,26 +579,30 @@ export interface RenderFactoryOutput<PROPS> {
}

function _useStyles(styles: QRL<string>, scoped: boolean) {
const [style, setStyle] = useSequentialScope();
const [style, setStyle, index] = useSequentialScope();
if (style === true) {
return;
}
setStyle(true);
const renderCtx = useRenderContext();
const styleQrl = toQrlOrError(styles);
const styleId = styleKey(styleQrl);
const styleId = styleKey(styleQrl, index);
const hostElement = useHostElement();
if (scoped) {
hostElement.setAttribute(ComponentScopedStyles, styleId);
}

useWaitOn(
styleQrl.resolve(hostElement).then((styleText) => {
const task: StyleAppend = {
type: 'style',
styleId,
content: scoped ? styleText.replace(//g, styleId) : styleText,
};
return task;
})
);
if (!hasStyle(renderCtx, styleId)) {
useWaitOn(
styleQrl.resolve(hostElement).then((styleText) => {
if (!hasStyle(renderCtx, styleId)) {
appendStyle(renderCtx, hostElement, {
type: 'style',
styleId,
content: scoped ? styleText.replace(//g, styleId) : styleText,
});
}
})
);
}
}
18 changes: 6 additions & 12 deletions packages/qwik/src/core/component/qrl-styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,20 @@ import { hashCode } from '../util/hash_code';
/**
* @public
*/
export function styleKey(qStyles: QRLInternal<string>): string;
export function styleKey(qStyles: QRLInternal<string> | null): string | null;
export function styleKey(qStyles: QRLInternal<string> | null): string | null {
return qStyles && String(hashCode(qStyles.getCanonicalSymbol()));
export function styleKey(qStyles: QRLInternal<string>, index: number): string {
return `${hashCode(qStyles.getCanonicalSymbol())}-${index}`;
}

/**
* @public
*/
export function styleHost(styleId: string): string;
export function styleHost(styleId: string | undefined): string | undefined;
export function styleHost(styleId: string | undefined): string | undefined {
return styleId && ComponentStylesPrefixHost + styleId;
export function styleHost(styleId: string): string {
return ComponentStylesPrefixHost + styleId;
}

/**
* @public
*/
export function styleContent(styleId: string): string;
export function styleContent(styleId: string | undefined): string | undefined;
export function styleContent(styleId: string | undefined): string | undefined {
return styleId && ComponentStylesPrefixContent + styleId;
export function styleContent(styleId: string): string {
return ComponentStylesPrefixContent + styleId;
}
33 changes: 26 additions & 7 deletions packages/qwik/src/core/render/cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ import { qDev } from '../util/qdev';
import { qError, QError } from '../error/error';
import { fromCamelToKebabCase } from '../util/case';
import type { OnRenderFn } from '../component/component.public';
import { CONTAINER, StyleAppend } from '../use/use-core';
import { CONTAINER, isStyleTask, StyleAppend } from '../use/use-core';
import type { Ref } from '../use/use-store.public';
import type { SubscriptionManager } from '../object/q-object';
import { getDocument } from '../util/dom';

export const SVG_NS = 'http://www.w3.org/2000/svg';

Expand Down Expand Up @@ -796,12 +797,10 @@ export function appendStyle(ctx: RenderContext, hostElement: Element, styleTask:
const containerEl = ctx.containerEl;
const stylesParent =
ctx.doc.documentElement === containerEl ? ctx.doc.head ?? containerEl : containerEl;
if (!stylesParent.querySelector(`style[q\\:style="${styleTask.styleId}"]`)) {
const style = ctx.doc.createElement('style');
style.setAttribute('q:style', styleTask.styleId);
style.textContent = styleTask.content;
stylesParent.insertBefore(style, stylesParent.firstChild);
}
const style = ctx.doc.createElement('style');
style.setAttribute('q:style', styleTask.styleId);
style.textContent = styleTask.content;
stylesParent.insertBefore(style, stylesParent.firstChild);
};
ctx.operations.push({
el: hostElement,
Expand All @@ -810,6 +809,26 @@ export function appendStyle(ctx: RenderContext, hostElement: Element, styleTask:
fn,
});
}

export function hasStyle(ctx: RenderContext, styleId: string) {
const containerEl = ctx.containerEl;
const doc = getDocument(containerEl);
const hasOperation = ctx.operations.some((op) => {
if (op.operation === 'append-style') {
const s = op.args[0];
if (isStyleTask(s)) {
return s.styleId === styleId;
}
}
return false;
});
if (hasOperation) {
return true;
}
const stylesParent = doc.documentElement === containerEl ? doc.head ?? containerEl : containerEl;
return !!stylesParent.querySelector(`style[q\\:style="${styleId}"]`);
}

function prepend(ctx: RenderContext, parent: Element, newChild: Node) {
const fn = () => {
parent.insertBefore(newChild, parent.firstChild);
Expand Down
11 changes: 5 additions & 6 deletions packages/qwik/src/core/use/use-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { getDocument } from '../util/dom';
import type { QRL } from '../import/qrl.public';
import type { Subscriber } from './use-subscriber';
import type { RenderContext } from '../render/cursor';
import type { ContainerState } from '../render/notify-render';

declare const document: QwikDocument;

Expand Down Expand Up @@ -149,11 +148,11 @@ export function getContainer(el: Element): Element | null {
return container;
}

export function useContainerState(): ContainerState {
export function useRenderContext(): RenderContext {
const ctx = getInvokeContext();
const containerState = ctx.renderCtx?.containerState;
if (!containerState) {
throw new Error('Cant access containerState for existing context');
const renderCtx = ctx.renderCtx;
if (!renderCtx) {
throw new Error('Cant access renderCtx for existing context');
}
return containerState;
return renderCtx;
}
4 changes: 2 additions & 2 deletions packages/qwik/src/core/use/use-store.public.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { qObject } from '../object/q-object';
import { getInvokeContext, useContainerState } from './use-core';
import { getInvokeContext, useRenderContext } from './use-core';
import { useHostElement } from './use-host-element.public';
import { getContext } from '../props/props';
import { wrapSubscriber } from './use-subscriber';
Expand Down Expand Up @@ -76,7 +76,7 @@ export function useStore<STATE extends object>(initialState: STATE | (() => STAT
return wrapSubscriber(store, hostElement);
}

const containerState = useContainerState();
const containerState = useRenderContext().containerState;
const value = typeof initialState === 'function' ? (initialState as Function)() : initialState;
const newStore = qObject(value, containerState);
setStore(newStore);
Expand Down
4 changes: 2 additions & 2 deletions packages/qwik/src/core/watch/watch.public.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { noSerialize, NoSerialize, notifyWatch } from '../object/q-object';
import { implicit$FirstArg, QRL } from '../import/qrl.public';
import { getContext } from '../props/props';
import { newInvokeContext, useContainerState, useWaitOn } from '../use/use-core';
import { newInvokeContext, useRenderContext, useWaitOn } from '../use/use-core';
import { useHostElement } from '../use/use-host-element.public';
import { logDebug, logError } from '../util/log';
import { then } from '../util/promises';
Expand Down Expand Up @@ -139,7 +139,7 @@ export function useWatchQrl(qrl: QRL<WatchFn>, opts?: UseEffectOptions): void {
const [watch, setWatch, i] = useSequentialScope();
if (!watch) {
const el = useHostElement();
const containerState = useContainerState();
const containerState = useRenderContext().containerState;
const watch: WatchDescriptor = {
qrl,
el,
Expand Down
3 changes: 3 additions & 0 deletions starters/apps/e2e/src/components/styles/child.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.child {
font-size: 20px;
}
3 changes: 3 additions & 0 deletions starters/apps/e2e/src/components/styles/child2.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.child2 {
font-size: 10px;
}
3 changes: 3 additions & 0 deletions starters/apps/e2e/src/components/styles/parent.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.parent {
font-size: 200px;
}
29 changes: 29 additions & 0 deletions starters/apps/e2e/src/components/styles/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { component$, Host, useStore, useStyles$ } from '@builder.io/qwik';
import parent from './parent.css';
import child from './child.css';
import child2 from './child2.css';

export const Styles = component$(() => {
useStyles$(parent);
const store = useStore({
count: 10,
});
return (
<Host class="parent">
Parent
<button type="button" onClick$={() => store.count++}>
Add Child
</button>
{Array.from({ length: store.count }).map(() => (
<Child />
))}
</Host>
);
});

export const Child = component$(() => {
useStyles$(child);
useStyles$(child2);

return <Host class="child">Child</Host>;
});
2 changes: 2 additions & 0 deletions starters/apps/e2e/src/entry.ssr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Watch } from './components/watch/watch';
import { EffectClient } from './components/effect-client/effect-client';
import { ContextRoot } from './components/context/context';
import { Toggle } from './components/toggle/toggle';
import { Styles } from './components/styles/styles';

/**
* Entry point for server-side pre-rendering.
Expand All @@ -36,6 +37,7 @@ export function render(opts: RenderToStringOptions) {
'/e2e/effect-client': () => <EffectClient />,
'/e2e/context': () => <ContextRoot />,
'/e2e/toggle': () => <Toggle />,
'/e2e/styles': () => <Styles />,
};
const Test = tests[url.pathname];

Expand Down
3 changes: 3 additions & 0 deletions starters/apps/e2e/src/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export const Root = component$(() => {
<p>
<a href="/e2e/toggle">Toggle</a>
</p>
<p>
<a href="/e2e/styles">Styles</a>
</p>
</section>
);
});

0 comments on commit 6fd80b9

Please sign in to comment.