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