Written in 2016.08.23
Project address: github.com/jrainlau/vu…
With the release of Vue 2.0, server-side rendering became a hot selling point. Prior to this, the first screen loading time and SEO issues of single-page apps have plagued developers, and also limited the use of front-end frameworks to a certain extent. React’s server-side rendering solution solves both of these pain points and is favored by developers, but it also gives Vue another reason to attack — Vue doesn’t have server-side rendering. To solve this problem, the community of Vue has also contributed a solution called VueServer. However, this is not a pure server rendering solution, but a Vue equivalent to another server, as can be seen from its readme:
VueServer.js is designed for static HTML rendering. It has no real reactivity. Also, the module is not running original Vue.js on server. It has its own implementation. It means VueServer.js is just trying to perfectly reproduce the same result as Vue.js does.
So is there a universal solution that allows us to use native Vue 1.x and still be happy with server-side rendering? Please listen to me carefully…
Server Side Rendering (SSR)
Before we get started, it’s worth taking a look at what server-side rendering is and why it’s needed (skip it if you know). Server Side Render (SSR for short), sounds lofty, in fact, the principle is our most common “server directly spit out the page”. As we know, the traditional website is the back end through splicing, template filling and other ways, the data and HTML combined, and then sent to the client. This process of combining data with HTML is called server-side rendering.
The benefits of server-side rendering, first of all, are the first screen load time. Because the HTML sent from the back end is complete HTML with data, the browser can just take it and use it. In contrast, for a single page application developed by Vue 1.x, the HTML sent by the server is just an empty template, and the browser asynchronously requests data from the back end of JS and renders it into HTML. A large single-page application usually has a large NUMBER of JS and a large number of asynchronous requests, which directly results in a long loading time for the first screen. In the case of poor network speed, a blank screen or loading process for a long time is really unfriendly to user experience.
In addition, general search engine crawlers cannot execute JS codes in HTML (except Google), so for single-page applications, crawlers only get empty HTML. Therefore, websites that need to do SEO rarely adopt single-page applications. We can look at examples
Let’s start by writing an HTML file that generates content from JS:
<! -- SPA.html --> <! DOCTYPE html> <html lang="en">
<head>
<meta charset="UTF-8">
<title>SPA-DEMO</title>
</head>
<body>
<script>
var div = document.createElement('div')
div.innerHTML = 'Hello World! '
document.body.appendChild(div)
</script>
</body>
</html>
Copy the code
The browser opens, and the output “Hello World! Good. No problem.
Let’s write a little crawler:
'use strict'
const superagent = require('superagent')
const cheerio = require('cheerio')
var theUrl = 'http://localhost:3000/spa.html'
const spider = (link) => {
let promise = new Promise( (resolve, reject) => {
superagent.get(link)
.end((err, res) => {
if (err) return console.log(err)
let $ = cheerio.load(res.text)
console.log($('html').html())
resolve($)
})
})
return promise
}
spider(theUrl)
Copy the code
Run, the output is as follows:
As you can see, the corresponding div is not generated within the tag, and the crawler cannot parse the JS code in the page.
PhantomJS
To implement server-side rendering, our main character PhantomJS comes into play.
PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fast and native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.
In short, PhantomJS packages a WebKit kernel, so you can use it to parse JS code, but it also has other very useful uses, which can be found on its website. Since PhantomJS is a binary file that needs to be installed and used, it is quite troublesome, so I found another NodeJS module that encapsulates PhantomJS — PhantomjS-node
PhantomJS integration module for NodeJS
With it, you can happily use PhantomJS with Node!
npm install phantom --save
Copy the code
Create a new phantom-demo.js file and say:
var phantom = require('phantom');
var sitepage = null;
var phInstance = null;
phantom.create()
.then(instance => {
phInstance = instance;
return instance.createPage();
})
.then(page => {
sitepage = page;
return page.open('http://localhost:3000/spa.html');
})
.then(status => {
console.log(status);
return sitepage.property('content');
})
.then(content => {
console.log(content);
sitepage.close();
phInstance.exit();
})
.catch(error => {
console.log(error);
phInstance.exit();
});
Copy the code
You will see the full content of http://localhost:3000/spa.html in the console < div > Hello World!
Server rendering of Vue 1.x project with Express.
Now it’s time for the real thing. First we will create a Vue 1.x project, which will be generated using vue-CLI:
npm install vue-cli -g
vue init webpack vue-ssr
Copy the code
Execute the following code in the generated project:
npm install
npm run build
Copy the code
You can see that a \dist directory is generated under the root directory, which contains the project for Vue 1.x:
|__ index.html
|__ static
|__ css
|__ app.b5a0280c4465a06f7978ec4d12a0e364.css
|__ app.b5a0280c4465a06f7978ec4d12a0e364.css.map
|__ js
|__ app.efe50318ee82ab81606b.js
|__ app.efe50318ee82ab81606b.js.map
|__ manifest.e2e455c7f6523a9f4859.js
|__ manifest.e2e455c7f6523a9f4859.js.map
|__ vendor.13a0cfff63c57c979bbc.js
|__ vendor.13a0cfff63c57c979bbc.js.map
Copy the code
Let’s set up the Express project anywhere:
express Node-SSR -e
cd Node-SSR && npm install
npm install phantom --save
Copy the code
Then, we copied and pasted all the code from \static\ CSS and \static\js in the \dist directory into the \public\stylesheets and \public\javascripts folders of the newly generated Express project. Be sure to include all *.map files), rename the index. HTML in the \dist directory to vue-index.ejs, and place it in the \ View folder of the Express project. Change all reference paths inside to start with /stylesheets/ or /javascripts/.
Next, open the \routes\index.js file in the Express project and rewrite it as follows:
const express = require('express')
const router = express.Router()
const phantom = require('phantom')
/* GET home page. */
router.get('/render-vue', (req, res, next) => {
res.render('vue-index')
})
router.get('/vue', (req, res, next) => {
let sitepage = null
let phInstance = null
let response = res
phantom.create()
.then(instance => {
phInstance = instance
return instance.createPage()
})
.then(page => {
sitepage = page
return page.open('http://localhost:3000/render-vue')
})
.then(status => {
console.log('status is: ' + status)
return sitepage.property('content')
})
.then(content => {
// console.log(content)
response.send(content)
sitepage.close()
phInstance.exit()
})
.catch(error => {
console.log(error)
phInstance.exit()
})
})
module.exports = router
Copy the code
Now we use the reptiles crawl before the content of http://localhost:3000/render-vue, the result is as follows:
You can see that there are some unexecuted JS.
Then we crawl on http://localhost:3000/vue, and see what the result is:
Full of content.
We can also open the above two addresses in the browser, and although the results are shown below, we can see that the requested HTML content is different through the Network option of the developer tools.
This concludes the server-side rendering practice based on PhantomJS + Node + Express + VueJS 1.x.
To optimize the
Because PhantomJS takes some time to open the page and parse the JS code, we should not redo the server render every time the user requests it. Instead, we should have the server cache the PhantomJS rendered result so that the user only needs to return the cached result for each request. Greatly reduces server stress and saves time.
Afterword.
This paper is only for the study of the use, did not carry out in-depth research. The method studied in this article is not only applicable to Vue projects, but also theoretically applicable to any single-page application project after construction. If readers find any mistakes or omissions in the article, please point out, thank you. If you have a better server-side rendering method, please share with me.
Thank you for reading. I am Jrain, welcome to pay attention to my column, will not regularly share their own learning experience, development experience, handling dry goods outside the wall. See you next time!