Как я пишу код
Надеюсь этим постом дать начало традиции делиться не только своими наработками (библиотеки, фреймворки, отдельные функции), но и стилем написания кода, а точнее структурированием проекта.
Буквально пару дней назад ко мне обратился мой старый клиент с просьбой обновить небольшое веб приложение, которое я делал около года назад. Когда я открыл код, я там буквально ничего не понял. Весь проект был засунут в одну функцию, срабатывающую по domready. Там же были объявлены переменные и функции, в один уровень (хотя и в самом верху, как бы соблюдая правила хорошего тона). Первой же мыслью было "переписать всё нахер". Переписал я это всё дело, используя готовый набор инструкций, которые я выдумал пять проектов назад и тупо копировал в дальнейшем, так как было лень что-то новое придумывать. Хочу показать, как я последнее время структурирую код, делая его более понятным, доступным везде (используя единое пространство имен) и масштабируемым. При этом, не добавляется никаких сложных вещей (по сути те же переменные с функциями), не нужно затачивать свой код "под что-то". Скажу честно, я не понимаю как применять всякие MVC на практике, да и код из примеров часто кажется слишком перегруженным. Часто намного проще изобрести свою собственную структуру, заточенную под проект, при этом прекрасно понимая, что, где и как работает. Как я уже сказал, все функции и данные хранятся в одном пространстве имен. Скажем, проект вашего заказчика называется "Project Name". Перед задействованием каких-либо скриптов, создаём пространство имен: <script> window.projectName = window.pn = {}; </script> Затем добавляем скрипты в произвольном порядке. Файлы скриптов именуются так: pn.something.js Файл скрипта начинается c объявления строгого режима и подпространства имен: "use strict"; pn.something = { } После этого инициализируем события (см. репозиторий внизу) Минимальный список файлов: pn.data.js Здесь хранятся данные и методы, которые манипулируют этими данными, скажем: pn.data = { i: 0, incrementI: function() { this.i++ } } В этот объект так же помещаются данные, которые мы получили в процессе работы скрипта: pn.ajax.getSome( function( some ) { pn.data.some = some; }) pn.ajax.js Здесь хранятся методы для получения данных с сервера. Сюда же можно кинуть урлы и пр. вещи, относящиеся к исключительно к аяксу pn.events.js Здесь инициализируется страница объявляются события (подробнее с несколькими комментариями см. в репо) pn.ui.js Функции, отвечающие за интерфейс, к примеру: pn.ui = { renderMenu: function() {...} } pn.utils.js Здесь всякие вспомогательные функции, например, получение массива, с нулями. Данные из pn.data здесь не модифицируются, а функции только возвращают что-то. Сделал для ознакомления небольшой репозиторий: https://github.com/finom/AG-JS-Boilerplate Информации там немного, но общая идея, надеюсь понятна. Со временем буду обновлять, в том числе, и для себя, так как память часто подводит. Хотя я не рассказал ничего особенно нового, не применил каких-нибудь сложных заумных штук, я надеюсь, новичкам будет полезно ознакомиться с простейшим набором универсальных приёмов, описанных выше, несколько уменьшая соотношение говнокод/код. |
И не обращайте внимания на сами методы. Например, pn.ajax._get и pn.ui._render сделаны не очень удачно, на скорую руку.
|
а зачем нам "use strict";?
В pn.data запятую вместо двоеточия нужно поставить |
Цитата:
Для построения более сложных структур делается так: Допустим у нас есть forum: Далее разбиваем на файлы и объекты: forum.content = {} соответствует forum.content.js forum.path = {} соответствует forum.path.js forum.tree = {} соответствует forum.tree.js ... Таким образом получается что взглянув на файлы, я уже знаю что у меня в объектах происходит. Проект на папки не бью, не вижу в этом смысла и сразу видна вся структура проекта, все лежит в одной папке: forum.content.js forum.path.js forum.tree.js Если создаются проекты с одним и тем же содержанием, но разными user скриптами, то добавляется файл userScript.js, который является управляющим и может собирать свои объекты и указывать на ещё пачку скриптов. Таким образом, при обновлении ПО проектов, пользователи имеют всегда последнее ПО и не теряют приписанный им скрипт(ы). |
Каркас с легкостью перевалит за 100к строк и более. Никаких проблем с пониманием структуры и что за что отвечает не предвидится. Можно заменять ui файлы на новый код, не влезая в переписывание строк основного проекта. Просто пишешь новый файл и заменяешь старый на новый. Над частями проекта могут работать хоть 100 человек, независимо друг от друга.
Либа лежит в отдельной папке. |
Цитата:
forum.content.js forum.path.js forum.tree.js Собственно это не сильно важно, т.к. проект все равно собирается и ужимается и можно пересобрать и распихать файлы по папкам. Хоть каждый файл в свою. На выходе получим те же один, два файла :) |
Я юзаю паттерн БЭМ, где все интерфейсные элементы абстрактно представляются, как атомарные блоки, а их состояния задаются через специальные модификаторы. Почитать описание паттерна можно на http://ru.bem.info/method/
Сразу скажу, что в личных проектах я не юзаю i-bem фреймворк Яндекса, т.к. он мне не нравится, а написал свой микро каркас. На работе разумеется приходиться работать с i-bem. Не нравится потому: 1) Слишком много туда засунули; 2) Излишнее количество неймспейсов и не самое приятное АПИ методов; 3) Не нравится их шаблонизатор; 4) Лишняя зависимость от jQuery и Яндекса (вдруг ребята из Лего захотят поменять концепцию и у меня всё сломается). А сам паттерн очень удобен для больших проектов, всё просто и очевидно, даже когда кода становится ну очень много, но следует отметить, что для маленьких проектов такой подход лучше не юзать, лишняя не нужная трата времени. |
Цитата:
Цитата:
|
Цитата:
|
Цитата:
Цитата:
pn.ui = {} // pn.ui.js pn.ui.charts = {} // pn.ui.charts.js Но всё равно сохраняя пространство ui для всего, что относится к интерфейсу. Цитата:
|
Часовой пояс GMT +3, время: 04:15. |