55+1 приемдля улучшения JavaScript-кодаБабич ТатьянаРуководитель Frontend отдела компании SimbirSoft
На старт! Внимание! Марш! Проект будет больше, чем предполагается
Состав команды может поменяться
Время реализации сильно ограничено
Соблюдайте LIFT - принципы 1
locate
identify
flat
try to stay DRY
LIFT
-
-
-
-
Соблюдайте LIFT - принципы.
Соблюдайте LIFT - принципы.
Спустя парумесяцев
controllers/LoginController.jsRegistrationController.jsProfileController.jsSearchResultsController.jsFeedController.js
directives.jsfilters.jsmodels/
Feed.jsCartModel.jsProfileModel.jsSearchResultsModel.jsUserModel.js
services/FeedService.jsCartService.jsUserService.jsProfileService.js
Соблюдайте LIFT - принципы.
Спустя полгодаcontrollers/
LoginController.jsRegistrationController.jsProductDetailController.jsSearchResultsController.jsUsersController.jsUsersProfileController.jsOpinionController.jsInstrumentsController.jsFeedController.jsRecoveryController.jsNavigationContoller.js
directives.jsfilters.js
models/CartModel.jsProductModel.jsSearchResultsModel.jsUserModel.jsNavigation.jsRecovery.jsFeed.jsInstruments.jsOpinion.jsUsers.jsUsersProfile.js
services/...
Модульность
Модульностьproduct/
search/ SearchResultsController.js SearchResultsModel.js
ProductDetailController.jsProductModel.jsProductService.js
user/LoginController.jsRegistrationController.jsRecoveryController.jsUserModel.jsUserService.js
instruments/ InstrumentsController.jsInstrumentsModel.jsInstrumentsService.js
feed/FeedController.js FeedModel.jsFeedService.js
Принцип единой ответственности
Модульность
2
/* 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 */}
Применяйте единый codestyle. Используйте анализаторы кода. Включите их в систему контроля версий
4
Модульность
Соблюдайте правила именования переменных3profile.controller.js ProfileController.js
Создайте константы для всех переменных от сторонних библиотек в вашем проекте
5
Модульность
Оптимизируйте ваши «велосипеды», тестируйте их, а главное документируйте
6
1 модуль = 1 задача
Модульность
приватностьслабая связность
Модульность
независимость
Создавайте много небольших независимых модулей
Создавайте ре-используемые модули9
8
Модульность
Создавайте главный модуль и не загромождайте его7
Модульность
Используйте IIFE10
;(function() { ... var app = angular.module('myApp'); ...})();
console.log(app) // not defined
Переносите логику из контроллера в фабрики и сервисы12
Контроллеры
Формируйте $scope в определенном месте11
Контроллеры
Используйте синтаксис 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
function User() { angular.extend(this, {
someVar: { name: 'Name'
},anotherVar: [],doSomething: function doSomething() {
} });}
angular .module('app') .controller('User', User);
Контроллеры
Замена vm синтаксиса
Задавайте отдельный контроллер для каждого шаблона
// route-config.jsangular .module('app') .config(config);
function config($routeProvider) { $routeProvider .when('/user', { templateUrl: 'user.html', controller: 'UserController', controllerAs: 'vm' });}
<!-- user.html --><div></div>
14
Контроллеры
Используйте одноразовую привязку данных {{:: ... }}
Не используйте ng-class для установки CSS свойств, если это возможно сделать средствами только CSS
17
16
Минимизация количества $watcher - ов
Правильно используйте $rootScope15
Минимизация количества $watcher-ов
Минимизация количества $watcher - ов
Переносите манипуляции с DOM в директивы19
Не храните ссылки на DOM элементы в $scope18
Избегайте работы с большими данными21
Создавайте директивы с изолированным $scope20
Используйте track by для циклов22
Минимизация количества $watcher - ов
Переносите тяжелую логику из фильтров в контроллеры и сервисы
24
Удаляйте ненужные фильтры23
Используйте $watchCollection вместо $watch (с 3-им параметром).Избегайте установки флага objectEquality в true
$scope.$watch(…, …, true);
26
Минимизация количества $watcher - ов
Откажитесь от использования $watch там, где это возможно25
Отписывайтесь от watches и event listeners27
Минимизация количества $watcher - ов
var stopFunction = $scope.$on('someEvent', function() { ... }); // Обработчик добавленstopFunction(); // Обработчик удален
Если это возможно, сокращайте количество вызовов ng-model
ng-model-options=”{debounce: 250}”
Старайтесь избегать использования ng-mouse-over и подобных директив
29
28
Оптимизация $digest вызовов
Оптимизация $digest вызовов
Избегайте использования сокращенного синтаксиса объявления зависимостей без учета минификации кода
angular .module('app') .controller('Dashboard', Dashboard);
function Dashboard(common, dataservice) {}
30
Аннотация внедрения зависимостей
Аннотация внедрения зависимостей
angular.module('app').controller('Dashboard', d);function d(a, b) { }
Используйте $inject для задания зависимостей
angular .module('app') .controller('Dashboard', Dashboard);
Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice'];
function Dashboard($location, $routeParams, common, dataservice) {}
Используйте ng-annotate с /** @ngInject */32
31
Аннотация внедрения зависимостей
Архитектура
Замените всплытие событий использованием Медиатора33
Архитектура
Паттерн «Фасад»скрытие деталей реализации конкретного функционала
Архитектура
Создавайте «врапперы» над вашими библиотеками 34
Переключайте CSS классы вместо перестроения DOM
Скрывайте элементы перед изменением37
36
Оптимизация CSS
Упрощайте DOM35
DOM - оптимизация
Генерируйте элементы отдельно от страницы 38
Оптимизация CSS
Клонируйте ваши узлы, изменяйте копии, а затем заменяйте ими оригиналы
Используйте только быстрые обертки над DOM операциями jquery
40
39
Не чередуйте запись и чтение DOM41
Тестируйте css-transitions43
Оптимизация CSS
Избавляйтесь от тяжелых CSS свойств: border-radius, box-shadow, rotate
42
Оптимизация CSS
.disable-hover { pointer-events: none;}
Отключайте сложные :hover анимации во время скроллинга44
Оптимизация CSS
Указывайте четкие размеры изображениям, тем самым ускоряя reflow и repaint
45
Выстраивайте цепочки методов46
Получение данных
Получение данных
(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')));
Используйте объекты вместо массива аргументов
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});
48
Получение данных
Получайте общие данные не блокируя загрузку основных данных47
Не изменяйте массив arguments. Копируйте его в настоящий, используя [].slice.call(arguments)
49
Получение данных
Не применяйте цикл for in для массивов50
class ExampleController {
constructor() {
this.controllerName = 'Example Controller';
}
}
export { ExampleController }
ES6
ES6Используйте классы для контроллеров и сервисов51
Помещайте export в конец модуля
function sumTwo(a, b) {return a + b;
}
function sumThree(a, b, c) {return a + b + c;
}
let api = {sumTwo,sumThree
};
export default api;
52
ES6
Оставьте декларации VAR внутри унаследованного кода, чтобы обозначить, что он должен быть тщательно переработан
53
ES6
Используйте стрелочные функции
когда вам нужно сохранить лексическое значение thisвместо функциональных выражений, когда это возможно
54
myModule.component('appFooter', { template: '<footer></footer>',
bindings: { entityName: '=' }, controller: function() {
this.getCurrentYear = function() {};}
});
ES6
Используйте API компонент55
+1 советПишите тесты.
Вопросы?