Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript with Blazor static server rendering #30922

Merged
merged 12 commits into from
Nov 6, 2023
Merged

Conversation

guardrex
Copy link
Collaborator

@guardrex guardrex commented Nov 2, 2023

@guardrex guardrex self-assigned this Nov 2, 2023
@guardrex guardrex marked this pull request as ready for review November 3, 2023 09:59
@MackinnonBuck
Copy link
Member

Here's a new version of BlazorPageScript.lib.module.js with the behavior that I described in this comment.

const pageScriptInfoBySrc = new Map();

function registerPageScriptElement(src) {
    if (!src) {
        throw new Error('Must provide a non-empty value for the "src" attribute.');
    }

    let pageScriptInfo = pageScriptInfoBySrc.get(src);

    if (pageScriptInfo) {
        pageScriptInfo.referenceCount++;
    } else {
        pageScriptInfo = { referenceCount: 1, module: null };
        pageScriptInfoBySrc.set(src, pageScriptInfo);
        initializePageScriptModule(src, pageScriptInfo);
    }
}

function unregisterPageScriptElement(src) {
    if (!src) {
        return;
    }

    const pageScriptInfo = pageScriptInfoBySrc.get(src);
    if (!pageScriptInfo) {
        return;
    }

    pageScriptInfo.referenceCount--;
}

async function initializePageScriptModule(src, pageScriptInfo) {
    // If the path is relative, normalize it by by making it an absolute URL
    // with document's the base HREF.
    if (src.startsWith("./")) {
        src = new URL(src.substr(2), document.baseURI).toString();
    }

    const module = await import(src);

    if (pageScriptInfo.referenceCount <= 0) {
        // All page-script elements with the same 'src' were
        // unregistered while we were loading the module.
        return;
    }

    pageScriptInfo.module = module;
    module.onLoad?.();
    module.onUpdate?.();
}

function onEnhancedLoad() {
    // Start by invoking 'onDispose' on any modules that are no longer referenced.
    for (const [src, { module, referenceCount }] of pageScriptInfoBySrc) {
        if (referenceCount <= 0) {
            module?.onDispose?.();
            pageScriptInfoBySrc.delete(src);
        } 
    }

    // Then invoke 'onUpdate' on the remaining modules.
    for (const { module } of pageScriptInfoBySrc.values()) {
        module?.onUpdate?.();
    }
}

export function afterWebStarted(blazor) {
    customElements.define('page-script', class extends HTMLElement {
        static observedAttributes = ['src'];

        // We use attributeChangedCallback instead of connectedCallback
        // because a page-script element might get reused between enhanced
        // navigations.
        attributeChangedCallback(name, oldValue, newValue) {
            if (name !== 'src') {
                return;
            }

            this.src = newValue;
            unregisterPageScriptElement(oldValue);
            registerPageScriptElement(newValue);
        }

        disconnectedCallback() {
            unregisterPageScriptElement(this.src);
        }
    });

    blazor.addEventListener('enhancedload', onEnhancedLoad);
}

@MackinnonBuck
Copy link
Member

@guardrex If we think this sample is too large to put in the docs, I could post it somewhere else and we could link to it.

@guardrex
Copy link
Collaborator Author

guardrex commented Nov 3, 2023

No ... it's fine. Give me a few minutes, and I'll have the commit up for a look 👀.

@guardrex
Copy link
Collaborator Author

guardrex commented Nov 3, 2023

Note that I always need to move code comments into the text to get them localized. If they stay in the code, they remain stuck in English forever. Just wanted to let you know why you always see me do that.

@guardrex
Copy link
Collaborator Author

guardrex commented Nov 3, 2023

Ok ... I'm done with little nits.

Copy link
Member

@MackinnonBuck MackinnonBuck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me after the pending feedback is addressed 🙂

@guardrex
Copy link
Collaborator Author

guardrex commented Nov 4, 2023

I see now that this won't work for general calls because the JS object reference is required for JS interop calls. DUH! 🙈😄 I merely detected a difference without thinking it through last night.

I'll cross-link this as an example in the JS initializers section. We have a few other cross-linked use cases there.

Also, I have an update on the next commit for "static server rendering" (lowercase "s" in "server"), which I think is what DR wants. I still don't know yet if he wants "interactive server rendering" (because "server" only means 'on the server') or "interactive Server rendering" (because "Server" refers to the 'Blazor Server' hosting model ... we're still rather unclear on its role in a BWA ... if it actually is running under a BWA or not for the server-side scenarios). If it refers to 'Blazor Server,' it should be capitalized ... and also because it pairs with "interactive WebAssembly rendering," which is capitalized because "WebAssembly" is a proper noun.

@guardrex
Copy link
Collaborator Author

guardrex commented Nov 4, 2023

I'm going to hold here for a sec so that you can confirm if the updates on the last couple of commits are correct.

@guardrex guardrex changed the title JavaScript with Blazor static Server rendering JavaScript with Blazor static server rendering Nov 4, 2023
Copy link
Member

@MackinnonBuck MackinnonBuck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@guardrex guardrex merged commit 32c0421 into main Nov 6, 2023
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Using JavaScript with Blazor Static Server Rendering
2 participants