Форум программистов, компьютерный форум, киберфорум
JavaScript
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.80/5: Рейтинг темы: голосов - 5, средняя оценка - 4.80
 Аватар для GlaDosEvolution
9 / 9 / 0
Регистрация: 13.06.2015
Сообщений: 61

9 способов оптимизации производительности Front-End

07.12.2016, 12:41. Показов 1051. Ответов 8
Метки css, html (Все метки)

Студворк — интернет-сервис помощи студентам
Поскольку современные браузеры стали поддерживать больше возможностей, а веб-индустрия стремительно перемещается в сторону мобильных устройств, появилась необходимость писать компактный и оптимизированный код, который не заставит долго ждать пользователя, пока загрузится сайт. Front-end хорош тем, что в нем содержится много простых стратегий и конвенций кода, которые мы можем использовать для обеспечения оптимальной производительности. В этой статье мы собрали 9 простых советов, которые помогут с оптимизацией кода.

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

1. Используйте DocumentFragments или innerHTML вместо массовых внедрений в элементы

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

К примеру, у нас есть ul элемент в пределах страницы, который изменяется через JavaScript после вызова AJAX, чтобы получить элемент списка JSON. Зачастую разработчики будут делать что-то вроде этого:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 var list = document.querySelector('ul');
 
 ajaxResult.items.forEach(function(item) {
 
    // создадим элемент списка li
    
    var li = document.createElement('li');
    
    li.innerHTML = item.text;
    
   // Здесь произойдут разнообразные структурные операции с нашим элементом,
   
   // например: добавление классов, модификация атрибутов и т.д. 
   
   // добавим элемент li к родительскому элементу ul
   
  list.apppendChild(li);
  
 });
Код выше показывает неправильный путь заполнения ul элементами – скорость дополнения DOM – дерева замедляется. Если вы действительно хотите использовать document.createElement и обработать элемент как узел дерева, было бы более производительно вместо этого использовать DocumentFragement.

DocumentFragement является так званым «мини-родителем», «облачным хранилищем» для его дочерних элементов. В нашем примере следует считать DocumentFragement как невидимый ul элемент, который находится за пределами DOM. Это позволит хранить Ваши узлы виртуально, пока они не будут введены в DOM. Первоначальный образец кода будет оптимизирован с помощью DocumentFragement следующим образом:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var frag = document.createDocumentFragment();
 
 ajaxResult.items.forEach(function(item) {
 
   // создадим элемент списка li
    
   var li = document.createElement('li');
    
   li.innerHTML = item.text;
    
    // Здесь произойдут разнообразные структурные операции с нашим элементом,
    
    // например: добавление классов, модификация атрибутов и т.д. 
    
    // *добавим наш элемент в фрагмент, вместо родительского элемента*
    
    frag.appendChild(li);
    
 });
Добавление дочерних элементов в отдельный DocumentFragement, а затем добавление данного фрагмента к родительскому списку используется с помощью только одной операции DOM. Таким образом, это получается намного быстрее, чем массовые внедрения. Если вам не нужно работать с такими элементами списка как узлы, то более выгодно строить HTML с помощью строк:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
var htmlStr = '';
 
 ajaxResult.items.forEach(function(item) {
 
    // Построим строку
    
    htmlStr += '<li>' + item.text + '</li>';
    
 });
 
   // Комбинация элементов списка через innerHTML
   
 document.querySelector('ul').innerHTML = htmlStr;
Таким образом, внедряя созданную строку через innerHTML по-прежнему остается только одна операция DOM и меньше кода даже чем у DocumentFragment метода.

2. Часто выполняемые события / методы

В большей части работы необходимо добавлять обработку событий, которые будут происходить довольно часто, во время взаимодействия с пользователем. К примеру события window's resize или onmouseover. Если обработка данных событий слишком ресурсозатратная, Вы можете дать большую загрузку на браузер, а это, в свою очередь, приведет к плохим последствиям на стороне пользователя. Вот откуда пришел метод debouncing. Debouncing будет ограничивать количество раз выполнения функции в пределах заданного временного интервала. Вот пример использования функции debouncing:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 // При работе с UnderscoreJS
 
 function debounce(func, wait, immediate) {
 
    var timeout;
    
    return function() {
    
        var context = this, args = arguments;
        
        var later = function() {
        
            timeout = null;
            
            if (!immediate) func.apply(context, args);
            
        };
        
        var callNow = immediate && !timeout;
        
        clearTimeout(timeout);
        
        timeout = setTimeout(later, wait);
        
        if (callNow) func.apply(context, args);
        
    };
  }
 
  // Добавим обработку события resize но не чаще чем через каждые 300 миллисекунд
 
  window.addEventListener('resize', debounce(function(event) {
 
   // Обработать событие resize (как Вам вздумается)
 
 }, 300));
Debounce метод возвращает функцию, которая оборачивает ответ, ограничивая его скорость выполнения до предела, указанного во втором аргументе. Теперь Ваши постоянные ответы не смогут повредить браузер пользователя.

3. Статичный кэш, не существенный контент в веб хранилище

API-интерфейсы по типу Web хранилищ стали огромным усилением и упрощением работы с API Cookie, которую разработчики использовали в течение многих лет. Одна из стратегий, которую можно использовать при работе с памятью для хранения несущественных данных был статичный контент. Здесь подразумеваются фрагменты HTML, содержание статьи, которая была загружена с помощью AJAX и других разнообразных методов, которые нужно запрашивать не более одного раза. Ниже показан небольшой код на JavaScript для работы с веб хранилищем:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
this[key] = value;
 
        }
    };
 
  return {
    
    get: function(key) {
        
        return this.isFresh(key);
            
    },
    
    set: function(key, value, minutes) {
        
        var expDate = new Date();
            
        expDate.setMinutes(expDate.getMinutes() + (minutes || 0));
 
        try {
            
            cacheObj.setItem(key, JSON.stringify({
                
                value: value,
                    
                expires: expDate.getTime()
                    
                }));
            }
            
            catch(e) {}
            
        },
        
    isFresh: function(key) {
        
        // Возвращаем значение или false
            
        var item;
            
        try {
            
            item = JSON.parse(cacheObj.getItem(key));
                
            }
            
        catch(e) {}
            
            if(!item) return false;
 
        // Высчитаем дату
            
            return new Date().getTime() > item.expires ? false : item.value;
        }
     }
 });
Этот плагин с помощью базовых get и set методов, либо через isFresh метод, проверяет, актуально ли сохраненное значение.

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 require(['storage'], function(storage) {
 
    var content = storage.get('sidebarContent');
    
    if(!content) {
    
        // Выполним AJAX запрос для получения контента
 
        // ... потом хранилище вернет контент через определенное время
        
        storage.set('sidebarContent', content, 60); 
    }
    
 });
Таким образом, мы избавляемся от частых повторяющихся запросов к серверу. Проанализируйте свои сайты на предмет материала, который не является динамичным, но вполне может быть часто запрашиваемым. Используйте данный способ для повышения эффективности сайта.

4. Использование curl.js

Асинхронная загрузка ресурсов стала популярной благодаря XMLHttpRequest (объект, который разрешен для AJAX), чтобы не блокировать загрузку ресурсов, уменьшить время загрузки страницы и позволить загрузить содержимое без ее перезагрузки.

Здесь будет использоваться замечательный плагин Джона Ханна curl.js. Curl загрузчик – это типичный асинхронный загрузчик, но немного с другими параметрами конфигурации, полезными плагинами и многим другим. Вот несколько примеров использования curl.js:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Базовое использование
 
    curl(['social', 'dom'], function(social, dom) {
    
    dom.setElementContent('.social-container', social.loadWidgets());
    
 });
 
 // Определяет модуль, который использует Google Analytics
 
 define(["js!//google-analytics.com/ga.js"], function() {
 
    // Возвращает Google Analytics контроллер
    
    return {
    
        trackPageView: function(href) {
        
            _gaq.push(["_trackPageview", url]);
            
        },
        trackEvent: function(eventName, href) {
        
            _gaq.push(["_trackEvent", "Interactions", eventName, "", href || window.location, true]);
            
         }
     };
 });
 
 // Единожды загружает JavaScript файл без обратной связи
 
 curl(['js!//somesite.com/widgets.js']);
 
 // Загрузит JavaScript and CSS файлы в модуле
 
 curl(['js!libs/prism/prism.js', 'css!libs/prism/prism.css'], function() {
 
    Prism.highlightAll();
    
 });
 
 // Загружает a URL (через an AJAX запрос)
 
 curl(['text!sidebar.php', 'storage', 'dom'], function(content, storage, dom) {
 
    storage.set('sidebar', content, 60);
    
    dom.setElementContent('.sidebar', content);
    
 });
Когда Вы начнете использовать данный метод, вместо простых асинхронных загрузчиков, разница будет заметна сразу, но, что более важно, эту разницу увидят пользователи!

Например, загружаем только виджеты Twitter, Facebook и Google Plus, если DIV с классом CSS-social существует. Если данного блока нет, виджеты мы не грузим. Следовательно, используя принцип «проверить – если это нужно перед загрузкой – то грузим» спасет пользователя от закачки нескольких совершенно ненужных килобайт.

5. Используйте Array.prototype.join вместо конкатенации строк

Одна микро-оптимизация на стороне клиента с помощью Array.prototype.join заменяет обычную конкатенацию. К примеру, в пункте №1 (см. выше), было использована элементарная конкатенация строк. А вот так это будет выглядеть после данной оптимизации

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
var items = [];
 
 ajaxResult.items.forEach(function(item) {
 
    // Построим строку
    
    items.push('<li>', item.text, '</li>');
    
    });
 
    // Комбинация элементов списка через innerHTML
    
 document.querySelector('ul').innerHTML = items.join('');
В следующей части мы закончим освоение оптимизации сайта.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
07.12.2016, 12:41
Ответы с готовыми решениями:

Практика для Front-end 'ера
Всем привет, хочу попросить форумчан поделиться со мной реальными ТЗ заказчиков (либо любым другим способом, где можно получить необходимые...

Front-end lite, в ногу со временем
Здравствуйте, впервые JavaScript на форуме да и вообще в JavaScript, хотел спросить у вас совет &quot;как быть и куда двигаться&quot;. ...

Ищу наставника для обучения front-end разработке,в частности JavaScript
Ищу наставника для обучения front-end разработке,в частности JavaScript. Умею создавать статические странички с помощью HTML CSS. Буду рада...

8
Эксперт HTML/CSS
 Аватар для Fedor92
2964 / 2621 / 1068
Регистрация: 15.12.2012
Сообщений: 10,091
Записей в блоге: 11
07.12.2016, 13:55
GlaDosEvolution, ну это всё конечно здорово, но Вам по хорошему - это всё надо запихать в блог, если хотите, чтобы Ваш труд пропал не зря...
1
 Аватар для GlaDosEvolution
9 / 9 / 0
Регистрация: 13.06.2015
Сообщений: 61
07.12.2016, 14:11  [ТС]
Цитата Сообщение от Fedor92 Посмотреть сообщение
запихать в блог, если хотите, чтобы Ваш труд пропал не зря...
Ну в блог не обязательно мне и тут хорошо. Но спасибо за замечание.

Добавлено через 1 минуту
Ну а если что я конечно и туда размещу если уж все будет туго здесь.
0
12 / 12 / 2
Регистрация: 02.12.2015
Сообщений: 620
07.12.2016, 14:20
Это перевод курса Джо Марини 2016 или собственная статья?
0
Эксперт HTML/CSS
 Аватар для Fedor92
2964 / 2621 / 1068
Регистрация: 15.12.2012
Сообщений: 10,091
Записей в блоге: 11
07.12.2016, 14:27
Цитата Сообщение от GlaDosEvolution Посмотреть сообщение
Ну а если что я конечно и туда размещу если уж все будет туго здесь.
Ваши темы через пару дней канут в бездну топиков и никто о них не узнает, поэтому и предлагаю Вам вести блог, но Ваше право...
0
 Аватар для GlaDosEvolution
9 / 9 / 0
Регистрация: 13.06.2015
Сообщений: 61
07.12.2016, 20:49  [ТС]
Цитата Сообщение от НиколайВасильев Посмотреть сообщение
Это перевод курса Джо Марини 2016 или собственная статья?
Собственная.

Цитата Сообщение от Fedor92 Посмотреть сообщение
Ваши темы через пару дней канут в бездну топиков и никто о них не узнает
Ну если кому нужно будет те посмотрят, а кому нет это их право не смотреть вот и все.
0
 Аватар для GlaDosEvolution
9 / 9 / 0
Регистрация: 13.06.2015
Сообщений: 61
07.12.2016, 20:50  [ТС]
6. Использование CSS Animations, когда это возможно

Можно утверждать, что рост библиотек JavaScript, таких как JQuery и MooTools привели к сложным анимационным оформлениям. Сегодня многие разработчики все еще используют JavaScript, чтобы оживить элементы, несмотря на то, что соответствующие браузеры поддерживают CSS анимации через transform и keyframe.

CSS анимации более эффективны, чем анимации JavaScript. CSS анимации также имеют дополнительное преимущество (обычно) — намного меньше кода. Многие анимации CSS обрабатываются GPU, и, таким образом, более сглажены. К примеру:

CSS
1
2
3
4
5
6
7
.myAnimation {
 
    animation: someAnimation 1s;
    
    transform: translate3d(0, 0, 0); /* ускорение */
    
 }
Подобные свойства CSS анимации повышают производительность и качество анимации используя субпиксельную интерполяцию. Такую анимацию процессору проще выполнять, так как задействуется графический движок, это очень важно для мобильных устройств. Однако наиболее приемлемым будет использование CSS анимации вместе с подключением управления через js. Например, когда правила анимации прописаны в CSS, а с помощью JavaScript мы изменяем входящие параметры (цвет, фон, размер и т.д.)

7. Используйте делегирование событий

Представьте себе неупорядоченный список ul, который может содержать любое количество дочерних элементов li, и каждый li должен что-то сделать по щелчку мыши. Вы можете добавить обработчики событий к отдельным элементам, но что, если элементы будут добавляться или удаляться часто? Вам предстоит иметь дело с добавлением и удалением обработчиков событий, а также самих элементов. Вот где нам понадобится делегирование событий.

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

Вот невероятно простой пример того, что происходит:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Берем элемент и добавляем к нему обработчик события click...
 
 document.querySelector('#parent-list').addEventListener('click', function(e) {
 
    // e.target кликабельный элемент!
    
    // Если это необходимый нам элемент
    
    if(e.target && e.target.tagName == 'LI') {
    
        // Необходимый элемент найден! Действуем!
        
    }
 });
Все фреймворки JavaScript обеспечивают соответствие делегирования селектора. Дело в том, что Вы избегаете размещения обработчиков событий на отдельные элементы, установив один на родителя. Этот метод более эффективен и менее затратен.

8. Используйте data URL’s, вместо SRC’s

Скорость загрузки страницы напрямую зависит от использования спрайтов и написания компактного кода. Число запросов отсылаемых данной страницей также играют роль в производительности. Минимизация запросов помогает Вашему сайту грузиться быстрее и один из способов устранения запросов (не считая использования спрайтов) использовать Data URL вместо атрибутов image SRC:

HTML5
1
2
3
4
5
6
7
8
9
10
11
<!—До… -->
 
 <img src="/images/logo.png" />
 
 <!-- …После -->
 
 <img src="data: image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAPAAA/+4ADkFkb2
 
 JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoKDBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fH
 
 x8fHwEHBwcNDA0YEBAYGhURFRofHx8fHx8fHx8fHx8fHx8fH ...." />
Конечно же размер страницы увеличится, но вы уменьшаете время ожидания и количество запросов к серверу. Большинство браузеров сейчас поддерживают Data URL’s в CSS файлах (для фоновых изображений) так что этот метод в целом широко может быть использован.

9. Используйте Media Queries для загрузки фоновых изображений необычных размеров.

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

CSS supports имеют более широкую поддержку, а медиа-запросы CSS близки к CSS «логике. Медиа-запросы CSS чаще всего используются для настройки (регулирования) CSS свойства под устройство (обычно по ширине экрана); с помощью этого способа мы часто меняем ширину или плавающую позицию элемента. Почему бы не изменить фоновые изображения на основе этого способа?

CSS
1
2
3
4
5
6
7
8
/* стандартно на Desktop */
 
 .someElement { background-image: url(sunset.jpg); }
 
 @media only screen and (max-width : 1024px) {
 
    .someElement { background-image: url(sunset-small.jpg); }
 }
Фрагмент выше загружает изображение меньшего размера, если пользователь использует мобильное устройство (имеется ввиду размер файла, не соотношение сторон). Таким способом, достаточно отрисовать спрайт меньшего размера, что избавит от необходимости проводить манипуляции с изображениями програмным методом или разрезать спрайт для мобильных устройств и т.д.

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

Небольшие обновления, большие успехи

Очень часто разработчики ставят себя и свой стиль кода на первое место вместо того, чтобы подумать сначала о своих пользователях. Много небольших обновлений построены для улучшения комфорта пользователя, таким образом, каждый бит оптимизации загрузки улучшает впечатление о Вашем сайте. Google выпустил достаточно удобное расширение для своего браузера, которое поможет Вам проанализировать скорость загрузки Вашего сайта и узнать, что еще можно оптимизировать, для повышение загрузки страницы. Называется данное расширение PageSpeed Insights. Использовать его можно через стандартную панель разработчика. Данный виджет покажет Вам минимальный анализ Вашего портала и даст советы по его оптимизации, к примеру:



В любом случае, оптимизировать сайт нужно максимально. Чем быстрее он будет работать, тем больше будут удовлетворены пользователи, тем больше заработает сайт. К сожалению, далеко не все задумываются об этом. Это даже видно по спросу: нам часто заказывают аудиты юзабилити или Back-End, и редко кто просит провести аудит Front-End, хотя он абсолютно не сложно делается и может дать хорошее поле для улучшений.
1
Кормпилятор
 Аватар для Quiet Snow
5044 / 1718 / 409
Регистрация: 25.04.2010
Сообщений: 4,827
Записей в блоге: 2
16.12.2016, 20:48
Цитата Сообщение от НиколайВасильев Посмотреть сообщение
Это перевод курса Джо Марини 2016 или собственная статья?
Это копипаст с хабры.
https://habrahabr.ru/post/316618/
0
 Аватар для GlaDosEvolution
9 / 9 / 0
Регистрация: 13.06.2015
Сообщений: 61
16.12.2016, 20:55  [ТС]
Цитата Сообщение от Quiet Snow Посмотреть сообщение
Это копипаст с хабры
Молодец. Я уже как раз уже хотел удалятся с кибер форума.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
16.12.2016, 20:55
Помогаю со студенческими работами здесь

Web-разработчик (back-end, front-end), Москва
Крупнейшая меховая компания в Европе ELENA FURS в поиске талантливого разработчика. (Москва, м. Фрунзенская) Ищем специалиста, который...

Передать картинку с back-end to front-end
пытаюсь с сервера передать и отрисовать картинку на front: читаю так: @Override public List&lt;Car&gt; getCars() { ...

Программист-разработчик (Углубленный back-end + базовый front-end) ЗАО "Вычислительные силы"
Программист-разработчик (Углубленный back-end + базовый front-end) ЗАО «Вычислительные силы» Уровень зарплаты ...

Front end vs Back end
Доброго времени суток. На данный момент изучаю бек енд, знаю основы php, MySql, ООП. Сейчас пробую разбираться с фреймворком Yii2. ...

Front-end и Back-end: что круче, что луче, где больше?
вопрос: какая разница зарплаты в Senior Front-end'a и Senior Back-end'a, в%. Знаю, странные у меня вопрос, но я сейчас Junior и я могу...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
Программный контроль заполнения реквизита табличной части документа
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать контроль заполнения реквизита "ПричинаСписания". . .
wmic не является внутренней или внешней командой
Maks 02.04.2026
Решение: DISM / Online / Add-Capability / CapabilityName:WMIC~~~~ Отсюда: https:/ / winitpro. ru/ index. php/ 2025/ 02/ 14/ komanda-wmic-ne-naydena/
Программная установка даты и запрет ее изменения
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: при создании документов установить период списания автоматически. . .
Вывод данных в справочнике через динамический список
Maks 01.04.2026
Реализация из решения ниже выполнена на примере нетипового справочника "Спецтехника" разработанного в конфигурации КА2. Задача: вывести данные из ТЧ нетипового документа. . .
Программное заполнения текстового поля в реквизите формы документа
Maks 01.04.2026
Алгоритм из решения ниже реализован на нетиповом документе "ВыдачаОборудованияНаСпецтехнику" разработанного в конфигурации КА2, в дополнении к предыдущему решению. На форме документа создается. . .
К слову об оптимизации
kumehtar 01.04.2026
Вспоминаю начало 2000-х, университет, когда я писал на Delphi. Тогда среди программистов на форумах активно обсуждали аккуратную работу с памятью: нужно было следить за переменными, вовремя. . .
Идея фильтра интернета (сервер = слой+фильтр).
Hrethgir 31.03.2026
Суть идеи заключается в том, чтобы запустить свой сервер, о чём я если честно мечтал давно и давно приобрёл книгу как это сделать. Но не было причин его запускать. Очумелые учёные напечатали на. . .
Модель здравосоХранения 6. ESG-повестка и устойчивое развитие; углублённый анализ кадрового бренда
anaschu 31.03.2026
В прикрепленном документе раздумья о том, как можно поменять модель в будущем
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru