I just took over the engineering work in the last two weeks. The leader hopes to integrate a plug-in that can pre-render HTML to solve the problem of blank templates generated by Webpack.
Our research showed that WebPack had a prerender-SPa-plugin that met our requirements, so we integrated the prerender-SPa-plugin into our internal build process. The whole process is still a lot of mistakes, mainly for Node, Webpack these are still small stage, so I made a lot of mistakes, specially recorded.
Why use prerender-SPa-plugin
There are two main reasons:
1. SEO: SEO of SPA applications is poor. Most of the page structure crawlers climb to is only the entrance page, but the entrance page has almost no content, so the search ranking is very low.
2. First-screen experience: The entry page generated by Webpack basically has only one main JS, and the page itself has no content, so until this JS is loaded, the user will see nothing. In order to solve this problem, we usually give the specified static page to the background. The user requests the static page directly, so that the user can still see something before the main JS loads. However, there are also problems in this way. For example, if we want to put a Loading component into the first screen, it is very troublesome, and also another cost of communicating with the background (in fact, the big guy does not want to always rely on the background -_-). So it would be nice if we could generate a good first screen ourselves.
This is where the prerender-SPa-plugin comes in, generating HTML with static structure and putting whatever you want into the front screen.
The installation
NPM I prerender-SPa-plugin will do, but if you can’t climb the wall, you’ll run into a pit.
The prerender-spa-plugin relies on puppeteer, Google’s headless browser plugin, which downloads the latest version of Chromium (about 200M+), so if it doesn’t work, it will download incorrectly.
As shown in the figure, this step can be skipped by setting parameters, but if omitted, an error will be reported when the call is made.
Most of the solutions found on the web are for using the Puppeteer itself by specifying a native path to boot, but prerender-spa-plugin is outside the Puppeteer and therefore does not apply. The process of dealing with this problem is quite long, and I will describe it next. Here, I will first talk about the use of the wall after the situation.
Webpack configured in
The main purpose of use is to replace the HTML generated after build, so it is the webpack.prod.conf.js file that needs to be modified. Normally there is a plugin for htMl-webpack-plugin in this file if you use scaffolding tools. Because prereder plug-ins require the HTML generated by the prereder plug-in as a template, you simply add the new configuration to the htMl-webpack-plugin configuration.
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const baseWebpackConfig = require('./webpack.base.conf')
const PrerenderSPAPlugin = require('prerender-spa-plugin') const Renderer = PrerenderSPAPlugin.PuppeteerRenderer const webpackConfig = merge(baseWebpackConfig, { plugins: New HtmlWebpackPlugin({filename: config.build.index, template:) {new HtmlWebpackPlugin({filename: config.build.index, template:)'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
chunksSortMode: 'dependency'}), // On the basis of vue-cli generated files, only the following is the new PrerenderSPAPlugin({// generated file path, also can be the same as webpakc package. // This is very important!! // This directory can only have one level. If the directory level is greater than one level, there will be no errors during generation and it will just be stuck during pre-rendering. staticDir: path.join(__dirname,'.. /dist'), // Corresponding to the route file, for example, index has parameters, you need to write /index/param1. routes: ['/'.'/index'.'/skin'.'/slimming'.'/exercise'.'/alPay'.'/wxPay'Renderer: new renderer ({inject: {foo:) renderer: new renderer ({inject: {foo:)'bar'
},
headless: false, // Document.dispatchEvent (new Event()'render-event'), the event names of the two should correspond. renderAfterDocumentEvent:'render-event'})})]})Copy the code
At this point, if it is for personal use only, the docurender-event event will be triggered in the JS of the page that needs to be pre-rendered (for example, in vue, a dispatchEvent will be added to the Mounted function of main.js). NPM run build generates pre-rendered HTML. For specific configuration items, Can go to a lot of prerender – spa – the plugin to see https://github.com/chrisvfritz/prerender-spa-plugin
Hit the pit
First of all, our project is to integrate into an engineering tool that all developers will use, so we need to consider two problems during the installation: 1. Assuming that all developers can’t get over the wall, how do we download this plug-in? 2, 200M size of chromium is too big? It would be too bloated to have such a large plugin for every project.
For these two problems, we finally discussed the following solutions: Change the prerender-spa-plugin to our own component (myPlugin for short). Change the puppeteer dependency in myPlugin to the global myPUppeteer (which is already pointing to its own puppeteer) 3. Make the Puppeteer component that is dependent on prerender-SPa-plugin our own (for relieving strong dependencies, MyPuppeteer for short) 4, I personally download the latest version of chromium in different systems to the FTP of the company 5, modify the download address of chromium in myPuppeteer component, pointing to FTP 6, add error message in myPlugin. Prompt developers to install myPuppeteer globally when require does not exist
The net effect of this operation is that our developers only need to install myPuppeteer globally once the first time they use the plug-in, with no network problems.
Of course, myPlugin should be installed for each project
Here’s how to do it in that order.
Implementation steps
Step one/two
Where does puppeteer depend on in the first place
prerender-SPa-plugin
@prerender/renderer-puppeteer
So we need to replace the dependency on puppeteer in @prerender/renderer-puppeteer
try{
puppeteer = require('prerender-browser')
}catch(err){
console.log("You are using prerender-html-plugin to generate html but no puppeteer installed");
console.log("If you don't want this function, you can set prerender to false in marauder.config");
console.log(If you want this function, you can run:1, NPM install -g prerender-browser 2, NPM link prerender-browser);
}
Copy the code
Prerender-browser is the myPuppeteer component from above.
Try catch is the ability to print an error statement on the console during use.
Here you can see me prompting you to execute two sentences of the node command
1. NPM install -g prerender-browser
2. NPM link prerender-browser
There is a global package reference problem: when I install globally and reference the code, I still get undefined errors. After a query, require queries the required packages from node_modules, up to the top-level global variable. If the global installation does not take effect, it is because the global node_modules path is not configured in the environment variable.
There are a lot of ways to set environment variables on the web, but another easy way is to simply run the node link <package_name> command. The NPM link command can link an NPM package anywhere to the global execution environment so that there are no problems with references not being found.
Step 3/4/5
The variables DEFAULT_DOWNLOAD_HOST and downloadURLs can be found in the browserfetcher. js file of puppeteer. Replace these two variables with the address of the resource accessible within the company.
const DEFAULT_DOWNLOAD_HOST = 'http://www.fanqiangma.com';
const downloadURLs = {
linux: '%s/chromium-browser-snapshots/Linux/chrome-linux.zip',
mac: '%s/chromium-browser-snapshots/Mac/chrome-mac.zip',
win32: '%s/chromium-browser-snapshots/Win32/chrome-win32.zip',
win64: '%s/chromium-browser-snapshots/Win64/chrome-win32.zip'};Copy the code
There are two more pits:
1, browserFetcher. js would have spelled the version number on the download address, for example:
const url = util.format(downloadURLs[this._platform], this._downloadHost,revision);
Copy the code
Revision is the version number that was read from puppeteer’s package.json. This parameter is used to concatenate the download URL and specify the chromium execution address. Here we only need to change the download address, so I removed the parameter revision from all the urls that need to concatenate the version number in the code, otherwise the message will not be able to download.
2. An error is reported that the puppeteer instance cannot be closed at runtime, even if it can be downloaded at installation time. There is also a need to specify the chromium installation address for puppeteer when the puppeteer is launched. Revision parameters are also required to modify the puppeteer.js startup function
static launch(options) {
options.executablePath = this.executablePath('revision');
return Launcher.launch(options);
}
Copy the code
Here the code is confusing, I do not understand, why just modify the download address but did not automatically specify the boot directory, interested big guy can explore.
TIPS:
When extracting and publishing components, we encountered two minor problems: 1. After splitting components into their own components, we need to manually add some dependencies to package.json. 2. I made a very stupid mistake when PUBLISHING components in NPM. First of all, Taobao source cannot publish components and must be replaced by NPM source, but most NPM sources I found on the Internet at that time are http://www.npmjs.org. The source installation depend on is not a problem, but if you want to release must be https://registry.npmjs.org, otherwise it will be an error. “I don’t think SO.” “I don’t think so.” “I don’t think so.”
At the end
At this point, all the work is completed, after the test, our group of small partners can be happy to generate static pages.
This is the first time for me to write an article to record the technology in my work, and it is also the first time to use Markdown. I may not write well. I hope you will forgive me for any wordy points or points not mentioned.
Puppeteer is a prerender-spa-plugin for a vUE project.