【问题标题】:How to Break up large Javascript file with RequireJS如何使用 RequireJS 分解大型 Javascript 文件
【发布时间】:2015-11-03 03:51:27
【问题描述】:

我是 Javascript 新手,因此 requireJS - 我目前正在使用 Openlayers 3 示例自学,其中,我刚刚附加到一个大型 JS 文件。看到这很快变得不守规矩,我阅读了有关 RequireJS 的内容,并认为我应该从一开始就养成做事的习惯; '这是我遇到问题的地方'。

[我认为这并不重要,但我使用的是 Asp.net MVC] 基本上,我希望将文件分解为较小的相关模块,例如

  • 地图【所有模块都使用,启动基础层地图】
  • 绘制[处理点/多边形等并添加到地图中 另一层]
  • 地理位置 [包含用于绘图的地理位置功能]
  • 等等等等

...提供了一次激活所有层的灵活性,或者选择几个易于管理的 JS 代码。

我曾多次尝试将这段代码分解成单独的 JS 文件 [map / draw / Geolocation],但都失败了,因为我觉得我没有掌握 requireJS 方法(以免混淆读者还有我自己,我忽略了添加我的尝试)。

这是有效的基本代码:

require.config({
    baseUrl: "/Scripts",
    paths: {
        //jquery: "/lib/jquery-1.11.1.min",
        ol: [
            "http://openlayers.org/en/v3.8.1/build/ol",
            "/lib/ol"
        ],
        domReady: "/lib/domReady"
    },
    //map: { main: { test: "/Modules/Test/scripts/test" } },
    //The shim section is to tell RequireJS about any dependencies your files have before they can be used.  
    //Here, we are saying if we call “ol” to load that module, we have to load “jquery” first.
    //shim: {
        //ol: ["jquery"]
    //},
    //packages: [
    //    {
    //name: 'test',
    //location: 'http://...
    //main: 'main'                
    //}]
});

我想拆分的文件:

define(["ol"], function (ol) {

    $(document).ready(function () {

        //****************
        //------MAP-------

        //Setup Map Base
        // creating the view
        var view = new ol.View({
            center: ol.proj.fromLonLat([5.8713, 45.6452]),
            zoom: 19
        });

        // creating the map
        var map = new ol.Map({
            layers: [
                new ol.layer.Tile({
                    source: new ol.source.OSM()
                })
            ],
            target: "map",
            controls: ol.control.defaults({
                attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
                    collapsible: false
                })
            }),
            view: view
        });

        //****************
        //-----DRAW------

        var features = new ol.Collection();
        var featureOverlay = new ol.layer.Vector({
            source: new ol.source.Vector({ features: features }),
            style: new ol.style.Style({
                fill: new ol.style.Fill({
                    color: "rgba(255, 255, 255, 0.2)"
                }),
                stroke: new ol.style.Stroke({
                    color: "#ffcc33",
                    width: 2
                }),
                image: new ol.style.Circle({
                    radius: 7,
                    fill: new ol.style.Fill({
                        color: "#ffcc33"
                    })
                })
            })
        });
        featureOverlay.setMap(map);

        var modify = new ol.interaction.Modify({
            features: features,
            // the SHIFT key must be pressed to delete vertices, so
            // that new vertices can be drawn at the same position
            // of existing vertices
            deleteCondition: function (event) {
                return ol.events.condition.shiftKeyOnly(event) &&
                    ol.events.condition.singleClick(event);
            }
        });
        map.addInteraction(modify);

        var draw; // global so we can remove it later
        function addInteraction() {
            draw = new ol.interaction.Draw({
                features: features,
                type: /** @type {ol.geom.GeometryType} */ (typeSelect.value)
            });
            map.addInteraction(draw);
        }

        var typeSelect = document.getElementById("type");


        /**
         * Let user change the geometry type.
         * @param {Event} e Change event.
         */
        typeSelect.onchange = function (e) {
            map.removeInteraction(draw);
            addInteraction();
        };

        addInteraction();

        //****************
        //---GEOLOCATION---//

        // Common app code run on every page can go here
        // Geolocation marker
        var markerEl = document.getElementById("geolocation_marker");
        var marker = new ol.Overlay({
            positioning: "center-center",
            element: markerEl,
            stopEvent: false
        });
        map.addOverlay(marker);

        // LineString to store the different geolocation positions. This LineString
        // is time aware.
        // The Z dimension is actually used to store the rotation (heading).
        var positions = new ol.geom.LineString([],
            /** @type {ol.geom.GeometryLayout} */ ("XYZM"));

        // Geolocation Control
        var geolocation = new ol.Geolocation( /** @type {olx.GeolocationOptions} */({
            projection: view.getProjection(),
            trackingOptions: {
                maximumAge: 10000,
                enableHighAccuracy: true,
                timeout: 600000
            }
        }));

        var deltaMean = 500; // the geolocation sampling period mean in ms

        // Listen to position changes
        geolocation.on("change", function (evt) {
            var position = geolocation.getPosition();
            var accuracy = geolocation.getAccuracy();
            var heading = geolocation.getHeading() || 0;
            var speed = geolocation.getSpeed() || 0;
            var m = Date.now();

            addPosition(position, heading, m, speed);

            var coords = positions.getCoordinates();
            var len = coords.length;
            if (len >= 2) {
                deltaMean = (coords[len - 1][3] - coords[0][3]) / (len - 1);
            }

            var html = [
                "Position: " + position[0].toFixed(2) + ", " + position[1].toFixed(2),
                "Accuracy: " + accuracy,
                "Heading: " + Math.round(radToDeg(heading)) + "°",
                "Speed: " + (speed * 3.6).toFixed(1) + " km/h",
                "Delta: " + Math.round(deltaMean) + "ms"
            ].join("<br />");
            document.getElementById("info").innerHTML = html;
        });

        geolocation.on("error", function () {
            alert("geolocation error");
            // FIXME we should remove the coordinates in positions
        });

        // convert radians to degrees
        function radToDeg(rad) {
            return rad * 360 / (Math.PI * 2);
        }

        // convert degrees to radians
        function degToRad(deg) {
            return deg * Math.PI * 2 / 360;
        }

        // modulo for negative values
        function mod(n) {
            return ((n % (2 * Math.PI)) + (2 * Math.PI)) % (2 * Math.PI);
        }

        function addPosition(position, heading, m, speed) {
            var x = position[0];
            var y = position[1];
            var fCoords = positions.getCoordinates();
            var previous = fCoords[fCoords.length - 1];
            var prevHeading = previous && previous[2];
            if (prevHeading) {
                var headingDiff = heading - mod(prevHeading);

                // force the rotation change to be less than 180°
                if (Math.abs(headingDiff) > Math.PI) {
                    var sign = (headingDiff >= 0) ? 1 : -1;
                    headingDiff = -sign * (2 * Math.PI - Math.abs(headingDiff));
                }
                heading = prevHeading + headingDiff;
            }
            positions.appendCoordinate([x, y, heading, m]);

            // only keep the 20 last coordinates
            positions.setCoordinates(positions.getCoordinates().slice(-20));

            // FIXME use speed instead
            if (heading && speed) {
                markerEl.src = "/OrchardLocal/Media/Default/Map/geolocation_marker.png"; //"data/geolocation_marker_heading.png";F:\DeleteMeThree\_Orchard-19x\src\Orchard.Web\Modules\Cns.OL\Contents/Images/geolocation_marker.png
            } else {
                //alert(markerEl.src); PETE: Not sure if this is std OL practice, but this is achieved by already having an element
                //called "geolocation_marker" in the dom as an img, which this uses? Strange to me
                markerEl.src = "/OrchardLocal/Media/Default/Map/geolocation_marker.png"; //I added img via media module - ridiculous?!
            }
        }

        var previousM = 0;
        // change center and rotation before render
        map.beforeRender(function (map, frameState) {
            if (frameState !== null) {
                // use sampling period to get a smooth transition
                var m = frameState.time - deltaMean * 1.5;
                m = Math.max(m, previousM);
                previousM = m;
                // interpolate position along positions LineString
                var c = positions.getCoordinateAtM(m, true);
                var view = frameState.viewState;
                if (c) {
                    view.center = getCenterWithHeading(c, -c[2], view.resolution);
                    view.rotation = -c[2];
                    marker.setPosition(c);
                }
            }
            return true; // Force animation to continue
        });

        // recenters the view by putting the given coordinates at 3/4 from the top or
        // the screen
        function getCenterWithHeading(position, rotation, resolution) {
            var size = map.getSize();
            var height = size[1];

            return [
                position[0] - Math.sin(rotation) * height * resolution * 1 / 4,
                position[1] + Math.cos(rotation) * height * resolution * 1 / 4
            ];
        }

        // postcompose callback
        function render() {
            map.render();
        }

        //EMP
        //$("#geolocate").click(function () {
        //    alert("JQuery Running!");
        //});

        // geolocate device
        var geolocateBtn = document.getElementById("geolocate");
        geolocateBtn.addEventListener("click", function () {
            geolocation.setTracking(true); // Start position tracking

            map.on("postcompose", render);
            map.render();

            disableButtons();
        }, false);
    });
})

考虑到我将来要附加更多模块,使用 RequireJS 分解此代码以提高效率和编码功能/维护的最佳方法是什么。

非常感谢您的指导/想法,干杯 WL

【问题讨论】:

    标签: javascript requirejs require


    【解决方案1】:

    每个 require 模块 (defined using define) 都应该返回一个 function/object。有问题的分解没有,而是拆分代码。考虑一些假设的模块桶,并将每段代码(或函数)放入一个模块中。然后将代码分组到require js模块中,并返回模块的接口。

    让我试着用一个例子来进一步解释。

    ma​​in.js

    $(document).ready(function(){
      $("#heyINeedMap").click(function(){
        require(['map'],function(Map){
          Map.render($target);
        });  
      });
    });
    

    $(document).ready(function(){
      require(['map','geolocation'],function(Map,Geolocation){
          window.App.start = true;
          window.App.map = Map;              //won't suggest, but you can do.
          window.App.geolocation = Geolocation; 
    
          //do something.
          $("#lastCoords").click(function(){
            var coords = App.geolocation.getLastSavedCoords();
            if(!!coords){
              coords = App.geolocation.fetchCurrentCoords();
            }
            alert(coords);
          });
      });
    });
    

    ma​​p.js

    define(['jquery'],function($){
      var privateVariableAvailableToAllMapInstances = 'something';
      var mapType = 'scatter';
      return function(){
        render: function(el){
          //rendering logic goes here
        },
        doSomethingElse: function(){
          privateVariable = 'some new value';
          //other logic goes here
        },
        changeMapType: function(newType){
          mapType = newType;
          //...
        }
      }
    });
    

    geolocation.js

    //Just assuming that it needs jquery & another module called navigation to work.
    define(['jquery','navigation'], function($,Gnav){
      return {
        var coordinates = Gnav.lastSavedCoords;
        fetchCurrentCoords: function(){
          //coordinates = [79.12213, 172.12342];  //fetch from API/something
          return coordinates;
        },
        getLastSavedCoords: function(){
          return coordinates;
        }
      }
    });
    

    希望这能提供有关如何进行的想法。

    【讨论】:

    • 感谢您的快速回复...请原谅我的无能,我毫不怀疑您的概念的合理性,但由于我对 JS 的新鲜感,我对您的示例感到有些困惑。我以前从未遇到过返回似乎是无名/无标识符函数数组的函数?很抱歉很痛苦,但你能否用你的例子稍微详细说明一下:map.js [......并尽量不要太厚脸皮 - 因为我非常感谢我能得到的任何例子/伪 - 但可能与具有“绘制”功能的 map.js 相关,给我一些视角]?在此先感谢 WL
    • 假定render 命名为draw
    • 我对相同的概念感兴趣,但看不到这如何回答或解决原始 OP 的愿望?看来这只会用另一个 [map.js] 替换他们已经很大的文件,但会被划分为函数——根据 OP 的后续问题,我无法开始工作:逗号分隔函数的语法不正确,而且它们都没有没有“预期标识符”——我们是否遗漏了什么,是否可以将此类代码分解/封装到单独的 javascript 文件中,但在这种情况下仍可以访问 OL 和映射变量?干杯
    • 不完全是函数,而是模块,可以根据自己的理解返回函数或对象。
    • 进一步阐述了答案,希望对您有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-15
    • 2013-02-02
    • 2019-04-13
    • 2021-04-29
    • 1970-01-01
    相关资源
    最近更新 更多