1. The scene

With the big changes of a few years ago, excessive Dom binding late maintenance has become a nightmare for large projects. Single-page apps have been around for a while, and now vue, React, and Angular have become the triumvirate of must-have skills on the front end. Today we mainly talk about single page SEO, other things will not go into.

2. Why is a single page bad for SEO?

1. The principle of a search engine crawler is to grab your URL, then get your HTML source code and parse it. The final rendering page of a single page application is dynamically generated by JS, and the HTML obtained by a crawler is only the model page of a single page, not the final rendered page, so using JS to render data is not friendly to SEO.

2. The essence of SEO is that one server sends a request to another server and parses the request. In general, search engines do not execute requested JS for reasons of speed. At this time the single page problem comes, THE HTML file in the server side and no rendering data, the actual rendering data is completed by the client side, so the search engine request to THE HTML is only a structure, so it is not conducive to the page content by the search engine search. So server side rendering is designed to solve single-page applications where the page has content before it’s sent to the browser.

3. Single-page SEO solutions

1. Pre-render pages

2. Server rendering

Server-side rendering is pretty mature, and there are libraries out there, so today we’re going to focus on pre-rendering.

Usage scenarios: For business scenarios where only a few pages need to be improved and HTML files are dynamically compiled in real time without interaction with the server, such as portal websites

Using plug-ins: Prerende-spa-plugin is a webpack plug-in, so single webpack-wrapped pages (react, Vue, Angular, etc.) can be used. Today’s practice is vUE, and the prerender-SPa-plugin principle is to use the browser kernel to pre-load the page rendering data to produce a complete HTML file

4. Pre-rendering practice

4.1 installation prerender – spa – the plugin

yarn add prerender-spa-plugin -D 
or 
npm install prerender-spa-plugin -D 
Copy the code

4.2 Changing the Vue router mode attribute to history

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      component: Index
    },
    {
      path: '/about',
      component: About
    }
  ]
})
Copy the code

If the mode is not changed to history, the static HTML packaged by prerender-SPa-plugin in Hash mode is complete only for the home page, and the other pages are still using the home page skeleton, and then dynamically generate and replace the HTML. The seo effect is not achieved.

4.3 build/webpack. Prod. Conf., js (vue – cli2 generated) under the structure of the build/webpack prod. Conf. Js

const path = require('path')
const PreRenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PreRenderSPAPlugin.PuppeteerRenderer;
const routes = [ '/'.'/about'];
const resolve = dir => path.join(__dirname, '.. ', dir);
const webpackConfig = merge(baseWebpackConfig, {
    plugins: [
      new PreRenderSPAPlugin({
          staticDir: resolve('dist'Renderer: new renderer ({inject: {}, // document.dispatchEvent in main.js (new Event)'render-event'), the event names of the two should correspond. / / render - the role of the event is executed after the render - event event preRender renderAfterDocumentEvent:'render-event', // Puppeteer, puppeteer, puppeteer, puppeteer'--no-sandbox'.'--disable-setuid-sandbox']})})]})Copy the code

ERROR: Fail to Lauunch Chrome!

Cause one: The Linux Docker container cannot start Chrome because the Docker shared memory is insufficient

Detailed answers to other questions: github.com/chrisvfritz…

4.4 SRC /main.js (both under vue-cli2 generated structures)

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'.mounted() {
    document.dispatchEvent(new Event('render-event'))}});Copy the code

After configuration, NPM run build generates HTML for routes in routes. Isn’t that easy? Structure after packaging:

│ index. The HTML ├ ─ about │ index. The HTML └ ─ static ├ ─ CSS ├ ─ fonts ├ ─ img └ ─ jsCopy the code

5. Automatically generate sitemap for all packaged pages

5.1 introduction of Sitemap

Baidu Baike: Sitemap allows webmasters to notify search engines of the availability of crawlable pages on their site. In its simplest form, Sitemap is an XML file that lists the urls in the site and other metadata about each url (when it was last updated, how often it changed, how important it is relative to other urls on the site, etc.) so search engines can crawl the site more intelligently.

Vulgar and easy to understand: Baidu crawlers like Sitemap very much. Sitemap protocol allows you to tell search engines which urls are available to crawl on the site. Sitemap generation is to make search engines better access to the site so that the site is included in a good role.

5.2 a sitemap. Js

Sitemap.js is an advanced sitemap generation library that makes it easy to create sitemap XML files.

Sitemap: sitemap.js: sitemap.js: sitemap.js: sitemap.js

Installation:

yarn add sitemap -D 
or 
npm install sitemap -D 
Copy the code

**5.3 Automatic sitemap generation with prerender-spa-plugin postProcess method **

PostProcess: prerender-spa-plugin allows you to personalize the generated HTML before it is written to the file, with each route that generates the HTML coming in once. The official explanation

PostProcess function argument: content

  • OriginalRoute: the originalRoute before redirection
  • Content-. route: The redirected pre-rendered route, which is also the output location of the file. Relative to staticDir, this property can be modified
  • Content. HTML: Output HTML content

Generating idea: Grab external links from the current HTML content and push them into the global address container to generate the final Sitemap each time the HTML is generated.

5.4 Sitemap Information To be Collected

Links to collect:

1. Single-page routing links

2. External links in the HTML content of the website

* * 5.5 practice generates a sitemap, modify the build/webpack prod. Conf. Js * *

// Generate HTML route
const fs = require('fs');
const path = require('path')
const sm = require('sitemap');
const PreRenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PreRenderSPAPlugin.PuppeteerRenderer;
const routes = [ '/'.'/about'];
/ / a sitemap url container
const siteMapUrls = [];
const resolve = dir= > path.join(__dirname, '.. ', dir);
const webpackConfig = merge(baseWebpackConfig, {
    plugins: [
      new PreRenderSPAPlugin({
      staticDir: resolve('dist'),
      routes,
      postProcess (context) {
        / / the content parameters
        const {originalRoute, route, html} = context;
        // Get the href content re globally
        const reg = / (? <=;
        // Filter urls that do not start with HTTP or HTTPS
        const urlList = html.match(reg).filter(url= > url.startsWith('http'));
        // Add the route to the global sitemap container
        siteMapUrls.push(originalRoute);
        // Add the outer chain in HTML to the global sitemap container
        if (urlList.length) {
          urlList.forEach(url= > siteMapUrls.push(url));
        }
        // When the current route is generated as the last route
        if (route === routes[routes.length - 1]) {
          // Remove duplicate links
          let currentSiteMapUrls = Array.from(new Set(siteMapUrls));
          // Filter out the content after the anchor point in the link
          currentSiteMapUrls = currentSiteMapUrls.map(url= > {
            const isMao = url.indexOf(The '#') > - 1;
            // Generate the data required by sitemap. For details, see sitemap.js
            return {url: isMao ? url.split(The '#') [0] : url, changefreq: 'weekly'.priority: 0.5.lastmod: new Date().toLocaleDateString()}
          });
          // Generate the siteMap file
          const siteMap = sm.createSitemap({
            // Route prefix address, full address automatically does not add hostname (https://www.baidu.com does not add hostname)
            hostname: 'https://www.test.com'.cacheTime: 600000.//600 sec (10 min) cache purge period
            urls: currentSiteMapUrls
          });
          // Add the sitemap file to the package folder dist
          fs.writeFileSync(resolve('dist/sitemap.xml'), siteMap.toString());
        }
        // Returns the current Contet object
        return context
      },
      renderer: new Renderer({
        inject: {},
        // Document.dispatchEvent (new Event('render- Event ')) in main.js.
        //render-event executes preRender after render-event
        renderAfterDocumentEvent: 'render-event'.// Puppeteer completely trusts what is opened in Chrome
        args: ['--no-sandbox'.'--disable-setuid-sandbox']})}),]})Copy the code

Generate the sitemap XML

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> <url> <loc>https://www.test.com</loc> <priority>0.5</priority> <lastmod> < changefreq > weekly < / changefreq > < url > < url > < loc > https://www.test.com/about < / loc > < priority > 0.5 < / priority > <lastmod>2019-11-14</lastmod> <changefreq>weekly</changefreq> </url> <url> <loc>https://www.baidu.com</loc> <priority>0.5</priority> <lastmod> <changefreq>weekly</changefreq> </url> </urlset>Copy the code

Conclusion:

Prerender-spa-plugin is suitable for static portal sites. For large data interaction sites with SEO needs, it is better to use server rendering. This is also a quick solution to SEO. Need more perfect external chain rules can be customized, this solution is used to solve the small portal site using a single page is not conducive to SEO pain point simple solution.

Personal blog address, interested can take a look