DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Stateless vs. Stateful Widgets: Make the Right Choice for Your Flutter App
  • React, Angular, and Vue.js: What’s the Technical Difference?
  • Building Micro-Frontends With Vue and Reusable Components
  • Build Your Own AI Avatar App From Scratch in Less Than 8 Hours

Trending

  • Transforming AI-Driven Data Analytics with DeepSeek: A New Era of Intelligent Insights
  • Why High-Performance AI/ML Is Essential in Modern Cybersecurity
  • Docker Model Runner: Streamlining AI Deployment for Developers
  • A Modern Stack for Building Scalable Systems
  1. DZone
  2. Coding
  3. Frameworks
  4. Pre-Render a Vue.js App (With Node Or Laravel)

Pre-Render a Vue.js App (With Node Or Laravel)

In this article, we'll explore how pre-rendering works with Vue.js and look at two examples; one with a Node.js project, one with a Laravel project.

By 
Anthony Gore user avatar
Anthony Gore
DZone Core CORE ·
Updated Feb. 10, 20 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
23.3K Views

Join the DZone community and get the full member experience.

Join For Free

Server-side rendering is all the rage right now. But it's not without its downsides. Pre-rendering is an alternative approach that may even be better in some circumstances.

Server-Side Rendering

One of the downsides to JavaScript-based apps is that the browser receives an essentially empty page from the server. The DOM cannot be built until the JavaScript has been downloaded and run.

This means that the user has to wait a bit longer to see anything. It can also have an impact on SEO if crawlers can't see the content of the page quickly.

Server-side rendering (SSR) overcomes this issue by rendering the app on the server so that the client receives the complete DOM content when the page is loaded before JavaScript is even run.

So instead of the browser receiving this from the server:

<head> ... </head>
<body>
<div id="app">
  <!--This is empty, Javascript will populate it later-->
</app>
</body>

With SSR, it receives a page with complete content:

<head> ... </head>
<body>
<div id="app">
  <div class="container">
    <h1>Your Server-Side Rendered App</h1>
    <div class="component-1">
      <p>Hello World</p>
      <!--etc etc. This was all rendered on the server-->
</app>
</body>

Server-Side Rendering Cons

  • Your app will need to be executable on the server, so you'll need to design your code to be "universal," i.e. it works in both the browser and a Node server.
  • Your app will run on each request to the server, adding additional load and slowing responses. Caching can partially alleviate this.
  • You can only do SSR with Node.js. If your primary backend is Laravel, Django, etc., you'll have to run a Node server alongside the main backend to take care of SSR.

Pre-Rendering

There's another way to tackle the empty page problem: pre-rendering. With this approach you run your app before deploying it, capture the page output and replace your HTML files with this captured output.

It's pretty much the same concept as SSR except it's done pre-deployment in your development environment, not a live server.

Pre-rendering is typically performed with a headless browser like PhantomJS and can be incorporated into a build flow with Webpack, Gulp, etc.

Pre-Rendering Pros

  • No additional server load, therefore faster and cheaper than SSR.
  • A simpler production setup and simpler app code, therefore less prone to error.
  • Doesn't require a Node.js production server.

Pre-Rendering Cons

  • Doesn't work well for pages that display changing data, e.g. tables.
  • Doesn't work for pages that have user-specific content, e.g. an account page with a user's personal details. However, these kinds of pages are less critical for pre-rendering anyway; it's our main, frequently used pages that we want to serve up fast.
  • You'll need to pre-render every route in the app individually.

Comparison Table

Image title

Vue.js Pre-Rendering Example

Let's do a simple example of pre-rendering a Vue.js app. I'll give the example once in a Node.js environment and once in a Laravel environment.

In these examples, we'll use Webpack with the prerender-spa-plugin to perform the pre-render.

Vue and Node

Step 1: Project Installation

We'll use vue-cli with the webpack-simple template.

$ vue init webpack-simple vue-node-pr-test
$ cd vue-node-pr-test
$ npm install

There are three additional modules we'll need, explanations to follow.

$ npm install --save-dev http-server html-webpack-plugin prerender-spa-plugin

Step 2: Include index.html in the Webpack Build

The webpack-simple template doesn't include the index.html file in the Webpack build output. However, when we pre-render the app, we'll need to overwrite our index.html, so let's add it to the output so as not to destroy the original.

Use the html-webpack-plugin in our webpack.config.js file to include the file in the Webpack build:

var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports.plugins.push(
  new HtmlWebpackPlugin({
    template: './index.html',
    inject: false
  }),
);

Now we change our Webpack publicPath since the index.html will now be in the same folder as the other static assets:

output: {
  path: path.resolve(__dirname, './dist'),
  filename: 'build.js',
  publicPath: '/', // was originally 'dist'
},

And we'll also need to change <script src="/dist/build.js"></script> in our index.html to <script src="/build.js"></script> due to the changed path.

Step 3: Test the Webpack Production Build

Now when we build:

$ npm run build

Our dist folder should look like this:

- dist
-- build.js
-- index.html
-- logo.png

And if we inspect dist/index.html it'll look like this:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>vue-node-pr-test</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="text/javascript" src="/build.js"></script>
  </body>
</html>

Now we can use http-server and serve the app from the dist folder. By default it will run at localhost:8080:

$ ./node_modules/.bin/http-server ./dist

Step 4: Pre-Render App

Now that our index.html file is in the Webpack build we can update it with the pre-rendered HTML.

Firstly, we need to add prerender-spa-plugin to our webpack config. Make sure it comes after html-webpack-plugin.

var PrerenderSpaPlugin = require('prerender-spa-plugin');

module.exports.plugins.push(
  new PrerenderSpaPlugin(
    path.join(__dirname, './dist'),
    [ '/' ]
  )
);

The first argument to PrerenderSpaPlugin is the location of our index.html file, the second is a list of routes in the app. For each one you add, you'll get a different output file! In this example, we just have one route, though.

Now we build again:

$ npm run build

Our build will take longer than it did before because the pre-render plugin is doing its thing:

  1. It creates an instance of Phantom JS and runs the app.
  2. Takes a snapshot of the DOM.
  3. Outputs the snapshot to an HTML file in our build folder.

It repeats this process for every route, so it can take quite a while to build the app if you have many pages.

After the build our dist/index.html should now include all the pre-rendered HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>prerender-test</title>
  <style type="text/css">#app {
    font-family: Avenir, Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-align: center;
    color: #2c3e50;
    margin-top: 60px
  }

  h1, h2 {
    font-weight: 400
  }

  ul {
    list-style-type: none;
    padding: 0
  }

  li {
    display: inline-block;
    margin: 0 10px
  }

  a {
    color: #42b983
  }</style>
</head>
<body>
<div id="app"><img src="/logo.png?82b9c7a5a3f405032b1db71a25f67021">
  <h1></h1>
  <h2>Essential Links</h2>
  <ul>
    <li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
    <li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
    <li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter Chat</a></li>
    <li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
  </ul>
  <h2>Ecosystem</h2>
  <ul>
    <li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
    <li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
    <li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
    <li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
  </ul>
</div>
<script type="text/javascript" src="/build.js"></script>

</body>
</html>

Vue and Laravel

If you skipped the Vue and Node example, I recommend you go back and read it first as it includes a more thorough explanation of any common steps.

Step 1: Project Installation

Firstly we'll set up a new Laravel project.

$ laravel new vue-laravel-pr-test
$ cd vue-laravel-pr-test
$ npm install

We'll also add two more NPM modules we're going to need:

$ npm install --save-dev html-webpack-plugin prerender-spa-plugin

Step 2: Serve a Plain HTML File

By default, Laravel serves a Blade template file at the root URL. To keep the example simple, we'll replace it with the following plain HTML file that we'll create at resources/views/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Laravel</title>
    <link rel="stylesheet" href="/css/app.css">
<body>
<div id="app">
  <example></example>
</div>
<script type="text/javascript" src="/js/app.js"></script>
</body>
</html>

Now we need to serve that file instead of the Blade template at the root route. Change routes/web.php to this:

Route::get('/', function () {
  return File::get(public_path() . '/index.html');
});

This is actually pointing to our build folder which we'll generate shortly.

Step 3: Add the HTML File to the Build

Like in the Node example, we want to include our index.html in the Webpack build so we can overwrite it later with the pre-rendered HTML.

We'll need to do some Webpack configuration. I'm using Laravel 5.4 in this example, which uses Laravel Mix. Mix doesn't give you a local webpack configuration file because it uses its own default file, so let's make one by copying from the laravel-mix module:

$ cp ./node_modules/laravel-mix/setup/webpack.config.js .

We'll also need to make our NPM production script point to this config file, so edit package.json and change the production script to this:

cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=webpack.config.js

Now we add html-webpack-plugin to our webpack.config.js file. Add this to the bottom of the file above the Mix Finalizing section:

var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports.plugins.push(
  new HtmlWebpackPlugin({
    template: Mix.Paths.root('resources/views/index.html'),
    inject: false
  });
);

Step 4: Test the Weback Production Build

Let's now build for production and serve:

$ npm run production
$ php artisan serve

You'll probably get an error in the browser when you run the app, though, because we never set a value for window.Laravel.csrfToken. For this simple example, it's quicker just to comment it out, so change resources/assets/js/bootstap.js like this:

window.axios.defaults.headers.common = {
  'X-Requested-With': 'XMLHttpRequest'
  // 'X-CSRF-TOKEN': window.Laravel.csrfToken;
};

Step 5: Pre-Render App

We now need to use prerender-spa-plugin in our webpack config to perform the pre-rendering. Make sure it comes after html-webpack-plugin.

var PrerenderSpaPlugin = require('prerender-spa-plugin');

module.exports.plugins.push(
  new PrerenderSpaPlugin(
    Mix.output().path,
    [ '/' ]
  )
);

Now we can do a production build:

$ npm run production

If you check the build folder, dist/index.html should now look like the following, complete with pre-rendered HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Laravel</title>
    <link rel="stylesheet" href="/css/app.css">
</head>
<body>
<div id="app">
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel panel-default">
                    <div class="panel-heading">Example Component</div>
                    <div class="panel-body">
                        I'm an example component!
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script src="/js/app.js"></script>
</body>
</html>
app Laravel Pre-rendering Build (game engine) Vue.js HTML Production (computer science)

Published at DZone with permission of Anthony Gore, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Stateless vs. Stateful Widgets: Make the Right Choice for Your Flutter App
  • React, Angular, and Vue.js: What’s the Technical Difference?
  • Building Micro-Frontends With Vue and Reusable Components
  • Build Your Own AI Avatar App From Scratch in Less Than 8 Hours

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!