Bugzilla – Attachment 176961 Details for
Bug 37911
Prototype vue islands within static pages
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 37911: (follow-up) Update to vue ^3.5.4, use vue's web component implementation instead
Bug-37911-follow-up-Update-to-vue-354-use-vues-web.patch (text/plain), 8.29 KB, created by
Matt Blenkinsop
on 2025-01-23 14:30:38 UTC
(
hide
)
Description:
Bug 37911: (follow-up) Update to vue ^3.5.4, use vue's web component implementation instead
Filename:
MIME Type:
Creator:
Matt Blenkinsop
Created:
2025-01-23 14:30:38 UTC
Size:
8.29 KB
patch
obsolete
>From a133f9ac4f98e139d996d07d64169c1de88219d1 Mon Sep 17 00:00:00 2001 >From: Paul Derscheid <paul.derscheid@lmscloud.de> >Date: Fri, 13 Sep 2024 01:38:35 +0200 >Subject: [PATCH] Bug 37911: (follow-up) Update to vue ^3.5.4, use vue's web > component implementation instead > >- This would require us to update vue to a new minor version. > - We could then use the defineCustomElement function with the shadowRoot option set to false (this is important). >- The current implementation in islands.ts is naive and would result in an intolerable bundle size. > - The right way to do code splitting here is to use dynamic imports. > >Signed-off-by: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com> >--- > .../prog/en/modules/intranet-main.tt | 7 +- > .../prog/js/vue/components/HelloIslands.vue | 8 +- > .../prog/js/vue/modules/islands.ts | 103 +++--------------- > package.json | 2 +- > rspack.config.js | 1 + > 5 files changed, 27 insertions(+), 94 deletions(-) > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt >index afdc284839c..fd9d2ffb3f9 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt >@@ -11,7 +11,7 @@ > [% t("Koha staff interface") | html %] > [% END %]</title> > [% Asset.css("css/mainpage.css") | $raw %] >-[% Asset.js("js/vue/dist/islands.js") | $raw %] >+[% Asset.js("js/vue/dist/islands.js", "defer" => 1, async => "1") | $raw %] > [% INCLUDE 'doc-head-close.inc' %] > </head> > >@@ -311,7 +311,7 @@ > <div class="row w-25 m-auto p-4 border border-4 border-warning br-4 rounded text-center"> > <h1>ð§ Static Page with Vue Components ð§</h1> > >- <div id="hello-islands" data-component="HelloIslands" data-props='{"message": "Hello from props!"}'></div> >+ <hello-islands message="This is an attribute on the web component, that's bound to a prop in the Vue component. Cool, right?" color="dodgerblue"></hello-islands> > </div> <!-- /.row --> > > [% MACRO jsinclude BLOCK %] >@@ -323,7 +323,8 @@ > }); > > setTimeout(() => { >- document.getElementById("hello-islands").dataset.props = JSON.stringify({ message: 'This is a delayed message! Changes to data-props currently unfortunately reset the internal state.' }); >+ const [firstHelloIslandsElement] = document.getElementsByTagName("hello-islands"); >+ firstHelloIslandsElement.message = 'This is a delayed update to the message attribute of the web commponent.' > }, 2000); > }); > </script> >diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/HelloIslands.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/HelloIslands.vue >index 782296d5e3b..9d8350f2877 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/HelloIslands.vue >+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/HelloIslands.vue >@@ -1,5 +1,5 @@ > <template> >- <h2>Hello from Islands!</h2> >+ <h2 :style="{ color }">Hello from Islands!</h2> > <p>This component is rendered as an island in a static HTML page.</p> > > <!-- Display message prop --> >@@ -20,7 +20,11 @@ export default { > props: { > message: { > type: String, >- default: "", >+ default: "No content", >+ }, >+ color: { >+ type: String, >+ default: "crimson", > }, > }, > data() { >diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts b/koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts >index 379c1c954a7..db8dcb5d928 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts >+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts >@@ -1,93 +1,20 @@ >-import { createApp, Component, App } from "vue"; >+import { Component, defineCustomElement } from "vue"; >+import HelloIslands from "../components/HelloIslands.vue"; > > /** > * A registry for Vue components. >- * @type {Map<string, Component>} >- */ >-export const componentRegistry: Map<string, Component> = new Map< >- string, >- Component >->(); >- >-/** >- * Registers a Vue component with a name. >- * @param {string} name - The name of the component. >- * @param {Component} component - The Vue component to register. >- * @returns {void} >- */ >-export function registerComponent(name: string, component: Component): void { >- componentRegistry.set(name, component); >-} >- >-/** >- * Mounts Vue components to DOM elements based on the `data-component` attribute. >- * Components are created with props parsed from the `data-props` attribute. >- * Watches for changes in props and updates the component accordingly. >- * @returns {void} >- */ >-export function mountComponents(): void { >- console.log("Mounting components"); >- >- const elements: NodeListOf<Element> = >- document.querySelectorAll("[data-component]"); >- elements.forEach((element: Element) => { >- const componentName: string | null = >- element.getAttribute("data-component"); >- if (!componentName) { >- console.warn("No data-component attribute found."); >- return; >- } >- >- const component: Component | undefined = >- componentRegistry.get(componentName); >- if (!component) { >- console.warn(`Component ${componentName} not found.`); >- return; >- } >- >- const props: string | null = element.getAttribute("data-props"); >- const parsedProps: Record<string, any> = props ? JSON.parse(props) : {}; >- >- // Create and mount the Vue component >- const app: App = createApp(component, parsedProps); >- app.mount(element); >- >- // Watch for updates to props >- watchProps(element, app, component); >- }); >-} >- >-/** >- * Watches for changes in props and updates the component accordingly. >- * @param {Element} element - The DOM element where the component is mounted. >- * @param {App} app - The Vue application instance. >- * @param {Component} component - The Vue component. >- * @returns {void} >+ * @type {Map<string, string>} > */ >-function watchProps(element: Element, app: App, component: Component): void { >- const propsAttr: string | null = element.getAttribute("data-props"); >- let prevProps: Record<string, any> = propsAttr ? JSON.parse(propsAttr) : {}; >- >- const observer = new MutationObserver(() => { >- const newPropsAttr: string | null = element.getAttribute("data-props"); >- if (newPropsAttr) { >- const newProps: Record<string, any> = JSON.parse(newPropsAttr); >- if (JSON.stringify(newProps) !== JSON.stringify(prevProps)) { >- prevProps = newProps; >- app.unmount(); // Unmount existing component >- createApp(component, newProps).mount(element); // Mount with new props >- } >- } >+export const componentRegistry: Map<string, () => Promise<Component>> = new Map( >+ [["hello-islands", HelloIslands]] >+); >+ >+// Register and define custom elements >+window.requestIdleCallback(() => { >+ componentRegistry.forEach((component, name) => { >+ customElements.define( >+ name, >+ defineCustomElement(component as any, { shadowRoot: false }) >+ ); > }); >- >- observer.observe(element, { >- attributes: true, >- attributeFilter: ["data-props"], >- }); >-} >- >-import HelloIslands from "../components/HelloIslands.vue"; >-registerComponent("HelloIslands", HelloIslands); >- >-// Automatically mount components when the DOM is fully loaded >-document.addEventListener("DOMContentLoaded", mountComponents); >+}); >diff --git a/package.json b/package.json >index 841f13b0a2f..95228fcd740 100644 >--- a/package.json >+++ b/package.json >@@ -38,7 +38,7 @@ > "pinia": "^2.0.13", > "sass": "^1.58.1", > "style-loader": "^3.3.1", >- "vue": "^3.2.31", >+ "vue": "^3.5.4", > "vue-flatpickr-component": "^9", > "vue-router": "^4.0.14", > "vue-select": "4.0.0-beta.3", >diff --git a/rspack.config.js b/rspack.config.js >index 12b621b3fea..7aa53b03bc4 100644 >--- a/rspack.config.js >+++ b/rspack.config.js >@@ -80,6 +80,7 @@ module.exports = { > new rspack.DefinePlugin({ > __VUE_OPTIONS_API__: true, > __VUE_PROD_DEVTOOLS__: false, >+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false, > }), > ], > externals: { >-- >2.48.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 37911
:
171425
|
171426
|
171432
|
171439
|
171440
|
171445
|
171459
|
171517
|
171538
|
171608
|
171685
|
172962
|
172963
|
172965
|
172966
|
172967
|
172968
|
172969
|
172970
|
172971
|
172972
|
172973
|
172974
|
172975
|
176959
|
176960
|
176961
|
176962
|
176963
|
176964
|
176965
|
176966
|
176967
|
176968
|
176969
|
176970
|
176972
|
178256
|
178257
|
178258
|
178259
|
178260
|
178261
|
178262
|
178263
|
178264
|
178265
|
178266
|
178267