Skip to content

[Discussion] Is there a better way to use layouts with current LayoutDistributor #878

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

Closed
meteorlxy opened this issue Sep 27, 2018 · 8 comments

Comments

@meteorlxy
Copy link
Member

meteorlxy commented Sep 27, 2018

Version 1.0.0-alpha.1

Sorry for not obeying the issue template, but I just want to discuss the LayoutDistributor that introduced in v1.0.0-alpha, which seems like to be one of the major breaking changes.

This component is not complicated. What it does is changing the "root" component according to the layout option in frontmatter:

<template>
<component :is="layout"/>
</template>
<script>
export default {
computed: {
layout () {
if (this.$page.path) {
return this.$page.frontmatter.layout || 'Layout'
}
return 'NotFound'
}
}
}
</script>

When switching between pages with different layouts, the "root" component will be replaced by another one, i.e. current layout will be replaced totally by another layout.

However, different layouts may extend from a same parent-layout, so they have some parts in common, which should not be replaced "totally".

In a common scenario, one may want to add transition when switching between layouts, but the "root" component is replaced so no transition could be added.


In addition, LayoutDistributor also changed the way how default theme handle custom layouts. Following code is buggy / has no effect when working together with LayoutDistributor.

<div
class="custom-layout"
v-if="$page.frontmatter.layout"
>
<component :is="$page.frontmatter.layout"/>
</div>


In my opinion:

  • For theme makers: current LayoutDistributor could help them to set layouts without extra code. But the "old way" is more flexible and extendable, which may be preferred by theme makers.
  • For theme users / docs writers: no difference.
@sebiniemann
Copy link

sebiniemann commented Nov 16, 2018

Besides the already mentioned usage of this.$page.frontmatter.layout (which I fully agree on, as we also use something like <component :is="$site.themeConfig.layout"/> to handle different layouts -- as the current implementation of layout: enforces to much redundancy in common layout features), I would also like to point out the enforced layout specialisation of 404-Routes, which makes them behave quite differently from other pages.

if (this.$page.path) {
return this.$page.frontmatter.layout || 'Layout'
}
return 'NotFound'

Currently, VuePress seems to behave very odd in these cases, since the 404s content can already be specified using a 404.md file (however, its content is sadly not leveraged for actual 404 cases to my knowledge, but fully replaced by the layout NotFound.vue file):

// if the user does not have a custom 404.md, generate the theme's default
if (!ctx.pages.some(p => p.path === '/404.html')) {
await renderPage({ path: '/404.html' })
}

However, the current implementation also forces the user to use another (or duplicated) layout by requiring NotFound as layout in these cases.

Given the current option to specify a new layout on demand, It would seem to be more expected and flexible (in case no additional layout is wanted), to simply provide a 404.md to describe its content and choose the layout via the frontmatter options, in case a different layout is wanted.

Users with multiple sites and a common layout could greatly benefit from this separation of content and layout (as we already do for all other kind of pages), as the 404s content can freely be chosen for each individual page (for example to display different, useful links for a lost customer), instead of building a workaround by shifting all the content (e.g. as objects or HTML strings) into the config.js, to retrieve it within the layout file.

For example (based on the default 404):

404.md

---
title: 404 - Nothing to see here
layout: NotFound
---
# 404

> {{ getMsg() }}

[Take me home.](/)

<script>
const msgs = [
  `There's nothing here.`,
  `How did we get here?`,
  `That's a Four-Oh-Four.`,
  `Looks like we've got some broken links.`
]
export default {
  methods: {
    getMsg () {
      return msgs[Math.floor(Math.random() * msgs.length)]
    }
  }
}
</script>

and

NotFound.vue

<template>
  <div class="theme-container">
    <Content></Content>
  </div>
</template>

<script>
export default {}
</script>

@sebiniemann
Copy link

sebiniemann commented Nov 16, 2018

A quick fix to achieve the latter (to normalise the 404 handling) could be to change

return {
path: '',
frontmatter: {}
}

into

return pages.find(page => page.path.toLowerCase() === '/404.html');

And

if (this.$page.path) {
return this.$page.frontmatter.layout || 'Layout'
}
return 'NotFound'

could be simplified to (as I see no other reason for this.$page.path to be empty)

return this.$page.frontmatter.layout || 'Layout'

Given that the /404.html is always generated by

// if the user does not have a custom 404.md, generate the theme's default
if (!ctx.pages.some(p => p.path === '/404.html')) {
await renderPage({ path: '/404.html' })
}

, it is safe to assume that the /404.html is always existing.

However, it would add the breaking change that -- compared to now -- the 404.htmls content is actually used to present a file not found error (instead of ignoring it).

@lorisleiva
Copy link

Agreed, replacing the entire Layout feels weird to me as I'm used to have at least one common component to work with.

As a workaround I just don't use layout and use another keyword in my frontmatter, for example:

---
title: Home
use: HomeLayout
---

Then I have complete control over my layout distribution (even for pages that did not provide the option in the frontmatter).

computed: {
    layout () {
        if (this.$page.regularPath.startsWith('/articles')) {
            return 'ArticleLayout'
        }
        return this.$page.frontmatter.use || 'PageLayout'
    }
}

@meteorlxy
Copy link
Member Author

After 1.0.0-alpha.32, if the layout in frontmatter does not exist, it will fallback to Layout.vue.

So another workaround is to have only one Layout.vue in the layouts dir, so that all layouts will fallback to it. Then handle frontmatter.layout as the old way in 0.x

@lorisleiva
Copy link

Isn’t that the same as my previous comment?

@meteorlxy
Copy link
Member Author

meteorlxy commented Jan 29, 2019

@lorisleiva Similar but not the same, as you can use layout: HomeLayout but do not have to use another keyword in your frontmatter after 1.0.0-alpha.32.

If you are writing a theme which allow users to control the layout of some page, it's better not to introduce extra keyword for that. If not, they are the same indeed.

@lorisleiva
Copy link

Oh great! Sorry I didn’t get that from your previous message.

@ulivz
Copy link
Member

ulivz commented Feb 16, 2019

Support configure globalLayout(i.e. the old LayoutDistributor) from 1.0.0-alpha.38.

@ulivz ulivz closed this as completed Feb 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants