Vue meta 没有得到更新

Posted

技术标签:

【中文标题】Vue meta 没有得到更新【英文标题】:Vue meta not getting updates 【发布时间】:2021-04-10 21:24:40 【问题描述】:

当我访问我的内部页面时,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。

澄清

    Vue-meta 更新但晚了 此延迟更新导致 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,
        ,
      ],
    ;
,

额外

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

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

问题

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

【问题讨论】:

【参考方案1】:

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

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

这样做的一种方法是使用Vue server side rendering - 你是对的,像 Nuxt 这样的框架正是使用它。

一般有两种口味:

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

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

注意:还有其他方法,例如使用headless chrome/puppeteer 或https://prerender.io/ 等服务

努克斯

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

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

更新:似乎可以做 Vue s-s-r directly in Laravel

【讨论】:

我对你的UPDATE 部分很感兴趣,我已经阅读了这篇文章,但我不知道我应该如何返回例如我的帖子元标记(作为我上面的代码) 从数据库返回实际数据没有任何意义。 从文章来看,它似乎是普通的 Vue s-s-r。简而言之,在服务器返回响应之前,它会在服务器内部的 V8 JS 虚拟机中执行您的 Vue 应用程序,并返回由它呈现的任何 HTML。因此,您无需编写任何 php 代码来生成元标记或查询数据库。一切都在 Vue 应用程序中完成。我并不是说你的 Vue 代码会像现在一样工作,但对于具体细节,你需要在我的帖子中查看 Vue 服务器端渲染链接.... 但是我对 Laravel 真的不太了解。也许在这里尝试 s-s-r 只是矫枉过正。也许您可以制作一个与 Post 组件具有相同路由的控制器,添加一些数据库调用,在 PHP 中生成 meta 标签并将 VUE js 脚本附加到响应中... 在我的项目中,s-s-r 为 false 并且使用 Nuxt,是否有任何其他方法可以在页面加载之前更新标题标签。我面临完全相同的问题,但就我而言,只是 s-s-r 是错误的。只是我想更新 Api 响应上的标题标签,并且标题应该显示在视图源上。如果有人可以帮助我。【参考方案2】:

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

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

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

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

我为我的 Laravel 应用程序选择了 this 包。它易于安装和注册。我确信有很多 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 您可以完全控制后端,稍作调整即可实施可维护的解决方案

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

【讨论】:

您好 hatef 感谢您的回答,如果我使用您提到的包,我还需要提供 crowlers 列表吗?因为该列表可能会有所不同,我将无法为每个机器人提供该列表,因此自定义机器人首先很难做到,其次没有意义。据说除了照顾控制器之外,在所有前端页面中提供 vue-meta 似乎需要在两个不同的地方提供元数据。你怎么看? 您好,该软件包仅应用元标记。爬虫列表是我自己添加的。原因是,我只想在请求来自爬虫时应用后端元标记。您可以省略它并将它们直接应用于每个请求。一般来说,拥有良好的 SEO 需要大量工作,并且会根据您的需求而有所不同。就我而言,我决定仅将后端元标记应用于我知道可以共享到社交媒体的页面(以支持预览功能)。将它放在两个地方似乎是多余的,但我认为最终它会得到回报。 此外,您也许可以只使用这个后端包,它可以满足您的需求。你只需要看看它是如何与前端路由一起工作的,因为你很可能会进行不更新 html 的&lt;head&gt; 的 API 调用。例如,您可以考虑一种方式,当收到某个 API 响应时,您会动态更新页面的头部。我自己还没有真正深入研究过,但这是一种可能性。当我有空闲时间时,我可能会深入研究:) 是的,我今天早上刚尝试过,但遇到了一些问题,但在 github 上仍然没有对这些问题做出任何回应,因为你提到你没有深入研究它所以我认为我没有必要向您推荐这些问题 :) 非常感谢您的解释和您的时间。 当然。如果我有任何发现,我会更新我的答案。顺便说一句,我在你的问题标签中添加了 laravel,这样也许一些 Laravel 开发人员也可以为答案做出贡献【参考方案3】:

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

这是 PHP - Laravel 的示例。

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

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

    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', '.*');
    
    ?>
    控制器

<?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'));
        
    
    
    ?>
    索引页

<!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>

【讨论】:

【参考方案4】:

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

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

例如:

// 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查看链接中的示例以了解其具体输出)

【讨论】:

谢谢,但我不想要静态数据,否则我会将其直接添加到我的 html 文件头中,我想要的是基于访问页面的动态标签

以上是关于Vue meta 没有得到更新的主要内容,如果未能解决你的问题,请参考以下文章

在 vue js 中使用单选按钮时 v-model 没有更新?

vue项目路由权限控制实现(前端控制)

vue项目中缓存问题

尝试使用字符串 + 变量设置 Vue Meta 页面标题

Vue / Typescript,得到模块'“*.vue”'没有导出成员

为啥 vue.js 不会更新 DOM?