vue项目改造nuxtSEO优化

欢迎大家进群,一起探讨学习

欢迎大家进群,一起讨论学习

微信公众号,每天给大家提供技术干货

在这里插入图片描述

博主技术平台地址


博主开源微服架构前后端分离技术博客项目源码地址,欢迎各位star


1. 背景

今年开源了一个博客系统;但其中一个项目开发完成后,遇到了大问题:由于采用的是Vue的单页面模式进行开发,网站信息搜索引擎无法做索引,导入收录只有是首页!

搜索引擎无法进行索引的核心原因就是,其在爬取网站数据的时候,是不会执行其中包含的JS过程的;而采用Vue的方式开发的应用,其数据都是来源于axios或者其它的ajax方法获取的数据!也就是说,想要友好的支持搜索引擎,就必须采用服务器端渲染的相关技术,比如JSP,就是一个典型的服务器端渲染技术,用户请求一个地址然后展示到浏览器中的数据都是服务器端处理好的,浏览器只管展示;又比如静态页面,所有页面都是预先编写或生成好的,浏览器将请求拿到的数据直接展现即可。

对于Vue生态来说,有以下方案可以实现服务器端渲染:

1.SSR服务器渲染;
2.静态化;
3.预渲染prerender-spa-plugin;
4.使用Phantomjs针对爬虫做处理。

对于Java生态来说,有以下方案可以实现服务器端渲染:

1.JSP
2.模板引擎,如Thymeleaf/Velocity等;

JSP基本已经算是步入老年,除了一些非常古老的系统,新的相信已经很少人使用。Thymeleaf在Spring官网文档中都有相关的集成案例,如果是一个全新的项目,应该算是比较好的方案;但对于已经完成前端所有功能开发的项目来说,使用模板引擎重新实现一套成本过高。对于我来说,也只能选择SSR服务器渲染方案了。

关于Vue服务器端渲染的介绍,可以参考官方文档:http://cn.vuejs.org/v2/guide/ssr.html。这其中主要有两种方式,其一是使用vue-server-renderer插件,其二是使用nuxt;在本项目做改造时,关于vue-server-renderer的介绍不如现有文档清晰,因此使用了nuxt的方案。

2. 现有博客项目改造

nuxt与传统的vue-cli项目,在目录结构、路由、组件生命周期上都有所不同;主要的改造步骤如下:

2.1创建项目

npx create-nuxt-app <项目名>

2.2将你的文件都复制过来

在这里插入图片描述

2.3 package.json

在这里插入图片描述

2.4安装组件

在这里插入图片描述

2.5 nuxt.config.js 引入组件

const webpack = require('webpack')
export default {
    // Global page headers: http://go.nuxtjs.dev/config-head
    head: {
        htmlAttrs: {
            lang: 'en'
        },
        meta: [
            { charset: 'utf-8' },
            { name: 'viewport', content: 'width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no' },
            { hid: 'description', name: 'description', content: '' }
        ],
        link: [
            { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
        ],
        // script: [
        //     //<!-- 51统计 -->
        //     {
        //         type: 'text/javascript',
        //         src: 'http://js.users.51.la/21038499.js'
        //     },
        //     // <!-- 百度统计 -->
        //     {
        //         src: 'http://hm.baidu.com/hm.js?88b551f7172ccc8bbee6f8928f5abbce'
        //     },
        //     //<!-- 360自动收录 -->
        //     {
        //         id: 'sozz',
        //         src: 'http://jspassport.ssl.qhimg.com/11.0.1.js?d182b3f28525f2db83acfaaf6e696dba'
        //     },
        // ],
    },

    // Global CSS: http://go.nuxtjs.dev/config-css
    css: [
        'view-design/dist/styles/iview.css',
        '@/icon/iconfont.css',
        'element-ui/lib/theme-chalk/index.css',
        '@/assets/stylus/index.styl',
        '@/assets/stylus/article.css',
        '@/assets/stylus/article.styl',
        '@/assets/stylus/main.css',
        '@/assets/stylus/theme.styl',
        '@/assets/stylus/mixin.styl',
        '@/assets/stylus/index.css',
        '@/assets/stylus/base.styl',
        'mavon-editor/dist/css/index.css',
        'babel-polyfill',
        'viewerjs/dist/viewer.css',
        '@/assets/css/main.css',
        'vue-social-share/dist/client.css'
    ],

    // Plugins to run before rendering page: http://go.nuxtjs.dev/config-plugins
    plugins: [{
            src: '@/plugins/view-ui',
            //是否需要服务端熏染
            ssr: true,
        }, {
            src: '~/icon/iconfont.js',
            //是否需要服务端熏染
            ssr: false,
        }, //elementui
        {
            src: '@/plugins/element-ui',
            //是否需要服务端熏染
            ssr: true,
        }
        //引入jquery的爱心js
        , {
            src: '@/assets/js/love',
            //是否需要服务端熏染
            ssr: false,
        }, {
            //mavon-editor 富文本编辑器
            src: '@/plugins/mavon-editor',
            //是否需要服务端熏染
            ssr: true,
        },
        //wangeditor
        { src: '@/plugins/wangeditor', ssr: false },
        {
            src: '@/plugins/vue-particles',
            //是否需要服务端熏染
            ssr: false,
        }, {
            //兼容ie
            src: '@/plugins/hls',
            //是否需要服务端熏染
            ssr: false,
        },
        {
            //移动端手指滑动监控插件
            src: '@/plugins/vue-touch',
            //是否需要服务端熏染
            ssr: false,
        }, {
            //滑动验证码
            src: '@/plugins/vue-monoplasty-slide-verify',
            //是否需要服务端熏染
            ssr: true,
        }, {
            //图片预览插件
            src: '@/plugins/v-viewer',
            //是否需要服务端熏染
            ssr: true,
        }, {
            //axios
            src: "~/plugins/axios",
            ssr: true
        }, {
            //分享插件
            src: "~/plugins/vue-social-share",
            ssr: false
        },
        //配置路由守卫
        { src: '@/plugins/permission', ssr: true },
        { src: '@/plugins/vue-video-player', ssr: false },
    ],
    //设置接口地址环境变量
    env: {
        baseUrl: process.env.BASE_URL
    },
    // Auto import components: http://go.nuxtjs.dev/config-components
    components: true,

    // Modules for dev and build (recommended): http://go.nuxtjs.dev/config-modules
    buildModules: [],

    // Modules: http://go.nuxtjs.dev/config-modules
    modules: [
        //cookie
        'cookie-universal-nuxt',
        //axios
        '@nuxtjs/axios',
    ],
    //路由进度条配置
    loading: {
        color: '#19be6b',
    },
    // Build Configuration: http://go.nuxtjs.dev/config-build
    build: {
        //使用jquery
        plugins: [
            new webpack.ProvidePlugin({ jQuery: "jquery", $: "jquery" })
        ],
    },
}

2.6配置路由守卫(路由拦截器)

在这里插入图片描述

// 不重定向白名单 [路由守卫]
const whiteList = ['/login', '/', '/regist', '/articleList', '/articles/search', '/recover', '/codes', '/talk', '/tool', "/article", "/messageBoard", "/leaderboard", "/code", "/classroom", "/tv", "/recharge"]
export default ({ app, $cookies, store }) => {
    app.router.beforeEach((to, from, next) => {
        //服务端直接放行,只做客户端处理
        let isClient = process.client
        if (isClient) {
            if ($cookies.get("token")) {
                if (to.path === '/login') {
                    next({ path: '/' })
                } else {
                    next()
                }
            } else {
                if (whiteList.indexOf(to.path) !== -1) {
                    next()
                } else {
                    next('/login')
                }
            }
        }
        next()
    })
}

2.7配置静态渲染模板

默认在根目录下,文件名字为app.html {{ HTML_ATTRS }} {{ HEAD_ATTRS }}都是固定写法

<!-- 模板,用来做定义script代码 -->
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>

<head {{ HEAD_ATTRS }}>
    {{ HEAD }}
    <!-- 51统计 v5 -->
    <script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
    <script>
        LA.init({
            id: "JH1Squ4C6adLVG0B",
            ck: "JH1Squ4C6adLVG0B"
        })
    </script>
    <!-- 51统计 v6 -->
    <script type="text/javascript" src="http://js.users.51.la/21038499.js"></script>
    <!-- 360自动收录 -->
    <script>
        (function() {
            var src = "http://jspassport.ssl.qhimg.com/11.0.1.js?d182b3f28525f2db83acfaaf6e696dba";
            document.write('<script src="' + src + '" id="sozz"><\/script>');
        })();
    </script>
</head>

<body {{ BODY_ATTRS }} class="docs">
    {{ APP }}
</body>

</html>

2.8 标签使用

由于很多是组件是客户端使用的,如果你在服务端渲染,就会报doment找不到的问题所以采用nuxt的client-only标签

在这里插入图片描述

2.9路由配置

具体我这里不做演示了,请参考官方文档 http://www.nuxtjs.cn/guide/routing

在这里插入图片描述

2.10数据存储

请使用cookie-universal-nuxt 进行存储,如果使用cookie.js会出现只能客户端获取收据服务端获取不到

在这里插入图片描述

2.11数据请求

请安装官网推荐的@nuxtjs/axios

封装axios

在这里插入图片描述

import {
    AESDecrypt
} from '@/assets/js/aes'
export default function({ store, redirect, app: { $axios, $cookies } }) {

    // 数据访问前缀,指定配置的环境变量接口地址
    $axios.defaults.baseURL = process.env.baseUrl

    // request拦截器,我这里设置了一个token,当然你可以不要
    $axios.onRequest(config => {
        //请求超时时间
        config.timeout = 600000
        if (store.state.token) {
            //如果有toekn才携带请求头中的token到后端
            config.headers.common['x-access-token'] = store.state.token
        }
        //服务端请求数据不需要参数加密和响应加密
        if (!process.client) {
            config.headers.common['x-aes'] = '0';
        }
    })
    $axios.onError(error => {
            //如果客户端密钥已经失效或者token失效提示用户重新登录
            if (error.response.status === 678) {
                $cookies.remove("token")
                $cookies.remove("user")
                store.commit('removeUserInfo');
                return Promise.reject(error);
            }
            if (error.response.status === 401) {
                redirect("/login")
                return Promise.reject(error);
            }
            return Promise.reject(error.response)
        })
        // response拦截器,数据返回后,你可以先在这里进行一个简单的判断
    $axios.interceptors.response.use(response => {
        /**
         * code为非200是抛错 可结合自己业务进行修改
         */
        var res;
        //判断服务器返回是否是加密数据
        if (response.data.responseData != null && response.data.responseData != "") {
            //进行解密数据
            let aesDecrypt = AESDecrypt(response.data.responseData, $cookies.get("key"));
            //解密后转换成json
            res = JSON.parse(aesDecrypt);
        } else {
            //不是加密的数据正常返回
            res = response.data
        }
        return res
    })
}

使用

在这里插入图片描述

2.12配置默认错误页面

在这里插入图片描述

<template>
  <div class="wscn-http404-container">
    <div class="wscn-http404">
      <div class="pic-404">
        <img
          v-if="error.statusCode === 404"
          src="@/assets/images/404.png"
          alt="404"
          class="pic-404__parent"
        />
        <img
          v-else
          src="@/assets/images/500.gif"
          alt="500"
          class="pic-404__parent"
        />
        <img
          class="pic-404__child left"
          src="@/assets/images/404_cloud.png"
          alt="404"
        />
        <img
          class="pic-404__child mid"
          src="@/assets/images/404_cloud.png"
          alt="404"
        />
        <img
          class="pic-404__child right"
          src="@/assets/images/404_cloud.png"
          alt="404"
        />
      </div>
      <div class="bullshit">
        <div class="bullshit__oops">OOPS!</div>
        <div class="bullshit__info">
          All rights reserved
          <a style="color: #20a0ff" href="http://xiyanit.cn" target="_blank"
            >wallstreetcn</a
          >
        </div>
        <div class="bullshit__headline" v-if="error.statusCode === 404">
          {{ message }}
        </div>
        <div class="bullshit__headline" v-else>{{ message1 }}</div>
        <div class="bullshit__info">
          Please check that the URL you entered is correct, or click the button
          below to return to the homepage.
        </div>
        <a href="/" class="bullshit__return-home">Back to home</a>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: ["error"],
  name: "Page404",
  computed: {
    message() {
      return "The page you visited has flown away! ! ! ! !";
    },
    message1() {
      return "Server Error! ! ! ! !";
    },
  },
};
</script>

<style lang="stylus" scoped>
.wscn-http404-container {
  margin: 250px 0px;
}

.wscn-http404 {
  display: flex;
  align-items: center;
  justify-content: center;

  .pic-404 {
    position: relative;
    float: left;
    width: 600px;
    overflow: hidden;

    &__parent {
      width: 100%;
    }

    &__child {
      position: absolute;

      &.left {
        width: 80px;
        top: 17px;
        left: 220px;
        opacity: 0;
        animation-name: cloudLeft;
        animation-duration: 2s;
        animation-timing-function: linear;
        animation-fill-mode: forwards;
        animation-delay: 1s;
      }

      &.mid {
        width: 46px;
        top: 10px;
        left: 420px;
        opacity: 0;
        animation-name: cloudMid;
        animation-duration: 2s;
        animation-timing-function: linear;
        animation-fill-mode: forwards;
        animation-delay: 1.2s;
      }

      &.right {
        width: 62px;
        top: 100px;
        left: 500px;
        opacity: 0;
        animation-name: cloudRight;
        animation-duration: 2s;
        animation-timing-function: linear;
        animation-fill-mode: forwards;
        animation-delay: 1s;
      }

      @keyframes cloudLeft {
        0% {
          top: 17px;
          left: 220px;
          opacity: 0;
        }

        20% {
          top: 33px;
          left: 188px;
          opacity: 1;
        }

        80% {
          top: 81px;
          left: 92px;
          opacity: 1;
        }

        100% {
          top: 97px;
          left: 60px;
          opacity: 0;
        }
      }

      @keyframes cloudMid {
        0% {
          top: 10px;
          left: 420px;
          opacity: 0;
        }

        20% {
          top: 40px;
          left: 360px;
          opacity: 1;
        }

        70% {
          top: 130px;
          left: 180px;
          opacity: 1;
        }

        100% {
          top: 160px;
          left: 120px;
          opacity: 0;
        }
      }

      @keyframes cloudRight {
        0% {
          top: 100px;
          left: 500px;
          opacity: 0;
        }

        20% {
          top: 120px;
          left: 460px;
          opacity: 1;
        }

        80% {
          top: 180px;
          left: 340px;
          opacity: 1;
        }

        100% {
          top: 200px;
          left: 300px;
          opacity: 0;
        }
      }
    }
  }

  .bullshit {
    position: relative;
    float: left;
    width: 300px;
    padding: 30px 0;
    overflow: hidden;

    &__oops {
      font-size: 32px;
      font-weight: bold;
      line-height: 40px;
      color: #1482f0;
      opacity: 0;
      margin-bottom: 20px;
      animation-name: slideUp;
      animation-duration: 0.5s;
      animation-fill-mode: forwards;
    }

    &__headline {
      font-size: 20px;
      line-height: 24px;
      color: #222;
      font-weight: bold;
      opacity: 0;
      margin-bottom: 10px;
      animation-name: slideUp;
      animation-duration: 0.5s;
      animation-delay: 0.1s;
      animation-fill-mode: forwards;
    }

    &__info {
      font-size: 13px;
      line-height: 21px;
      color: grey;
      opacity: 0;
      margin-bottom: 30px;
      animation-name: slideUp;
      animation-duration: 0.5s;
      animation-delay: 0.2s;
      animation-fill-mode: forwards;
    }

    &__return-home {
      display: block;
      float: left;
      width: 110px;
      height: 36px;
      background: #1482f0;
      border-radius: 100px;
      text-align: center;
      color: #ffffff;
      opacity: 0;
      font-size: 14px;
      line-height: 36px;
      cursor: pointer;
      animation-name: slideUp;
      animation-duration: 0.5s;
      animation-delay: 0.3s;
      animation-fill-mode: forwards;
    }

    @keyframes slideUp {
      0% {
        transform: translateY(60px);
        opacity: 0;
      }

      100% {
        transform: translateY(0);
        opacity: 1;
      }
    }
  }
}
</style>

大概基本改造完了,git源码地址:http://gitee.com/bright-boy/xiyan-blog

原文地址:https://blog.csdn.net/qq_40942490/article/details/116931179

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


前端里面如何进行搜索引擎优化(SEO) 如何进行SEO优化: (1) 避免head标签js堵塞: 所有放在head标签里面的js和css都会堵塞渲染;如果这些css和js需要加载很久的话,那么页面就空白了; 解决办法:一是把script放到body后面,这也是很多网站采取的方法。 第二种是给scri
网站页面(前端)seo优化方法及建议 很多时候,网站页面(前端)seo优化所涉及的点在于页面精简。本教程概述了如何对网页进行精简,以及提供相关建议,加快网站加载速度,提升网站性能。 从相反方面考虑,如果网页庞杂,网页打开速度慢,会有什么后果? 首先,网页代码 […] 很多时候,网站页面(前端)seo
网站流量短时间内的少量波动一般来说是正常现象。当流量波动持续时间较长且幅度较大时,则需要排查原因;建议站长进行以下排查跟处理:
持续输出原创优质内容,获得更多的用户点击与认可,会提高网站的评分,从而获得更多的搜索展现。
百度信息流配置为用户的自然流量,即会根据用户属性和配置的内容进行匹配后综合决定是否进行展示,因此需要开发者不断优化素材从而获取流量。
百度搜索的索引量与流量有什么关系?百度搜索的索引量与流量是什么意思:百度搜索基于用户需求和资源质量等维度对索引量数据进行评估,不定期的更新索引量数据库。可能会删除低质量、用户无需求的资源,也会增加高质
随着手机移动端的快速崛起,慢慢的占领了大部分用户的访问入口,SEO优化方面,大家也开始更加重视针对移动端SEO的安排;
首先说下SSR,最近很热的词,意为ServerSideRendering(服务端渲染),目的是为了解决单页面应用的SEO的问题,搜索引擎无法抓取页面相关内容,也就是用户搜不到此网站的相关信息。用NUXT来做SSR,作用就是在node.js上进一步封装,然后省去我们搭建服务端环境的步骤,只需要遵循这个库的一些
   我是一个站长,现在建站seo是比较重要的部分,买了独立ip的云服务器主机,为了就是能够seo效果好点.建站优化我不担心,最郁闷的就是linux服务器运维这块,宝塔linux面板是必须安装到服务器上,比较消耗服务器内存,运维比较麻烦. 还有就是购买宝塔面板的附带插件比较贵,基本买个
在我们学习网站seo高质量外链建设之前,首先我们要先了解一下,究竟什么是外链呢?外链其实就是我们网站的外部链接,也称为反向链接,就是由其他的网站指向我们网站的链接。那么外链的作用是什么呢?外链可以为自己的网站带来流量,将其他网站的流量导入到自己的网站,同时也可以提高网站的权重排
大表哥相信不少朋友学习seo优化技术都是为了能够靠seo赚到钱,多数的朋友会选择去找一份seo优化的工作来赚钱,seo优化者在选择工作的时候都会普遍接触到一项职位,那就是seo专员,很多朋友都想知道seo专员是做什么的,那么今天大表哥就和大家聊一聊seo专员日常工作内容是什么?Seo专员是什么
又到了分享经验的时刻,每到这时候,高粱seo心里总是美滋滋的,因为分享是一种快乐,更是一种收获,能够与人分享,本身就是一种成功。在上一篇文章当中,高粱seo用两个实战案例,证明了目前资源导入对seo快速排名的帮助时非常给力的,之后很多seo优化伙伴纷纷来咨询,都想知道具体是怎么操作的。高粱se
 SEO阅读笔记目录基本规则URL优化代码优化代码内容优化页面头部优化图片优化网页结构基本规则1.资源越浅越容易被收录2.资源越浅越容易被收录3.关键词越靠文档(html)前,越容易被收录4.关键词出现的次数越多越好 URL优化Url中携带关键词组合关键词:eg.IPHONE
最近在自学SEO,互联网运营,把做的笔记干货分享给大家啊!希望能帮到大家,如有好的建议可以关注我【磨人的小妖精】或留言,大家一起探讨。之前还写过一篇文章互联网运营+SEO:推荐必看的5本书籍,学习还是需要系统化的书本来学习,帮助会更大。做SEO,关键词和工具比较重要,今天就先分享这两个,日
在我们接到优化网站需求的时候,一般可以从如下几个步骤进行:1.网站的机构2.页面的布局3.关键词的选取4.关键词的分布首页title标题的组成格式核心关键词+网站名称(最好含有关键词)keywords的设置核心关键词由主到次,由左到右依次出现,保持在3-4个最好,以免有关键词
    众所周知,每个人都是独立的个体,都有自己的思想和判断,具体一个什么样的网站才能称得上是成功优秀的,对于这个问题不同的人会给出不同的解说,并且衡量一个网站好坏的方式有很多的,但无论如何,我们都需要把各方面都做到最好,力争让网站能够在众多的网站中脱颖而出,而这应该是衡
现在SEO的最新算法、技巧,希望大家运用这些技巧,都像我一样能够在百度上获得客户:第一步:了解搜索引擎的工作原理爬——抓——处——排——展第二步:大家要建立一个整体框架,影响SEO的排名有哪些因素如果你网站的收录量很低,那么需要优化以下8个因素:内链结构优化URL结构优化产品内容
要想了解网站降权或者被K的原因,首先要去深度分析降权被K产生的因素,从搜索引擎本质上来说,并不会有特定的所谓被降权K站的条件来制定网站降权或者被K。并且网站降权和网站被K属于两种不同的现象。降权最明显的现象是网页还处于收录状态,但是整个标题或某个关键词搜索的时候搜索不到
本文首发于:风云社区(scoee.com)最近开始学习和研究互联网运营和SEO,对于我这个小白来讲,还是有些吃力,毕竟从来没接触这方面的,尽管在之前的软件公司做过售前和产品相关的工作,但毕竟与互联网产品运营和SEO,还是差别很大。So,在网上零散看了一些互联网产品运营和SEO相关文章,感觉讲的太片面
任何一个流量类的网站都需要长期的seo优化与维护,多数站长搭建网站的目的也是通过网站获得一定的流量和转化,但是做过seo的朋友都知道,网站获得大量流量最好的方法就是获得高的排名,但是通常新网站想要通过seo获得排名是需要长时间的积累的,那么网站建立前期如何获得流量就是很多站长比