简体   繁体   English

如何在使用Browserify时公开Goog​​le地图的回调功能?

[英]How to expose callback function for Google Maps when using Browserify?

I'm using Gulp and Browserify to bundle my JavaScripts. 我正在使用Gulp和Browserify捆绑我的JavaScripts。 I need to expose a callback function that should be executed after the Google Maps API loads. 我需要公开一个应该在Google Maps API加载后执行的回调函数。

How can this be done without using something like window.initMap ? 如何在不使用window.initMap类的情况下完成这项工作? The problem with this is that I need to fire a large number of other methods inside initMap, so there has to be a better way of doing it besides using window.functionName and polluting the global namespace. 这个问题是我需要在initMap中触发大量其他方法,因此除了使用window.functionName和污染全局命名空间之外,还必须有更好的方法。

On the other hand, is it alright to just exclude the callback parameter and do something like this instead? 另一方面,是否可以排除callback参数并执行类似的操作?

$.getScript('https://maps.googleapis.com/maps/api/js').done(function() {
  initMap();
});

Any help would be greatly appreciated. 任何帮助将不胜感激。 I have spent more time that I would ever admit in getting this to work. 我花了更多的时间来承认让它发挥作用。

gulpfile.js: gulpfile.js:

gulp.task('browserify', ['eslint'], function() {
  return browserify('/src/js/main.js')
    .bundle()
    .pipe(source('main.js'))
    .pipe(buffer())
    .pipe(gulp.dest('/dist/js'))
    .pipe(reload({ stream: true }));
});

main.js: main.js:

require('jquery');
require('./map');

map.js: map.js:

var map = (function() {
  'use strict';

  var mapElement = $('#map');

  function googleMapsAPI() {
    $.getScript('https://maps.googleapis.com/maps/api/js?callback=initMap');
  }

  function initMap() {
    var theMap = new google.maps.Map(mapElement);
    // functions...
  }

  function init() {
    googleMapsAPI();
  }
});

map.init();

No, it's not okay to not include the callback parameter. 不,不包括callback参数也不行。

The google maps API library calls a bunch of other scripts to be loaded on the page and then, when they have all been loaded, the callback parameter is called on the window object. 谷歌地图API库调用要在页面上加载的一堆其他脚本,然后,当它们全部被加载时,在窗口对象上callback参数。

Just declare it on the window object: 只需在window对象上声明它:

var MyApp = {
    init: function() {
         //all your stuff
    }
}

window.initMap = function() {
   window.initMap = null; //set this to null this so that it can't get called anymore....if you want
   MyApp.init();
};

and then just include the script tag on your page: 然后只需在页面上包含脚本标记:

<script src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>

If you want to load the script and then do something when the script has been loaded, you can set the attributes async and onload when injecting the script . 如果要加载脚本,然后在加载脚本时执行某些操作,则可以在注入script时设置属性asynconload By wrapping all the code into an IIFE we will keep private all objects defined inside the IIFE , avoiding populate the global namespace window . 通过将所有代码包装到IIFE中,我们将保留在IIFE内定义的所有对象的私有 ,从而避免填充全局命名空间window See the following example: 请参阅以下示例:

// IIFE (Immediately-Invoked Function Expression)
// Keeps all private
!function() {
/**
 * Injects the script asynchronously.
 *
 * @param {String} url: the URL from where the script will be loaded
 * @param {Function} callback: function executed after the script is loaded
 */
function inject(url, callback) {
  var tag = 'script',
    script = document.createElement(tag),
    first = document.getElementsByTagName(tag)[0];
  script.defer = script.async = 1; // true
  script.type = 'text/javascript';
  script.src = url;
  script.onload = callback;
  first.parentNode.insertBefore(script, first);
}

/**
 * Injects and initializes the google maps api script.
 */
function injectMapsApi() {
  var key = 'your-api-key';
  var query = '?key=' + key;
  var url = 'https://maps.googleapis.com/maps/api/js' + query;
  inject(url, initMapsApi);
}

/**
 * Callback that initializes the google maps api script.
 */
function initMapsApi() {
  var maps = window.google.maps;
  // ... code initializations
  console.log(maps);
}

injectMapsApi();

}(); // end IIFE

You need to register and claim you API key in order to use the google maps API. 您需要注册并声明API密钥才能使用Google Maps API。 More information here: 更多信息:

I honestly think here it is a better solution to simply define a global initMap function to keep things simple while taking advantage of Google Maps asynchronous initialization. 老实说,我认为这是一个更好的解决方案,只需定义一个全局initMap函数,以便在利用Google Maps异步初始化的同时保持简单。 It might sound like a hack, but you can define a random name for the function and then simply remove it from the global scope once Google Maps SDK has called it. 这可能听起来像是黑客攻击,但您可以为该功能定义一个随机名称,然后在Google Maps SDK调用它后将其从全局范围中删除。 This mechanism is similar to the one used in JSONP. 此机制类似于JSONP中使用的机制。

var functionName = getRandomName();
window[functionName] = function() {
    window[functionName] = undefined;
    // call to your initialization functions
};

In this answer you can check out that the way to prevent polluting the global scope is to make the google maps script load synchronously, what could harm user experience, specially on smartphones. 这个答案中,您可以看出防止污染全球范围的方法是使谷歌地图脚本同步加载,这可能会损害用户体验,特别是在智能手机上。

I've had issues with this approach Google has taken also. 我对谷歌采取的这种方法也存在疑问。 I don't like it very much myself. 我自己并不喜欢它。

My way to deal with this as of late has been creating the global function, with a twist of firing an event to trigger my actual application javascript. 我最近处理这个问题的方法是创建全局函数,其中包括触发事件来触发我的实际应用程序javascript。 This way I have my application JS clean of dealing the maps API handling, and it's one small global function call outside of my main object. 这样我的应用程序JS就可以处理地图API处理了,它是我主要对象之外的一个小型全局函数调用。

function initMap(){
  $(document).ready(function(){
    $(window).on('GoogleMapsLoaded', myObj.init());
    $(window).trigger('GoogleMapsLoaded');
  });
};

With that I just include the callback=initMap in the script url. 我只是在脚本url中包含callback=initMap

UPDATE: Another option is to just include your callback as a function inside your object. 更新:另一种选择是将回调作为函数包含在对象中。 Ex: your object could be something like 例如:你的对象可能是这样的

var app = app || {};

(function($){

   $(function(){
      $.extend(app, {
        initMap:function(yourMainWrapDiv){
           //Do whatever you need to do after the map has loaded
        },
        mapLoadFunction(){
           //Map API has loaded, run the init for my whole object
           this.initMap($('#mainWrapper'))
        },
        mainInit: function(){
           ///do all your JS that can or needs  
           // to be done before the map API loads
           this.maybeSetSomeBindings();
        },
        maybeSetSomeBindings: function(){
             //do things
        }
      });
      //On document ready trigger your mainInit
      //To do other things while maps API loads
      app.mainInit()
   });
})(jQuery);

Then you can just use the callback to jump inside your one global object and run what you need to run just for the map handling. 然后,您可以使用回调跳转到一个全局对象中,并运行您只需要为地图处理运行的内容。 Your API url could be with callback=app.initMap 您的API网址可以使用callback=app.initMap

That could keep it cleaner also 这也可以保持清洁

UPDATE 2: Yet another option (I minimally tested) would be to NOT use the callback parameter in the Google API url, and link it with whatever else, library wise, you needed. 更新2:另一个选项(我经过最低限度测试)将使用Google API网址中的callback参数,并将其与您需要的任何其他图书馆链接相关联。 (places, search, etc). (地方,搜索等)。 https://maps.googleapis.com/maps/api/js?key=YOUR-KEY-HERE&libraries=places for example. https://maps.googleapis.com/maps/api/js?key=YOUR-KEY-HERE&libraries=places例如。

Then in your object init function just set a timer with to see if the google object is available! 然后在你的对象init函数中设置一个计时器,看看google对象是否可用! Maybe something like this: 也许是这样的:

 var app = app || {}; (function($){ $(function(){ $.extend(app, { init:function(){ var self = this; var timer = setInterval(function(){ if ($('.ex').length){ //but really check for google object console.log('things exist google, elements, etc..'); self.next(); clearInterval(timer); } }); }, next:function(){ console.log('google object exists') } }); app.init() }); })(jQuery); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class='ex'>as an example for something to trigger a flag (true/false) to clear the interval</div> 

in any case where you try to access the global object, in this case app , as a callback in the URL you would set callback=app.yourFunctionToCall NOT callback=app.funtionToCall() you script tag should also have the async and defer attributes attributed to it to promote further html parsing (your app's js should be directly after the maps script) 在任何情况下,你试图访问全局对象,在这种情况下app ,作为URL中的回调你将设置callback=app.yourFunctionToCall NOT callback=app.funtionToCall()script标签也应该有asyncdefer属性归因于它促进进一步的html解析(你的应用程序的js应该直接在地图脚本之后)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM