18

当我访问我的内部页面vue-meta时,新页面值不会更新。

代码

app.js

import VueMeta from 'vue-meta'
Vue.use(VueMeta, {
    refreshOnceOnNavigation: true
})

App.vue(主要成分)

export default {
  metaInfo() {
    return {
      title: process.env.MIX_APP_NAME,
      titleTemplate: `%s | ${process.env.MIX_APP_NAME}`,
      meta: [
        { name: "robots", content: "index,follow" },
        {
          vmid: "description",
          name: "description",
          content:
            "..........",
        },
        // and many more....
      ],
   }
  }
}

post.vue(内部组件)

export default {
  name: "singePost",
  data() {
    return {
      post: "",
    };
},
metaInfo() {
    return {
        title: this.post.name, // not receiving data
        meta: [
            {
                vmid: "description",
                name: "description",
                content: this.post.metas[0].description, // not receiving data
            },
            // others......
        ],
    }
},
mounted() {
    this.getPost();
},
methods: {
    getPost() {
        axios
        .get("/api/articles/" + this.$route.params.slug, {
          headers: {
            Authorization: localStorage.getItem("access_token"),
          },
        })
        .then((response) => {
          this.post = response.data.data;
        })
        .catch((error) => {
            //....
        });
    },
},

任何想法?

更新

当我发布这个问题时,它是关于没有更新的,然后在做了一些研究并玩弄了我的代码之后,我意识到我的vue-meta 得到了更新,但是,晚了,它会导致社交网络网站和 SEO 检查器无法正确检索我的 URL。

阐明

  1. Vue-meta 得到更新但迟到了
  2. 此延迟更新导致共享和验证的时间链接无法呈现 SEO。

My full meta tags code

metaInfo() {
    return {
      title: this.post.name,
      meta: [
        {
          vmid: "keyword",
          name: "keyword",
          content: this.post.metas[0].tags,
        },
        {
          vmid: "description",
          name: "description",
          content: this.post.metas[0].description,
        },
        // Open Graph / Facebook
        { vmid: "og:type", name: "og:type", content: "website" },
        {
          vmid: "og:url",
          name: "og:url",
          content: process.env.MIX_APP_URL + this.$router.currentRoute.fullPath,
        },
        {
          vmid: "og:site_name",
          name: "og:site_name",
          content: `"${process.env.MIX_APP_NAME}"`,
        },
        {
          vmid: "og:title",
          name: "og:title",
          content: this.post.name,
        },
        {
          vmid: "og:description",
          name: "og:description",
          content: this.post.metas[0].description,
        },
        {
          vmid: "og:image",
          name: "og:image",
          content: this.post.imagebig,
        },
        //   Twitter
        {
          vmid: "twitter:card",
          name: "twitter:card",
          content: "summary",
        },

        {
          vmid: "twitter:author",
          name: "twitter:author",
          content: "@xxxxxx",
        },
        {
          vmid: "twitter:site",
          name: "twitter:site",
          content: "@xxxxxx",
        },
        {
          vmid: "twitter:creator",
          name: "twitter:creator",
          content: "@xxxxxx",
        },
        {
          vmid: "twitter:url",
          name: "twitter:url",
          content: process.env.MIX_APP_URL + this.$router.currentRoute.fullPath,
        },
        {
          vmid: "twitter:title",
          name: "twitter:title",
          content: this.post.name,
        },
        {
          vmid: "twitter:description",
          name: "twitter:description",
          content: this.post.metas[0].description,
        },
        {
          vmid: "twitter:image",
          name: "twitter:image",
          content: this.post.imagebig,
        },
      ],
    };
},

额外的

  1. 最近我读到一篇文章,因为基于 JavaScript 社交媒体爬虫的 vue-meta(一般 Vue)加载不会缓存它们,因此当我在 FB 或 Twitter 等中分享它们时,无法看到我的链接详细信息。

  2. 建议的解决方案是使用 Nuxt 并返回元数据服务器端。

问题

  1. 我不确定上面的#1 有多少是正确的,但它有可能
  2. 我的应用程序一般不使用 Nuxt,但我只是安装了它的 npm 包,所以可能值得一试(正如我提到的,我从未使用过 Nuxt,所以如果你的帮助解决方案是这种情况,如果你能提供一点,我将不胜感激额外的细节到你的答案)。
4

4 回答 4

10

Vue 本身就是客户端的 JS 框架。构建时,您index.html没有任何内容 - 只有在执行时生成内容的 JS。同样适用于 VueMeta。问题是,当您共享链接(FB、Twitter 等)时,他们使用自己的机器人(本质上是爬虫)下载链接页面并在不执行任何 JS的情况下分析内容- 所以是的,他们看不到由生成的任何元数据VueMeta...

唯一的解决方案是在不执行 JS 的情况下交付包含所有重要信息的完全(或部分)预渲染页面

这样做的一种方法是使用Vue 服务器端渲染- 你是对的,像 Nuxt 这样的框架正是使用它。

一般有两种口味:

SSR - 页面在客户端(或机器人)请求时呈现。在大多数情况下,它需要运行 Node 服务器(因为 Vue SSR 是在 JS 中实现的)。最突出的例子是 Nuxt.js

SSG - 服务器端生成。页面在构建时生成,包括所有 HTML。加载到浏览器服务器时返回 HTML + 所有 JS/CSS,但加载时它是相同的 Vue SPA。您不需要 Node 服务器,因此您可以托管在 CDN 或任何静态托管服务(如 Netlify)上。Vue 世界中的例子有 Gridsome、VuePress、Nuxt 也可以做到……

注意:还有其他方法,例如使用无头 chrome/puppeteerhttps://prerender.io/等服务

纽斯特

如前所述,Nuxt 很棒,但对您的应用程序的结构(基于文件的路由)、如何获取数据等非常固执己见。因此切换到 Nuxt 可能意味着完全重写应用程序。最重要的是,它需要运行具有自身(托管)后果的节点服务器。

在我看来,您已经在使用服务器 - Laravel。所以你最好的选择可能是直接在 Laravel 中实现你的元渲染。

更新:似乎可以直接在 Laravel中执行 Vue SSR

于 2021-01-07T12:40:32.103 回答
4

你的假设是正确的。不久前,我也花了相当长的时间试图找到解决同样问题的方法。这是我在一天结束时想出的:

  1. 继续使用vue-meta, 对于那些运行 JavaScript 的爬虫(它没有害处,对吧?)。
  2. 实现服务器端解决方案(使用 Laravel 包)。

选项 1 应该很清楚,因为您已经有了类似的实现。

对于选项 2,这是我的方法:

  • 我为我的 Laravel 应用程序选择了这个包。它易于安装和注册。我确信 Laravel 或其他框架和语言有很多包可以做到这一点。

  • 我在路由文件的末尾添加了这条路由(web.php如果您使用的是 Laravel),它捕获了所有前端路由请求:

Route::get('/{any}', 'IndexController@index')->where('any', '.*');

IndexController中,我首先检查请求是否来自爬虫。如果是这样,我会应用相关的元标记。这是一瞥:

<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use Butschster\Head\Facades\Meta;
use Butschster\Head\Packages\Entities\OpenGraphPackage;

class IndexController extends Controller
{
    const CRAWLERS = [
        'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
        'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1 (compatible; AdsBot-Google-Mobile; +http://www.google.com/mobile/adsbot.html)',
        'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Safari/537.36',
        'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
        'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b',
        'Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)',
        'Googlebot-Image/1.0',
        'Mediapartners-Google',
        'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)',
        'facebookexternalhit/1.1',
        'Twitterbot/1.0',
        'TelegramBot (like TwitterBot)',
    ];

    public function index()
    {
        if ($this->isACrawler()) {
            $this->applyMetaTags();

            return view('layouts.crawler');
        }

        return view('layouts.index');
    }

    public function isACrawler()
    {
        if (in_array(request()->userAgent(), self::CRAWLERS)) {
            return true;
        }

        return false;
    }

    private function applyMetaTags()
    {
        // Here you can check the request and apply the tags accordingly
        // e.g.
        //        preg_match("/articles\/[0-9]+/i", request()->path(), $url)
        //        preg_match("/[0-9]+/i", $url[0], $id);
        //        $article = Article::find($id);
        //
        //        Meta::prependTitle($article->name)
        //            ->addMeta('description', ['content' => $article->description]);
        //
        //        $og = new OpenGraphPackage('some_name');
        //
        //        $og->setType('Website')
        //            ->setSiteName('Your website')
        //            ->setTitle($article->name)
        //            ->setUrl(request()->fullUrl())
        //            ->setDescription($article->description);
        //
        //        if ($article->picture) {
        //            $og->addImage(asset($article->picture));
        //        }
        //
        //        Meta::registerPackage($og);
    }
}

最后,我创建了一个仅包含head部分的模板(这是爬虫关心的 html 的唯一部分)并应用元标记:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        @meta_tags

        <link rel="shortcut icon" href="{{ asset('favicon.ico') }}">
    </head>
</html>

注意事项:

  • 您需要自定义每个请求的元标记
  • 您需要维护一个爬虫列表

好处:

  • 它很简单,不需要对代码进行太多更改
  • 它向爬虫返回一个快速且轻量级的 HTML
  • 您可以完全控制后端,稍作调整即可实施可维护的解决方案

希望这可以帮助!如果有不清楚的地方,请告诉我。

于 2021-01-07T15:15:15.760 回答
3

您需要实现服务器端渲染来处理元标记。因为几乎所有爬虫都不支持javascript进程。

这是 PHP - Laravel 的示例。

我们知道 vue.js 是一个单页应用程序。所以每次它从一个根页面呈现。

因此,对于 laravel,我按原样配置了路由,并且每次返回带有标签数组的索引页面并在视图中呈现该页面(索引页面)

  1. Laravel 路由

<?php
    
    use Illuminate\Support\Facades\Route;
    
    Route::get('users/{id}', 'UserController@show');
    
    Route::get('posts/{id}', function () {
        $tags = [
            'og:app_id' => '4549589748545',
            'og:image' => 'image.jpg',
            'og:description' => 'Testing'
        ];
    
        return view('index', compact('tags'));
    });
    
    Route::get('/{any}', function () {
        $tags = [
            'description' => 'Testing',
            'keywords' => 'Testing, Hello world',
        ];
    
        return view('index', compact('tags'));
    })->where('any', '.*');
    
    ?>

  1. 控制器

<?php
    
    use App\Http\Controllers\Controller;
    use App\Models\User;
    
    class UserController extends Controller
    {
        public function show(User $user)
        {
            $tags = [
                'og:app_id' => '4549589748545',
                'og:image' => 'image.jpg',
                'og:description' => 'Testing'
            ];
    
            return view('index', compact('tags'));
        }
    }
    
    ?>

  1. 索引页面

<!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        @foreach($tags as $key => $value)
            <meta property="{{$key}}" content="{{$value}}">
        @endforeach
    </head>
    <body id="app">
    
    <script type="text/javascript" src="{{ asset('js/app.js') }}"></script>
    </body>
    </html>

于 2021-01-12T06:06:57.860 回答
0

你可以配置 webpack 来注入静态标签

vue-cli 3 将 webpack 配置文件抽象出来(在运行时生成),因此为了配置它,您需要将 vue.config.js 添加到项目根目录(如果没有,通常不会)

例如:

// vue.config.js
module.exports = {
    configureWebpack: {
        output: {
            publicPath: '/static/'
        },

        plugins: [
          new HtmlWebpackPlugin(),
          new HtmlWebpackTagsPlugin(
            {tags: ['a.js', 'b.css'], append: true },
            {metas: [{
                path: 'asset/path',
                attributes: {
                    name: 'the-meta-name'
                    }}]
            })
        ]
    }
}

(使用https://github.com/jharris4/html-webpack-tags-plugin查看链接中的示例以获取其具体输出)

于 2021-01-18T23:26:32.593 回答