Here are few thoughts from my end on this topic, but in a non-wp context. There might be other solutions too.
Generally assets (js/css) files are cached, so “downloading” twice is not really a concern, as the second call to the asset will generally be picked from the cache. So, loading two js files of 100kb doesn’t always end up “downloading” 200kb (of course, there are always exceptional scenarios, but they are uncommon). Same applies to css files too. To beat this caching is why build tools like webpack, vite add a “hash” to these assets.
The real concern is the “re-execution” of the second redundant script tag, unless the script is protected against double-inclusion. For eg, jQuery doesn’t, function foo(){} in the script file will be executed twice, and might attach duplicate event listeners. The same concern for css, as the second style tag, will end up overriding the first one. So, avoiding double inclusion of js/lib files must be avoid for this reason, not necessary to save data download.
What you are attempting to do with two blocks is similar to the concepts of “islands”, lately popularised by js frameworks like Astro & Iles. These modern frameworks use module scripts (type=“module”) instead of the traditional classic scripts.
Module scripts are different to classic scripts:
- Singleton (loaded only once, even if imported multiple times)
- Deferred by default (executed after HTML content is parsed, so can be used anywhere within the document. Nice to co-locate them with it’s content.
- Strict mode - all objects (let, const, var, function, class) are private unless explicitly exported.
- Uses modern ES module syntax, can be broken down into smaller chunks and import/export nicely. No file protocol, so external module imports (src=“./my-module.js”) won’t work if the page is opened directly in the browser, without a live-server.
For eg, if you add the below two islands to two blocks, and check your network tab in your browser, you will notice that the petite-vue will be downloaded & executed only once (singleton). Not sure if uikit provides a module script via cdn.
So, for javascript, you could use module scripts (type=“module”), but I believe when using it with WP, you probably have to “Do not enqueue” it as currently Pinegrow WP builder doesn’t differentiate module vs classic scripts and ignores the type (type=“module”) when exporting theme/plugin.
With regards to how to avoid double css download & execution (cascading) in the WP block context, I’m not sure about it, lets wait to hear more from the community on this topic. Obviously, double download of css import could be avoided by using javascript, but that’s not very clean. And I’m not very familiar with WP blocks world, so I better don’t say anything inaccurate.
Island-1:
<script type="module" data-pg-name="Pt-App-Hero" id="hero-app">
import { createApp } from 'https://unpkg.com/petite-vue?module'
const state = {
// state exposed to all expressions within v-scope regions
count: 0,
get oddOrEven() {
return this.count % 2 === 0 ? 'even' : 'odd' // computed property
},
// methods
increment() {
this.count++
},
decrement() {
this.count--
},
}
createApp(state).mount('div#hero-island')
</script>
<div
id="hero-island"
data-pg-name="Pt-Island-Hero"
v-scope="{}"
style="
padding: 20px;
margin: 20px;
border-radius: 4px;
border-width: 2px;
outline: 1px solid #cccccc;
"
data-pg-collapsed
>
<div
style="
display: flex;
margin: 8px;
padding: 8px;
justify-content: center;
align-items: center;
"
>
<p style="text-align: center; width: 50%; min-width: 400px">
Hello, I'm within an island, and the root tag of the island
<code>div#hero-island</code> has an exclusive app mounted by
<code>script#hero-app</code>, and marked as a
<code>v-scope</code> region. Any sprinkles of interactions within this
<code>v-scope</code> region are managed by this exclusive app.
</p>
</div>
<div
style="
display: flex;
margin: 8px;
padding: 8px;
justify-content: center;
align-items: center;
"
>
<button
v-on:click="decrement"
style="margin-left: 4px; margin-right: 4px"
>
⬇️
</button>
<button
v-on:click="increment"
style="margin-left: 4px; margin-right: 4px"
>
⬆️
</button>
<div v-cloak style="text-align: left; width: 180px; margin-left: 4px">
<span>Count is: </span>
<span style="width: 30px; display: inline-block; text-align: center"
>{{count}}</span
><span v-cloak>({{oddOrEven}})</span>
</div>
</div>
</div>
Island-2:
<script type="module" data-pg-name="Pt-App-Feature" id="feature-app">
import { createApp } from 'https://unpkg.com/petite-vue?module'
const state = {
// state exposed to all expressions within v-scope regions
msg: 'Happy Life!',
}
createApp(state).mount('div#feature-island')
</script>
<div
id="feature-island"
data-pg-name="Pt-Island-Feature"
v-scope="{}"
style="
padding: 20px;
margin: 20px;
border-radius: 4px;
border-width: 2px;
outline: 1px solid #cccccc;
"
>
<div
style="
display: flex;
margin: 8px;
padding: 8px;
justify-content: center;
align-items: center;
"
>
<p style="text-align: center; width: 50%; min-width: 400px">
Hello, I'm within an island, and the root tag of the island
<code>div#feature-island</code> has an exclusive app mounted by
<code>script#feature-app</code>, and marked as a
<code>v-scope</code> region. Any sprinkles of interactions within this
<code>v-scope</code> region are managed by this exclusive app.
</p>
</div>
<div
style="
display: flex;
margin: 8px;
padding: 8px;
justify-content: center;
align-items: center;
flex-direction: column;
"
>
<input v-model="msg" style="text-align: center" /><span
v-cloak
style="margin: 8px"
>{{msg}}</span
>
</div>
</div>