【问题标题】:MEAN.js Social Sharing?MEAN.js 社交分享?
【发布时间】:2015-06-02 19:43:49
【问题描述】:

所以我使用 MEAN.js 构建了一个应用程序,我对文章(博客)部分进行了一些更新,以获得更好的 SEO、可读性、设计等。不过,我似乎无法弄清楚的一个问题是如何使用 Facebook、Google+、Twitter 等分享文章,并让他们使用 og 元标记填充正确的数据。

我想要什么

我想要的只是能够从我的 MEAN.js 应用程序共享文章(博客文章),并在我将链接发布到社交网站(例如 Facebook)时显示文章内容。

我已经尝试过什么

我已经尝试为博客文章创建一个单独的服务器布局,但这会破坏很多其他的东西,我意识到工作量可能不值得 - 必须有一个更聪明的方法。

我也尝试在客户端使用 Angular 更新 og 元标记数据,但这些值不能在社交网站获取这些标记之前更新……换句话说,它实际上并没有达到我想要的效果到。

我尝试在呈现索引时获取 Angular 路由 URL,以便在呈现索引之前更新这些 og 元值,但我无法在 req 数据中的任何位置找到这些值。

我认为问题是什么

从概念上讲,这就是我认为正在发生的事情:

  1. 请求到达我的服务器,但由于它是使用 Angular 路由的单页面应用程序,req.url 值只是根页面 ('/')。

  2. 加载索引文件,该文件使用标准服务器模板布局。

  3. Angular 被加载并进行 AJAX 调用以获取文章数据,然后将该数据绑定到页面上的变量。

所以基本上在 Angular 确定要抓取的文章信息之前,布局已经被渲染(使用 og 元值)。

我猜理想的解决方案是什么

在我的express.js文件中,应用的局部变量设置如下:

// Setting application local variables
app.locals.siteName = config.app.siteName;
app.locals.title = config.app.title;
app.locals.description = config.app.description;
app.locals.keywords = config.app.keywords;
app.locals.imageUrl = config.app.imageUrl;
app.locals.facebookAppId = config.facebook.clientID;
app.locals.jsFiles = config.getJavaScriptAssets();
app.locals.cssFiles = config.getCSSAssets();

这些局部变量然后由 Swig 在layout.server.view.html 文件中呈现如下:

// Note the {{keywords}}, {{description}}, etc. values. 
<!-- Semantic META -->
<meta id="keywords" name="keywords" content="{{keywords}}">
<meta id="desc" name="description" content="{{description}}">

<!-- Facebook META -->
<meta id="fb-app-id" property="fb:app_id" content="{{facebookAppId}}">
<meta id="fb-site-name" property="og:site_name" content="{{siteName}}">
<meta id="fb-title" property="og:title" content="{{title}}">
<meta id="fb-description" property="og:description" content="{{description}}">
<meta id="fb-url" property="og:url" content="{{url}}">
<meta id="fb-image" property="og:image" content="{{imageUrl}}">
<meta id="fb-type" property="og:type" content="website">

<!-- Twitter META -->
<meta id="twitter-title" name="twitter:title" content="{{title}}">
<meta id="twitter-description" name="twitter:description" content="{{description}}">
<meta id="twitter-url" name="twitter:url" content="{{url}}">
<meta id="twitter-image" name="twitter:image" content="{{imageUrl}}">

所以理想情况下,我认为我们希望在渲染页面之前使用文章特定信息更新这些值。问题是,如果布局在 Angular 甚至确定要填充哪些文章数据之前被渲染,如何我可以这样做吗?同样,Angular 路由似乎在 req 对象中的任何地方都不可用,所以我完全不知道如何做到这一点。

所以我回到我最初的愿望 - 我如何使用 MEAN.js 以“漂亮”的方式在社交媒体上分享我的文章?我在正确的轨道上吗?当前的文章设置是否可行?我是否需要构建一个完全不使用 Angular 的完整博客模块?

【问题讨论】:

    标签: javascript angularjs express mean meanjs


    【解决方案1】:

    我终于在没有 Nginx 或 MEANJS 框架之外的任何其他东西的情况下为我的应用程序工作了。你的里程可能会有所不同,但我想我还是会分享结果。它对我有用,但可能不适合你。

    基本上我已经设置的是一种获取非散列 URL 并重定向到散列 URL 的方法。所以用户可以分享他们的个人资料,例如example.com/myprofile,它会重定向到example.com/#!/profile/myprofile

    然后,我为社交机器人创建了一个单独的布局(尽管回想起来我不确定这是否完全必要),并在网站被抓取时提供单独的布局。我就是这样做的:

    social-layout.server.view.html

    ...some stuff here...
    //Note the variable names, e.g. {{siteName}}
    <meta id="fb-app-id" property="fb:app_id" content="{{facebookAppId}}">
    <meta id="fb-site-name" property="og:site_name" content="{{siteName}}">
    <meta id="fb-title" property="og:title" content="{{socialTitle}}">
    <meta id="fb-description" property="og:description" content="{{socialDescription}}">
    <meta id="fb-url" property="og:url" content="{{socialUrl}}">
    <meta id="fb-image" property="og:image" content="{{socialImageUrl}}">
    <meta id="fb-type" property="og:type" content="website">
    
    ...other stuff here...
    

    然后在我的 Express 文件中,我明确检查 user-agents 以确定是否需要新布局。如果我找到一个机器人,我会从我的数据库中获取一些与 URL 相关的关键数据,然后填充变量,如下所示:

    express.js

    // This code happens just after app.locals variables are set.
        // Passing the request url to environment locals
        app.use(function(req, res, next) {
            // Let's check user-agents to see if this is a social bot. If so, let's serve a different layout to populate the og data so it looks pretty when sharing.
            if(req.headers['user-agent'] === 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)' ||
                req.headers['user-agent'] === 'facebookexternalhit/1.0 (+http://www.facebook.com/externalhit_uatext.php)' ||
                req.headers['user-agent'] === 'facebookexternalhit/1.1 (+https://www.facebook.com/externalhit_uatext.php)' ||
                req.headers['user-agent'] === 'facebookexternalhit/1.0 (+https://www.facebook.com/externalhit_uatext.php)' ||
                req.headers['user-agent'] === 'visionutils/0.2' ||
                req.headers['user-agent'] === 'Twitterbot/1.0' ||
                req.headers['user-agent'] === 'LinkedInBot/1.0 (compatible; Mozilla/5.0; Jakarta Commons-HttpClient/3.1 +http://www.linkedin.com)' ||
                req.headers['user-agent'] === 'Mozilla/5.0 (Windows NT 6.1; rv:6.0) Gecko/20110814 Firefox/6.0 Google (+https://developers.google.com/+/web/snippet/)' ||
                req.headers['user-agent'] === 'Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)') {
    
                var urlAttempt = req.url;
                urlAttempt = urlAttempt.substr(1);
    
                Users.findOne({ link: urlAttempt }, function(err, results) {
                    if(err) {
                        res.locals.url = req.protocol + '://' + req.headers.host;
                        next();
                    } else if (results !== null) {
                        // Found link. Populate data.
                        res.status(200).render('social-index', {
    
                            // Now we update layout variables with DB info.
                            socialUrl: req.protocol + '://' + req.headers.host + req.url,
                            socialTitle: results.orgName,
                            socialDescription: results.shortDesc,
                            socialImageUrl: req.protocol + '://' + req.headers.host + '/profile/img/' + results.imgName
                        });
                    } else {
                        res.locals.url = req.protocol + '://' + req.headers.host;
                        next();
                    }
                });
            } else {
                res.locals.url = req.protocol + '://' + req.headers.host;
                next();
            }
        });
    

    同样,您的里程可能会有所不同,但这对我有用(部分)。我仍在致力于社交分享整个 URL(包括哈希)。希望它在某些方面有所帮助。

    【讨论】:

    • 我可能还应该提到 Twitter 是唯一一个我没有验证的(当我分享 URL 时它没有尝试获取站点信息),我正在使用我在另一个网站。我为 Facebook、Google+ 和 LinkedIn 验证的其他 user-agent 值。最后,当 Facebook 抓取我的网站时,visionutils/0.2 出现了,当我用 Google 搜索时,猜测是这是用于面部识别图像或类似的东西。
    • 这非常有用。你用的是什么模板引擎?我有一个很好的时间让它与 HTML 和车把一起工作
    • @tcmoore 在我使用 Swig 时,它是 MEANJS.org 样板中的标准配置。我很高兴你觉得它很有价值!
    • 是的,您不会相信关于如何在网络上执行此操作的文档如此之少。我一搞定模板引擎,你的解决方案就像做梦一样。
    • 很高兴听到!祝你的项目好运。
    【解决方案2】:

    我也遇到过同样的问题。首先我安装了 Mean-Seo 模块。你可以在 mean.js 官方 github repo 上找到它。模块本质上是这样做的:爬虫(Google 等)在遇到 SPA URL 时将_escaped_fragment_ 部分添加到他们的请求中。 Mean-Seo 拦截包括_escaped_fragment_ 的请求。然后使用 Phantom.Js,从动态 HTML 中渲染出一个静态 HTML 并保存,将这个静态版本提供给爬虫。

    对于 Facebook 和 Twitter,我更改了 Mean-Seo 的 mean-seo.js 文件,在它缓存和保存静态文件之前,我相应地替换了元标记。由于 Phantom.Js 已经渲染了整篇文章页面,因此您无需再进行 API 调用。只需解析 HTML。我还用cheerio.js方便地解析HTML。

    这种解决了我的问题,但并不完美。我还在为 hashbang 和 HTML5 模式而苦苦挣扎。当我的 URL 类似于 https://example.com/post/1 时,Twitter 和 Facebook 不要请求 _escaped_fragment_

    更新:过了一段时间,我放弃了这个方法。 Phantomjs 似乎不可靠,我不想为这样的工作浪费系统资源、CPU 时间、RAM、磁盘空间。不必要的文件创建也很愚蠢。我目前的做法是这样的:

    我为 Twitter 和 Facebook 添加了一条新的快速路线。在服务器控制器中,实现了爬虫的新功能。创建了一个简单的服务器模板,没有 Angular、Bootstrap 和所有闪亮的东西:只有元标记和简单的文本。使用Swig(已经包含在 Meanjs 中),相应地渲染了这个视图模板。我还使用Nginx 作为代理。所以我基于用户代理定义了一个新的 Nginx rewrite。像这样的:

    if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
        rewrite ^/posts/(.*)$ /api/posts/crawl/$1 last;
        }
    

    当爬虫请求帖子的 URL 时,Nginx 会触发我的新简单爬虫路由并生成小页面。

    【讨论】:

    • 这似乎是我正在考虑的方法,如何在没有 ngix 并且只有 mean.js 应用程序的情况下实现它?
    • 我尝试过 Angular 和 Express 路由而不是 Nginx 重写,但没有奏效。我不确定。我已经在 Nginx 后面提供应用程序(SSL 密钥和所有),所以我只是更改了配置。
    • 谢谢。我没有 nginx,我已经在生产中,所以我不认为要添加它。无论如何,我只需要检测用户代理并重新路由到只有元数据的空页面,权利? github.com/biggora/express-useragent 适合吗?
    • 试一试,如果它有效,请告诉我。我认为您只需要确保在应用程序加载之前启动快速路线即可。否则爬虫会解析标准元标记。
    • 我读到如果内容更加不同(使用不同的用户代理很难找到),谷歌会惩罚网站,并且更推荐使用 mean-seo.. 我会尝试使用 mean -seo 第一!
    猜你喜欢
    • 2012-07-29
    • 1970-01-01
    • 2015-07-08
    • 2013-02-08
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多