网页预渲染方案

起因

业务需要,对官网进行改版,服务对象由C端转为B端,对SEO有比较强的需求。

目前项目是采用vue框架,多页配置。页面内容由js动态加载,不利于搜索引擎抓取,首屏渲染速度也有一定影响。

为了解决爱尖子官网的SEO优化,及页面渲染速度优化问题,有如下几种方案。

  1. 后端渲染不使用vue,react框架(node express,jsp)
    • 该方案直接pass
  2. vue SSR
    • 需要对项目结构重新构建
    • 需要node服务器,相对于静态文件来说,服务器压力比较大
    • 服务端渲染,某些生命周期不可用
  3. nuxt
    • 没咋用过
  4. prerender-spa-plugin
    • 该插件也是基于puppeteer无头浏览器
    • 问题是每当站点内容需要更新时,都需要重新打包发布
  5. 自己使用无头浏览器(puppeteer),生成静态页面
    • 自己写脚本比较灵活
    • 内容变化时,可以随时执行脚本,更新静态内容

方案

我们选择第5种方案,以下是基础流程图。

黄色部分为预渲染流程

流程图,大图看这里

image

实施

搭建预渲染服务

npm 下载 puppeteer,由于网络问题,使用cnpm

cnpm install puppeteer --save

npm 下载express,搭建node服务

使用puppeteer 获取html

const puppeteer = require('puppeteer');                               
let browser;                                                          
async function createInstance(options) {                              
  if(browser) return;                                                 
    const config = Object.assign(                                     
          {},                                                         
          {                                                           
                  ignoreHTTPSErrors: true,                            
                  devtools: false,                                    
                  headless: true,                                     
                  args: ['--no-sandbox', '--disable-setuid-sandbox'], 
                },                                                    
          options                                                     
        );                                                            
                                                                      
    browser = await puppeteer.launch(config);                         
}                                                                     
async function closeInstance() {                                      
}                                                                     
module.exports =                                                      
  async function getHtml(url) {                                       
    await createInstance();                                           
    const page = await browser.newPage();
    // 设置header 重要 nginx根据此header做跳转
    page.setExtraHTTPHeaders({prerender:"ajz"});
    await page.goto(url, {waitUntil: 'networkidle2'});                
    const html = await page.$eval('html', e => e.outerHTML);          
    await page.close();                                               
    return html;                                                      
}                                                                                                                                  

编写请求处理逻辑

 var express = require('express');                        
 var router = express.Router();                           
 var getHtml = require('../getHtml');                     
                                                          
 /* GET home page. */                                     
 router.get('/', function(req, res, next) {              
      res.render('index', { title: 'Express' });         
    });                                                  
 router.get('/render', async function(req, res, next) {  
      let query = req.query.url;                         
      if (query) {                                       
           let url = decodeURIComponent(query);        
           let html = await getHtml(url);              
           res.send(html);                             
       } else {                                      
           res.send('url is undefind')                 
       }                                              
    })                                                   
                                                         
 module.exports = router;                                
nginx配置
 server {                                                     
         listen 80;                                           
         server_name localhost testy.aijianzi.com;            
         access_log /var/log/nginx/testy.access.log;       
         location / {                                         
                 root /www/www.aijianzi.com/dist-static; 
                 // 根据header跳转
                 if ($http_prerender = "ajz") {               
                         root /www/www.aijianzi.com/dist-static;
                 }                                            
                 index index.html;                            
                                                              
                 autoindex off;                               
                                                              
         }                                                    
 # redirect server error pages to the static page /50x.html   
 error_page 500 502 503 504 /50x.html;                        
 location = /50x.html {                                       
         root /var/www/nginx-default;                         
 }                                                            
  # deny access to .htaccess files, if Apache’s document root 
 location ~ /\.ht {                                           
         deny all;                                            
 }                                                            
 }                                                            

配置之后的访问效果

通过预渲染服务器访问 获取到真实的页面

image

用户直接通过域名访问,获取到已经渲染好的静态页面

image
编写shell脚本

通过脚本,快捷的更新静态文件。可手动触发,计划任务定时触发

#!/bin/bash
static-workspace="/www/www.aijianzi.com/dist-static"
readonly static-workspace
cd ${static-workspace}
curl -o index.html http://localhost:3000/render?url=http://testy.aijianzi.com
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • TL;DR 无头 Chrome 是一个将动态 JS 页面转成静态 HTML 页面的即插即用的解决方案。将其运行于 ...
    苏星河阅读 6,186评论 0 4
  • 一:什么是闭包?闭包的用处? (1)闭包就是能够读取其他函数内部变量的函数。在本质上,闭包就 是将函数内部和函数外...
    xuguibin阅读 9,604评论 1 52
  • 基于Vue的一些资料 内容 UI组件 开发框架 实用库 服务端 辅助工具 应用实例 Demo示例 element★...
    尝了又尝阅读 1,151评论 0 1
  • UI组件 element- 饿了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的组件库 m...
    柴东啊阅读 15,853评论 2 140
  • 大家好,首先我是一个职专的学生,我现在跟着学校在工厂实习,以后我会每天给大家讲述我的工厂生活,希望对大家以后有所帮...
    帅帅的小师哥阅读 433评论 2 2