Плагин jQuery с «локальными» переменными, принадлежащими данному экземпляру плагина - PullRequest
0 голосов
/ 12 декабря 2018

Я работаю над плагином jQuery, но у меня возникают проблемы с «областью видимости» переменных.Каждый плагин должен отслеживать многомерный массив значительного размера, а также корневой элемент, к которому подключен плагин jQuery.

Как показано ниже, я определил var $rootEl = null; и var multidArray = null; вверхняя часть кода плагина;однако, когда я запускаю $(anySelector).pluginName("value"); на нескольких элементах (или когда я вызываю .pluginName() дважды с разными селекторами), может показаться, что эти переменные не помещаются в изолированную программную среду / область видимости для экземпляра плагина, поэтому второй экземпляр перезаписывает оба значенияи первый экземпляр выглядит пустым в DOM (потому что все действия jQuery применяются к $rootEl с исходным значением, перезаписываемым при втором вызове плагина jQuery).

(function ($) {

    var $rootEl = null;
    var multidArray = null;

    directChildren = function(uuid) {

        return $rootEl.children("[data-uuid=" + uuid + "]");

    },

    incrementCounter = function() {
        this.counter++;
    },

    resetGenIteration = function() {
        this.counter = 0;
    },

    process = function(item) { // Will be called from a callback(val) in another
        // javascript file but still needs to have access to the local variables
        // of the current jQuery plugin

        incrementCounter();

        ...

    },

    ...

    $.fn.pluginName = function(rootvar) {

        this.each(function() {

            $rootEl = $(this);

            resetGenIteration();
            populate(rootvar);

            $rootEl.addClass("pluginName").delegate("div", "click", divClick);

        });

    }

}(jQuery));

Вчера я попытался преобразовать плагиниспользовать init и this.varname для хранения значений, и он, казалось, правильно удерживал свойство this.$rootEl для каждого экземпляра плагина, но мне пришлось переместить все свои функции внутри var PluginName = { ... } и переключить их с funcName = function() до funcName: function(), чтобы иметь возможность видеть "локальные" переменные экземпляра плагина.Затем я столкнулся с многочисленными ошибками, когда функции не были определены, поэтому я попытался поставить перед всеми вызовами функций префикс this. и PluginName., чтобы они читались как this.functionName() или PluginName.functionName() соответственно, что работало для некоторых функций, но не для всех.Некоторые из моих функций вызываются из другого плагина jQuery, который запускает функцию обратного вызова и передает в нее данные, и в этих случаях this.$rootEl становится undefined.

(function ($){

    var PluginName = {

        init: function(options, rootEl) {

            this.options = $.extend({}, this.options, options);

            this.rootEl  = rootEl;
            this.$rootEl = $(rootEl);

            this.setListeners();

            return this;

        },

        options: {
            rootvar: "test"
        },

        ...

        setListeners:function (){

            var self = this;

            this.$rootEl.on("div", "click", function () {
                $.proxy(self.divClick, self);
            });

        }

    };

    ...

    $.fn.pluginName = function(options) {

        this.init = function(options, rootEl) {

            this.options = $.extend({}, this.options, options);

            this.rootEl  = rootEl;
            this.$rootEl = $(rootEl);
            this.multidArray = null;


            return this;

        };

        this.each(function() {

            var plugInstance = Object.create(PluginName);
            plugInstance.init(options, this);
            $.data(this, 'pluginName', plugInstance);

        });

    }

}(jQuery));

Как мне выполнить рефакторинг моего кода вхранить многомерный массив и корневой элемент таким образом, чтобы только этот плагин мог извлекать и изменять его изнутри?Конечно, я не должен использовать .data() для больших массивов?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

Как сказал @Damon, в плагине jquery нет ничего особенного - это просто функция, а JS - это все о функциях;)

Так что все проблемы у вас возникают, потому что вы не продумываете все ваши привычки.Я не знаю, какие у вас привычки (или планы), но вот как бы я это сделал.Это плагин, который хранит "bigArray" для каждого экземпляра.Увеличиваем его и помещаем в объекты html.Пояснения после:

(function($) {
        let _plugInstances = [];

        function PlugFactory(options) {
            let _plugInstances = [],
                _create = function(options) {
                    let $el = $(this),
                        id = $el.data('PlugID') || (+new Date +''+Math.random()).replace('.', '_');
                    if (_plugInstances[id])
                        return _plugInstances[id];
                    $el.data('PlugID', id);
                    _plugInstances[id] = new Plug($el, options);
                    return _plugInstances[id];
                };

            this.instantiate = function(options) {
                let newPlugs = this.map(_create);
                return newPlugs.length > 1 ? newPlugs : newPlugs[0];
            }
        }
        function Plug(rootEl, options) {
            let _$rootEl = rootEl,
                _options = options,
                _bigArr = [];
            this.add = (item) => {
                _bigArr.push(item);
                return this;
            };
            this.refresh = () => {
                _$rootEl.html(_$rootEl.data('PlugID') + ":" + _bigArr.join('-'));
                return this;
            }
        }

        $.extend({plug: new PlugFactory});
        // extend plugin scope
        $.fn.extend({
            plug: $.plug.instantiate
        });
    })(jQuery);

    // Usage
    $(document).ready(function () {
        $('div').plug({opt: 'whatever'});
        $('div').on('click', function() {
            $(this).plug()
                .add(1)
                .refresh();
        })
        $('#i4').plug({opt: 'whatever'}).add(2).refresh();
    });
div{border: solid 2px gray;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>1</div><div>2</div><div>3</div>
<br/>
<div id="i4">4</div>

Пояснения:

Как вы можете видеть, что вы использовали в $ .fn.pluginNameперемещен в отдельную функцию (скажем, в класс) (PluginFactory)

Это на самом деле функция, которая будет хранить экземпляры вашего плагина.

Как вы, наверное, заметили, переменные начинаются с подчеркиванияони частные свойства , дело в том, что они замыкания , поэтому они видны только внутри функций, в которых они определены, и поскольку они являются переменными, они не доступны как свойство экземпляра функции.

Также, пожалуйста, примите во внимание ES6 "контекстно-безопасные" вещи, такие как краткая запись функций и let .

Теперь дело с экземплярами.

Как вы можете видеть, у нас есть некоторый UID, сгенерированный для каждого элемента (вы можете использовать любой другой генератор, это просто POC )

Далее мы создадимновый экземпляр с новым оperator

И сохраните его в Map , чтобы упростить процесс получения экземпляров.

И сохраните наш UID в элементах данных jquery (PliginID), так что теперь мы знаемесли у нашего элемента уже есть экземпляр плагина.

Теперь основная часть (функция плагина).

Это на самом деле ваш экземпляр, который будет жить во время вызовов ".plug ".

Каждый раз, когда вы получаете $ ('# element'). plug (), вы получите свой изначально созданный экземпляр.

Таким образом, у вас будет доступ ко всем вашим личными разработайте ваши публичные методы.

PS.Я не утверждаю, что это лучший, безусловно, один из многих, в качестве примера того, как решить задачу "хранить многомерный массив и корневой элемент таким образом, чтобы только этот плагин мог внутренне извлекать и изменять"

Надеюсь, это достаточно общее, чтобы быть полезным;) Ура!

0 голосов
/ 19 декабря 2018

Плагин - это просто функция, которая прикрепляется к объекту jQuery.Он не имеет нескольких экземпляров, но может вызываться несколько раз.Каждый вызов создает новую область видимости, однако переменные, объявленные вне функции pluginName, будут совместно использоваться каждым вызовом.

Вместо объектно-ориентированного подхода, который вы пытаетесь использовать во второй итерации, я бы предложил вернуться киспользуя несколько функций.Однако вы можете передать этим функциям все необходимые аргументы, чтобы им не приходилось полагаться на неоднозначные this значения.

Поскольку вы включили только частичный фрагмент своего кода, я не могу получить четкое представлениеиз того, что вы пытаетесь достичь, но я включил пример плагина ниже, который подчеркивает, как правильно использовать область.

(function ($) {
  // declare variables that can be shared by every call of "pluginName" here

  var defaultColor = 'green'

  // passing the element as an argument means we don't have to rely on ambiguous 'this'.
  function someTransformation ($el, someColor) {
    return $el.css({backgroundColor: someColor})
  }


  $.fn.pluginName = function (color, idx) {
    // declare variables that should be reinitialized on every call of "pluginName" here
    var els = []

    this.each(function () {
      var $el = $(this)
      someTransformation($el, defaultColor)
      els.push($el)
    })

    // we can still use the array we stored
    if (els[idx]) {
      someTransformation(els[idx], color)
    }
  }
})($)

$('#first i').pluginName('red', 2)
$('#second i').pluginName('blue', 0)
i {
  display: inline-block;
  width: 50px;
  height: 50px;
  margin: 10px;
}

div {
 border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="first">
 <i></i><i></i><i></i><i></i>
</div>

<div id="second">
  <i></i><i></i><i></i><i></i>
</div>

Если у вас все еще возникают проблемы, начните с выполнения all в функции pluginName.без объявления дополнительных помощников.Это поможет вам убедиться, что вся ваша логика работает правильно.Затем начните перемещать любой повторно используемый код в вспомогательные функции, которые принимают все аргументы, которые им нужны для запуска.

Несколько актуально, но не сразу полезно* например) являются глобальными.Не забудьте предвосхитить их с помощью var, чтобы они оставались в пределах вашего IIFE.

...