Top Banner
Как построить DOM Роман Дворнов Ostrovok.ru Киев, Октябрь 2013
100

Как построить DOM

Jun 15, 2015

Download

Documents

basisjs

Шаблонизаторы упрощают процесс формирования HTML и только. Но браузеру нужен совсем не HTML, а DOM. Необходимо преобразование. И вот тут начинается самое интересное: танцы с бубном и стрельба по ногам. В докладе пойдёт речь об общепринятом подходе получения DOM фрагмента, постпроцессинге и альтернативах. Сравним, измерим и узнаем как это делать быстрее всего.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Как построить DOM

Как построить DOM

Роман ДворновOstrovok.ru

Киев, Октябрь 2013

Page 2: Как построить DOM

Немного о себе

• Работаю в Ostrovok.ru

• Ранее руководилвеб-разработкой в ПС Единый кошелек

• Автор и мейтейнер фреймворка basis.js

2

Page 3: Как построить DOM

Зачем нам DOM?

3

Page 4: Как построить DOM

Веб-приложения

• Компонентный подход, переиспользование• Разделение отвественности• Много разных данных• Ленивое построение• Точечные обновления• Динамика, динамика, динамика...

4

Page 5: Как построить DOM

Компонентный подход=

декомпозиция

5

Page 6: Как построить DOM

Обычный подход

6

list

item

item

item

item

item

Page 7: Как построить DOM

Компонентный подход

7

list item

item

item

item

item

Page 8: Как построить DOM

Обычный подход

8

window

form

field

field

button panel

button button

Page 9: Как построить DOM

Компонентный подход

9

window formfield

field

button panel

button button

Page 10: Как построить DOM

Обычный подходfunction renderList(data){ var html = ''; html = html + '<ul class="mylist' + (cls ? ' ' + cls : '') + '">'; for (var i = 0; i < items.length; i++) html = html + '<li class="item ' + (items[i].selected ? ' selected' : '') + '>' + items[i].title + '</li>'; html = html + '</ul>'; return html;};...element.innerHTML = renderList({ .. });

10

Page 11: Как построить DOM

Обычный подходfunction renderList(data){ var html = ''; html = html + '<ul class="mylist' + (cls ? ' ' + cls : '') + '">'; for (var i = 0; i < items.length; i++) html = html + '<li class="item ' + (items[i].selected ? ' selected' : '') + '>' + items[i].title + '</li>'; html = html + '</ul>'; return html;};...element.innerHTML = renderList({ .. });

11

Page 12: Как построить DOM

DOM спасет мир динамический веб

12

Page 13: Как построить DOM

Первый подходили жизнь без шаблонов

13

Page 14: Как построить DOM

Hardcore – DOMvar Item = basis.ui.Node.subclass({ init: function(config){ this.element = basis.dom.create('li.item', // создание DOM basis.dom.create('span.caption', this.title = basis.dom.createText(config.title) ) ); }, setTitle: function(title){ this.title.nodeValue = title; // точечное обновление }, destroy: function(){ ... this.element = null; // обнуление ссылок this.title = null; }});

14

Page 15: Как построить DOM

Hardcore – событияvar Item = basis.ui.Node.subclass({ init: function(config){ ... // подписка на события basis.dom.event.on(this.element, 'click', onclick, this); }, ... onclick: function(event){ // подписанные методы получают event this.selected ? this.unselect() : this.select(); }, ... destroy: function(){ ... // отписываемся basis.dom.event.off(this.element, 'click', onclick, this); }});

15

Page 16: Как построить DOM

Как быстро?

16

Page 17: Как построить DOM

17

Speed tree – «подопытный кролик»

Page 18: Как построить DOM

Результаты

18

IE11 Firefox26 Chrome30 Opera12 iPhone4S

100 узлов, ms 45 15 8 10 50

2000 узлов, ms 1014 309 183 174 981

Page 19: Как построить DOM

Доля работы с DOM

19

IE11 Firefox26 Chrome30 Opera12 iPhone4S

общее время, ms 1014 309 183 174 981

создание DOM, ms 785 246 151 134 807

создание DOM, % 77,4 79,6 82,5 77,0 82,3

Speed tree – 2000 узлов

Более 75% тратится на создание DOM

Page 20: Как построить DOM

Минусы подхода

20

1. Медленое создание DOM фрагмента

2. Сложно менять "описание"

3. Нужно самостоятельно привязывать и удалять обработчики событий

4. Много кода по обновлению DOM

5. Не наглядно

6. Легко получить утечки памяти

Page 21: Как построить DOM

Медленное создание DOM

21

Решаем проблему

Page 22: Как построить DOM

Отказ от удобств

22

this.element = basis.dom.create('li.item');

this.element = document.createElement('li');this.element.className = 'item';

Page 23: Как построить DOM

23

Отказ от удобств

IE11 Firefox26 Chrome30 Opera12 iPhone4S

выигрыш, ms 463 171 110 92 540

выигрыш, % 59,0 % 69,5 % 72,8 % 68,7 % 66,9 %

было 785 246 151 134 807

Speed tree – 2000 узлов

Page 24: Как построить DOM

Привязка событий

• Добавляем обработчики на элемент корневого компонента

• Жертвуем возможностью определять слушаемые события в коде дочерних компонент

• Один обработчик для каждого типа события вместо N

24

Page 25: Как построить DOM

Привязка событий

25

IE11 Firefox26 Chrome30 Opera12 iPhone4S

выигрыш, ms 115 37 23 13 118

выигрыш, % 14,6 % 15,0 % 15,2 % 9,7 % 14,6 %

было 785 246 151 134 807

Speed tree – 2000 узлов

Page 26: Как построить DOM

cloneNode(true)var Leaf = basis.ui.Node.subclass({ proto: basis.dom.create('li.item', // эталон basis.dom.create('span.caption', basis.dom.createText('no title') ) ), init: function(cfg){ ... // получаем клон this.element = this.proto.cloneNode(true);

this.title = this.element.firstChild.firstChild; this.title.nodeValue = cfg.title || 'no title'; }});

26

Page 27: Как построить DOM

Минимизируем операции с DOM• Любая операция с DOM чего-то стоит

• Даже присвоение того же значения, что уже есть у свойства, часто равносильно присвоению нового значения

• Даже операции чтения, то есть обращение к свойствам firstChild, lastChild, nextSibling и т.д.

27

Page 28: Как построить DOM

Меньше операций с DOM

28

IE11 Firefox26 Chrome30 Opera12 iPhone4S

выигрыш, ms 49 18 5 15 9

выигрыш, % 6,2 % 7,3 % 3,3 % 11,2 % 1,1 %

было 785 246 151 134 807

Speed tree – 2000 узлов

Page 29: Как построить DOM

После всех оптимизаций

29

Speed tree – 2000 узлов

IE11 Firefox26 Chrome30 Opera12 iPhone4S

было 785 246 151 134 807

стало 158 20 13 14 140

выигрыш, ms 627 226 138 120 667

выигрыш, % 79,9 % 91,9 % 91,4 % 89,6 % 82,7 %

Page 30: Как построить DOM

Минус один

30

1. Медленое создание DOM фрагмента

2. Сложно менять "описание"

3. Нужно самостоятельно привязывать и удалять обработчики событий

4. Много кода по обновлению DOM

5. Не наглядно

6. Легко получить утечки памяти

Page 31: Как построить DOM

Сложность описания

31

Решаем проблему

Page 32: Как построить DOM

И тут появляются шаблоны

32

Page 33: Как построить DOM

basis.html.Templatevar Leaf = basis.ui.Node.subclass({ template: new basis.html.Template( '<li{element} class="item">{title}</li>' ), init: function(){ ... // этот вызов создаст DOM фрагмент и вернет карту this.tmpl = this.template.createInstance();

this.tmpl.title.nodeValue = this.title; }});

33

Page 34: Как построить DOM

template.createInstance(..)

34

<div{element}> <h2>{title}</h2> <ul{content}/></div>

{ element: <div>, title: #text, content: <ul>}

Создает DOM фрагменти возвращает карту ссылок

Page 35: Как построить DOM

Парсинг описания

• Без использования innerHTML

• Комбинация RegExp и конечного автомата

• Позволяет использовать собственные конструкции и теги

• Делается только раз

35

Page 36: Как построить DOM

Минус два

36

1. Медленое создание DOM фрагмента

2. Сложно менять "описание"

3. Нужно самостоятельно привязывать и удалять обработчики событий

4. Много кода по обновлению DOM

5. Не наглядно

6. Легко получить утечки памяти

Page 37: Как построить DOM

Привязка событий

37

Решаем проблему

Page 38: Как построить DOM

В шаблоне

<li{element} class="item" event-click="select"> {title}</li>

38

Page 39: Как построить DOM

В коде

var Leaf = basis.ui.Node.subclass({ ... action: { select: function(event){ this.select(); }, ... }});

39

action – дополняется при наследовании

Page 40: Как построить DOM

Как это работает

40

Page 41: Как построить DOM

<BODY>

Событийная модель

41

Document

<HTML>

<DIV>

<BUTTON>

Page 42: Как построить DOM

<BODY>

Событийная модель

41

Document

<HTML>

<DIV>

<BUTTON>

Capture phaseнельзя прервать

Page 43: Как построить DOM

<BODY>

Событийная модель

41

Document

<HTML>

<DIV>

<BUTTON>

Bubbling phaseможно прервать

Capture phaseнельзя прервать

Page 44: Как построить DOM

Добавляем обработчик

42

<BODY>

Document

<HTML>

<DIV>

<BUTTON>

Bubbling phaseможно прервать

Capture phaseнельзя прервать

Page 45: Как построить DOM

Добавляем обработчик

42

<BODY>

Document

<HTML>

<DIV>

<BUTTON>

document.addEventListener( 'eventName', find, true);

Page 46: Как построить DOM

<BODY>

Document

<HTML>

<DIV>

<BUTTON>

document.addEventListener( 'eventName', find, true);

43

Page 47: Как построить DOM

<BODY>

Document

<HTML>

<DIV>

<BUTTON> find(event)43

Page 48: Как построить DOM

<BODY>

Document

<HTML>

<DIV>

<BUTTON>

event

find(event)

1. Ищем ближайший от target элементс атрибутом event-{eventName}

2. От него ближайший элементс basisTemplateId

3. По ID определяем шаблон и владельца

43

<BUTTON>

Page 49: Как построить DOM

IE8 и старые браузерыОписание шаблона<div event-click="action"> {title}</div>

Шаблонизатор добавляет атрибут в эталонный DOM фрагмент, но только для браузеров без addEventListener<div event-click="action" onclick="basisTemplateHandler('click', event)"> {title}</div>

// basisTemplateHandler вызывает find

44

Page 50: Как построить DOM

Привязка событий• Один глобальный обработчик на каждое уникальное событие

• Привязки и отвязки обработчиков как таковых нет

• Можно добавить одно и тоже действие на несколько узлов

• Изменение шаблона ничего не ломает• Нет места для утечек памяти

45

Page 51: Как построить DOM

Минус три

46

1. Медленое создание DOM фрагмента

2. Сложно менять "описание"

3. Нужно самостоятельно привязывать и удалять обработчики событий

4. Много кода по обновлению DOM

5. Не наглядно

6. Легко получить утечки памяти

Page 52: Как построить DOM

Обновление DOM

47

Решаем проблему

Page 53: Как построить DOM

В шаблоне

<li class="item {selected} {disabled}"> <span title="title is {title}"> {title} ({count}) </span></li>

48

Page 54: Как построить DOM

В кодеvar Leaf = basis.ui.Node.subclass({ title: 'no title', binding: { title: function(leaf){ return leaf.title; } }, setTitle: function(newTitle){ this.title = newTitle; this.updateBind('title'); // когда надо обновить }});

49

binding – дополняется при наследовании

Page 55: Как построить DOM

Если есть событияvar Leaf = basis.ui.Node.subclass({ binding: { disabled: { events: ['disable', 'enable'], getter: function(leaf){ return leaf.isDisabled(); } } }});

50

Page 56: Как построить DOM

Проблемы решены

51

1. Медленое создание DOM фрагмента

2. Сложно менять "описание"

3. Нужно самостоятельно привязывать и удалять обработчики событий

4. Много кода по обновлению DOM

5. Не наглядно

6. Легко получить утечки памяти

Page 57: Как построить DOM

Компоненты независимы от описания шаблона

52

большая часть операций с DOM автоматизирована и осуществляется

шаблоном

Page 58: Как построить DOM

Разделение логики и представления

53

Объект Шаблон(DOM fragment)

binding

action

JavaScript Document

DOMоперации

eventlisteners

Page 59: Как построить DOM

DOM vs. Template

54

Speed tree 2000 узлов

IE11 Firefox26 Chrome30 Opera12 iPhone4S

DOM 785 246 151 134 807

DOM+ оптимизации 158 20 13 14 140

basis.js templates 230 65 55 58 337

Page 60: Как построить DOM

DOM vs. innerHTML

55

Page 61: Как построить DOM

Причем тут innerHTML?

• Является простым и наиболее популярным трансформаторомHTML → DOM

• Его использует метод jQuery.html

56

Page 62: Как построить DOM

innerHTML baseline

      this.element = document.createElement('li');      this.element.className = 'tree-node';      this.element.innerHTML =        '<div class="title">' +          '<span class="caption">' +            this.data.title +          '</span>' +        '</div>';

57

Page 63: Как построить DOM

DOM vs. innerHTML

58

Speed tree – 2000 узлов

IE11 Firefox24 Chrome30 Opera12 iPhone4S

DOM+ оптимизации 158 20 13 14 140

basis.js templates 230 65 55 58 337

innerHTML 217 42 44 37 272

Page 64: Как построить DOM

DOM vs. innerHTML

59

Но здесь нет пост-процессинга

Speed tree – 2000 узлов

IE11 Firefox24 Chrome30 Opera12 iPhone4S

DOM+ оптимизации 158 20 13 14 140

basis.js templates 230 65 55 58 337

innerHTML 217 42 44 37 272

Page 65: Как построить DOM

DOM vs. innerHTML

60

А тут есть

Speed tree – 2000 узлов

IE11 Firefox24 Chrome30 Opera12 iPhone4S

DOM+ оптимизации 158 20 13 14 140

basis.js templates 230 65 55 58 337

innerHTML 217 42 44 37 272

Page 66: Как построить DOM

Пост-процессинг

• Добавление классов и другие изменения DOM фрагмента

• Навешивание обработчиков событий• Получение ссылок на элементы

61

Page 67: Как построить DOM

Пример из TodoMVC на backbone.js

app.TodoView = Backbone.View.extend({ ... render: function () { this.$el.html(this.template(this.model.toJSON())); this.$el.toggleClass('completed', this.model.get('completed')); this.toggleVisible(); this.$input = this.$('.edit'); return this; }, ...});

62

tinyurl.com/nkpcyd6

Page 68: Как построить DOM

Делаем так же

this.$el = $('<li>').attr({ class: 'tree-node' });this.element = this.$el[0];this.$el.html('...html...');

this.childNodesElement = $el.find('.content')[0];

this.$el.toggleClass('selected', this.selected);this.$el.toggleClass('disabled', this.disabled);

this.$el.on('click', '.caption', this.select.bind(this));

63

Page 69: Как построить DOM

DOM vs. innerHTML

64

Speed tree – 2000 узлов

IE11 Firefox24 Chrome30 Opera12 iPhone4S

DOM+ оптимизации 158 20 13 14 140

basis.js templates 230 65 55 58 337

innerHTML 217 42 44 37 272

backbone style 499 296 306 208 1138

Page 70: Как построить DOM

— У вас никогда не будет 2000 узлов?

65

Page 71: Как построить DOM

«640КБ должно быть достаточно для каждого»

66

Билл Гейтс, 1981-й год

Page 72: Как построить DOM

67

Page 73: Как построить DOM

Цифры не включаютreflow и repaint

68

Page 74: Как построить DOM

Не включают время создания компонент

69

Page 75: Как построить DOM

В бою

• Более сложные шаблоны, композиция

• Дополнительная логика• Обработка данных• Создание нескольких view в один момент

• И многое другое...

70

Page 76: Как построить DOM

В бою то же время будет для гораздо меньшего количества узлов

71

Page 77: Как построить DOM

Выводы

72

Page 78: Как построить DOM

innerHTML достаточно быстрый, но медленныйпост-процессинг

73

Page 79: Как построить DOM

Построение DOMприменяя API и оптимизации

быстрее innerHTML

74

Page 80: Как построить DOM

Шаблоны basis.js – удобный способ создавать и обслуживать DOM

фрагменты

75

Page 81: Как построить DOM

И эти шаблоны умеют много чего еще

76

Подключение стилей, live update, темы, локализация, интроспекция,

инструменты, ...

Page 82: Как построить DOM

Заключение

77

Page 83: Как построить DOM

Изучайте и используйтеDOM

78

Page 84: Как построить DOM

Сравнивайте

79

Page 85: Как построить DOM

Экспериментируйте

80

Page 86: Как построить DOM

Ищите решения

81

Page 87: Как построить DOM

И получится что-то крутое :)

82

Page 88: Как построить DOM

Но это еще не все...

83

Page 89: Как построить DOM

basis-templates.js

84

basis.js template & l10n modulesas standalone library

Page 90: Как построить DOM

basis-templates.js

85

63.2 KB min23.7 KB min + gzip

Page 92: Как построить DOM

basis-templates.js

87

библиотека ориентирована на встраивание в системы и фреймворки, в виде модулей или плагинов

Page 93: Как построить DOM

bbt.js

88

backbone basis-templates.js plugin

Page 94: Как построить DOM

backbone.js – 510 ms

backbone.js + bbt.js – 202 ms

89

Page 95: Как построить DOM

TodoMVC

90

100 todo 1000 todo

AngularJS 125 ms 1491 ms

Backbone.js 53 ms 510 ms

Knockout 39 ms 489 ms

vanilla 23 ms 1882 ms

jQuery 20 ms 184 ms

Backbone.js + bbt.js 18 ms 202 ms

basis.js 8 ms 95 ms

Page 96: Как построить DOM

Переделаны только шаблон и код TodoViewдля использованияbasis-templates.js

91

Page 97: Как построить DOM

В планах

92

• Уменьшить объем• Улучшить API

• Обеспечить поддержку live update, тем и других возможностей шаблонов basis.js

• Tasks для grunt (live update и сборка)

• Больше примеров и документации

Page 98: Как построить DOM

Пробуйте, используйте, принимайте участие!

93

github.com/basisjs/templates

basis-templates.js

Page 99: Как построить DOM

Сделаем веб быстрее!

94

github.com/basisjs/templates

basis-templates.js

Page 100: Как построить DOM

Вопросы?

Роман Дворнов @[email protected]

basis.jsbasisjs.com

github.com/basisjsМинск, Октябрь 2013