关于前端spa项目seo优化改造方案(预渲染,ssr,nuxt比较)

目前的的前端项目为基于vuecli3搭建的spa项目,由于需求提出需要对首页,部分内容页面做seo优化,涉及到前端项目的框架和部分页面的改造。

目录

    • SEO简介
      • 关于收录
      • 关于链接
    • 一、预渲染
    • 二、ssr服务端渲染
    • 三、nuxt

SEO简介

SEO(Search Engine Optimization):汉译为搜索引擎优化。利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。目的是让其在行业内占据领先地位,获得品牌收益。
搜索引擎判断一个网站权重高低的尺度无非两个:收录和链接
目前百度spider抓取新链接的途径有两个:
一是主动出击发现抓取
二就是从百度站长平台的链接提交工具中获取数据,其中通过主动推送功能“收”上来的数据最受百度spider的欢迎。

关于收录

  1. 完整的head信息
<head>
  	<title>PKS信创产业生态平台-为PKS生态建设赋能title>
    <meta name="description" content="PKS信创产业生态平台是国内第一个PKS线上适配平台、第一个基于飞腾2000+ cpu的公有云服务、第一个以PKS为核心的知识内容平台,能为PKS生态合作伙伴、开发者和终端用户提供多样性PKS生态支撑性服务,提升生态服务能力。">
  <meta name="keyword" content="PKS信创产业生态平台,信创,飞腾云主机,麒麟操作系统,自主云服务器,PKS适配迁移,飞腾处理器">
head>
  1. 良好的页面语义化
<header id="header" class="web">
	<h1 id="logo">
	   <a href="/" target="_blank" title="鹏翔书画装裱培训">书画字画装裱培训_裱字裱画学习 - 山东曹州(菏泽)鹏翔书画装裱培训中心a>
	h1>
header>
<nav id="nav" class="web_">
	<ul class="web">
		<li class="nav_home"><a href="/" class="nav_on">网站首页a>li>
		<li><a href="/single_About_1.html">中心简介a>li>
		<li><a href="/article_ZhuangBiaoZhiShi_2.html">装裱知识a>li>
		<li><a href="/article_ZhuangBiaoZaTan_3.html">装裱杂谈a>li>
		<li><a href="/article_ZhuangBiaoZhaoSheng_4.html">招生信息a>li>
		<li><a href="/ZhuangBiao_ZhuangBiaoZuoPin_5.html">装裱作品a>
		li><li><a href="/video_ZhuangBiao_6.html">装裱视频a>li>
		<li><a href="/ZhuangBiao_ZhuangBiaoJiaoXue_7.html">现场教学a>li>
		<li><a href="/article_zhuanyejieshao_10.html">专业介绍a>li>
		<li><a href="/single_contact_8.html">联系我们a>li>    
	ul>
nav>
<section id="banner" class="cf">
	<ul>
		<li>
		  <img src="/image/banner1.jpg" alt="三分画,七分裱——书画字画装裱的艺术魅力">
		li>
		<li>
		  <img src="/image/banner2.jpg" alt="继承传统技法,发扬中华文化——鹏翔书画装裱培训">
		  li>
	ul>
section>
  1. 内容
    1. 有固定的更新频率。最起码也要一周一篇内容的更新。否则,搜索引擎会判断你的网站是死站,权重就会下降的。这里需要注意的是频率,不要一次性更新很多,然后很长时间不更新。
    2. 内容一定要有原创性。
    3. 坚持更新。每一条都要设置关键词和描述,并且不雷同。
    4. 如果是图片类内容的添加,一定要附注一些图片相关的文字内容。搜索引擎自己识别图片内容的效率非常低下,因此文字是不可缺少的。
    5. 视频或者其他媒体内容,比照图片处理。

关于链接

链接分为三种,一种是网站内部的链接,简称内链。比如从首页进入栏目页面,从栏目页面进入内容页面。第二种是网站外部链接,简称外站链接。第三种是别人的网站给你的网站的链接,简称外链。下面我逐一阐述。

  1. 网站内链设计
    在网站内,尽量让网页形成交叉的网状链接设计。很多企业网站在设计得时候,到达了内容页面之后,只能返回到列表页面再进入另外一条内容,这样的树状链接设计是低效的。
    简单的做法是在内容页面加上“上一篇”、“下一篇”这样的链接,可以让蜘蛛从一个内容页面直接进入另一个内容页面。从而达到网状内链的目的。
    但是仅仅是如此是不够的。我个人建议在内容页面下面最好加上一个“相关内容”的模块,里面可以罗列一些与内容相关的其他的内容链接,十条还是二十条随便,根据排版来即可。
    有条件的话,可以让这部分内容是活的,也就是随机的。这样爬虫进来的时候,会不断的得到不同的内容,从而抓取到更多的内容。
  2. 外站链接设计
    一句话总结,尽可能的少。尽量让蜘蛛来到你的网站后,就在你的网站里面转圈,走不出去才好。
  3. 外链设计
    一句话总结,尽可能的多!
    1. 基础做法是和其他站长交换友情链接,进阶做法是花钱买一些外链。但要注意的是,要找同类型的网站或者相关的站点来做。
    2. 这里有一个误区是将所有的外链都指向你的网站的首页。这也不是不可以,但是我们还可以做得更好一些,就是有针对的链接到你的特定内容的内页,效果会更好。比如,你提供某服务项目,你有一个内页是专门介绍这个项目的,那么在其他站点增加一个链接到你这个内页,效果也是很好的。
    3. 如果你有较强的内容生产能力,可以写一些优秀的文章,然后让其他网站转载你的文章,并标注首发地址是你的这个文章的网址,这样效果会很好的。
    4. 还有就是找大型网站做软文,比如写一些公司的创办理念,创始人的介绍等等,全部都链接到你的网站,效果也是很好的。

目前前端所面临的的问题是内容无法被爬虫获取,页面的title,keyword,descriptions等无法动态变化
经过调研后,目前有以下几种解决方案提供:

一、预渲染

构建阶段生成匹配预渲染路径的 html 文件(注意:每个需要预渲染的路由都有一个对应的 html)。构建出来的 html 文件已经有静态数据,需要ajax数据的部分未构建。
如果你调研服务器端渲染 (SSR) 只是用来改善少数营销页面(例如首页,关于我们,联系我们 等)的 SEO,那么你可能需要预渲染。

  1. 增加prerender-spa-plugin puppeteer两个依赖
    npm install prerender-spa-plugin puppeteer
  2. 修改vue.config.js
config.plugins.push(
        new PrerenderSPAPlugin({
          // 生成文件的路径,也可以与webpakc打包的一致。
          // 下面这句话非常重要!!!
          // 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
          staticDir: path.join(__dirname, 'dist'),

          // Optional - The location of index.html
          // indexPath: path.join(__dirname, '../dist', 'index.html'),

          // 对应自己的路由文件,比如a有参数,就需要写成 /a/param1。
          routes: ['/', '/school'],

          // 预渲染代理接口
          // server: {
          //   proxy: {
          //     '/api': {
          //       target: 'http://localhost:9018',
          //       secure: false
          //     }
          //   }
          // },

          // 这个很重要,如果没有配置这段,也不会进行预编译
          renderer: new Renderer({
            // headless: false,
            renderAfterDocumentEvent: 'render-event', // 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
            args: ['--no-sandbox', '--disable-setuid-sandbox']
          })
        })
      )
  1. 在入口main.js中添加
new Vue({
  router,
  store,
  // 添加mounted,不然不会执行预编译
  mounted() {
    document.dispatchEvent(new Event('render-event'))
  },
  render: h => h(App)
}).$mount('#app')
  1. 运行打包命令npm run build后根据配置出现文件
    关于前端spa项目seo优化改造方案(预渲染,ssr,nuxt比较)_第1张图片
  2. 放到nginx中进行测试,效果如图
    关于前端spa项目seo优化改造方案(预渲染,ssr,nuxt比较)_第2张图片

总结:

  1. 配合vue-meta-info可以针对spa的vue项目配置单独的title,meta,keyword,descriptions等
  2. 预渲染无法渲染异步加载的数据
  3. 配置简单,开箱即用,对原有框架基本没有影响
  4. 需要路由为history模式

二、ssr服务端渲染

在 2.3 发布后我们发布了一份完整的构建 Vue 服务端渲染应用的指南。这份指南非常深入,适合已经熟悉 Vue、webpack 和 Node.js 开发的开发者阅读。请移步 ssr.vuejs.org
与传统 SPA (单页应用程序 (Single-Page Application)) 相比,服务器端渲染 (SSR) 的优势主要在于:

  1. 更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。

请注意,截至目前,Google 和 Bing 可以很好对同步 JavaScript
应用程序进行索引。在这里,同步是关键。如果你的应用程序初始展示 loading 菊花图,然后通过 Ajax
获取内容,抓取工具并不会等待异步完成后再行抓取页面内容。也就是说,如果 SEO
对你的站点至关重要,而你的页面又是异步获取内容,则你可能需要服务器端渲染(SSR)解决此问题。

  1. 更快的内容到达时间 (time-to-content),特别是对于缓慢的网络情况或运行缓慢的设备。无需等待所有的 JavaScript 都完成下载并执行,才显示服务器渲染的标记,所以你的用户将会更快速地看到完整渲染的页面。通常可以产生更好的用户体验,并且对于那些「内容到达时间(time-to-content) 与转化率直接相关」的应用程序而言,服务器端渲染 (SSR) 至关重要。

针对spa项目的修改(vue-cli3):

  1. 引入相关依赖
    npm install vue-server-renderer lodash.merge webpack-node-externals cross-env --registry=https://registry.npm.taobao.org --save-dev
  2. 在根目录下新建server.js
    服务端使用koa2
    npm install koa koa-static --save --registry=https://registry.npm.taobao.org
// server.js
// 第 1 步:创建一个 Vue 实例
const Vue = require("vue");

const Koa = require("koa");
const app = new Koa();
// 第 2 步:创建一个 renderer
const renderer = require("vue-server-renderer").createRenderer();


// 第 3 步:添加一个中间件来处理所有请求
app.use(async (ctx, next) => {
  const vm = new Vue({
    data: {
      title: "ssr example",
      url: ctx.url
    },
    template: `
访问的 URL 是: {{ url }}
`
}); // 将 Vue 实例渲染为 HTML renderer.renderToString(vm, (err, html) => { if(err){ ctx.res.status(500).end('Internal Server Error') return } ctx.body = html }); }); const port = 3000; app.listen(port, function() { console.log(`server started at localhost:${port}`); });
  1. 新增template.html文件

<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Documenttitle>
  head>
  <body>
    
  body>
html>

修改main.js

// main.js
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from "./router";

// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp () {
  const router = createRouter();
  const app = new Vue({
    router,
    // 根实例简单的渲染应用程序组件。
    render: h => h(App)
  })
  return { app,router }
}

创建entry-client.js文件

import { createApp } from './main'

// 客户端特定引导逻辑……
const { app } = createApp()

// 这里假定 App.vue 模板中根元素具有 `id="app"`
app.$mount('#app')

创建entry-server.js文件

import { createApp } from "./main";

export default context => {
  // 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise,
  // 以便服务器能够等待所有的内容在渲染前,
  // 就已经准备就绪。
  return new Promise((resolve, reject) => {
    const { app, router } = createApp();

    // 设置服务器端 router 的位置
    router.push(context.url);

    // 等到 router 将可能的异步组件和钩子函数解析完
    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents();
      // 匹配不到的路由,执行 reject 函数,并返回 404
      if (!matchedComponents.length) {
        return reject({
          code: 404
        });
      }
      // Promise 应该 resolve 应用程序实例,以便它可以渲染
      resolve(app);
    }, reject);
  });
};

  1. 修改router.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

export function createRouter(){
  return new Router({
    mode: 'history', //一定要是history模式
    routes: [
      {
        path: '/',
        name: 'home',
        component: Home
      },
      {
        path: '/about',
        name: 'about',
        component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
      }
    ]
  })
}
  1. 修改vue.config.js,增加服务端渲染相关配置
// vue.config.js

const VueSSRServerPlugin = require("vue-server-renderer/server-plugin");
const VueSSRClientPlugin = require("vue-server-renderer/client-plugin");
const nodeExternals = require("webpack-node-externals");
const merge = require("lodash.merge");
const TARGET_NODE = process.env.WEBPACK_TARGET === "node";
const target = TARGET_NODE ? "server" : "client";


module.exports = {
  css: {
    extract: process.env.NODE_ENV === 'production'
  },
  configureWebpack: () => ({
    // 将 entry 指向应用程序的 server / client 文件
    entry: `./src/entry-${target}.js`,
    // 对 bundle renderer 提供 source map 支持
    devtool: 'source-map',
    target: TARGET_NODE ? "node" : "web",
    node: TARGET_NODE ? undefined : false,
    output: {
      libraryTarget: TARGET_NODE ? "commonjs2" : undefined
    },
    // https://webpack.js.org/configuration/externals/#function
    // https://github.com/liady/webpack-node-externals
    // 外置化应用程序依赖模块。可以使服务器构建速度更快,
    // 并生成较小的 bundle 文件。
    externals: TARGET_NODE
      ? nodeExternals({
          // 不要外置化 webpack 需要处理的依赖模块。
          // 你可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件,
          // 你还应该将修改 `global`(例如 polyfill)的依赖模块列入白名单
          whitelist: [/\.css$/]
        })
      : undefined,
    optimization: {
          splitChunks: undefined
    },
    plugins: [TARGET_NODE ? new VueSSRServerPlugin() : new VueSSRClientPlugin()]
  }),
  chainWebpack: config => {
    config.module
      .rule("vue")
      .use("vue-loader")
      .tap(options => {
        merge(options, {
          optimizeSSR: false
        });
      });
      
    // fix ssr hot update bug
    if (TARGET_NODE) {
      config.plugins.delete("hmr");
    }
  }
};

  1. 修改package.json,增加三个脚本
"build:client": "vue-cli-service build",
"build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build --mode server",
"build:win": "npm run build:server && move dist\\vue-ssr-server-bundle.json bundle && npm run build:client && move bundle dist\\vue-ssr-server-bundle.json",
  1. 运行打包命令和启动服务
    npm run build:win
    node server.js
  2. store的引入和router基本一致,导出createStore方法并在main.js中引入即可
    参考文章:
    https://juejin.im/post/5b98e5875188255c8320f88a

总结:

  1. 对原有框架变动较大,路由,store,请求方式,打包方式均需修改
  2. 涉及较多nodejs知识,对不熟悉node.js的同学可能比较吃力

三、nuxt

从头搭建一个服务端渲染的应用是相当复杂的。幸运的是,我们有一个优秀的社区项目 Nuxt.js
让这一切变得非常简单。Nuxt 是一个基于 Vue 生态的更高层的框架,为开发服务端渲染的 Vue 应用提供了极其便利的开发体验。更酷的是,你甚至可以用它来做为静态站生成器。推荐尝试。
原理图:
关于前端spa项目seo优化改造方案(预渲染,ssr,nuxt比较)_第3张图片
1、用户打开浏览器,输入网址请求到Node.js
2、部署在Node.js的应用Nuxt.js接收浏览器请求,并请求服务端获取数据
3、Nuxt.js获取到数据后进行服务端渲染
4、Nuxt.js将html网页响应给浏览器

  1. 修改思路
  • 路由直接放在pages文件夹下,相当于做了多入口配置
  • 异步数据的获取通过asyncData方法获得
  • 页面布局放在layouts文件夹下
  • 每个页面都需要的js放在plugins下
  1. 区别
  • 相较于预渲染,nuxt可以处理异步数据的获取
  • 相较于ssr改造,框架搭建更加简便,社区资源更加丰富,开发中遇到困难更容易找到解决办法
  • 将seo优化的页面单独抽离成项目,独立部署,和其余项目并行开发,实现解耦
    样例效果图,返回的文件已经有了动态数据,更有利于seo爬虫的获取
    关于前端spa项目seo优化改造方案(预渲染,ssr,nuxt比较)_第4张图片
  1. 公共组件的处理(构建自己的包管理仓库,组件打成npm包进行引用)
  • 在公司内部服务器上搭建npm代码仓库(sinopia,pm2)
  • 将一些公共组件如头部,尾部以及基本框架(包含登录,权限逻辑)直接以组件形式打成npm包和脚手架并发布至内部仓库中
  • 在项目中运行npm i xxx安装依赖,避免了大量的重复代码和合并过程中的繁琐操作(只需修改依赖版本号即可)
  1. 公共依赖的处理(webpack打lib包)
  • 将所有项目中共同用到的第三方依赖库通过webpack的library打成一个js,放在内部服务器中
  • 之后如果有做cdn加速将大幅提高页面加载速度和打包构建速度

你可能感兴趣的:(前端)