Ember-cli & Facebook-share 按钮 - 如何?
Ember-cli & Facebook-share buttons - how to?
目标 - 拥有 Facebook 的分享按钮
我遇到过各种各样的问题,我设法解决了大部分问题,但从来没有同时解决过所有问题。
- ReferenceError: FB 未定义(控制台错误)
- “FB init function gives wrong version error”(控制台错误)
- 我的按钮在转换到另一条路线后没有呈现。
- 我的按钮呈现,但按钮周围与初始路线上的相邻内容没有间距
关于渲染问题(-/连字符是为了注意间距问题)
- 第一次渲染:
- 渲染第 2 次以上:
我学到了什么:
- Facebook 想要
<div id="fb-root"></div>
作为您 <body>
中的第一个元素
FB.XFBML.parse()
可以在 didInsertElement 之后调用以在转换后渲染组件
我做了一个 JSBin 样板尝试,它目前卡在一个未定义的 FB 错误。
我也感兴趣的部分答案:
- 了解解决方案至少需要多复杂才能获得良好的结果(“它必须包含一个初始值设定项和一个 view/component!”或“您只需拥有...就可以解决这个问题”)
可能有用的部分
关于“在 FB.init()”之后的 post
- How to detect when facebook's FB.init is complete
一个初始化程序
/* global FB */
export default {
name: 'facebook',
initialize: function() {
var fbAsyncInit = function() {
FB.init({
appId : 123,
xfbml : true,
version : 'v2.2'
});
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
window.fbAsyncInit = fbAsyncInit;
}
};
一个组件
/* global FB */
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'div',
classNames: 'fb-like',
attributeBindings: [
'data-href',
'data-layout',
'data-action',
'data-show-faces',
'data-share'
],
onDidInsertElement: function() {
Ember.run.schedule('afterRender', FB.XFBML.parse);
}.on('didInsertElement'),
init: function() {
Ember.run.schedule('afterRender', FB.XFBML.parse);
}
});
一个脚本标签
<script type="text/javascript" src="//connect.facebook.net/en_US/sdk.js#xfbml=1&appId=123&version=v2.2"></script>
root div facebook 请求
<div id="fb-root"></div>
正在加载 Facebook SDK
import ENV from "my-app/config/environment";
import setupOfflineMode from "my-app/utils/offline-mode";
export function initialize(container, application) {
// offline mode stubs `FB`
if (ENV.offlineMode) { return setupOfflineMode(); }
// Wait for Facebook to load before allowing the application
// to fully boot. This prevents `ReferenceError: FB is not defined`
application.deferReadiness();
var fbAsyncInit = function() {
initFacebook(window.FB);
application.advanceReadiness();
};
loadFacebookSDK();
window.fbAsyncInit = fbAsyncInit;
}
function loadFacebookSDK() {
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
}
function initFacebook(FB) {
FB.init({
appId: ENV.FB_APP_ID,
status: true,
cookie: true,
xfbml: true,
version: ENV.GRAPH_API_VERSION
});
}
export default {
name: 'facebook',
initialize: initialize
};
分享链接
我认为这就是我需要做的;我希望我没有忘记什么...
我没有设置组件,所以这只是一个常规视图,但它应该大致相同。
<div class="fb-share-button" {{bind-attr data-href=link}} data-type="button"></div>
export default Ember.View.extend({
setupSocialNetworks: function() {
Ember.run.scheduleOnce('afterRender', this, function() {
FB.XFBML.parse();
});
}.on('didInsertElement')
});
更新:替代解决方案
我认为您使用哪种解决方案完全取决于您的需求。我专注于缩短首次渲染时间,因此我已将我的项目更改为不针对 Facebook SDK deferReadiness
。
我一直在尝试两种解决方案,我认为这完全取决于您的需求。
在初始化程序中加载 Facebook SDK,但设置全局访问承诺。
这会在启动时启动加载,但允许您的应用程序继续启动而无需等待 Facebook。所有对 Facebook API 的调用都需要通过 promise 访问。
如果需要,我会分享这个的细节,但现在我只关注下一个解决方案:
仅在服务中按需加载 Facebook SDK。
和以前一样,所有对 Facebook API 的访问都需要通过一个承诺,但这次它很好地封装在一个服务中,并且只按需加载:
// app/services/facebook.js
import Ember from 'ember';
import ENV from "juniper/config/environment";
var computed = Ember.computed;
var RSVP = Ember.RSVP;
var _facebookSDKDeferrable = Ember.RSVP.defer();
var fbAsyncInit = function() {
_initFacebook(window.FB);
_facebookSDKDeferrable.resolve(window.FB);
};
window.fbAsyncInit = fbAsyncInit;
export default Ember.Service.extend({
// Resolves when the Facebook SDK is ready.
//
// Usage:
//
// facebook: Ember.inject.service(),
// foo: function() {
// this.get('facebook.SDK').then(function(FB) {
// // Facebook SDK is ready and FB is a reference to the SDK
// });
// }
SDK: computed.alias('FB'),
FB: computed(function() {
_loadFacebookSDK();
return _facebookSDKDeferrable.promise;
})
// I've also put promisified helpers for Facebook SDK
// methods here.
});
function _loadFacebookSDK() {
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
}
function _initFacebook(FB) {
FB.init({
appId: ENV.FB_APP_ID,
status: true,
cookie: true,
xfbml: false,
version: ENV.GRAPH_API_VERSION
});
}
另请注意,我在 FB.init
中将 xfbml
设置为 false
。如果您的共享链接在 SDK 加载之前已经呈现,FB.init({ xfbml: true })
将 "parse" 它们,并且您的 FB.XFBML.parse()
调用将再次执行。通过将 xfbml
设置为 false
,您可以确保 FB.XFBML.parse()
只会被调用一次。
目标 - 拥有 Facebook 的分享按钮
我遇到过各种各样的问题,我设法解决了大部分问题,但从来没有同时解决过所有问题。
- ReferenceError: FB 未定义(控制台错误)
- “FB init function gives wrong version error”(控制台错误)
- 我的按钮在转换到另一条路线后没有呈现。
- 我的按钮呈现,但按钮周围与初始路线上的相邻内容没有间距
关于渲染问题(-/连字符是为了注意间距问题)
- 第一次渲染:
- 渲染第 2 次以上:
我学到了什么:
- Facebook 想要
<div id="fb-root"></div>
作为您<body>
中的第一个元素
FB.XFBML.parse()
可以在 didInsertElement 之后调用以在转换后渲染组件
我做了一个 JSBin 样板尝试,它目前卡在一个未定义的 FB 错误。
我也感兴趣的部分答案:
- 了解解决方案至少需要多复杂才能获得良好的结果(“它必须包含一个初始值设定项和一个 view/component!”或“您只需拥有...就可以解决这个问题”)
可能有用的部分
关于“在 FB.init()”之后的 post
- How to detect when facebook's FB.init is complete
一个初始化程序
/* global FB */
export default {
name: 'facebook',
initialize: function() {
var fbAsyncInit = function() {
FB.init({
appId : 123,
xfbml : true,
version : 'v2.2'
});
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
window.fbAsyncInit = fbAsyncInit;
}
};
一个组件
/* global FB */
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'div',
classNames: 'fb-like',
attributeBindings: [
'data-href',
'data-layout',
'data-action',
'data-show-faces',
'data-share'
],
onDidInsertElement: function() {
Ember.run.schedule('afterRender', FB.XFBML.parse);
}.on('didInsertElement'),
init: function() {
Ember.run.schedule('afterRender', FB.XFBML.parse);
}
});
一个脚本标签
<script type="text/javascript" src="//connect.facebook.net/en_US/sdk.js#xfbml=1&appId=123&version=v2.2"></script>
root div facebook 请求
<div id="fb-root"></div>
正在加载 Facebook SDK
import ENV from "my-app/config/environment";
import setupOfflineMode from "my-app/utils/offline-mode";
export function initialize(container, application) {
// offline mode stubs `FB`
if (ENV.offlineMode) { return setupOfflineMode(); }
// Wait for Facebook to load before allowing the application
// to fully boot. This prevents `ReferenceError: FB is not defined`
application.deferReadiness();
var fbAsyncInit = function() {
initFacebook(window.FB);
application.advanceReadiness();
};
loadFacebookSDK();
window.fbAsyncInit = fbAsyncInit;
}
function loadFacebookSDK() {
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
}
function initFacebook(FB) {
FB.init({
appId: ENV.FB_APP_ID,
status: true,
cookie: true,
xfbml: true,
version: ENV.GRAPH_API_VERSION
});
}
export default {
name: 'facebook',
initialize: initialize
};
分享链接
我认为这就是我需要做的;我希望我没有忘记什么...
我没有设置组件,所以这只是一个常规视图,但它应该大致相同。
<div class="fb-share-button" {{bind-attr data-href=link}} data-type="button"></div>
export default Ember.View.extend({
setupSocialNetworks: function() {
Ember.run.scheduleOnce('afterRender', this, function() {
FB.XFBML.parse();
});
}.on('didInsertElement')
});
更新:替代解决方案
我认为您使用哪种解决方案完全取决于您的需求。我专注于缩短首次渲染时间,因此我已将我的项目更改为不针对 Facebook SDK deferReadiness
。
我一直在尝试两种解决方案,我认为这完全取决于您的需求。
在初始化程序中加载 Facebook SDK,但设置全局访问承诺。
这会在启动时启动加载,但允许您的应用程序继续启动而无需等待 Facebook。所有对 Facebook API 的调用都需要通过 promise 访问。
如果需要,我会分享这个的细节,但现在我只关注下一个解决方案:
仅在服务中按需加载 Facebook SDK。
和以前一样,所有对 Facebook API 的访问都需要通过一个承诺,但这次它很好地封装在一个服务中,并且只按需加载:
// app/services/facebook.js
import Ember from 'ember';
import ENV from "juniper/config/environment";
var computed = Ember.computed;
var RSVP = Ember.RSVP;
var _facebookSDKDeferrable = Ember.RSVP.defer();
var fbAsyncInit = function() {
_initFacebook(window.FB);
_facebookSDKDeferrable.resolve(window.FB);
};
window.fbAsyncInit = fbAsyncInit;
export default Ember.Service.extend({
// Resolves when the Facebook SDK is ready.
//
// Usage:
//
// facebook: Ember.inject.service(),
// foo: function() {
// this.get('facebook.SDK').then(function(FB) {
// // Facebook SDK is ready and FB is a reference to the SDK
// });
// }
SDK: computed.alias('FB'),
FB: computed(function() {
_loadFacebookSDK();
return _facebookSDKDeferrable.promise;
})
// I've also put promisified helpers for Facebook SDK
// methods here.
});
function _loadFacebookSDK() {
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
}
function _initFacebook(FB) {
FB.init({
appId: ENV.FB_APP_ID,
status: true,
cookie: true,
xfbml: false,
version: ENV.GRAPH_API_VERSION
});
}
另请注意,我在 FB.init
中将 xfbml
设置为 false
。如果您的共享链接在 SDK 加载之前已经呈现,FB.init({ xfbml: true })
将 "parse" 它们,并且您的 FB.XFBML.parse()
调用将再次执行。通过将 xfbml
设置为 false
,您可以确保 FB.XFBML.parse()
只会被调用一次。