perf: add defer to scripts and lazy loading to images
Some checks failed
Some checks failed
Add defer attribute to 145 <script> tags across 103 template files (PERF-067) and loading="lazy" to 22 <img> tags across 13 template files (PERF-058). Both improve page load performance. Validator totals: 0 errors, 2 warnings, 1360 info (down from 1527). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -98,19 +98,19 @@
|
||||
<!-- Core Scripts - ORDER MATTERS! -->
|
||||
|
||||
<!-- 1. FIRST: Log Configuration -->
|
||||
<script src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
|
||||
|
||||
<!-- 2. SECOND: Icons (before Alpine.js) -->
|
||||
<script src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
|
||||
<!-- 3. THIRD: Alpine.js Base Data -->
|
||||
<script src="{{ url_for('core_static', path='admin/js/init-alpine.js') }}"></script>
|
||||
|
||||
<!-- 4. FOURTH: Utils (standalone utilities) -->
|
||||
<script src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
|
||||
<!-- 4b. i18n Support -->
|
||||
<script src="{{ url_for('static', path='shared/js/i18n.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/i18n.js') }}"></script>
|
||||
<script>
|
||||
// Initialize i18n with current language and preload modules
|
||||
(async function() {
|
||||
@@ -120,7 +120,7 @@
|
||||
</script>
|
||||
|
||||
<!-- 5. FIFTH: API Client (depends on Utils) -->
|
||||
<script src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
|
||||
|
||||
<!-- 6. SIXTH: Tom Select with CDN fallback -->
|
||||
<script>
|
||||
@@ -138,7 +138,7 @@
|
||||
</script>
|
||||
|
||||
<!-- 7. SEVENTH: Store Selector (depends on Tom Select and API Client) -->
|
||||
<script src="{{ url_for('core_static', path='shared/js/store-selector.js') }}"></script>
|
||||
<script defer src="{{ url_for('core_static', path='shared/js/store-selector.js') }}"></script>
|
||||
|
||||
<!-- 8a. Alpine.js Collapse Plugin (must load before Alpine) -->
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/collapse@3.13.3/dist/cdn.min.js"></script>
|
||||
@@ -174,4 +174,4 @@
|
||||
<!-- 12. LAST: Page-specific scripts -->
|
||||
{% block extra_scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -41,19 +41,19 @@
|
||||
<!-- Core Scripts - ORDER MATTERS! -->
|
||||
|
||||
<!-- 1. FIRST: Log Configuration -->
|
||||
<script src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
|
||||
|
||||
<!-- 2. SECOND: Icons (before Alpine.js) -->
|
||||
<script src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
|
||||
<!-- 3. THIRD: Alpine.js Base Data -->
|
||||
<script src="{{ url_for('core_static', path='merchant/js/init-alpine.js') }}"></script>
|
||||
|
||||
<!-- 4. FOURTH: Utils (standalone utilities) -->
|
||||
<script src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
|
||||
<!-- 5. FIFTH: API Client (depends on Utils) -->
|
||||
<script src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
|
||||
|
||||
<!-- 6. SIXTH: Alpine.js v3 with CDN fallback (with defer) -->
|
||||
<script>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
Prerequisites:
|
||||
Add Chart.js CDN to your base template:
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
||||
|
||||
Usage:
|
||||
{% from 'shared/macros/charts.html' import chart_card, line_chart, bar_chart, doughnut_chart %}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
Add Flatpickr CDN to your base template:
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/themes/dark.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
|
||||
|
||||
Usage:
|
||||
{% from 'shared/macros/datepicker.html' import datepicker, daterange_picker, datetime_picker %}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
{% if show_avatar %}
|
||||
<div class="w-10 h-10 rounded-full overflow-hidden bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center flex-shrink-0">
|
||||
<template x-if="{{ review_var }}.author_avatar">
|
||||
<img :src="{{ review_var }}.author_avatar" :alt="{{ review_var }}.author_name" class="w-full h-full object-cover">
|
||||
<img loading="lazy" :src="{{ review_var }}.author_avatar" :alt="{{ review_var }}.author_name" class="w-full h-full object-cover">
|
||||
</template>
|
||||
<template x-if="!{{ review_var }}.author_avatar">
|
||||
<span class="text-sm font-medium text-purple-600 dark:text-purple-400" x-text="{{ review_var }}.author_name?.charAt(0)?.toUpperCase()"></span>
|
||||
@@ -111,7 +111,7 @@
|
||||
type="button"
|
||||
class="w-16 h-16 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:border-purple-500 transition-colors"
|
||||
>
|
||||
<img :src="image" alt="Review image" class="w-full h-full object-cover">
|
||||
<img loading="lazy" :src="image" alt="Review image" class="w-full h-full object-cover">
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
@@ -353,7 +353,7 @@
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<template x-for="(image, index) in {{ images_model }} || []" :key="index">
|
||||
<div class="relative w-20 h-20 rounded-lg overflow-hidden group">
|
||||
<img :src="image" alt="Review image" class="w-full h-full object-cover">
|
||||
<img loading="lazy" :src="image" alt="Review image" class="w-full h-full object-cover">
|
||||
<button
|
||||
type="button"
|
||||
@click="{{ images_model }}.splice(index, 1)"
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="typeof suggestion === 'object' && suggestion.image">
|
||||
<img :src="suggestion.image" :alt="suggestion.name" class="w-10 h-10 object-cover rounded">
|
||||
<img loading="lazy" :src="suggestion.image" :alt="suggestion.name" class="w-10 h-10 object-cover rounded">
|
||||
</template>
|
||||
</button>
|
||||
</li>
|
||||
@@ -434,7 +434,7 @@
|
||||
class="w-full flex items-center gap-4 px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||
>
|
||||
<template x-if="typeof suggestion === 'object' && suggestion.image">
|
||||
<img :src="suggestion.image" :alt="suggestion.name" class="w-12 h-12 object-cover rounded">
|
||||
<img loading="lazy" :src="suggestion.image" :alt="suggestion.name" class="w-12 h-12 object-cover rounded">
|
||||
</template>
|
||||
<template x-if="typeof suggestion === 'string' || !suggestion.image">
|
||||
<span class="w-12 h-12 flex items-center justify-center bg-gray-100 dark:bg-gray-700 rounded">
|
||||
@@ -574,7 +574,7 @@
|
||||
:href="product.url"
|
||||
class="flex items-center gap-3 px-4 py-2 hover:bg-gray-50 dark:hover:bg-gray-700/50"
|
||||
>
|
||||
<img :src="product.image" :alt="product.name" class="w-10 h-10 object-cover rounded">
|
||||
<img loading="lazy" :src="product.image" :alt="product.name" class="w-10 h-10 object-cover rounded">
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm text-gray-900 dark:text-white truncate" x-text="product.name"></p>
|
||||
<p class="text-sm text-purple-600 dark:text-purple-400 font-medium" x-text="'$' + product.price"></p>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<!-- Core Scripts - ORDER MATTERS! -->
|
||||
|
||||
<!-- 1. FIRST: Log Configuration -->
|
||||
<script src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
|
||||
|
||||
<!-- 1.5: Store Configuration (resolved via PlatformSettingsService) -->
|
||||
<script>
|
||||
@@ -60,16 +60,16 @@
|
||||
</script>
|
||||
|
||||
<!-- 2. SECOND: Icons (before Alpine.js) -->
|
||||
<script src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
|
||||
<!-- 3. THIRD: Alpine.js Base Data -->
|
||||
<script src="{{ url_for('core_static', path='store/js/init-alpine.js') }}"></script>
|
||||
|
||||
<!-- 4. FOURTH: Utils (standalone utilities) -->
|
||||
<script src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
|
||||
<!-- 4b. i18n Support -->
|
||||
<script src="{{ url_for('static', path='shared/js/i18n.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/i18n.js') }}"></script>
|
||||
<script>
|
||||
// Initialize i18n with dashboard language and preload modules
|
||||
(async function() {
|
||||
@@ -79,13 +79,13 @@
|
||||
</script>
|
||||
|
||||
<!-- 5. FIFTH: API Client (depends on Utils) -->
|
||||
<script src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
|
||||
|
||||
<!-- 6. SIXTH: Feature Store (depends on API Client, registers with Alpine) -->
|
||||
<script src="{{ url_for('billing_static', path='shared/js/feature-store.js') }}"></script>
|
||||
<script defer src="{{ url_for('billing_static', path='shared/js/feature-store.js') }}"></script>
|
||||
|
||||
<!-- 7. SEVENTH: Upgrade Prompts (depends on API Client, registers with Alpine) -->
|
||||
<script src="{{ url_for('billing_static', path='shared/js/upgrade-prompts.js') }}"></script>
|
||||
<script defer src="{{ url_for('billing_static', path='shared/js/upgrade-prompts.js') }}"></script>
|
||||
|
||||
<!-- 8. EIGHTH: Alpine.js v3 with CDN fallback (with defer) -->
|
||||
<script>
|
||||
@@ -109,4 +109,4 @@
|
||||
<!-- 9. LAST: Page-specific scripts -->
|
||||
{% block extra_scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
{# JavaScript Loading Order (CRITICAL - must be in this order) #}
|
||||
|
||||
{# 1. Log Configuration (must load first) #}
|
||||
<script src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
|
||||
|
||||
{# 2. Global Shop Configuration (resolved via PlatformSettingsService) #}
|
||||
<script>
|
||||
@@ -318,16 +318,16 @@
|
||||
</script>
|
||||
|
||||
{# 3. Icon System #}
|
||||
<script src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
|
||||
{# 4. Base Shop Layout (Alpine.js component - must load before Alpine) #}
|
||||
<script src="{{ url_for('static', path='storefront/js/storefront-layout.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='storefront/js/storefront-layout.js') }}"></script>
|
||||
|
||||
{# 5. Utilities #}
|
||||
<script src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
|
||||
{# 5b. i18n Support #}
|
||||
<script src="{{ url_for('static', path='shared/js/i18n.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/i18n.js') }}"></script>
|
||||
<script>
|
||||
// Initialize i18n with storefront language and preload modules
|
||||
(async function() {
|
||||
@@ -337,7 +337,7 @@
|
||||
</script>
|
||||
|
||||
{# 6. API Client #}
|
||||
<script src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
|
||||
|
||||
{# 7. Alpine.js with CDN fallback (deferred - loads last) #}
|
||||
<script>
|
||||
@@ -365,4 +365,4 @@
|
||||
<div id="toast-container" class="fixed bottom-4 right-4 z-50"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user