Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Инструментируй это
Роман Дворнов
Avito
Минск 2015
Работаю в Avito
Делаю SPA
Автор basis.js
Я…
За любую движуху, 

кроме голодовки ;)
DEMO

поговорим про боль и немного интриги
Инструментирование
кода
«под инструментированием понимают
возможность отслеживания или установления
количественных параметров …, а также
возможность диагностировать ошибки и
записывать информацию для отслеживания
причин их возникновения…»
5
en.wikipedia.org/wiki/Instrumentation_(computer_programming)
• Трассировка
• Отладка
• Регистрация событий
• Подмена кода
• Счетчики производительности
• ...
6
Области применения
Инструментируй это
Code coverage
Пример №1
Как узнать что исполнялось?
9
function foo(a) {	
if (a > 5) {	
return 1;	
} else {	
return 2;	
}	
}
Оригинальный код
Как узнать что исполнялось?
9
function foo(a) {	
if (a > 5) {	
return 1;	
} else {	
return 2;	
}	
}
function foo(a) {	
__visit(1);	
if (a > 5) {	
__visit(2);	
return 1;	
} else {	
__visit(3);	
return 2;	
}	
}
Оригинальный код Трансформированный код
API (runtime)
10
var __count = [0, 0, 0, ...]; // столько нулей, сколько	
// частей в коде, которые	
// нас интересуют	
!
window.__visit = function(idx) {	
__count[idx - 1]++;	
};	
Упрощенная версия
После выполнения тестов 

в __count будет 

необходимая информация
11
Пример результатов
12
__count = [3, 3, 0]
функция исполнялась 3 раз
ветка if исполнялась 3 раза
ветка else не исполнялась
Покрытие кода
~67%
13
Проецируем данные на исходники
• трансформация оригинального кода
• запуск тестов
• агрегация данных или визуализация
• PROFIT!!!
14
Code coverage: все вместе
• трансформация оригинального кода
• запуск тестов
• агрегация данных или визуализация
• PROFIT!!!
15
это и есть инструментирование

(делается автоматически)
Code coverage: все вместе
Не стоит путать с другими
типами трансформаций,
например, транспиляцией
16
Транспиляция – преобразование
код в равнозначный код,
например, ES6 → ES5
17
en.wikipedia.org/wiki/Source-to-source_compiler
Инструментирование –
дополнение кода другим кодом,
который делает что-то еще
18
Инструментирование = 

код, добавляющий код в код
19
Инструментирование = 

код, добавляющий код в код
19
Loupe
Пример №2
Визуализация того, как работают
некоторые части в JavaScript 

(call stack, event loop, callback queue)
21
latentflip.com/loupe
(там есть видео доклада, рекомендую)
DEMO
22
• Инструментируем код
• отправляем код в Web Worker, где он исполняется
• по мере исполнения, в основной процесс
отправляются сообщения о том, что происходит –
это визуализируется
• немного «магии» ;)
23
Как это работает
React Hot Loader
Пример №3
Модификация React-компонентов

в реальном времени
25
gaearon.github.io/react-hot-loader/
DEMO
26
• Достаточно сложно объяснить в двух словах
• Ключевое:
• исходный код инструментируется
• патчатся классы компонент
• подменяются методы классов при изменении
исходного кода и делается полный re-render
27
Как это работает
Ищем концы
Пример №4
или с чего все начиналось
Начальная идея
29
Имея ссылку на объект уметь
определять местоположение
фрагмента кода его декларации
Инструментируй это
DEMO
31
!
• Не завязано на фреймворк/библиотеку
• Библиотеки могут расширять возможности и
предоставлять дополнительную информацию
32
Главное – идея
33
За пределами браузера
Браузер dev-server
Редактор
Файл
Команда в терминале
Запрос на
открытие файла
Инструментированный
код + source maps
Части решения
34
• Инструментирование кода
• Runtime API
• Инспекция DOM дерева
• dev-сервер
Инструментирование
36
Код Код
AST
(ESTree)
Парсинг
Трансформации
(Babel)
Трансляция
Esprima
Делаем трансформацию кода правильно
36
Код Код
AST
(ESTree)
Парсинг
Трансформации
(Babel)
Трансляция
Esprima
Делаем трансформацию кода правильно
Просто добавь 

свой плагин
Инструментирование делается
плагином для Babel
37
github.com/restrry/babel-plugin-source-wrapper
Что делает плагин
38
var foo = $devinfo(function(a, b) {	
return a + b;	
}, { … });	
!
var obj = $devinfo({ foo: 1 }, { … });
var foo = function(a, b) {	
return a + b;	
};	
!
var obj = { foo: 1 };
Делается автоматически

и только на этапе разработки
39
Runtime API
41
window.$devinfo = (function() {	
var map = new WeakMap();	
var api = function(ref, data) {	
if (!map.has(ref))	
map.set(ref, data);	
return ref;	
};	
api.get = function(ref) {	
return ref ? map.get(ref) : undefined;	
}	
return api;	
})();
Основное API
Упрощенный код
42
window.$devinfo = (function() {	
var map = new WeakMap();	
var api = function(ref, data) {	
if (!map.has(ref))	
map.set(ref, data);	
return ref;	
};	
api.get = function(ref) {	
return ref ? map.get(ref) : undefined;	
}	
return api;	
})();
Основное API
Упрощенный код
Используем WeakMap для
хранения информации:
- объекты как ключи
- не трансформирует объекты
- не создает утечек памяти
43
window.$devinfo = (function() {	
var map = new WeakMap();	
var api = function(ref, data) {	
if (!map.has(ref))	
map.set(ref, data);	
return ref;	
};	
api.get = function(ref) {	
return ref ? map.get(ref) : undefined;	
}	
return api;	
})();
Основное API
Упрощенный код
Основная функция регистрации,
"добавляет" информацию только
если ее еще нет у объекта
44
window.$devinfo = (function() {	
var map = new WeakMap();	
var api = function(ref, data) {	
if (!map.has(ref))	
map.set(ref, data);	
return ref;	
};	
api.get = function(ref) {	
return ref ? map.get(ref) : undefined;	
}	
return api;	
})();
Основное API
Упрощенный код
Получение информации,
используется инструментами
45
var obj = {};	
!
$devinfo(obj, { data: 123 });	
!
console.log($devinfo.get(obj));	
// > { data: 123 }
Использование
Возможность хранить
информацию, можно
использовать для других задач
46
DEMO
47
github.com/lahmatiy/t8
Инспектирование DOM
• знать границу компонента, т.е. определять 

элемент-контейнер компонента (DOM узел)
• определять владельца компонента (view) 

по элементу-контейнеру (по DOM узлу)
49
Для инструмента нужно
• если у DOM узла есть свойство __view – 

значит это контейнер компонента
• в свойстве __view хранится ссылка на view
50
Решение в лоб
Но лучше использовать

WeakMap (node ➝ view)
Крохотный патч для Backbone
51
!
var _setElement = Backbone.View.prototype._setElement;	
Backbone.View.prototype._setElement = function() {	
_setElement.apply(this, arguments);	
this.el.__view = this;	
};
Или с WeakMap
52
!
var map = new WeakMap();	
var _setElement = Backbone.View.prototype._setElement;	
Backbone.View.prototype._setElement = function() {	
_setElement.apply(this, arguments);	
map.add(this.el, this);	
};	
window.getBackboneViewByNode = function(el) {	
return map.get(el)	
};
Готовые решения
54
Работает из коробки
(нужен basis.js 1.5)
basis.js
component-inspector
55
github.com/lahmatiy/component-inspector
Другие фреймворки
npm install component-inspector --save-dev
React
56
Подключение в html перед React!
<script src="node_modules/component-inspector/dist/react.js">	
</script>	
<script src="react.js"></script>
Backbone
57
Подключение в html после Backbone
<script src="backbone.js"></script>	
<script src="node_modules/component-inspector/dist/backbone.js">	
</script>
Свое решение?
Запросто!
58
59
!
<script src="node_modules/component-inspector/dist/base.js"></script>	
Инициализация
<script>	
initComponentInspector({	
// просто добавь API	
});	
</script>
60
Описываем API
initComponentInspector({	
isComponentRootNode: function(node) {	
return Boolean(node && node.__view);	
},	
getInstanceByNode: function(node) {	
if (node) {	
return node.__view;	
}	
},	
…	
});
Можно определить многое
61
• isComponentRootNode(node)
• getComponentNameByNode(node)
• getInstanceByNode(node)
• getInstanceRootNode(instance)
• getInstanceClass(instance)
• getInstanceLocation(instance)
• ...
github.com/lahmatiy/component-inspector#api-free-build
dev-сервер
• отдавать инструментированный код
• исполнять команды, например, открытие
файла в редакторе
63
dev-сервер должен
64
Вариант настройки: webpack
Инструментирование
webpack
+ babel + babel-plugin-source-wrapper
!
Открытие файла в редакторе
express или webpack-dev-server
+ express-open-in-editor
65
Пример конфигурации
для Webpack + React
tinyurl.com/pwj6bln
Вариант настройки: basisjs-tools
66
> npm install basisjs-tools -g	
> npm install basisjs-tools-instrumenter	
// создать конфиг basis.config	
> basis server
{	
"plugins": [	
"basisjs-tools-instrumenter"	
]

}
Сервер будет отдавать
инструментированный код и
встраивать необходимое в html
• open-in-editor

npm пакет для программного открытия файла в редакторе

github.com/lahmatiy/open-in-editor
• express-open-in-editor

расширение для Express для открытия файла по урлу

github.com/lahmatiy/express-open-in-editor
• extract-code-fragment

получение раскрашенного фрагмента кода из файла

скоро
67
Побочные продукты
Проблемки
69
Проблема №1

«Захламляется» код
70
Решение: Source maps
71
Проблема №2

Runtime API мешает отладке
72
Решение: Blackbox Script
73
Проблема №2

Показывается не то место
74
function createObj() {	
return $devinfo({	
example: true	
}, {	
loc: "lib.js:…"	
});	
}
var obj = $devinfo(createObj(), {	
loc: "app.js:…"	
});
lib.jsapp.js
Нам нужно это место Но покажется это
75
require('babel-plugin-source-wrapper')({	
blackbox: [	
"/path/to/lib.js"	
]	
})
blackbox: [	
"/bower_components/**",	
"/node_modules/**"	
]
По умолчанию:
Решение: blackbox в инструментере
76
function createObj() {	
return $devinfo({	
example: true	
}, {	
loc: "lib.js:…",	
blackbox: true	
});	
}
var obj = $devinfo(createObj(), {	
loc: "app.js:…"	
});
lib.jsapp.js
Решение: blackbox в инструментере
Теперь будет показываться
это место
У такой информации меньший
приоритет
77
Проблема №4

«Зашумляется» стек вызова
78
ХотимВидим
79
Решение: магия :)
$devinfo(function()  {  
    …  
},  {  …  })
($devinfo)(function()  {  
    …  
},  {  …  })
Заключение
Инструментирование – метод,
расширяющий возможности
разработки
81
Местами это сложно, 

но безумно интересно!
82
Попробуйте!
Интересно, что получится у вас ;)
83
Вопросы?
84
Роман Дворнов
@rdvornov
rdvornov@gmail.com
github.com/lahmatiy
tinyurl.com/wsd-ci
Component Inspector

More Related Content

Инструментируй это