您的位置:首页 > Web前端

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

2020-05-10 04:18 1966 查看

目前的的前端项目为基于vuecli3搭建的spa项目,由于需求提出需要对首页,部分内容页面做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. 内容 [ol] 有固定的更新频率。最起码也要一周一篇内容的更新。否则,搜索引擎会判断你的网站是死站,权重就会下降的。这里需要注意的是频率,不要一次性更新很多,然后很长时间不更新。
    2. 内容一定要有原创性。
    3. 坚持更新。每一条都要设置关键词和描述,并且不雷同。
    4. 如果是图片类内容的添加,一定要附注一些图片相关的文字内容。搜索引擎自己识别图片内容的效率非常低下,因此文字是不可缺少的。
    5. 视频或者其他媒体内容,比照图片处理。
    [/ol]

    关于链接

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

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

    目前前端所面临的的问题是内容无法被爬虫获取,页面的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后根据配置出现文件
    2. 放到nginx中进行测试,效果如图

    总结:

    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: `<div>访问的 URL 是: {{ url }}</div>`
    });
    // 将 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文件
    <!DOCTYPE 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>Document</title>
    </head>
    <body>
    <!--vue-ssr-outlet-->
    </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 应用提供了极其便利的开发体验。更酷的是,你甚至可以用它来做为静态站生成器。推荐尝试。
    原理图:

    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爬虫的获取
    1. 公共组件的处理(构建自己的包管理仓库,组件打成npm包进行引用)
    • 在公司内部服务器上搭建npm代码仓库(sinopia,pm2)
    • 将一些公共组件如头部,尾部以及基本框架(包含登录,权限逻辑)直接以组件形式打成npm包和脚手架并发布至内部仓库中
    • 在项目中运行
      npm i xxx
      安装依赖,避免了大量的重复代码和合并过程中的繁琐操作(只需修改依赖版本号即可)
    1. 公共依赖的处理(webpack打lib包)
    • 将所有项目中共同用到的第三方依赖库通过webpack的library打成一个js,放在内部服务器中
    • 之后如果有做cdn加速将大幅提高页面加载速度和打包构建速度
    AwesomeCPA 原创文章 17获赞 0访问量 1751 关注 私信
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: