Open samanthaming opened 4 years ago
Thanks for filing this @samanthaming! We really appreciate the time and effort you took to investigate this and will add this our list of items to discuss for our roadmap! Would you be interested in code pairing sometime so I can get a better understanding of the problem and your current solution?
Thanks @bencodezen for looking into this π Absolutely! I'll reach out to you and we can set something up π
To follow up on @samanthaming's comment, I also wrote a custom plugin that injects metadata into the ssrTemplate
- would be glad to share, if helpful.
@adamdehaven wooo you have a custom plugin π€© is it an open source that you can provide a link to? Would love to check it out π
@samanthaming I haven't released it yet, but plan to (still doing a little testing). I just redesigned my site with VuePress as well and am getting ready to push the site itself live in the coming days.
The plugin I wrote injects the canonical URL, Schema.org structured data, as well as extra metadata into the ssrTemplate during the build process (so the tags will not be present when running npm run docs:dev
)
@bencodezen I may (huge question mark here) have found a somewhat easy way to implement part of the ask here (canonical URL tag), which could likely be expanded to allow for dynamic injection.
If the check for headerType
was updated in vuepress/packages/@vuepress/core/lib/client/root-mixins/updateMeta.js
as shown below, I believe the canonical URL could be injected along with the rest of the metadata in the frontmatter
.
// vuepress/packages/@vuepress/core/lib/client/root-mixins/updateMeta.js
export default {
// created will be called on both client and ssr
created () {
this.siteMeta = this.$site.headTags
// .filter(([headerType]) => headerType === 'meta') // OLD
.filter(([headerType]) => ['meta', 'link'].includes(headerType)) // NEW: allows for 'meta' and 'link' tags
.map(([_, headerValue]) => headerValue)
// ... more code
Then, in a page's frontmatter:
---
# Defining page-level metadata - unchanged (goes back to original request to allow for dynamic metadata)
meta:
- name: description
content: Page description text
# Defining other link data (although the requested link in Issues is currently canonical URL)
link:
- href: https://www.example.com/canonical/url/link/
rel: canonical
---
This still requires the values be hard-coded into the frontmatter; however, is an easy way to expand the type of tag that can be injected via frontmatter.
The problem with other canonical URL solutions (as well as injecting metadata) is that they only load for the current page and then are not updated dynamically (as in the build process, not client-side) as the pages change. The correct URLs display in the /dist/
directory pages after build; however, the VuePress SPA never refreshes the attributes when moving from page to page. As an example, the code below probably looks familiar:
created() {
if (typeof this.$ssrContext !== "undefined") {
this.$ssrContext.userHeadTags += `\n <link rel="canonical" href="${this.computeURL()}"/>`;
}
}
This only loads the proper URL (or other content) for the first-requested page. Any subsequent navigation within the VuePress SPA does not update the properties, even if you watch $page
or any of the other hacks that are out there.
@adamdehaven Great research! Would you be interested in opening up a PR on this?
In the long run, we probably need to find a way to integrate vue-meta as its API is fairly ubiquitous in the Vue community these days. Hoping to get to this at some point, but your solution seems to be a great way to open up possibilities in the meantime. Let me know what you think!
@bencodezen integrating vue-meta
would be great, but I'm not familiar what all it allows you to do... I'll have to take a look.
I should be able to submit a PR; however, I'm traveling and it may be next week.
Question: would implementing the way I suggested above also open up the properties to the extendPageData
method in the Option API? If yes, then I'll probably create the PR to also include script
tags, which will also allow for injecting Schema.org structured data into the page as well! π
If I allow script tags as well, and wanted to include JSON as the content (instead of adding an attribute) how would this be structured in YAML?
@adamdehaven No worries as far as urgency. In the event I manage to get a head start, I'll be sure to update the thread so you're aware π
In regards to your question, I inherited the project after its release, so I'm still learning the ins and outs of the codebase. If you discover the answer to this before I do, would love to get your findings documented so other people have an easier time contributing in the future!
Started my cursory overview of this and had a few quick questions here:
Would we want to implement vue-meta
here first, or come up with a stopgap?
If moving to vue-meta
is the answer, do we try and incorporate its API into our existing frontmatter, or do we push to use vue-meta
and introduce breaking changes?
If the answer isn't vue-meta, then would we try to implement our own API? I'm not too keen on this one as it seems to reinvent the wheel with the added bonus maintenance for the VuePress team
Any and all feedback/dialogue is welcome!
@d-pollard I think implementing vue-meta
may be the way to go; however, the big piece here is to allow that data to be available in the DOM immediately (as is the case with current meta info) instead of it having to render along with the rest of the Vue components. I have a custom plugin I wrote (similar to @samanthaming) where I dynamically inject the metadata; however, the vue-meta
implementation wouldn't account for canonical URLs, structured data, or as I understand how it would work with VuePress, allowing for the tags to be available for SEO purposes (e.g. site scrapers, robots, etc.)
@adamdehaven - thanks for the feedback
From what I've read on vue-meta
, it seems like they come with SSR support so I believe it'd be available straight away in the DOM
I'm concerned, however, with how we'd introduce vue-meta
then, since we already have meta information coming from frontmatter, would we try to build on top of that API with vue-meta (something I'm not sure is possible) or would we move the meta information away from frontmatter and try to use a special component that binds to vue-meta?
@d-pollard so just thinking out loud, but here goes:
What if VuePress included vue-meta
available to turn on/off via a setting in the .vuepress/config.js
?
extendPageData
method, again, as is currently supported via the VuePress Options API.vue-meta
integration is turned on, then you could go a couple routes:
vue-meta
metaInfo
object IS defined by the user, it would take precedence. Any meta attributes could be defined on the app level, or individual page level, with the more-specific (e.g. page) level taking precedence.vue-meta
metaInfo
object is NOT defined by the user, then it could still be automatically used by VuePress under the hood, but instead of looking for the metaInfo
object provided by the user, the values could be mapped to the the classic frontmatter
meta properties, if they are set (some or all) on the app/page level. This should happen after all VuePress Option API methods are complete (e.g. if the user is utilizing the extendPageData
method) to allow the user to still be able to dynamically customize values, etc.If VuePress does do something to this effect, I strongly plead for a way to also include a page's canonical URL
in the frontmatter so that it can be injected directly into each page during SSR so it's available in the DOM (and, this would hopefully be compatible if using the VuePress Blog Plugin as well). With AMP pages, article syndication (e.g. Medium, Dev.to, etc.) the canonical URL tag is super-important. I realize this would likely be a separate feature/PR, I believe it could likely be integrated in a similar fashion to what is trying to be solved for here π
Edit: spelling is hard.
@adamdehaven - since the canonical bit was a quick win here, I've opened this PR: https://github.com/vuejs/vuepress/pull/2658
Mind reviewing it and giving your thoughts for me?
~@d-pollard from what I've tested so far, PR #2658 does not work. The canonical
link tag is added to the first page that contains the corresponding frontmatter
entry; however, the value of the link
tag does not update when the route changes.~
~For example, using the official @vuepress/plugin-blog
on my site, when I add the changes in the PR, the canonical URL is correct on the first page /about/
but does not update on route change to /blog/post-slug/
.~
~(Same behavior occurs without using the blog plugin as well)~
Update: Moving comments to PR #2658
@adamdehaven - my apologies, I forgot to commit my most recent change; please try again with the newest changes
@d-pollard moving the PR conversation to #2658 thread π
@adamdehaven @samanthaming
This PR introduces Vue Meta: https://github.com/vuejs/vuepress/pull/2661
it is a backwards compatible replacement for current setup, please add any questions or concerns in that thread
Feature request
Currently the way to set custom metadata is through the frontmatter in the markdown file. It'd be very helpful if we could set this on the component level.
The Problem
Here's a scenario of why this is important. I use Cloudinary to store my images and I use a gulp task to upload the images. Which then populates a single javascript object with all of the ID of the images. These are the images I want to use in my meta head (especially for my social media links on twitter and facebook). And I really don't want to set them manually in each of my markdown frontmatter.
Ideal Solution
It'd be so nice, if I could set the meta head directly in my layout or component. Something like vue-meta from Nuxt.
Alternative Solution
Unfortunately, I had trouble trying to figure out how to set the
vue-meta
plugin to work with VuePress via the SSR way. So instead I created a custom plugin that will programmatically set the frontmatter withfrontmatter.meta
. It's working in my dev environment, however, when the site is built, it then has duplicates (I just found this out and have not looked into it yet, when I find out why, I'll update this issue).Conclusion
I know VuePress is mainly built for technical documentation. But for those using it for more than that, I think meta data is a super important feature. I used VuePress to build my blog and I picked a static site generator for SEO reason. And meta data is an integral part of that.
Overall I loved using VuePress to build my new site (the markdown support and the built-in header search are amazing!). All that's missing is this meta data support. Not trying to compare VuePress to others, but Gridsome offers this. And it's probably one of the feature that might make me want to switch over.
Anyways, I think the team did a fabulous job on this. I hope you will consider this feature! π