Ошибки неизбежны в разработке программного обеспечения, но что происходит, когда они возникают в вашей среде JavaScript с открытым исходным кодом?
Как их исправить, не теряя собственного пользовательского кода в процессе перехода на новую версию фреймворка?

Давайте углубимся в проблемы, связанные с локальным исправлением ошибок, и предложим решение, лежащее за пределами основного исходного кода фреймворка.

Подводные камни перезаписи исходного кода

Перезапись исходного кода фреймворка может показаться простым решением, но делать это не рекомендуется. Каждая установка npm перезапишет ваши изменения.

И если никто в вашей команде не занимается сравнением исходного кода после каждого обновления, вы рискуете потерять все исправления и настройки.

Альтернативное решение: перезапись

Мы представляем концепцию перезаписи — метода настройки и исправления ошибок в среде без изменения основного исходного кода.

Это решение находится за пределами исходного исходного кода, что делает его легко доступным и удобным для всех в команде. В качестве примера мы демонстрируем реализацию Overwrites в Neo.MJS, платформе с открытым исходным кодом на основе классов.

С помощью перезаписи мы показываем, как вы можете легко расширять классы фреймворка и вносить изменения в масштабах всего проекта, не дожидаясь мерж-реквестов в фреймворке и не беспокоясь о совместимости с будущими обновлениями фреймворка.

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

Расширение базового класса с помощью конфигураций и методов

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

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

Neo.overwrites = {
    Neo: {
        component: {
            Base: {
                // Test Case 1: new config
                bar: 2,
                // Test Case 2:with trailing underscore
                // which calls afterSetFlex on change
                flex_: 1,

                // Test Case 3:new method
                afterSetFlex (value, oldValue) {
                    console.log('[Overwrite:afterSetFlex] value: ' + value);
                },

                // Test Case 4:existing method
                afterSetHeight(value, oldValue) {
                    console.log('[Overwrite:afterSetHeight] before callOverwritten');
                    this.callOverwritten(...arguments);
                    console.log('[Overwrite:afterSetHeight] after callOverwritten');
                }
            }
        }
    }
};

export default Neo.overwrites;

Основываясь на этом, я стремлюсь улучшитьNeo.component.Base, краеугольный камень всех визуальных классов, обратившись к 4 ключевым сценариям.

  1. Просто добавьте конфигурацию. Эта новая конфигурация должна иметь геттер и сеттер.
  2. Перезаписатьили добавитьконфигурациюдля запуска afterSetи beforeSetпри изменении конфигурации.
  3. Добавьте новый метод.
  4. Перезаписатьметод, но сохранить исходный метод неповрежденный и вызываемый с помощью this.callOverwritten.

Разница между 1 и 2 заключается в том, что некоторые конфигурации (2) идут со знаком подчеркивания в конце, а (1 ) других нет. Тем не менее, логика должна быть реализована.

Разница между 3 и 4 заключается в том, что иногда вы просто хотите что-то добавить к логике и поэтому вам нужен доступ к исходному методу. .

Где применить перезапись

Здесь есть два варианта.
Либо сразу после запуска Neo, когда все загружено, либо перед созданием экземпляра.

Neo поддерживает настоящую отложенную загрузку, поэтому я могу загружать компонент на лету и работать с ним асинхронно. Поэтому подход применения перезаписей после старта не вариант.

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

Затем Тобиас сразу понял правильное место, которое устанавливает триггер для классов. Непосредственно перед обработкой конфигов (см. первую ссылку в конце).

if (Neo.overwrites) {
    ctor.applyOverwrites(ctor.config);
}

Это звучит так: Если создан экземпляр класса и есть перезапись, примените его.

Как обрабатывается перезапись

В Neo все строится поверх друг друга, а самый низкий уровень — Neo.core.Base. На этом основаны как отображаемые классы (ViewElements), так и все остальное. Чтобы интегрировать перезапись, я хочу реализовать ее как можно более ресурсосберегающей.
Поэтому я определил свой applyOverwriteкак статический. Конфиги могут быть реализованы в 1-строчный:

overwrites && Object.assign(cfg, overwrites);

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

Я также мог бы сделать это с помощью методов, но тогда я не мог бы получить доступ к исходным методам. Поэтому мне нужно больше функциональности.

В случае перезаписи метода

Во-первых, я должен обернуть исходные методы, затем разрешить доступ к ним новому методу и, наконец, установить новый метод. Вот код (см. 2-ю и 3-ю ссылку в конце):

static overwrittenMethods = {}

static applyOverwrites(cfg) {
    … 
    if (Neo.isFunction(overwrites[item])) {
        cls = this.prototype;

        // Already existing ones
        if (cls[item]) {
            // add to overwrittenMethods
            cls.constructor.overwrittenMethods[item] = cls[item];
        }
    }
    …
}

Если перезаписьявляется уже существующим методом, задайте его в overwriteMethods как ключ:значение. Как только это будет сохранено, мы можем продолжить.

Теперь я также хотел бы иметь доступ к сохраненному методу из метода перезаписи. Для этого в классе Base есть новый метод, который можно вызывать из любого места перезаписанных Methods.

callOverwritten(...args) {
    let methodName = Base.getMehtodName();

    this.__proto__.constructor.overwrittenMethods[methodName].call(this, ...args);
}

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

Метод this.callOverwritten — ключевой инструмент, который позволяет разработчикам добавлять эту дополнительную логику, сохраняя при этом исходный код. Он часто используется в объектно-ориентированных языках программирования, таких как Java, где он является частью механизма наследования классов.

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

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

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

В заключение, метод this.callOverwritten — это мощный инструмент, который позволяет разработчикам добавлять собственную логику в существующий код, сохраняя при этом исходную реализацию. Используя этот метод, разработчики могут создавать сложные программные приложения, которые являются одновременно гибкими и масштабируемыми.

И это все, что нам нужно.

В заключение

Теперь я могу использовать все четыре сценария и легко переносить изменения в исходники после тщательного тестирования своих модификаций.

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

Как вы подходите к такой задаче?
Вы заглянули в Нео?
Используете ли вы перезапись в своих приложениях?

хорошего дня и с наилучшими пожеланиями,

Торстен

Ссылки

Перезаписать код в Neo.mjs
https://github.com/neomjs/neo/blob/dev/src/Neo.mjs#L78

Перезаписать код в Neo.core.Base
https://github.com/neomjs/neo/blob/dev/src/core/Base.mjs#L29
https://github. com/neomjs/neo/blob/dev/src/core/Base.mjs#L157
https://github.com/neomjs/neo/blob/dev/src/core/Base.mjs#L217

Обо мне

После 20 лет профессиональной работы с JavaScript я пожалел, что наконец-то не появилась новая структура, использующая возможности браузера, и нашел ее с помощью Neo.MJS.
На данный момент я создал 3 приложения в Neo, одно из которых был прототипом для производителя автомобилей, целью которого является параллельная работа над одним и тем же приложением на нескольких мониторах. Было весело работать над этим проектом и наблюдать за Нео в действии.

Так как Neo очень легко расширяется и постоянно поддерживается, он будет близок к тому, что возможно с браузерами в будущем.
И менее опытных программистов легче сделать более продуктивными в проекте, чем с другими фреймворками.

Как только вы поймете, как Neo работает под капотом, вы можете с улыбкой вспоминать прошлые JS-фреймворки.