MEAN.js 社交分享?
MEAN.js Social Sharing?
所以我使用 MEAN.js 构建了一个应用程序,并对文章(博客)部分进行了一些更新,以获得更好的搜索引擎优化、可读性、设计等。一个问题我似乎无法弄清楚,不过,如何使用 Facebook、Google+、Twitter 等分享文章,并让它们使用 og 元标记填充正确的数据。
我想要什么
我想要的只是能够从我的 MEAN.js 应用程序共享文章(博客 posts),并在我 post [=] 时显示文章内容86=] 在社交网站(例如 Facebook)中。
我尝试了什么
我试过专门为博客 post 创建一个单独的服务器布局,但这破坏了很多其他事情,我意识到工作量可能不值得 - 必须有一个更聪明的方式。
我也试过在客户端用 Angular 更新 og 元标签数据,但这些值在社交网站获取这些标签之前不能更新……换句话说,它没有实际上做我想做的事。
我尝试在渲染索引时获取 Angular 路由 URL 以便我可以在渲染索引之前更新那些 og 元值,但我无法在任何地方找到这些值在 req
数据中。
我认为问题是什么
从概念上讲,这就是我认为正在发生的事情:
请求到达我的服务器,但由于它是使用 Angular 路由的单页应用程序,因此 req.url
值只是根页面 ('/') .
加载索引文件,它使用标准服务器模板布局。
Angular 被加载并进行 AJAX 调用以获取文章数据,然后将该数据绑定到页面上的变量。
所以基本上在 Angular 甚至找出要抓取的文章信息之前就已经渲染了布局(使用 og 元值)。
我猜理想的解决方案是
在我的express.js
文件中,app的局部变量设置如下:
// 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 以 "pretty" 的方式在社交媒体上分享我的文章?我在正确的轨道上吗?当前的 Article 设置是否可行?我是否需要构建一个完全不使用 Angular 的完整博客模块?
我遇到过同样的问题。首先我安装了 Mean-Seo 模块。您可以在 mean.js 官方 github 存储库中找到它。模块本质上是这样做的:爬虫(Google 等)在遇到 SPA URL 时向他们的请求添加一个 _escaped_fragment_
部分。 Mean-Seo 拦截包括 _escaped_fragment_
的请求。然后使用 Phantom.Js,从动态渲染出静态 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 时间、内存、磁盘 space。不必要的文件创建也很愚蠢。我目前的做法是这样的:
我为 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/ last;
}
当爬虫请求 post 的 URL 时,Nginx 触发我新的简单爬虫路由并生成小页面。
我终于在没有 Nginx 或 MEANJS 框架之外的任何其他东西的情况下为我的应用程序工作了。你的里程可能会有所不同,但我想我还是会分享结果。它适用于我,但可能不适合你。
基本上我已经设置的是一种获取非散列 URLs 并重定向到散列 URLs 的方法。因此用户可以分享他们的个人资料,例如example.com/myprofile
并且它会重定向到 example.com/#!/profile/myprofile
。
然后我专门为社交机器人创建了一个单独的布局(虽然回想起来我不确定这是否完全必要)并在网站被抓取时提供单独的布局。我这样做:
社交-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(包括散列)。希望它能以某种方式提供帮助。
所以我使用 MEAN.js 构建了一个应用程序,并对文章(博客)部分进行了一些更新,以获得更好的搜索引擎优化、可读性、设计等。一个问题我似乎无法弄清楚,不过,如何使用 Facebook、Google+、Twitter 等分享文章,并让它们使用 og 元标记填充正确的数据。
我想要什么
我想要的只是能够从我的 MEAN.js 应用程序共享文章(博客 posts),并在我 post [=] 时显示文章内容86=] 在社交网站(例如 Facebook)中。
我尝试了什么
我试过专门为博客 post 创建一个单独的服务器布局,但这破坏了很多其他事情,我意识到工作量可能不值得 - 必须有一个更聪明的方式。
我也试过在客户端用 Angular 更新 og 元标签数据,但这些值在社交网站获取这些标签之前不能更新……换句话说,它没有实际上做我想做的事。
我尝试在渲染索引时获取 Angular 路由 URL 以便我可以在渲染索引之前更新那些 og 元值,但我无法在任何地方找到这些值在 req
数据中。
我认为问题是什么
从概念上讲,这就是我认为正在发生的事情:
请求到达我的服务器,但由于它是使用 Angular 路由的单页应用程序,因此
req.url
值只是根页面 ('/') .加载索引文件,它使用标准服务器模板布局。
Angular 被加载并进行 AJAX 调用以获取文章数据,然后将该数据绑定到页面上的变量。
所以基本上在 Angular 甚至找出要抓取的文章信息之前就已经渲染了布局(使用 og 元值)。
我猜理想的解决方案是
在我的express.js
文件中,app的局部变量设置如下:
// 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 以 "pretty" 的方式在社交媒体上分享我的文章?我在正确的轨道上吗?当前的 Article 设置是否可行?我是否需要构建一个完全不使用 Angular 的完整博客模块?
我遇到过同样的问题。首先我安装了 Mean-Seo 模块。您可以在 mean.js 官方 github 存储库中找到它。模块本质上是这样做的:爬虫(Google 等)在遇到 SPA URL 时向他们的请求添加一个 _escaped_fragment_
部分。 Mean-Seo 拦截包括 _escaped_fragment_
的请求。然后使用 Phantom.Js,从动态渲染出静态 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 时间、内存、磁盘 space。不必要的文件创建也很愚蠢。我目前的做法是这样的:
我为 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/ last;
}
当爬虫请求 post 的 URL 时,Nginx 触发我新的简单爬虫路由并生成小页面。
我终于在没有 Nginx 或 MEANJS 框架之外的任何其他东西的情况下为我的应用程序工作了。你的里程可能会有所不同,但我想我还是会分享结果。它适用于我,但可能不适合你。
基本上我已经设置的是一种获取非散列 URLs 并重定向到散列 URLs 的方法。因此用户可以分享他们的个人资料,例如example.com/myprofile
并且它会重定向到 example.com/#!/profile/myprofile
。
然后我专门为社交机器人创建了一个单独的布局(虽然回想起来我不确定这是否完全必要)并在网站被抓取时提供单独的布局。我这样做:
社交-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(包括散列)。希望它能以某种方式提供帮助。