vue2项目SEO:基于nuxt2的ssr

429 阅读6分钟

vue2项目SEO优化指南

nuxt是一个基于node和vue的ssr框架,运行在node环境

公司要求vue2官网做seo优化,2025年nuxt的默认版本是4.x,不支持vue2。因此选择nuxt2版本

nuxt2官网地址:v2.nuxt.com/ ,没有官翻中文文档,加上部署有些坑,因此整理本文档供记录和参考。

1. 创建项目

  • 1.1 Node版本:目前已知的支持nuxt2的node版本:v16.20.2,这也是我用的服务器最高支持的版本
  • 1.2 新建一个目录 取名 nuxt-ssr ,也可以根据自己需要取名
  • 1.3 cd nuxt-ssr
  • 1.4 创建一个package.json文件,内容如下:
{
  "name": "my-app",
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "generate": "nuxt generate",
    "start": "nuxt start"
  },
  "dependencies": {
    "nuxt": "^2.18.1"
  },
}
  • 1.5 安装依赖 npm install
  • 1.6 在根目录新建一个/pages目录,用于存放路由文件。
mkdir pages
touch pages/index.vue

不习惯命令行创建也可以手动新建文件夹 创建一个index.vue文件,内容如下:

<template>
  <h1>Hello world!</h1>
</template>

执行npm run dev启动项目 访问http://localhost:3000/,就可以看到你的Hello world页面了

2.路由配置

  • 2.1 用脚手架创建的话,nuxt2项目会自动生成一个/pages目录,用于存放路由文件。手动创建的话没有,就自己手动建一个。
  • 2.2 路由示例:
-| pages/
---| index.vue
---| posts.vue
---| posts/
-----| _slug.vue
-----| index.vue

这样的目录会被nuxt自动解析为:

Route           Page
/               ~/pages/index.vue
/posts          ~/pages/posts.vue (parent) + ~/pages/posts.vue (child route)
/posts/         ~/pages/posts.vue (parent) + ~/pages/posts.vue (child route)
/posts/foo      ~/pages/posts.vue (parent) + ~/pages/_slug.vue (child route)
/posts/foo/     ~/pages/posts.vue (parent) + ~/pages/_slug.vue (child route)

上面的解析说明是摘自nuxt官网的解释,其实简单一句话就是,浏览器访问/a/b,那么就会指向pages/a/b/index.vue这个文件解析后的页面。

  • 2.3 路由基础路径,对应nginx的location。编辑nuxt.config.js文件,并添加以下内容:
export default {
    router: {
        base: '/your-base-path/'
    }
}

3. 添加全局静样式。编辑nuxt.config.js文件,并添加以下内容:

export default {
    // 添加全局 Less 文件(可选)
    css: [
        '~/assets/less/main.less', // 你的全局 Less 文件路径
    ],
}

4. 使用element-ui。

  • 4.1 安装element-ui。
  npm install element-ui --save
  • 4.2 引入element-ui。在plugin目录下新建一个element-ui.js文件,内容如下:
// plugins/element-ui.js
import Vue from 'vue';
// import ElementUI from 'element-ui';
// import 'element-ui/lib/theme-chalk/index.css';
import { Backtop, Timeline, TimelineItem,Carousel,CarouselItem,Dialog,Pagination } from 'element-ui'; // 只引入需要的组件


Vue.component(Backtop.name, Backtop);
Vue.component(Timeline.name, Timeline);
Vue.component(TimelineItem.name, TimelineItem);
Vue.component(Carousel.name, Carousel);
Vue.component(CarouselItem.name, CarouselItem);
Vue.component(Dialog.name, Dialog);
Vue.component(Pagination.name, Pagination);
// 全局注册 Element UI
// Vue.use(ElementUI);

其中被注释掉的是全局的注册方式,我的项目采用的是按需引入方式

  • 4.3 编辑nuxt.config.js文件,并添加以下内容:
export default {
    // 添加全局 Less 文件(可选)
    plugins: [
        { src: '~/plugins/element-ui', ssr: true },// SSR 模式
    ],
    // 配置构建选项
    build: {
        ...
        babel: {
            plugins: [
                [
                    "component",
                    {
                        libraryName: "element-ui",
                        styleLibraryName: "theme-chalk"
                    }
                ]
            ]
        },
    }
}
  • 4.4 使用element-ui。经过上述操作后就可以在vue文件中直接使用element-ui组件了
<el-backtop target=".el-main"></el-backtop>
<el-timeline>

5. 数据请求。推荐使用@nuxtjs/axios库

关于请求可以参考nuxt2官网:v2.nuxt.com/docs/direct…

  • 5.1 安装@nuxtjs/axios库。
npm install @nuxtjs/axios --save
  • 5.2 创建plugins目录,并添加axios.js文件,内容如下:
// plugins/axios.js
export default function ({ $axios, redirect }) {
  $axios.onError(error => {
    if (error.response.status === 500) {
      redirect('/sorry') //这里是接口请求失败后的页面
    }
  })
}
  • 5.3 编辑nuxt.config.js文件,并添加以下内容:
// nuxt.config.js
export default {
    ...
    modules: ['@nuxtjs/axios'],
    plugins: ['~/plugins/axios.js']
}
  • 5.4 使用axios。 做完以上操作,你会发现在vue文件中可以通过this.$axios访问axios对象了。

注意: 凡是需要服务端完成的请求都要放在官方提供的fetch勾子中,或者在asyncData中完成。fetch推荐component目录下的vue文件使用,asyncData推荐在pages目录下的vue文件使用。 其中fetch勾子的用法:

<template>
  <h1>{{ post.title }}</h1>
</template>

<script>
let baseUrl = 'xxxx' //java或者其他后端服务地址
export default {
    data() {
        return {
        post: {}
        };
    },
    async fetch ({ $axios, params }) {
        const response = await this.$axios.get(baseUrl + 'aaa/bbb?id=' +'需要的参数')
        if(response.status === 200){
        this.post = response.data.data //这里只是示例,实际根据返回结构获取
        }
    },
    fetchKey: 'join-us', //每个页面唯一
    fetchOnServer: true, //设置为true代表服务器端渲染
}
</script>

asyncData的用法:(在表示页面的组件中)

export default {
    name: "newsDetail",
    data() {
        return {
            newsDetail: {}
        };
    },
    async asyncData({ query, $axios }) {
        const res = await $axios.get(baseUrl + '/ccc/ddd?id=' + '需要的参数')

        if (res.data.code == 200) {
            return { newsDetail: res.data.data }
        }
    },
    fetchKey: 'news-detail',
    fetchOnServer: true,
    ...
}

这样使用后,数据就会在服务端完成请求从而返回给浏览器完整的html。

6.部署服务器(通过宝塔面板)

  • 6.1 打开宝塔面板,左侧菜单选择网站,选择node项目,点击node版本管理器,安装需要的node版本,本项目的版本是v16.20.2
  • 6.2 创建一个目录,将以下文件放入:
package.json
nuxt.config.js
package-lock.json
  • 6.3 执行npm install --production,安装生产所需要的依赖
  • 6.4 在本地环境执行npm run build,生成.nuxt目录
  • 6.5 上传.nuxt目录到服务器
  • 6.6 在宝塔面板中创建一个node项目,目录就选我们上传的目录,启动命令为npm start
  • 6.7 也可以用pm2启动
  • 6.8 访问http:// + 你的IP或域名 + 你的routebase路径,即可看到页面

注意:调试出一个功能就要在服务器部署一下,看能不能正常用,比如第一个页面调好了,第一个二级路由调好了,第一个请求写好了,element-ui装好了,这些关键环节都建议在服务器上看效果后再继续,免得全部写完再部署发现一堆问题。

以上,nuxt项目就可以在服务端正常访问了,且访问时会返回完整的html内容而不是通过ajax从客户端请求。

7.关于页面源信息,带参数页面,二级页面等细节

  • 7.1. 带参数页面如何处理参数并请求数据?

    可以参考上面的asyncData的示例代码:

    async asyncData({ query, $axios }) {
            const res = await $axios.get(baseUrl + '/ccc/ddd?id=' + query.id)
            if (res.data.code == 200) {
                return { newsDetail: res.data.data }
            }
        },
    

asyncData和fetch都会在参数中获取contenxt上下文,其中query属性就是页面的参数。

  • 7.2. 页面源信息如何设置?

统一设置: 可以在nuxt.config.js中添加head属性,内容如下:


export default {
  head: {
        title: '****官网',
        meta: [
            { charset: 'utf-8' },
            { name: 'viewport', content: 'width=device-width, initial-scale=1' },
            {
                hid: 'description',
                name: 'description',
                content: 'my website description'
            }
        ],
        link: [{ rel: 'icon', type: 'image/png', href: '/sss/logo.png' }]
  },
}

单独设置 可以在pages下面的文件中单独添加meta和其他head信息,例如在pages/index.vue中:

// pages/index.vue
<script>
export default {
  head: {
    title: 'Home page',
    meta: [
      {
        hid: 'description',
        name: 'description',
        content: 'Home page description'
      }
    ],
  }
}
</script>

如果两种途径都设置了,单独设置的配置会优先生效。

  • 7.3 二级页面跳转 有5种方式跳转:

    • 7.3.1. nuxt-link跳转
      <nuxt-link :to="`/detail/${item.id}`">{{ item.title }}</nuxt-link>
    
    • 7.3.2. 新页面跳转
    const route = this.$router.resolve({
        path:'/sss/aaa',
        query: {
            id: id
        }
    })
    window.open(route.href, '_blank')
    
    • 7.3.3. a标签跳转:
    <a href="/detail/1">跳转</a>
    
    • 7.3.4. 重定向(服务端跳转)
    // 在中间件中
    export default function ({ redirect }) {
      redirect('/detail/1');
    }
    
    // 在页面逻辑中
    async asyncData({ redirect }) {
      if (!userLoggedIn) {
        redirect('/login');
      }
    }
    
    • 7.3.5. 客户端跳转:
    window.location.href = `/detail/${id}`; // 
    

8. 关于网站的robots.txt文件和sitemap.xml文件

robots.txt代表爬虫协议,sitemap.xml文件是网站地图文件,用于告诉爬虫网站有哪些页面。

  • 8.1. 如何设置?分为静态和动态两种:先说下静态设置:

在项目根目录的 static/ 文件夹下创建 robots.txt(Nuxt 会自动将该文件夹下的文件映射到网站根路径),

User-agent: *
Allow: /
Sitemap: https://你的域名.com/sitemap.xml

在 static/ 下创建 sitemap.xml,内容示例:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://你的域名.com/</loc>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://你的域名.com/about</loc>
    <priority>0.8</priority>
  </url>
</urlset>

注意如果nginx设置了代理,那么需要在域名后面加上你的代理路径,如:https://你的域名.com/proxy/

  • 8.2. 接下来介绍一下动态生成方案(基于nuxt2.18.1)
npm install @nuxtjs/sitemap@2.4.0 --save
  npm install @nuxtjs/robots@2.4.0 --save

在nuxt.config.js中添加以下代码:

export default {
  ...
  modules: [ '@nuxtjs/robots', '@nuxtjs/sitemap'],
  robots: {
      UserAgent: '*',
      Allow: '/',//允许爬虫访问代理路径下的所有内容
      Disallow: ['/admin', '/test2', '/test3'],//屏蔽代理路径下的敏感目录
      Sitemap: 'http://xxx.com/sitemap.xml'
  },
  // sitemap 配置
  sitemap: {
      hostname: 'http://xxx.com',//站点域名
      exclude: ['/**'], // 禁用所有自动生成的路由,按需要配置
      cacheTime: 1000 * 60 * 15, // 缓存15分钟
      gzip: true, // 生成 sitemap.xml.gz
      // 百度专用优化参数
      baidu: true, // 启用百度专用扩展
      defaults: {
          changefreq: 'daily',
          priority: 0.8,//优先级默认值
          lastmod: new Date().toISOString() // 必须包含最后修改时间
      },
      routes: [
          { url: '/xxx/home', priority: 1 },  // 首页最高优先级
          { url: '/xxx/news', priority: 0.6 },  
          // 动态路由示例(需自行实现数据获取)
          // ...getDynamicRoutes()
      ]
  },
}

设置完成后执行npm run build ,然后将.nuxt目录上传到服务器,服务器执行npm install --production ,将nuxt.config.js内容同步到服务器,然后在宝塔运行,访问 xxx.com/sitemap.xmlxxx.com/robots.txt 就可以在浏览器看到最新的 sitemap.xmlrobots.txt 文件了。注意这个文件并不是静态的,而是是动态生成的,每次访问都会生成新的文件。

9. pm2 启动

创建ecosystem.config.js 文件

module.exports = {
  apps: [
    {
      name: "my-nuxt-app",
      script: "node_modules/nuxt/bin/nuxt.js",
      args: "start",
      exec_mode: "fork", // 或 "cluster"(多进程)
      instances: 2,
      autorestart: true,
      watch: false,
      max_memory_restart: "1G",
      env: {
        NODE_ENV: "production",
      },
    },
  ],
};

执行以下命令

pm2 start ecosystem.config.js

启动了2个进程的集群,可在pm2的面板看到集群的运行情况

可以配置多个实例、最大内存、最大进程数量、启动顺序、环境变量、日志文件、错误文件、重启策略等等。

10. 环境变量配置。

在根目录新建.env文件,写入环境变量。如

NUXT_PROTOCOL=https

NUXT_ROUTER_BASE=/

NUXT_DOMAIN_NAME=www.xxx.com

NUXT_PORT=3001

nuxt2默认集成了dotenv。在nuxt.config.js中引入dotenv,并调用dotenv.config()方法。

require('dotenv').config({ path: path.resolve(__dirname, `.env.${env}`) })

//然后就能获取到对应的环境变量了
const NUXT_ROUTER_BASE = process.env.NUXT_ROUTER_BASE
const NUXT_PROTOCOL = process.env.NUXT_PROTOCOL
const NUXT_DOMAIN_NAME = process.env.NUXT_DOMAIN_NAME
const NUXT_PORT = process.env.NUXT_PORT

//可以根据获取到的变量动态地配置项目的路由、域名、端口等信息,如:
export default {
    server: {
        port: NUXT_PORT,
        host: '0.0.0.0'
    },
    router: {
        base: NUXT_ROUTER_BASE,
    },
}

接下来需要修改package.json文件,修改部分如下:

{
  "scripts": {
      "dev": "nuxt",
      "build": "nuxt build",
      "build:uat": "cross-env NODE_ENV=uat nuxt build",
      "build:prod": "cross-env NODE_ENV=production nuxt build",
      "generate": "nuxt generate",
      "start:dev": "cross-env NODE_ENV=development nuxt start",
      "start:uat": "cross-env NODE_ENV=uat nuxt start",
      "start:prod": "cross-env NODE_ENV=production nuxt start",
      "startbypm2": "pm2 start npm --name \"my-app\" -- run start"
  }
}

这里用到了一个cross-env库,它可以在不同的平台为我们设置环境变量,是一个好用的跨平台工具,可以通过npm install cross-env --save-dev安装

注意: 如果线上环境没有动态读取env文件的需求,那么在start时直接执行nuxt start即可,不需要再执行cross-env NODE_ENV=production nuxt start

11. 重定向。

这里推荐的重定向配置是在nuxt.config.js文件中配置的,具体代码如下:

export default {
  ...
  router: {
      base: NUXT_ROUTER_BASE,//路由基础路径
      extendRoutes(routes, resolve) {
          // 添加重定向规则
          routes.push({
              path: '/',
              redirect: '/home'
          })
      }
  },
}

通过这样的配置,即可使发往NUXT_ROUTER_BASE的请求自动跳转到/home。

注意:如果你的NUXT_ROUTER_BASE是/,则需要在nginx中设置默认首页为/home

image.png