Server Side Rendering and Templating (puppeteer alternatives) #576
Replies: 8 comments
-
Coming out of #580 , some additional thoughts
|
Beta Was this translation helpful? Give feedback.
-
Also as part of #580 , need to find a way to statically render templates because without pre-rendering, GraphQL wont run and so If this could also be used as a fallback for simple navigation, that would be great too. |
Beta Was this translation helpful? Give feedback.
-
Opened a PR to upgrade the website to |
Beta Was this translation helpful? Give feedback.
-
Pretty cool tweet of zero JS GraphQL |
Beta Was this translation helpful? Give feedback.
-
I think having / keeping pre-rendering as an option / alternative to an SSR solution could be nice, or that others can piggy back off of Greenwood and also have access to that kind of utility (thinking #574 ). Or as the default until further advancements in SSR can be achieved and proved confidently first. For those reasons, it might nice to make sure we investigate / document / implement being able to leverage puppeteer-core as an alternative? Or at least in a way that only invokes the Chromium dependency / download in a JiT way? Case in point, from our quick start # with NodeJS already installed
# create a pages directory for your content
$ mkdir -p src/pages
# create an index.md file as your home page
$ echo "## hello world" > src/pages/index.md
# run one of Greenwood's commands, and that's it!
$ npx @greenwood/cli develop You still have to download Chromium when there is nothing to even pre-render. 😞 npx @greenwood/cli develop
Downloading Chromium r818858 - 132.4 Mb [====================] 100% 0.0s
-------------------------------------------------------
Welcome to Greenwood (v0.15.2) ♻️
-------------------------------------------------------
Running Greenwood with the develop command.
Initializing project config
Initializing project workspace contexts
Generating graph of workspace files...
Started local development server at localhost:1984 That said, puppeteer is also cool because we can provide cool stuff like this! Another thought with puppeteer is we may want to consider implementing a queue to avoid trying to run hundreds of pages at once and totally overwhelming a user's computer resources. Probably with a reasonable default that can be overridden by the user via greenwood.config.js |
Beta Was this translation helpful? Give feedback.
-
OK, now that I've some time to think on this and play around with a couple experiments
Will break this down as such for our next project which should hopefully get us to a place where can probably close this Discussion out
|
Beta Was this translation helpful? Give feedback.
-
On the templating side for purely markdown projects, I wonder if we can support some sort of react helmet like solution? Or somehow inject variables into markdown? I guess this is effectively what a templating language would do, but would like to keep this all to just the realm of markdown. Maybe something in the vein of react-helmet for Greenwood? Current viable to grab frontmatter from a markdown is through the graph and. pre-render that out with puppeteer and grab all that custom metadata out of the graph via JavaScript. // https://github.com/thegreenhouseio/www.thegreenhouse.io/blob/3.2.0/src/templates/post.html
<!DOCTYPE html>
<html lang="en" prefix="og:http://ogp.me/ns#">
<head>
<script type="module" src="../components/blog-post/blog-post.js" data-gwd-opt="static"></script>
<link rel="stylesheet" href="../styles/post-details.css"></script>
</head>
<body>
<app-blog-post>
<article slot="contentoutlet">
<content-outlet></content-outlet>
</article>
</app-blog-post>
</body>
</html> // https://github.com/thegreenhouseio/www.thegreenhouse.io/blob/3.2.0/src/components/blog-post/blog-post.js
import { css, html, LitElement } from 'lit-element';
import client from '@greenwood/plugin-graphql/core/client';
import GraphQuery from '../../queries/graph.gql';
class BlogPostComponent extends LitElement {
constructor() {
super();
this.post = {
title: '',
data: {
date: '',
image: ''
}
};
}
static get properties() {
return {
post: {
type: Object
}
};
}
async connectedCallback() {
super.connectedCallback();
const currentRoute = window.location.pathname;
const response = await client.query({
query: GraphQuery
});
this.post = response.data.graph.filter((page) => {
return page.route.lastIndexOf(currentRoute) >= 0;
})[0];
}
render() {
const { title } = this.post;
const { date, image } = this.post.data;
const headerBackgroundStyle = image ? `background-image: url("${image}")` : '';
return html`
<div class="header" style="${headerBackgroundStyle}">
<h1 class="title">${title}</h1>
<h5 class="date">Published: ${date}</h5>
</div>
<slot name="contentoutlet" slot="postoutlet"></slot>
`;
}
}
customElements.define('app-blog-post', BlogPostComponent); But, seems to work well? I guess the challenge here it improve upon this workflow while still keeping to HTML / md. 🤔 |
Beta Was this translation helpful? Give feedback.
-
This has been released and more features are in progress! 🎉 |
Beta Was this translation helpful? Give feedback.
-
Overview
Right now, the current solution for generating HTML from a Web Component customElement / JavaScript, is running the user’s site page by page through puppeteer. This is the default behavior. As detailed in the Discussion, the goal would be to do a more traditional SSR technique where the HTML as a string would be passed to some sort of function that could execute JavaScript and spit out the needed HTML representation. (e.g.
ReactDOMServer.renderToString
). You could also compile it down, like Svelte.Ideally, the same tool would also be providing “hydration markers” so that if / when client side JavaScript loads that maps to that generated HTML (e.g. a customElement), that JavaScript would be smart enough to bootstrap itself from that HTML, check if it matches its initial state, and “hydrates” from that. This can be extremely valuable so that additional initialization work doesn’t have to happen twice, which is important for performance and user experience.
Additionally, then in theory it would be possible to SSR in development too? 👀 🤔
Thoughts
To carry this through, Greenwood should then be able to support additional pre-renderers. This would open the door for additional pre-rendering tools like the new experimental lit-labs/ssr package, which has tools not only for Lit projects, but also vanilla web components or koa middleware. In this way, projects could choose any pre-renderer, for SSG or true SSR and helping provide additional options not as heavy as puppeteer.
ssr
, which we could then allow users to run Greenwood as a true service, or even in a serverless function!Ultimately, with this we may be able to switch to something less “hammer” like as Puppeteer, since Puppeteer involves downloading and running a Chromium instance, which is both heavy from a dependency perspective, but also from a build time perspective. Although Greenwood supports opting out of pre-rendering if all your content is HTML / markdown based, being able to render HTML on the fly yields incredible opportunities to template JavaScript at build time as well, which means excess JavaScript can be eliminated from being shipped entirely!
Additionally, I suppose this would be a good time to explore an SSR "mode", wherein Next or Nuxt, you can basically run Greenwood as a process on a server to just be doing SSR "full time". Which I suppose would be like
yarn serve
but with all the develop middlewares enabled like when runningyarn develop
? 🤔Beta Was this translation helpful? Give feedback.
All reactions