-
Notifications
You must be signed in to change notification settings - Fork 2
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
vue-meta 3.0 #19
Comments
now that we have control over the repo, let's improve it 💪 Have you thought about going for a completely separate block? Like: <template>
<div>
<pre>{{ user }}</pre>
</div>
</template>
<script>
export default {
async asyncData({ $axios, params }) {
const user = await $axios.$get(`https://.../api/users/${params.userId}`)
return { user }
}
}
</script>
<n-head>
<title>{{ user.name }}</title>
<meta key="description" name="description" :content="user.description">
</n-head> |
I was thinking also or using a separate block, and by looking at the example, I prefer it :) But I don't know how to do something like this, I guess it's a plugin for |
@atinux We can probably look at https://github.com/kazupon/vue-i18n-loader for an overview |
A webpack loader is required for handling the custom block. See https://vue-loader.vuejs.org/guide/custom-blocks.html#example P.S. There's no need to add prefix <head>
<title>{{ user.name }}</title>
<meta key="description" name="description" :content="user.description">
</head>
<template>
<div>
<pre>{{ user }}</pre>
</div>
</template>
<script>
export default {
async asyncData({ $axios, params }) {
const user = await $axios.$get(`https://.../api/users/${params.userId}`)
return { user }
}
}
</script> |
Thanks @znck for the clarifications :) The main issue I see here with |
Maybe we can take some features from headful as well? E.g. to have the possibility to add both |
@pimlie Definitely! We could make meta tag definitions way easier (and also incorporate changes from the meta package of https://github.com/nuxt-community/pwa-module) |
Another feature that would be nice to have, would be if this package provided (the tools for) a specific event for tracking meta changes after vue-router navigation. The problem with vue-meta is that the meta changes are asynchronously implemented after navigation. You can listen for the vue-meta.changed event, but that event doesnt always trigger as it only triggers after some meta changes happened. (Also it seems the name changed in vue-meta is slightly wrong, updated would be more accurate as it also fires if you update with exactly the same meta information). This would solve a small issue I have with nuxt-matomo where I would like to include the document.title in page tracking. |
A good resource of things to test against, if someone wanted to implement a test suite for a vue-meta replacement: https://github.com/joshbuchea/HEAD FWIW, the ease of ability to modify the head and meta tags for SSR is the primary reason I chose Nuxt in the first place as a platform over others. |
I think it would be a clean solution to have a separate component for the head just like the loader component. <template>
<head>
<title>{{head.title}}</title>
</head>
</template>
<script>
export default {
props: {
head: Object // Old head output of page components
}
}
</script> This requires nearly zero overhead. A default head component can be included like the loading indicator. But the head component can also be customized. It can be made that similar input is accepted like I do not think it's the page component's responsibility to render the head. It should supply some data at most. It is pretty important information regarding SEO. If a lot of people are working in a project, one (less experienced developer) should not easily be able to adjust head information. That's why I think nesting the |
<head>
<title v-if="user">{{ user.name }}</title>
<title v-else>{{ page.name }}</title>
<meta key="description" name="description" :content="user.description" v-if="user">
</head>
<template>
...
</template>
<script>
...
</script> I like this syntactically more than any other solution. |
Thanks for your comments :) @pimlie already started a refactor of vue-meta: https://github.com/nuxt/vue-meta/tree/next I believe we don't need I also suggest that for vue-meta 2.0, @pimlie what will be your vision for vue-meta 2 and the breaking changes your already have in mind? |
@atinux We should also probably decide whether we first want to release a refactored version within the vue-meta v1 branch (and maybe add deprecation warnings already if we have decided on those). Because in my mind vue-meta v2 will more be a rewrite then a refactor. ComponentsAs mentioned above we would like to use components so vue-meta eg doesnt need to render its own html. For this to work we would need to mount two additional root components for resp. head and body-scripts. As the head element doesnt have any possible container elements (except for maybe Here are some more considerations with regards to these proposed changes and new structure Considerations
Deep mergingBesides using components I would also like to remove the need for deep-merging. A possible solution for this is to just dont merge ourselves but provide a callback so the user can implement different strategies for which value needs to be set at the time a change occurs. A proof-of-concept for this functionality is here where Status / feedbackCurious to find out what you think about these suggestions! Do you see any caveats with the suggestions above? Please let us know! |
@pimlie |
If I may. The largest problem I had with vue-meta is copy paste. Plenty of copy paste. I had to configure meta tags for every social network (twitter is the most painful) three times in 12 months. Is this issue a right place to ask a configuration option like this:
and then it just works... (sorry, I might don't know what I'm talking about) |
@koresar I know what you mean. 🙈 If you have any ideas to improve, please add them here or in the vue-meta repo |
There is also always a need for structured data from Schema.org, it is really annoying to be writing the complete structure every time you need it, for example a website with blog, cooking recipes, author blog pages and chefs where each one of those has a similar structure but changing slightly in all of them, so I think it would be a good idea to be able to generate labels in the head from components that the developer believes for his project. Another situation in which these components can be used is when you start having data repetition through different labels, such as having the standard canonical url plus the facebook URL, plus the one from {'@type': 'WebSite'} of Schema.org All this also would have to use deepmerge and added as when you have several images for og: image or you want to change the description for only one of the nested objects inside the schema ld + json Currently in my team we do this manually based on a template with a nested Map that searches and adds or modifies based on point syntax. |
seconding the need for a ready event (or observable attribute) following a navigation event; to trigger once all defined attributes from all nested views !== undefined. Our use case would be primarily async attributes for SSR. |
While I like the idea of a template as mentioned by various contributors above - the format looks good and for the most part says what it is (noted it doesn't include everything contained in the In my experience the meta tags implemented across pages rarely change (only the values they contain), except perhaps for the addition / exclusion of some schema.org style tags defined by content type, etc. Because of this the format is likely to be overwhelmingly redundant across components, save for a variable changed here or there. Also important (and as mentioned by @koresar), the same value is more often than not shared across multiple meta tags. So Finally, would I be wrong in suggesting that meta data, at least in the head of the document, is generally tied explicitly to the url? Of course there are edge cases, say games running in the title, or applications where state isn't necessarily reflected by url ... I'm sure accessibility could be argued. But in browser and search-engine land, it feels like the url should be the default case for defining distinct meta states... ... Given the above, could meta tags be more closely tied to the router than a component - controlled by stateful data (e.g. Vuex) or events? |
@koresar Agreed, re-using titles and descriptions for eg @cesasol Could you maybe share the implementation you currently use? I am also interested to add this (eg breadcrumbs are also a nice use-case), so having an idea what currently works for you might be helpful :) @theprojectsomething You might want to take a look at the release candidate for vue-meta 2.0, it adds a Although at the moment vue-meta only supports updating using a component property, that prop is only required on one of all your components. Eg you can easily implement that property only on your root component and have it return stateful data from Vuex or from some flow of events. Although it might be nicer to support that directly in vue-meta (and we will certainly have a look at that), I think your suggestion is already possible for 99% 😄 |
For vue-meta v3 I think it would be interesting to move to a mono-repository consisting of the following packages (but list is subjected to new insights 😄 )
|
I will actually rename |
Actually I am thinking about / trying to come up with a more pluggable configuration (eg looking at webpack for inspiration). I would like to be able to split components per type eg:
If you load all those pkgs then you should be able to use either one of these: <meta name="description" content="description">
<html-description value="description">
<og-description value="description">
<twitter-description value="description">
<!-- // and in preparation for Vue v3 -->
<description value="description" html="true" og="true" twitter="true"> But adding an abstraction on top of html components like this is probably only useful when we will also use Vue to render them (at least client side). And we have take this into account before we decide on that: nuxt/vue-meta#394 --edit-- |
@pimlie what is the advantage of splitting it like that? It adds another unnecessary layer of complexity imo. Imagine adding meta tags for a few things and then realizing you need another import to handle twitter, etc...how annoying would that be? |
@connecteev fair point, as often the main reason would be separation of concerns. E.g. we could still provide a E.g. the main thing I am still struggling with is how to use those tags. There is an initial pr for using a custom-block in vue-loader so you can write a Would appreciate some feedback on that last part ;) |
I agree with this. The more we deviate from vue (after all, nuxt is a vue framework), the worse it gets because developers have to now learn 2 formats - one for vue, and one for how nuxt interprets it. So yes, the closer we get to the vue conventions (and push the vue community to adopt better conventions if the current ones dont make sense), the better off everyone will be. |
It would be really nice to have access to the current metas in plugins and not just a ref to the root instance. We've had to resort to a mixin that is used between layouts and page comps and it would really be nice to separate some of it's logic into a plugin. @pimlie :) |
What exactly do you mean with that? Can you show an example of the api you need? |
Right now in the context object you get app.head. The problem with this is, if you do anything with it you are changing the root vue instance. If I had a server side plugin that did app.head.meta.push({ name: 'viewport', content: 'width=device-width, initial-scale=1' }). What happens in prod mode, is that tag gets added to the stack on every hit. So after 10 visits to the website, you have 10 viewport tags instead of one. It would be great if either a) app.head was a clone of the root metas and was created on every request (like it acts in dev mode) or b) there was another property or method passed to the context object that is the current metas for the hit that's getting rendered. |
@pimlie does that make sense? |
Hello people. Replying to this:
I have this, probably crazy radical, idea. Some of you might also had it. Where the idea is coming from.Recently I had to refactor a configuration-driven backend system. The huge problem with it is that any configuration (or similar DSL) is not scaleable, hard to maintain. If there is a lot of configuration then your system is screwed. As part of that refactoring I replaced massive 100-1000s config files with 10 lines of JavaScript each. As the result the configuration shrunk from ~2500 lines of code to ~270. Significant improvement. Isn't it? So here is why I came to the following conclusion - the "functional" way is always better than any DSL. Examples:
The ideaThe Then why are we trying to render HTML tags with config JSON (essentially a DSL) rather than functions? I envision A Nuxt.js page: <template>
<Whatever/>
</template>
<script>
export default {
name: "MyPage",
head() {
return (
<MyHead main-title="Example.com website">
<SeoMeta :twitter="true" :og="true" :facebook="true" content="My example site page" />
</MyHead>
);
};
</script>
<style scoped>
</style> Or, alternatively, to avoid some repetitive code it can be like this Same Nuxt.js page: <template>
<Whatever/>
</template>
<head main-title="Example.com website">
<SeoMeta :twitter="true" :og="true" :facebook="true" content="My example site page" />
</head>
<script>
export default {
name: "MyPage"
};
</script>
<style scoped>
</style> You see where I'm going? Similar things already exist in React world (I've just googled them). |
@hecktarzuli Thanks for explaining. It makes sense partially I guess, but I think its beyond the scope of vue-meta to facilitate any user pattern. The issue you are describing seems to be caused by Vue's @koresar Thanks for the nice idea! I've made a (now stale, so you couldnt know unfortunately) proof of concept PR that does this more or less: nuxt/vue-meta#392 |
Hi! I'm the author of headful (npm) and vue-headful (npm). I only just found this issue now even though headful was already mentioned in this issue. I appreciate that you are considering to implement features from it :) FWIW, I consider deprecating vue-headful in favour of vue-meta. |
Inspired by this conversation, I just published the first version of import { useHead } from '@baleada/vue-features'
import { useRoute } from 'vue-router'
import { context } from '/path/to/global-store'
export default {
setup () {
const route = useRoute()
useHead({
title: computed(() => context.article.frontMatter?.title ?? SITE_NAME),
metas: [
{
property: 'og:title',
content: computed(() => context.article.frontMatter?.title ?? SITE_NAME)
},
{
property: 'og:description',
content: computed(() => context.article.frontMatter?.summary ?? '')
},
{
property: 'og:image',
content: computed(() => context.article.frontMatter?.image ?? '')
},
{
property: 'og:url',
content: computed(() => route.fullPath),
},
]
})
}
} Under the hood, I had to reimplement a few Vue features:
As others in this issue have noted, those actions are easily available when you're working with a Vue template, but they're not accessible from inside composition functions. I have a feature request open in the Vue 3 repo asking the Vue team to give us lower-level APIs for patching DOM attributes and creating nodes in the Virtual DOM, then relying on Vue to schedule renderer updates efficiently. In the meantime, I wrote a bunch of composition-function-friendly reimplementations of Vue template features like This experience has convinced me that composition functions are the right choice for If anyone wants to use |
Thank you @AlexVipond I would love to see a repo even without docs |
@atinux Sure thing! Here's the source for the now-published Note that only I haven't deeply explored server-side rendering with this composition function, so I'm unsure of how fully it solves SEO in Vue. |
Related: the API in vue-head feels perfect to me because it so closely resembles plain HTML (much like the original proposal): <template>
<Head>
<title>Hello Vue</title>
<meta name="description" content="Do you like it?" />
</Head>
</template>
<script>
import { Head } from '@egoist/vue-head'
export default {
components: {
Head,
},
}
</script> It also makes simple work of controling the HTML generated by SSR output: import { createApp, h, Fragment } from 'vue'
import { renderToString } from '@vue/server-renderer'
const app = createApp()
const appHTML = await renderToString(app)
const headHTML = await renderToString(
h(Fragment, app.config.globalProperties.$head.headTags)
)
const finalHTML = `
<html>
<head>${headHTML}</head>
<body>${appHTML}</body>
</html>
`
What about the following API so everything feels close to plain HTML? <template>
<Html lang="en"/>
<Head>
<title>Hello Vue</title>
<meta name="description" content="Do you like it?" />
</Head>
<Body class="foo"/>
</template> |
We discussed about something like this for Nuxt 3 with @pi0, we are going to explore and keep this thread updated as soon as we have something |
per @znck comment above regarding webpack, can we please ensure that vite is also considered from a plugin perspective. |
Introducing
@nuxt/vue-app-head
to replace vue-meta.Issues of
vue-meta
1.0vue-meta
is created by Declan Dewet who cannot maintain this project anymore, I took over 2 years ago after helping him so I could maintain it for Nuxt.The Nuxt team has no push access for itWe now have!vue-meta 2.0
Will be simply a refactor of the actual one with optimisations and bug fixes.
vue-meta 3.0
vue-meta
is the default package used by@nuxt/vue-app
but it could be disabled by another module (introducing the way to work withhooks
with these external modules).I believe we could introduce a new component:
<n-head>
Example (
pages/users/[userId].vue
):The point of this component is simply a shortcut to write inside the
head
key (we should keep this key to let librairies likeVuetify
add mixins for it when used with Nuxt).Here,
key
is simply thevmid
we have inhead
, I believe by using a functional component, we could achieve easily this behaviour.This
<n-head>
should have some props to handlebody-attrs
andhtml-attrs
, abouthead-attrs
, well, it's all the others non-defined props directly :)What do you think?
The text was updated successfully, but these errors were encountered: