1.Не изменяйте конструкцию страницы с помощью DOM манипуляций.
В jQuery вы верстаете страницу, а затем делаете ее динамической. Это потому, что jQuery был предназначен для проектирования системы улучшения характеристик и невероятно вырос из этой простой предпосылки.
Но на angularjs, вы должны начать с нуля свою задумку. Вместо того, чтобы начать думать «у меня часть DOM и я хочу сделать X», вы должны начать с того, что вы хотите достичь, затем думать о разработке приложения, и, наконец, интересоваться разработкой представления.

2.Не дополняйте jQuery c AngularJS.
Аналогично, не начинайте с идеи, что jQuery делает X, Y, и Z, так я буду только добавлять angularjs в главенствующей роли для моделей и контроллеров. Это действительно заманчиво когда вы только начинаете, поэтому я всегда рекомендую, чтобы новые разработчики angularjs не использовали jQuery для всего, по крайней мере, пока они не привыкнут делать вещи «угловым путем».

Я видел много разработчиков, здесь и в рассылке, которые создают сложные решения с плагинами jQuery длинной в 150-200 строк кода, которые склеивают в angularjs с запутанными коллекциями каллбэков и $apply, но они, в конечном счете, заставляют код работать!
Проблема в том, что в большинстве случаев jQuery плагин можно переписать на angularjs, в части кода, где все вдруг становится понятным и простым.

Суть такова: когда разрабатываете, сначала «думайте на angularjs»; если вы не можете придумать решение, спросите у коллег, если после всего, вы не найдете решения, то не стесняйтесь обратиться за помощью к jQuery. Но не позволяйте jQuery стать костылем, или вы не мастер в angularjs.

3.Всегда думайте с точки зрения архитектуры.
Помните, что одностраничные приложения — это приложения. Они — не веб-страницы. Поэтому мы должны думать одновременно как серверный разработчик и как клиентский разработчик. Мы должны думать о том, как разделить наше приложение на отдельные, расширяемые, тестируемые компоненты.
Как ты это делаешь? Как вы «думаете на angularjs»? — вот некоторые общие принципы, противопоставленные jQuery.
Представление – это протокол.
В jQuery мы программно изменяем представление. Мы могли бы создать выпадающее меню с помощью ul так:
<ul class="main-menu">
    <li class="active">
        <a href="#/home">Home</a>
    </li>
    <li>
        <a href="#/menu1">Menu 1</a>
        <ul>
            <li><a href="#/sm1">Submenu 1</a></li>
            <li><a href="#/sm2">Submenu 2</a></li>
            <li><a href="#/sm3">Submenu 3</a></li>
        </ul>
    </li>
    <li>
        <a href="#/home">Menu 2</a>
    </li>
</ul>

В jQuery, в нашей логике приложения, мы хотели активировать его с чем-то вроде:
$('.main-menu').dropdownMenu();

Когда мы просто смотрим на представление, не сразу видно его роль в приложении. Для небольших приложений это нормально. Но для нетривиальных приложений код быстро становится запутанным и его трудно поддерживать.
На angularjs наше объявление ul будет выглядеть следующим образом:
<ul class="main-menu" dropdown-menu>
    ...
</ul>

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

Новые разработчики angularjs часто задают такой вопрос: как мне найти все ссылки на конкретный вид и добавить директиву на них? Разработчик всегда изумлен, когда мы отвечаем: никак. Но почему вы не сделаете это, как половину-jQuery и половину-angularjs — это не сулит ничего хорошего.

Проблема здесь в том, что разработчик пытается «сделать на jQuery» в контексте angularjs. Это никогда не будет работать хорошо. Вне директивы (подробнее об этом ниже), вы никогда, никогда, никогда не измените DOM. И директивы применяются в этом представлении, цель ясна.

Помните: не проектируйте, а потом верстайте. Необходимо разработать, а потом проектировать.
Привязка данных
Это, безусловно, одна из самых удивительных особенностей angularjs, которая удаляет много DOM манипуляций, которые нужно было бы сделать, я уже упоминал об этом в предыдущем разделе. В angularjs автоматически обновляется ваше представление, так что вам не придется делать это! В jQuery мы реагируем на события и обновляем контент. Что-то вроде:
$.ajax({
  url: '/myEndpoint.json',
  success: function ( data, status ) {
    $('ul#log').append('<li>Data Received!</li>');
  }
});

В коде это выглядит так:
<ul class="messages" id="log">
</ul>

Помимо проблем смешивания, мы также имеем те же проблемы, означающие намерение, которое я упоминал ранее. Но главное, нам пришлось вручную ссылаться и обновлять узел DOM. И если мы хотим удалить запись из журнала, надо кодить через DOM для этого тоже. Как мы тестируем логику только через DOM? А что, если мы хотим изменить представление?
Это немного грязно и мелочно, на AngularJS мы можем сделать это:
$http( '/myEndpoint.json' ).then( function ( response ) {
    $scope.log.push( { msg: 'Data Received!' } );
});

И наше представление станет таким:
<ul class="messages">
    <li ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>

Но, впрочем, код может быть и таким:
<div class="messages">
    <div class="alert" ng-repeat="entry in log">
        {{ entry.msg }}
    </div>
</div>

И теперь вместо того, чтобы использовать неупорядоченный список, мы используем контейнеры. И нам никогда не придется менять код контроллера! Но что еще более важно, независимо от того, где и как лог обновляется, представление изменится. Автоматически. Аккуратно!

Хотя я не показал это здесь, привязка данных является двунаправленной. Так эти сообщения лога могут также быть доступны для редактирования в представлении, просто делаю это:
<input ng-model="entry.msg" />

И будет много радости.
Отдельные модели уровня.
В jQuery, DOM — вроде как модель. Но на angularjs, у нас есть различные уровни модели, которыми мы можем управлять в любом случае, как мы хотим, совершенно независимо от представления. Это помогает вышеуказанной привязке данных, поддерживает разделение обязанностей и вводит гораздо большую тестируемость. Другие ответы упомянули этот момент, поэтому закончим разговор об этом.
Разделение обязанностей.
И все вышеперечисленное объедняется в всеобщей теме: держите ваши обязанности разделенными. Ваше представление действует как протокол о том, что должно произойти (по большей части); модель представляет ваши данные, у вас есть слой сервиса для выполнения многоразовых заданий, вы делаете DOM манипуляции и увеличиваете ваше представление директивами; и склеиваете все это вместе с контроллерами. Это также упоминалось в других ответах, и единственное, что я хотел бы добавить, касается тестирования, о котором я расскажу в другом разделе.
Внедрение зависимости.
Внедрение зависимости (di) поможет нам с разделением обязанностей. Если вы пришли из серверных языков (из java в PHP) вы, наверное, уже знакомы с этим понятием, но если вы на стороне клиента, пришли с jQuery, это понятие может показаться глупым и лишним. Но это не так. :-)
С широкой точки зрения, di означает, что вы можете объявить компоненты очень свободно и потом просто попросите его экземпляр, и он будет предоставлен. Вы не должны знать о порядке загрузки, или о местоположении файлов, или что-нибудь подобное. Питание может не сразу быть видно, но я приведу только один (общий) пример: тестирование.
Допустим, в нашем приложении, мы требуем службу, которая реализует серверные хранилища через RESTAPI и, в зависимости от состояния приложения, и локального хранилища.

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

Говоря о тестировании…

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

Из всего множества jQuery плагинов, которые вы видели, использовали, или написали, сколько из них имели сопутствующие тесты? Не очень много, потому что jQuery не очень склонна к этому. Но angularjs склонен.

В jQuery, единственный способ для теста — это создать самостоятельно образец/демо-страницу, чтобы показать что наши тесты могут выполнять манипуляции DOM. Значит, мы должны разработать компоненты по отдельности, а затем интегрировать их в наше приложение. Как неудобно! Так много времени, когда разрабатываем с jQuery, мы выбираем итеративный путь, а не тестирование. И кто может обвинить нас?

Но ведь у нас есть разделение обязанностей, мы можем сделать тестирование итерационно на angularjs! Например, скажем, мы хотим с помощью супер-простой директивы указывать в меню на наш нынешний маршрут. Мы можем заявить, что мы хотим, на наш взгляд:
<a href="/hello" when-active>Hello</a>

Хорошо, теперь мы можем написать тест:
it( 'should add "active" when the route changes', inject(function() {
    var elm = $compile( '<a href="/hello" when-active>Hello</a>' )( $scope );
    $location.path('/not-matching');
    expect( elm.hasClass('active') ).toBeFalsey();
    $location.path( '/hello' );
    expect( elm.hasClass('active') ).toBeTruthy();
}));

Мы запускаем наш тест и убеждаемся, что это не удается. Так что теперь мы можем написать наши директивы:
.directive( 'whenActive', function ( $location ) {
    return {
        scope: true,
        link: function ( scope, element, attrs ) {
            scope.$on( '$routeChangeSuccess', function () {
                if ( $location.path() == element.attr( 'href' ) ) {
                    element.addClass( 'active' );
                }
                else {
                    element.removeClass( 'active' );
                }
            });
        }
    };
});

Теперь наш тест проходит, и наше меню выполняется позапросу. Наша разработка как итерационная и тестируемая вместе.

5.Теоретически, директивы не упакованы jQuery.
Вы часто будете слышать «только манипуляции DOM в директиве». Это необходимость. Относитесь к ней с уважением!
Но давайте углубимся немного глубже…

Некоторые директивы просто украшают то, что уже в представлении (думаю, ngClass) и поэтому иногда делаем манипуляций с DOM, а потом, в основном, выполняем. Но если директива как «виджет» и есть шаблон, необходимо также соблюдать разделение обязанностей. То есть, шаблон тоже должен оставаться в значительной степени независимым от его реализации в ссылке и функции контроллера.

Angularjs поставляется с полным набором инструментов, чтобы сделать это очень легко; с ngClass можно динамически обновлять классы; ngBind позволяет проводить двустороннюю привязку данных; ngShow и ngHide программно показать или скрыть элемент; и многое другое, в том числе то, что мы сочиняем сами. Другими словами, мы можем сделать все преобразования, без манипуляций с DOM. Чем меньше манипуляций с DOM, тем легче директивы для теста, тем легче их стиль, тем легче они будут меняться в будущем, и более многоразовый и распространять их.

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

Логгер мы запрограммировали в главе 3. Даже если мы предположим, что в директиве, мы все же хотим сделать «угловой путь». Он по-прежнему не принимает никаких манипуляций с DOM! Есть много моментов, когда необходимы манипуляции с DOM, но это намного реже, чем вы думаете! Прежде чем делать манипуляции с DOM в вашем приложении, спросите себя, вам действительно нужно это? Может быть способ лучше.

Вот быстрый пример, который показывает картину, которую я вижу чаще всего. Мы хотим сделать кнопку переключаемой. (Примечание: этот пример немного надуманный и нужно немного подробнее представлять более сложные случаи, которые решаются точно таким же способом.)
.directive( 'myDirective', function () {
    return {
        template: '<a class="btn">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            var on = false;
            $(element).click( function () {
                if ( on ) {
                    $(element).removeClass( 'active' );
                }
                else {
                    $(element).addClass( 'active' );
                }
                on = !on;
            });
        }
    };
});

Здесь есть несколько неправильных моментов.
Во-первых, jQuery и не надо было. Мы ничего не делали здесь, чтобы был нужен jQuery во всем!
Во-вторых, даже если у нас уже есть jQuery на странице, нет никаких причин, чтобы использовать его здесь, мы можем просто использовать angular.element и компонент по-прежнему будет работать при перетаскивании в проект, который не имеет jQuery.
В-третьих, даже если предположить, что jQuery был необходим для работы этой директивы, jqLite (angular.element) всегда будет использовать jQuery, если он был загружен! Поэтому мы не должны использовать $ — мы можем просто использовать angular.element.
В-четвертых, тесно связанный с третьим, элементы jqLite не нужно было оборачивать в $ — элемент, который передается в функцию по ссылке, может быть уже элемент с jQuery!
И в-пятых, момент, который мы уже упоминали в предыдущих разделах, почему мы смешиваем элементы шаблона в нашей логике?
Эта директива может быть переписана (даже для очень сложных случаев!) гораздо проще, вот так:
.directive( 'myDirective', function () {
    return {
        scope: true,
        template: '<a class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            scope.on = false;
            scope.toggle = function () {
                scope.on = !scope.on;
            };
        }
    };
});

Опять же, наполнение шаблона, так вы (или ваши пользователи) можете легко поменять его на то, что соответствует любому нужному стилю, а логика никогда не должна быть задета. Повторное использование — бум!

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

Так что директивы — не просто коллекции jQuery функций, какие они? Директивы –это расширения HTML-кода. Если HTML не делает что-то нужное, вы напишите директиву, чтобы она сделала это для вас, а затем будете использовать её так, как будто это было частью HTML.

Иначе говоря, если angularjs не сделает что-нибудь из коробки, думаю, команда бы выполнила это с ngClick и ngClass вместе.

Итог.
Даже не используйте jQuery. Даже не включайте его. Оно будет тормозить вас. И когда вы приходите к проблеме, если вы думаете, что знаете, как решить проблему в jQuery, прежде чем тянуться за $, попробуйте подумать, как это сделать в рамках в в angularjs. Если Вы не знаете — спросите! 19 раз или 20, будет лучше, если вы сделаете приложение без jQuery, ведь оно, в результате, принесет больше работы для вас.
Поделиться с друзьями
-->

Комментарии (18)


  1. Fen1kz
    15.07.2016 18:25
    +5

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


    1. Trikstor
      15.07.2016 18:29

      Тут важен сам принцип, а не версия.


  1. Per_Ardua
    15.07.2016 19:10

    Статья интересная. По сути, лепить код на jQuery, вместо использования возможностей фреймворка будет только совсем новичок. Но статья всё равно интересна.

    Перевод, скажу честно, подхрамывает. Но так как это первый перевод автора, то хочется пожелать успехов, в надежде увидеть в следующий раз более качественный перевод! =)


    1. Trikstor
      15.07.2016 19:22
      -2

      Спасибо. Возникли сложности с некоторыми идиоматическими конструкциями.


      1. impwx
        15.07.2016 23:23

        Если не понимаете смысл фразы, и особенно если она при этом не критически важна для понимания смысла — лучше ее вообще опустить. А то получается «повторное использование — бум».


  1. ZapevalovAnton
    15.07.2016 20:07
    -1

    На мой сугубо личный взгляд, неправильно сравнивать такие вещи как AngularJS и jQuery (кстати об этом не раз говорилось на хабре). Но ещё мне кажется, что неправильно объяснять Frontend разработчикам как нужно думать при использовании фреймворка, на примере библиотеки, к которой они привыкли.

    Может быть это для меня, человека работающим больше с Backend'ом было тяжело перейти с одной технологии на другую (во Frontend'e), потому что у меня вызывало трудности как работать с данными, передавать их на сервер и обрабатывать.

    Короче, хочу дать совет Fullstack-разработчикам, которые хотят забыть про jQuery и перейти к фреймворкам типа AngularJS, на стороне Frontend'a. Просто забудьте всё то, чему вас учил jQuery. JS фреймворки — это полноценные фреймворки с моделями, контроллерами, представлениями и т. д., такие же как и Symfony, Laravel, Yii, которые задают свои правила написания кода, только полностью выполняющиеся на стороне клиента, то есть в браузере.


    1. PerlPower
      16.07.2016 09:18

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


      1. VolCh
        16.07.2016 09:57
        -2

        Или используйте js на бэкенде


  1. Grammka
    15.07.2016 21:33
    +2

    Достаточно было одного пункта: для jQuery заводите директивы. Все…


    1. symbix
      15.07.2016 23:46

      Только не «для», а «вместо». В смысле, не писать jquery-код в директивах, а jquery вообще не нужен.

      Нет, правда. Если не нужна поддержка доисторических браузеров, все легко и просто пишется на vanilla js. Я, если честно, уже давно забыл, как этим jquery пользоваться-то.


      1. Grammka
        15.07.2016 23:48

        Бывают разные требования заказчиков… Я например подключал baron, jquery.kinect и кучу других «плюшек» оборачивая их в директивы / сервисы. Так что не всегда удается рассмотреть вариант отказа от jquery на совсем…


        1. symbix
          15.07.2016 23:51

          Верю. У меня пока что получалось либо найти angular/vanilla-аналог, либо самостоятельно написать/портировать нужное подмножество.


      1. Fen1kz
        16.07.2016 10:36
        +2

        Если нужны хитровымученные манипуляции с DOMом, которые декларативно не заманипулируешь, то на vaniila JS в итоге пишешь свою jQuery, всякие там toggleClass, prepend. Ибо как правило идет работа с графикой, вот и получается что вместо бизнес-логики, пишешь православные без-jQueryийные функции а-ля width+margin


  1. crocodile2u
    16.07.2016 00:45

    Что-то не пошло чтение дальше двух абзацев. Перевод, уж извините, откровенно плохой ((


    1. lleo_aha
      16.07.2016 01:04
      +1

      Да и статья что то ни о чём :) «всё так круто как только ангуляр» — есть ощущение что автор статьи (оригинала имеется в виду) внезапно проснулся и выяснил что есть angularjs :) где то рядом такие же товарищи пишут «итить, с jquery жизнь стала легкой», а мы читаем зачем то


  1. Shifty_Fox
    16.07.2016 05:27
    +1

    Это перевод, но источник автор сильно лукавит.

    В первом случае в версии jQuery у ul обязан был быть класс js-dropdown-menu. И соответственно, в глобальном скрипте есть $('js-dropdown-menu').dropdownMenu();, вызванное один раз. Внезапно, пример с jQuery становится полностью эквивалентным решению с angular, только в синтаксисе jQuery.

    Во втором случае в версии jQuery отличие в том, что у angular уже есть $scope.log, а в jQuery почему-то элемент ищут в DOM.
    Элемент должен быть получен при инициализации страницы (var scopeLog = $('ul#log');), и тогда код снова становится эквивалентен коду на angular, с отличием в синтаксисе.


  1. Hamper
    16.07.2016 09:17
    -3

    Единственное, чем мне не нравится angular — это то, что если браузер не поддерживает javascript (js отключен или используется текстовый браузер типа lynx), то страница чаще всего превращается в кашу, по той причине, что сама верстка по большей части представляет из себя шаблон а не чистый html как при использовании jquery или чистого js.


  1. yurash
    16.07.2016 13:26

    jQuery был предназначен для проектирования системы улучшения характеристик и невероятно вырос из этой простой предпосылки

    В оригинале: jQuery was designed for augmentation and has grown incredibly from that simple premise.
    Первые же слова статьи выдают пример жутчайшего гугл-транслейта. Я ожидаю ещё не мало подобных перлов.