55+1 прием
для улучшения JavaScript-кода

Бабич Татьяна

Руководитель Frontend отдела компании Simbirsoft

На старт! Внимание! Марш!

 

        Проект будет больше, чем предполагается

        Состав команды может поменяться

        Время реализации сильно ограничено

Соблюдайте LIFT - принципы.

L
I
F
T

̶
̶
̶
̶

locate
identify
flat
try to stay DRY

1

Соблюдайте LIFT - принципы.

Спустя пару

месяцев

controllers/
    LoginController.js
    RegistrationController.js
    ProfileController.js
    SearchResultsController.js
    FeedController.js
directives.js
filters.js
models/
    Feed.js
    CartModel.js
    ProfileModel.js
    SearchResultsModel.js
    UserModel.js
services/
    FeedService.js
    CartService.js
    UserService.js
    ProfileService.js

Соблюдайте LIFT - принципы.

Спустя полгода

controllers/
    LoginController.js
    RegistrationController.js
    ProductDetailController.js
    SearchResultsController.js
    UsersController.js
    UsersProfileController.js
    OpinionController.js
    InstrumentsController.js
    FeedController.js
    RecoveryController.js
    NavigationContoller.js
directives.js
filters.js
models/
    CartModel.js
    ProductModel.js
    SearchResultsModel.js
    UserModel.js
    Navigation.js
    Recovery.js
    Feed.js
    Instruments.js
    Opinion.js
    Users.js
    UsersProfile.js
services/
    ...

Соблюдайте LIFT - принципы.

Спустя полгода

views/
    partials/
              ...
    users.html
    userprofile.html
    search.html
    register.html
    recovery.html
    profile.html
    opinionPage.html
    login.html
    instruments.html
    instrumentsPage.html
    myinstruments.html
    feed.html
    oauth.html
    404.html
    index.html    

шаблоны

Соблюдайте LIFT - принципы.

Модульность

product/
    search/
        SearchResultsController.js
        SearchResultsModel.js
    ProductDetailController.js
    ProductModel.js
    ProductService.js
user/
    LoginController.js
    RegistrationController.js
    RecoveryController.js
    UserModel.js
    UserService.js
instruments/   
    InstrumentsController.js
    InstrumentsModel.js
    InstrumentsService.js
feed/
    FeedController.js   
    FeedModel.js
    FeedService.js   

Модульность

Принцип единой ответственности

/* directives.js */

angular
   .module('app.project')

   .directive('orderCalendarRange', orderCalendarRange)

   .directive('salesCustomerInfo', salesCustomerInfo)

   .directive('sharedSpinner', sharedSpinner);

function orderCalendarRange() {
   /* implementation details */
}

function salesCustomerInfo() {
   /* implementation details */
}

function sharedSpinner() {
   /* implementation details */
}
/* calendarRange.directive.js */

angular
   .module('sales.order')
   .directive('acmeOrderCalendarRange', orderCalendarRange);

function orderCalendarRange() {
   /* implementation details */
}

/* customerInfo.directive.js */
   
angular
   .module('sales.widgets')
   .directive('acmeSalesCustomerInfo', salesCustomerInfo);

function salesCustomerInfo() {
   /* implementation details */
}

/* spinner.directive.js */

angular
   .module('shared.widgets')
   .directive('acmeSharedSpinner', sharedSpinner);

function sharedSpinner() {
   /* implementation details */
}

Модульность

2

Соблюдайте правила именования переменных

profile.controller.js

ProfileController.js

Применяйте единый codestyle. Используйте анализаторы кода. Включите их в систему контроля версий

Оптимизируйте ваши «велосипеды», тестируйте их, а главное документируйте

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

3

4

5

6

Модульность

Модульность

приватность

слабая связность

независимость

1 модуль = 1 задача

Модульность

Используйте IIFE

Создавайте ре-используемые модули

Создавайте главный модуль и не загромождайте его

Создавайте много небольших независимых модулей

;(function() {
  ...
  var app = angular.module('myApp');
  ...
})();

console.log(app) // not defined

7

8

9

10

Модульность

Контроллеры

Переносите логику из контролера в фабрики и сервисы

Формируйте $scope в определенном месте

11

12

Контроллеры

Замена vm синтаксиса

function User() {
  angular.extend(this, {
    someVar: {
      name: 'Name'
    },
    anotherVar: [],
    doSomething: function doSomething() {

    }
  });
}

angular
  .module('app')
  .controller('User', User);

angular.extend

Используйте синтаксис controllerAs

<div ng-controller="UserController as user">
  {{ user.name }}
</div>

Для шаблонов

function UserController() {
   this.name = {};
   this.someFuncion = function() { };
}

Для контроллеров

function UserController() {
   var vm = this;
   vm.name = {};
   vm.sumeFunc = function() { };
}

или

13

Контроллеры

Задавайте отдельный контроллер для каждого шаблона

// route-config.js
angular
   .module('app')
   .config(config);

function config($routeProvider) {
   $routeProvider
       .when('/user', {
           templateUrl: 'user.html',
           controller: 'UserController',
           controllerAs: 'vm'
       });
}

<!-- user.html -->
<div></div>

14

Контроллеры

Минимизация количества $watcher-ов

Используйте одноразовую привязку данных {{:: ... }}

Не используйте ng-class для установки CSS классов, если это возможно сделать средствами CSS

Не храните ссылки на Dom элементы в scope

Переносите манипуляции с DOM в директивы

Правильно используйте $rootScope

Минимизация количества $watcher - ов

15

16

17

18

19

Избегайте работы с большими данными

Удаляйте ненужные фильтры

Переносите тяжелую логику из фильтров в контроллеры и сервисы

Используйте track by для циклов

Минимизация количества $watcher - ов

21

22

23

24

Создавайте директивы с изолированным $scope

20

$scope.$watch(…, …, true);

Используйте $watchCollection вместо $watch (с 3-им параметром)

Избегайте установки флага objectEquality в true

Отписывайтесь от watches и event listeners

var stopFunction = $scope.$on('someEvent', function() { ... }); // Обработчик добавлен
stopFunction(); // Обработчик удален

Минимизация количества $watcher - ов

25

26

27

Откажитесь от использования $watch там, где это возможно

Оптимизация $digest вызовов

Если это возможно, сокращайте количество вызовов ng-model

ng-model-options=”{debounce: 250}”

Старайтесь избегать использования ng-mouse-over и подобных директив

Оптимизация $digest вызовов

28

29

Избегайте использования сокращенного синтаксиса объявления зависимостей без учета минификации кода

Аннотация внедрения зависимостей

angular
   .module('app')
   .controller('Dashboard', Dashboard);

function Dashboard(common, dataservice) {
}
angular.module('app').controller('Dashboard', d);function d(a, b) { }

Аннотация внедрения зависимостей

30

Используйте $inject для задания зависимостей

angular
   .module('app')
   .controller('Dashboard', Dashboard);

Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice'];

function Dashboard($location, $routeParams, common, dataservice) {
}

Используйте ng-annotate с /** @ngInject */

Аннотация внедрения зависимостей

31

32

Замените всплытие событий использованием Медиатора

Архитектура

33

Архитектура

Паттерн «Фасад»

Создавайте «врапперы» над вашими библиотеками

скрытие деталей реализации конкретного функционала

Архитектура

34

DOM - оптимизация

Упрощайте DOM

Клонируйте ваши узлы, изменяйте копии, а затем заменяйте ими оригиналы

Используйте только быстрые обертки над DOM операциями jquery

Переключайте CSS- классы вместо перестроения DOM

Скрывайте элементы перед изменением

Генерируйте элементы отдельно от страницы

Не чередуйте запись и чтение DOM

DOM - оптимизация

36

37

38

39

35

40

41

Оптимизация CSS

Избавляйтесь от тяжелых CSS свойств:

border-radius, box-shadow, rotate. Заменяйте их на svg

Тестируйте css-transitions

Отключайте сложные :hover анимации во время скроллинга

.disable-hover {
  pointer-events: none;
}

Указывайте четкие размеры изображениям, тем самым ускоряя reflow и repaint

Оптимизация CSS

43

44

45

42

(function(module) {
    'use strict';
    module.factory('signUpService', signUpService);
    signUpService.$inject = ['$ionicLoading', 'firebaseService', '$ionicPopup', '$state', '$q'];
    function signUpService($ionicLoading, firebaseService, $ionicPopup, $state, $q) {

        return {
            signUp: function(user, userCred) {
                var newUser = {
                    user: user,
                    userCred: userCred
                };

                /* Registration chain */
                checkUsername(newUser)
                    .then(createUser)
                    .then(authByPassword)
                    .then(pushUserDetails)
                    .then(reserveUsername)
                    .catch(throwError);
            }
        };
        [...]
    }
}(angular.module('starter')));

Выстраивайте цепочки методов

Получение данных

Получение данных

46

function signUpQ() {
    $ionic Loading.show({
                template: ’Registration...’
            }; firebaseService.checkUsername(vm.user.userName).then(function(exist) {
                    if (!exist) {
                        firebaseService.createUser(vm.userCred.email, vm.userCred.password).then(function() {
                                firebaseService.authByPassword(vm.userCred.email, vm.userCred.password).then(function(response) {
                                        firebaseService.pushl) serDetails(response.uid, vm.user), then(function(error) {
                                            firebaseService.reserveUsername(vm.user.userName, response.uid).then(function(error) {
                                                    $ionicLoading.hide();
                                                    if (error) {
                                                        var promise = SionicPopup.alert({
                                                            title: 'Something wrong',
                                                            template: 'Your account is created but you have to change your User Name directly from from your profile'
                                                        });
                                                        promise.then(function() {
                                                            Sstate.go('main.login');
                                                        });
                                                    } else {
                                                        Sstate.go('main.login');
                                                    }
                                                },
                                                function(error) {
                                                    $ionicLoading.hide();
                                                    var promise = SionicPopup.alert({
                                                        title: 'Something wrong',
                                                        template: 'Your account is created but you have to change your User Details directly from your profile'
                                                    });
                                                    promise.then(function() {
                                                        Sstate.go('main.login');
                                                    };);
                                                })
                                        },
                                        function(error) {
                                            SionicLoading.hide();
                                            SionicPopup.alert({
                                                title: 'Registration error, template: error'
                                            });
                                        }
                                    }
                                }
                            } else {
                                SionicLoading.hide();
                                SionicPopup.alert({
                                    title: 'Registration error',
                                    template: 'Nickname already in use'
                                });
                            }
                        };

Получайте общие данные не блокируя загрузку основных данных

Получение данных

47

Используйте объекты вместо массива аргументов

48

var alert = new Alert(id, {
 x: 100, y: 75,
 width: 300, height: 200,
 title: "Error", message: message,
 titleColor: "blue", bgColor: "white",
 textColor: "black",
 icon: "error", modal: true
});

Не изменяйте массив arguments. Копируйте его в настоящий, используя [].slice.call(arguments)

49

При последовательном переборе элементов массива отдавайте предпочтение циклу for, а не циклу for...in

50

ES6

Используйте let и const. Не забывайте про ReferenceError

Оставьте декларации VAR внутри унаследованного кода, чтобы обозначить, что он должен быть тщательно переработан

(function () {
    var food = 'Meow Mix';
}());

console.log(food); // Reference Error
Using ES6 Blocks:

{
    let food = 'Meow Mix';
}

console.log(food); // Reference Error

ES6

51

52

Используйте стрелочные функции

ES6

когда вам нужно сохранить лексическое значение this

вместо функциональных выражений, когда это возможно

53

Используйте экспорт модулей. Помещайте export в конце модуля.

function sumTwo(a, b) {
    return a + b;
}

function sumThree(a, b, c) {
    return a + b + c;
}

let api = {
    sumTwo,
    sumThree
};

export default api;

Экспортируемые элементы - это связи, а не ссылки. Избегайте изменения общего интерфейса этих экспортируемых значений

ES6

54

55

+1 совет

Пишите тесты.

+1 совет

Вопросы?

Вопросы?

Copy of Copy of 55+1

By tatyana_babich

Copy of Copy of 55+1

  • 515