Если требуется расширить старый функционал на фронте, есть две альтернативы:
Поскольку в нашем проекте в большинстве случаев фронт делался давно, написанный в контексте старых фрейворков код автоматически становится неподдерживаемым и требует рефакторинга. Поэтому предлагается использовать второй способ создания нового функционала.
window.pageData = {
// пример с необходимыми данными
serviceId: $('#service').val(), // <-- смотрите, это же jQuery!
orderNum: document.querySelector('#orderNum').value // контекстно-независимое обращение к DOM-элементу (Vanilla JS)
}
relativeAppFilePath - адрес до главного файла вашего модуля, который будет располагаться в frontend/embeddedApps relativeOutputPath - каталог с бандлами (сборками) - /www/Resource/<ваш_модуль>
require(__dirname+'/frontend/webpack/modules/<ваш_модуль>.module.js')
'use strict';
import Vue from 'vue'; // уже можно использовать любой компонент в т.ч. Vue-файлы
import YourModuleView from 'views/YourModule/YourModuleView.vue';
// главное приложение модуля
const YourNewModuleApp = {
mountPoint: '#your-module-container', // точка сборки
// инициализация модуля
init: function() {
new Vue({
components: {
YourModuleView
},
data: function() {
// получение нужных данных из глобального объекта
orderId: window.pageData.orderId,
serviceId: window.pageData.serviceId
},
// передача параметров вниз по иерархии
template: '<your-module-view v-bind:order-num="orderNum" v-bind:service-id="serviceId"></your-module-view>',
}).$mount(this.mountPoint);
}
}
if (document.querySelector(YourNewModuleApp.mountPoint)) {
// в случае обнаружения точки монтирования запускается инициализация модуля
YourNewModuleApp.init();
}
// старый код, обработка события
window.addEventListener("messageFromNewModule", onReceiveMessageFromNewModule);
function onReceiveMessageFromNewModule(stringPayload) {
// операции со старым кодом, выполняемые после получения события из нового модуля
}
// новый код, публикация события
methods: {
postMessageToExternalApp: function() {
window.postMessage("messageFromNewModule", '{"result":true}');
}
}
<?php
// когда в одном языке используется другой язык, это гарантированно неподдерживаемый код:
if ($value === 1) {
$js = ' if( !confirm("' . MyMessageEnum::ANSWER . '") ){ $("#btnClose").click(); }';
}
if ($value > 1) {
$js = ' $("#agreementStatus").val("' . MyServiceEnum::STATUS . '"); ';
$js .= ' alert("' . MyServiceEnum::STATUS_TEXT . '"); ';
}
?>
<script>
<?=$js;?>
// более-менее поддерживаемый вариант:
var value = <?=$value;?>;
var answer = "<?=MyMessageEnum::ANSWER;?>";
var status = <?=MyServiceEnum::STATUS;?>;
var statusText = "<?=MyServiceEnum::STATUS_TEXT;?>";
if (value === 1 && !confirm(answer)) {
$("#btnClose").click();
}
if (value > 1 && !confirm(message)) {
$("#agreementStatus").val(status);
alert(statusText);
}
</script>
Таким образом, мы стараемся не ухудшать старый код.
Сейчас старый код представляет собой зачастую скрипты, "заточенные" под конкретную страницу. При этом идут прямые обращения к объектам, существование которых в скрипте не проверяется, например, функции из других js-скриптов. Функции часто перечислены без какой либо логической связности (к примеру: www/js/bodyDivS2.js - смена бланков, сохранение услуги, загрузка инфы по заказу; логика выбора услуги размазана по нескольким скриптам).
Предлагается следующая стратегия рефакторинга этих файлов:
1. Создание прослойки кода, закрывающей прямые вызовы старых функций. Прослойка будет представлять из себя некие локальные модули, объявляемые вначале каждого старого js-файла с предварительной проверкой на существование. Лучше всего использовать понятное название модуля, соответствующее бизнес-процессу (например, работа с заказом должны быть заключена в модуль window.Order{}). В некоторых случаях, когда трудно определить принадлежность кода к модулю, допускается давать название, одноименное файлу: для www/js/bodyDivS2.js -> window.BodyDivS2{}
2. В каждом таком модуле должна появиться функция, вызывающая первоначальную функцию (либо, если ничего не мешает, можно переместить только тело функции, а старое объявление функции удалить - понятно, что это возможно, только когда старая функция нигде не вызывается). Вероятно, что мы столкнемся с дублированием функционала в разных файлах с незначительными различиями. В этом случае, собираем все в один модуль и делим на мелкие методы, которые можно повторно вызвать.
3. После появления обертки, обращение из других файлов должно происходить к ней. При этом, обязательно требуется выполнить проверку на существование объекта модуля.
// -- Module.js
window.Module: {
moduleFunc: function() {
oldFunction();
}
}
// --- Module1.js
if (typeof window.Module === 'undefined') { // проверяем на существование
window.Module = {
moduleFunc: function() {}
}
}
Module1: {
someFunc: function() {
// обращение к функции внешнего модуля window.Module.moduleFunc();
}
}
4. После того, как код примет вид небольших модулей с перекрестными вызовами, следует начать создание приложений, собираемых в webpack bundle. Для этого следует сгруппировать все модули, созданные в старых файлах, внутри одного или нескольких независимых приложениях на основе логической связности кода. Старый код придется почистить от проверок на существование модулей, и заменить их на импорты.
5. Следует стараться максимально отказываться от использования старых фреймворков, таких как jQuery. Для работы с DOM на старых страницах (поиск узла, назначение событий на input и т.д.) предлагается следующий способ: вынести работу с элементами на странице в отдельный локальный модуль, внутри которого реализовать минимальный набор функций для работы с DOM-ом (аналоги $.get(), $().click(), итд). Так, чтобы в коде других модулей не встречались прямые вызовы jQuery. Позже, в этом модуле взаимодействие с элементами на странице будет проще переделать под новый фреймворк или совсем избавиться в пользу Vue.js
6. Стараться использовать функции обратного вызова при обращении к внешним модулям. К примеру, частая история - показ загрузчика (www/js/loading/Loading.js). Когда процесс начинается в одном файле (напр. клик по услуге), вызывается лоадер, затем вызываются внешние функции из других файлов, и где-то там лоадер прячется (в теле последней функции в стеке). Так вот, показывать и прятать лоадер нужно в одном и том же месте, а вызов внешних функций должен сопровождаться функцией обратного вызова, тело которой будет располагаться там же, где и начался весь процесс. Но стоит следить за их вложенностью, чтобы не попасть в "Ад обратных вызовов".
someFunction: function() {
Loader.show();
ExternalModule.func1(a, b, function() {
// обратный вызов
Loader.hide();
});
}
Комментарии