Vite Plugin SSR - critical style generation (VUE.js) - vue.js

I'm using vite-plugin-ssr + vue-router and I want to implement critical styling.
My project has a pages folder which has the following structure:
pages/SomePage/index.vue
<template>
<div class="TestPage">
TestPage Content
</div>
</template>
<script>
export default {
name: "TestPage"
}
</script>
<style lang="css" src="./critical.css"/>
pages/SomePage/critical.css
.TestPage {
background: red;
}
I want the styles from the critical.css file to go into the <head/> tag as internal ~ <style> .SomePage {background: blue;} </style> for each route.
I tried to get inspired by the rollup-plugin-critical idea, but my knowledge is apparently not enough, because a positive result could not be achieved.
How can this be implemented?

Related

Vue.js (v3): How to have a unique data-v-* hash for each component instance

I have the following code:
blah-foo.vue:
<template>
<div>Hello {{ name }}</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
props: {
name: {
type: String,
}
},
});
</script>
<style scoped>
div {
color: white;
}
</style>
and App.vue:
<template>
<blah-foo
name="Alice"
></blah-foo>
<blah-foo
name="Bob"
></blah-foo>
</template>
The result in my browser is the following:
<div data-v-73bdd40c>Hello Alice</div>
<div data-v-73bdd40c>Hello Bob</div>
Is there any way I could tell the vue loader to generate an unique data-v-* attribute for each of them ?
What is happening in production is that since the component blah-foo is called on many different lazy-loaded pages, I end up having many times
div[data-v-73bdd40c] {
color: white;
}
all overriding each other.
That isn't a problem in itself, it just does seem very disgraceful (code wise) after having loaded a few pages when inspecting elements.
That is not possible with vue-loader. And it shouldn't be done anyway.
The whole point of the data-v-xxx attribute is to identify the components in the DOM so vue can apply to them the correct scoped css.
If it ever applied uniq data-v attributes, it would not be able to apply the correct css.
I understand your problem is that, on production, you see in the css inspector several times the css code, right?
My guess is that it's related with sourcemaps, which may mess with the css inspector. But I can't help more without additional details on your project configurations.
Even if your component is used on several pages, its module will be fetched and loaded only once. You don't have the scoped css loaded several times, it's just an inspector problem.

Rendering nested components

I am starting to learn VueJS by building a simple website.
Right now I have made three components:
Header
Navigation
Topbar
I want to render navigation and topbar inside header, so I can call the header component inside every page (I haven't found a way to make a "layout" or something, that every page uses).
Header.vue
<template>
<header id="topNav">
<topbar />
<navigation />
</header>
</template>
<script lang="ts">
import Vue from 'vue'
import navigation from '../navigation/navigation.vue'
import Topbar from '../topbar/topbar.vue'
export default Vue.extend({
components: { Topbar, navigation },
})
</script>
<style scoped>
#topNav {
background-color: #fff;
-webkit-box-shadow: 0 0 35px 0 rgb(154 161 171 / 15%);
box-shadow: 0 0 35px 0 rgb(154 161 171 / 15%);
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 1001;
padding: 0 12px;
}
</style>
Topbar
<template>
<div id="topBar">
<h2>this is the topbar</h2>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({})
</script>
<style scoped>
#topBar {
border-bottom: 1px solid #dee2e6;
height: 70px;
padding: 0 10px;
z-index: 100;
position: fixed;
left: 0;
right: 0;
}
</style>
And when I call it on for example my home page (index.vue) nothing renders.
<template>
<main>
<header />
<div id="page-content">
<h1>Home</h1>
</div>
</main>
</template>
<script lang="ts">
import Vue from 'vue'
import Header from '../components/header/header.vue'
export default Vue.extend({
name: 'Home',
components: { Header },
})
</script>
I've tried reading the documentation and search around, but haven't been able to figure out what I'm doing wrong.
Since the question is tagged with a Nuxt tag, I'll recommend looking into Nuxt layouts: https://nuxtjs.org/docs/concepts/views#layouts
There is a default named default that you could use by creating the file /layouts/default.vue and passing the components inside of it.
You can of course change that with a layout: 'yolo' if you want another 'yolo` layout.
Pro tip: you don't need to import the components yourself in Nuxt.
You have named your component header, which is a standard html element. Therefore the browser will probably just try to render a standard <header> element instead of your component.
Therefore it is advised to always use multi word component names. See docs here. You can use eslint in your code editor to help you spot these mistakes.
PS: if you are learning vue from the start, I would advise you to use the composition api with the script setup approach, as it makes things easier and provide the opportunity to write clearer code as components grow.

Serving SVG content from static/assets folder in Nuxt

I have a lot of SVGs in my site and they have lots of paths so I don't want to clutter my code with them, but display the full code in the browser.
In PHP there's a magic function called file_get_contents('path')
Is there an alternative to this in Nuxt? So far the only option is to serve it as a regular img tag which prohibits all styling.
Check out the #nuxtjs/svg module (NPM). Using that module you can import your svgs in your script and use them like components in your template.
<template>
<NuxtLogo class="logo" />
</template>
<script>
import NuxtLogo from "~/assets/nuxt.svg?inline";
export default {
components: { NuxtLogo },
};
</script>
<style scoped>
.logo {
fill: #fff;
}
</style>

Rendering a vue2leaflet map in jsfiddle via CDN

I am trying to render a Leaflet map using Vue2Leaflet in a jsfiddle so I can get help with a specific problem but I can't even get it to render properly in the trivial case. I have already looked up how to load libraries via CDN in jsfiddle and a far as I can tell, I am doing it right. There are also no errors in the console. But the map will not render.
This is the jsfiddle: https://jsfiddle.net/iboates/bywzgf1q/3/
Vue2Leaflet also requires the vue-client-only library. I have it working in my local codebase but maybe it has something to do with why it isn't working in jsfiddle. I am also loading this library via a CDN.
I've also looked at other jsfiddles using Vue with custom libraries loaded via CDN and I don't see what is being done differently.
Apparently StackOverflow requires that a jsfiddle link requires code in the post as well, which is a bit weird to me since the code is literally contained in a link which can also execute it, but here it goes:
HTML:
<div id="app">
<client-only>
<l-map id="map" ref="map">
<l-tile-layer
:attribution="'x'"
:url="'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'"
/>
<!-- <l-geo-json
:geojson="geojsonData"
/> -->
</l-map>
</client-only>
</div>
JS:
import { LMap, LTileLayer } from "./vue2-leaflet";
new Vue({
el: "#app",
components: {
LMap,
LTileLayer,
ClientOnly
},
data: {
geojsonData: {
type: "FeatureCollection",
features: []
}
},
mounted() {
}
})
CSS
#app {
background-color: black;
height: 500px;
width: 500px;
}
#map {
height: 500px;
width: 500px;
}
There are multiple errors in your fiddle.
First of all, Vue is not installed. In addition, you try to import Vue-Leaflet like in a Webpack / Rollup build system and not the way you can use it from a CDN.
To begin with, install your CDN (Vue, Leaflet CSS, Leaflet js, Vue-Leaflet):
https://cdn.jsdelivr.net/npm/vue#2.6.12/dist/vue.js
https://unpkg.com/leaflet#1.7.1/dist/leaflet.css
https://unpkg.com/leaflet#1.7.1/dist/leaflet.js
https://unpkg.com/vue2-leaflet#2.6.0/dist/vue2-leaflet.min.js
Then, add your components the CDN-way (check official documentation: https://vue2-leaflet.netlify.app/quickstart/#if-imported-by-cdn
):
components: {
'l-map': window.Vue2Leaflet.LMap,
'l-tile-layer': window.Vue2Leaflet.LTileLayer
}
Check working fiddle: https://jsfiddle.net/scpta4jq/1/

What does the name: 'app' line do in this Vue.js CLI code?

What function does the name: 'app' have in the following Vue.js CLI code?
I understand that the export default is used for
"creating JavaScript modules to export functions, objects, or
primitive values from the module so they can be used by other programs
with the import statement" (docs)
and I understand the module to be what is in the template element, but I don't see where it is being imported.
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Nor do I see what name: 'app' does exactly since if I change the name, the Vue.js. code still works, until when you change this in non-CLI Vue.js, e.g.
new Vue({
el: '#app',
...
});
If you change the '#app' then the code won't reference the element and it won't work anymore.
The name Attribute of a Vue component is not really required in projects generated with the vue cli webpack template. In your case, the component name is set elsewhere (since you use vue-router: in src/router/index.js), hence it is not technically required in the export default of the *.vue file.
If you generated your vue cli webpack template without vue-router you wouldn't have to supply the component name either, because your <script> block would look like this:
<script>
import Hello from './components/Hello'
export default {
...
components: {
Hello
}
}
</script>
The components: {Hello} is a shorthand for components: {'Hello': Hello} in EcmaScript 6. This is where the component name would be set in this case.
That being said, setting the component name in the export default object of your *.vue file is considered good style, because your components will be named consistently across the project and thus be easier to debug. If you changed the import Hello from .... to something like import HelloComponent from .... it would show up as HelloComponent in things like vue-devtools and warning messages. You would also now have to reference it as <hello-component></hello-component> in your <template>. Since you want your component names to be consistent, especially when writing components that you intend to be reusable, you should set the name property in your vue component once and for all.