-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Feature request: ability to generate a static website #2299
Comments
@ExNG You are right, Vue renders page in browser - what static generators do is that they just run app, virtually visit selected routes and save rendered output. Then you can serve prerendered pages and let javascript take over that later when it loads. Hexo can do that (used for Quasar docs), Vue-press, Vue-prerender plugin etc.. |
@ExNG @panstromek I'm here talking about how Nust generate(prerender) a static website, it will not affect at all how Quasar currently works, it just an extra feature(maybe mode) that makes Quasar in my opinion "complete solution". It just extra feature/mode so if you don't need it then don't use it, however regarding all the available tools(like Vue-press) that generate(prerender) a static website it is recommended that your website must not contain that much pages(I believe several hundred is ok) however it depends on the system specifications that you will generate the static files on, from its name it is good for "static websites". |
Quasar is very responsive by design. It takes into account platform, screen size and more. When you prerender / create a static website you can't know beforehand anything about the client who will consume the page and that's a problem. We've just launched SSR. Let's take it step by step. At some point I'll find a solution for this too. But don't expect it to be too soon as the focus will gradually become shipping v1.0. |
@rstoenescu cool ;) It would definitely be nice - especially with latest shift to JAMstack and sites that are heavily prerendered. JAMstack philosophy is actually against SSR. |
Now I am not sure if I am talking about same subject - is this considered the same as what prerender SPA plugin does? There used to be a page in docs for that and I guess it should still work.. I need to try. Or is this about generating static-only (no SPA) website like hexo does? Another point is that Vue-press generate all routes but still runs as SPA then, so it is the first case. |
Nuxt generate feature works like Hexo |
I would really appreciate features like gatsby or react-static . pre-rendered and then take over by spa. |
@asarkar1990 I think this is done by that Vue-press also does it. They do it by leveraging SSR which I think is the way to go in Quasar case, too. They simply run SSR server, visit each route and save the output. In other words client can't recognize if the app uses SSR or is built statically. I think that with some custom code you could already do similar thing with Quasar right now. |
I agree this would be a great feature. I am using Jekyll at the moment to generate static websites,as it's one of the more mature and widely supported SSGs out there. Relatively simple to use and lots of plugins and services such as headless CMS, shopping carts, headless eCommerce engines and much more. Been thinking about VuePress, but then it only does static. For me if Quasar could add in a SSG channel, then just one tool to learn.... |
I like the idea a lot. We are currently stabilizing and documenting the app-extension system - so I could envision a few ways to do this - probably using phantomJS or something similar |
I would really like this feature to be added in Quasar. It would help me use it for netlify-cms later. |
There are some reasons why making a static site is complicated (especially regarding final bundle size), but we think that there is hope for this. @codenamezjames has said he will be working on a possible solution. |
Is there a way to integrate gridsome into the quasar build system? |
@mckelveygreg - currently, there isn't. Scott |
@mckelveygreg I think it would actually be easier to do it the other way around - Integrate Quasar into Gridsome. Still a lot of glue work probably. |
Can't say when it'll be done exactly, but I am currently working on a solution - QPublisher |
Hmm, probably too much glue :) |
@mckelveygreg |
UPDATED TO INTEGRATE FOLLOWING ANSWER I confirm that with
import { Quasar } from 'quasar';
const { ssrUpdate } = Quasar;
export default ({ app }) => {
ssrUpdate({ app });
};
const PrerenderSPAPlugin = require("prerender-spa-plugin");
const path = require("path");
// ...
boot: [
...(ctx.prod && ctx.mode.spa ? ['ssg'] : []),
// ... other boot files
],
// ...
extendWebpack(cfg) {
if (process.env.NODE_ENV === "production") {
// ...
cfg.plugins.push(
new PrerenderSPAPlugin({
// Required - The path to the webpack-outputted app to prerender.
staticDir: path.join(__dirname, "dist/spa"),
// Required - Routes to render.
routes: [
'/', // Homepage
// ...other routes
'/error-404' // 404 page, it works because this route doesn't actually exist
],
postProcess: context => {
context.html = context.html
// Defer scripts
.replace(/<script (.*?)>/g, '<script $1 defer>')
.replace('id="app"', 'id="app" data-server-rendered="true"');
return context;
}
})
);
}
} Like this it adds a folder for each page into Remember that you must use it with router This Vue plugin con also be an interesting source of insights, but watch out because as of today it uses an old Production environment check is made because prerendering is an intensive task and we probably don't want it to happen during development. Note that |
@IlCallo Put this in a new boot file: // src/boot/static.js
import { Quasar } from 'quasar'
const { ssrUpdate } = Quasar
export default ({ app }) => {
ssrUpdate({ app })
} In "quasar.conf.js" you can include this boot file only for production in spa mode like this: // quasar.conf.js
module.exports = function (ctx) {
return {
// ...
boot: ['your_boot_files'].concat(ctx.prod && ctx.mode.spa ? ['static'] : []),
// ...
}
} |
Interesting, can you expand on why it works? To me it seems it is forcing Vue to update after not having updated because it found |
From vuejs ssr documentation:
Hydration mode reduces the time to interactive (input latency) by not rerendering the DOM. "ssrUpdate" is a function from Quasar which is called at boot time in ssr mode. We just do the same with spa prerendered. At client side ssrUpdate inject this global mixin: mounted () {
queues.takeover.forEach(run => {
run(this.$q)
})
} For responsiveness if we look at the Quasar Screen plugin, we can see that the "start" function is pushed to
If we don't call I hope my english is enough good to understand my explanations. |
Yeah, I more or less understood, I'll try this on my projects, thanks! |
@freddy38510 I tryed your solution and it actually works, I updated my previous comment to include your. While trying everything out, I noticed something strange with network downloads, so I dug deeper. My website must serve the homepage both on If you really want to check how your SSG is performing, a custom node server should be added to your package and run, much like how it works with SSR (except this is a much easier server, it should just point to the right folder and everything should work). Apart from this, I think the last missing step before SSG could be added to Quasar seamlessly (either into core or with an AE) is to automatically get the routes to presender from the |
@IlCallo
I actually using my own Quasar app extension to generate static pages from SSR (much faster to prerender pages than using a browser). I could share this extension with the community after some polishing. Some help could be great to write the documentation and review the code. |
For development, @freddy38510 I can help you with the AE if you like, I was thinking of writing it myself actually. If you want to add a node server to the distributable for production use, here's an example.
/* eslint-env node */
import cors from 'cors';
import express from 'express';
import { join, resolve, extname } from 'path';
import { existsSync } from 'fs';
const pagePath = urlPath => join(resolve(), urlPath, 'index.html');
const resourcePath = urlPath => join(resolve(), urlPath);
const PORT = 4000; // Insert your port
const app = express();
app.use(cors({ origin: `http://localhost:${PORT}` }));
app.get('*', (req, res) => {
// eslint-disable-next-line no-console
console.log(`Requested path: ${req.path}`);
let filePath =
extname(req.path) === '' ? pagePath(req.path) : resourcePath(req.path);
// Page not found path
if (!existsSync(filePath)) {
filePath = pagePath('error-404');
}
res.sendFile(filePath);
});
app.listen(PORT, () => {
// eslint-disable-next-line no-console
console.log(`Server is listening on localhost:${PORT}...`);
});
{
"name": "src-ssg",
"version": "1.0.0",
"type": "module",
"main": "index.js",
"license": "MIT",
"scripts": {
"serve": "node index.js"
},
"dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1"
}
} Then make sure that those files are copied over to dist folder using build: {
afterBuild({ quasarConf }) {
const distDir = quasarConf.build.distDir;
const composeDistPath = src => path.join(distDir, src);
const composeSsgPath = src => path.join(__dirname, 'src-ssg/' + src);
const ssgFileToCopy = ['index.js', 'package.json'];
for (const fileName of ssgFileToCopy) {
fs.copyFileSync(composeSsgPath(fileName), composeDistPath(fileName));
}
}
} Finally, you can add a script into the root level "scripts": {
"serve:ssg": "cd dist/spa && yarn install && yarn serve"
} When developing, go with |
@IlCallo I mean, don't use The extension i talked about is for generating pre-rendered pages from SSR like Nuxt does in universal mode. This is not the same behavior than prerender-spa-plugin which generate prerendered pages from headless browser like Puppeteer. |
As far as I know, If the output is the functionally the same, I guess how you get there isn't really important. As you told it, it seems like it, if it's faster it's not a problem I guess 😂 If instead the output is actually different, I'll just create my own AE :) |
@IlCallo
If you don't use this middleware every request are looking for an "index.html" at the requested location (from url path). This is what we want for prerendered pages. You're right, the output is more or less the same. One advantage for SSR over browser based rendering is that we don't need to make some workaround for Quasar responsiveness. This is already handle by Quasar in ssr mode. We also don't need to defer scripts manually as it is already done by vue-server-renderer. Same thing for preloading and prefetching resources. We could also prefetching data and inlining the initial state of the app at server side. Quasar Prefetch feature becomes useful for that. See all the possibilites in the vue ssr guide. A last advantage i can think of is the CSS management. |
I thought that Your SSR-based AE seems way more advanced than the one I proposed, I can help if you want to open source it |
Another (small) problem of |
There are more problems - If you prerender with a browser like I am very intersted in SSR based app extension, too ;) I wanted to have this for a long time. |
@freddy38510 any news? |
@IlCallo |
@IlCallo, @panstromek So, to test it, clone the repo then follow the instructions on the Quasar doc like if you were developing it locally. After installing it, you can set the routes to render in the file "src-static/route.js" present at the root of your project. This file simply export an array of routes to render the static pages. This is the best way i found to have the ability to generate static pages from dynamic routes. I didn't add a command to generate static pages, you should just run I will enjoy to have some feedbacks, new ideas and PR to the repo. |
@IlCallo , @panstromek So, to test your website you can use this command You can generate a fallback SPA file with the name of your choice. By default this is "404.html". This will be useful to replace the default 404 page of your static website server. I also recommend to create a route for catching 404 since our website will operate as a SPA for subsequent navigations. // src/router/routes.js
{ path: '*', component: () => import('pages/error404.vue') } |
Thank you! I'll check it out next week |
I added some issues into https://github.com/freddy38510/quasar-app-extension-static where I think things could be improved. |
That can be an awesome feature... any news? |
Freddy open sourced it's app extensions but I had some problems and couldn't help him polish it yet. |
@eladcandroid Did you test the app extension ? |
I'm looking forward to implementing it in a Quasar based website! |
@cabassi |
Looking forward to the AE. |
Would it be possible to add @freddy38510 's work into the repository.
Edit: AE = App extension |
Hi @chintan-mishra, you could use freddy work as an app extension and contribute to that project |
@IlCallo Since after finding this project, I have been using the app extension project. I mentioned adding this project to quasar-framework repo as Static Site Generation and JAMstack are introducing faster experience. It is hard to find this plugin. I have a large but mostly static website with some dynamic content. I have been using server side rendering(SSR) for my use case. With advent of static site generator and JAMstack, I can finally move my complete website to CDN. Searching for static site generator or JAMstack on search engine and Quasar Framework forums leads to very few results relevant to Quasar Framework. I believe there is some value to be gained by including this plugin in official repo and adding static site generator in one of the ways to deliver Quasar Framework website. Something as simple as mentioning Freddy's Static Site Generator plugin in docs with its own section can help show Quasar Framework in search results when someone looks up for 'JAMstack framework for VueJS' |
I would love to see Quasar be able to generate a fully static site. I was recently trying to get Quasar working in gridsome as a plugin and ran into troubles around the |
To all guys that are using the mentionned app extension, i made a big update. What i did:
To all of them, i suggest you to read the upgrading section. I hope to have more testing to make this extension reaching a stable version. And obviously i encourage all of you to contribute to this project. |
Per Vue's Official SSR Guide, you can give a shot to prerender-spa-plugin. Setting it up is as easy as adding 5 lines in // quasar.conf.js
const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
...
extendWebpack (cfg) {
cfg.plugins.push(
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist/spa'),
routes: ['/', '/about', '/path/to/prerender']
})
)
... |
@eyedean #2299 (comment) |
@IlCallo SSR versus SSG is a huge debate. One should consider so many factors before making a decision. In my case, I was open to both but found that this quick Webpack plugin suffices for my need. Not to mention that Quasar's SSR output is a whole "server" (not a "function", "controller", or "module") so merging it into an existing server with its own configurations (e.g. Typescript, custom middlewares and logging, etc.), which was the case for me, is rather challenging per see. PS. Quasar is great! :) |
@eyedean I meant that there is an App Extension which helps you to create a SSG app by leveraging Quasar SSR mode behind the curtains. Which is a different path to the same result of using PrerenderSPA plugin, but should generate more cleaner and deterministic code |
@IlCallo I actually finished working with the PrerenderSPA Plugin and then switched to the SSG extension. Here are what I found that I can share for the future folks who come here. PrerenderSPA Plugin (a Webpack tool, not Vue-specific, nor Quasar-specific)Pros:
Cons:
Quasar's SSG App Extension (Quasar-only, by @freddy38510)Pros:
To name the SPA -> SSR challenges:
Other challenge(s):
My ConclusionI am going to keep working with Quasar SSG for now. Thanks to @freddy38510 for making it, thanks @IlCallo for your comments, and thanks to Quasar team and community for building such an awesome framework and maintaining it! |
Thanks for sharing!
Remember you can decide to run boot files only on client, on server or both directly from
|
It seems
import { Quasar } from 'quasar';
const { ssrUpdate } = Quasar;
export default ({ app }) => {
ssrUpdate({ app });
}; |
I think Quasar is almost perfect and I think adding the ability to generate a static website like Nuxt is better than maintaining a "Quasar + Nuxt starter kit" because I think Quasar gave us a freedom better than Nuxt and capabilities much better than Nuxt.
I don't expect this feature to be available in the next release I just want to see it someday available in the most perfect framework Quasar!
The text was updated successfully, but these errors were encountered: