Вопросы?

Вопросы!

Принцип единственной отвественности ( Single Responsibility Principle, SRP )

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

Такой класс может измениться по двум причинам:
- может измениться само содержимое отчёта
- может измениться формат отчёта.

SRP говорит, что в таком случае нужно разделить класс на два новых класса, для которых будет характерна только одна ответственность.

Вопросы?

Программирование на интерфейсах
Любой код представляет из себя
  1. Интерфейс (принимаемые/возвращаемые параметры и побочные эффекты)
  2. Реализацию (как оно внутри устроено)
Ориентация на "интерфейс" позволяет писать код, с заменяемыми частями Ориентация на "интерфейс" приучает не делать "сильную связность" в коде
Использование реализации и связность
Что можно сделать?
  1. Определить интерфейс
  2. Реализовать интерфейс / сделать заглушку
  3. Внедрение зависимости (англ. Dependency injection, DI)
Определить интерфейс

// тут псевдо-код
var IStorage = {
  setData('key', value),
  getData('key')
};
Реализовать интерфейс / сделать заглушку

var lsStorage = {
   setData: (key, value) => window.localStorage.setItem(key, value),
   getData: (key) => window.localStorage.getItem(key)
}
Использовать интерфейс ( Внедрить зависимость )

(function(window, storage) {
  // ....
  var data = storage.getData('calendarData');
  storage.setData('calendarData', {});
})(window, lsStorage);
Использовать другую реализацию

var sStorage = {
   setData: (key, value) => window.sessionStorage.setItem(key, value),
   getData: (key) =>window.sessionStorage.getItem(key)
}
(function(window, storage) {
  // ....
  var data = storage.getData('calendarData');
  storage.setData('calendarData', {});
})(window, sStorage);

Вопросы?

Модуль

  1. Скрыть реализацию (инкапсуляция)
  2. Обеспечить атомарность
  3. Дает возможность DI
Скрыть реализацию

(function() {
  // тут код модуля
  // то, что нужно скрыть остается в замыкании 
  // и недоступно снаружи
})();
Обеспечить атомарность / DI

(function(global) {
  // теперь код может работать в любом окружении
})(window);
Обеспечить атомарность / DI

(function(global, dependencyInterface1, dependencyInterface2) {
  // теперь код может работать в любом окружении
  // c любыми реализациями интерфейса
})(window, implementation1, implementation2);
А раньше...

(function(global, dependencyInterface1, dependencyInterface2, undefined) {
  // теперь код может работать в любом окружении
  // c любыми реализациями интерфейса
})(window, implementation1, implementation2);

Вопросы?

Состояние приложения

Совокупность всех переменных определяющих "состояние" приложения - какие данные отображаются, какие действия можно сделать.

Например:

  • Залогинен пользователь или нет
  • Имя пользователя
  • Какую страницу он сейчас смотрит
  • Открыта ли модальное окно
  • Состояние полей формы

Для "сервера" доступны следующие способы сохранить состояние:

  • Сессия
  • База данных
  • "in memory" / В объекте приложения
  • *

Способы хранить состояние на "клиенте":

  • Cookie
  • Storage (local/session)
  • База данных
  • DOM *
  • "in memory" / в объекте приложения *
  • Адресная строка

Вопросы?

URL

Единый указатель ресурса (англ. Uniform Resource Locator, URL) — единообразный локатор (определитель местонахождения) ресурса.

Ранее назывался Universal Resource Locator — универсальный указатель ресурса. URL служит стандартизированным способом записи адреса ресурса в сети Интернет.

scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
http://forum.onliner.by/viewtopic.php?t=1425824#p18162130

Способы упраления URL на клиенте

History API

MDN + Управление историей браузера
  • location.href
  • .back / .forward / .go
  • .pushState / .replaceState
  • Событие popstate
Пример

document.body.addEventListener('click', (ev) => {
  if(!ev.target.matches('a')) { // <--
    return;
  }
  ev.preventDefault();
  let url = ev.target.getAttribute('href');
  history.pushState({}, url, url); // <--
});
            

Особенности:

  • Новое API
  • Нужна настройка сервера,
    т.к. при обновлении / передаче ссылки должна загрузиться начальная страница

Hash State (by name / id )

  • Старое API, которое использовалось для ссылок внутри документа
  • window.onhashchange / "hashchange" event
  • window.location.href
  • Пример
    
    document.body.addEventListener('click', (ev) => {
      if(!ev.target.matches('a')) {
        return;
      }
      ev.preventDefault();
      let url = ev.target.getAttribute('href');
      window.location.hash = url;
    });
                

    Вопросы?

    Общая схема работы с состоянием через URL

    1. Создать обработчик URL
    2. Подписаться на изменения URL (5)
    3. При загрузке страницы - считать состояние и запустить обработчик
    4. Переопределить поведение внутренних ссылок
    5. При клике по ссылке - обновлять URL (2)
    Создать обработчик URL
    
    var counter = document.querySelector('#counter');
    var content = document.querySelector('#content');
    setInterval(() => counter.innerText = new Date(), 1000);
    
    function handleUrl(url) {
      document.querySelectorAll('a.active').forEach(el => el.classList.remove('active'));
      document.querySelectorAll('a[href="' + url.split('#').pop() + '"]').forEach(el => el.classList.add('active'));
      
      content.innerHTML = url;
    }
                
    Подписаться на изменения URL
    
    window.addEventListener('hashchange', (ev) => handleUrl(ev.newURL));
                
    При загрузке страницы - считать состояние и запустить обработчик
    
    handleUrl(window.location.href);            
    Переопределить поведение внутренних ссылок
    
    document.body.addEventListener('click', (ev) => {
       if(!ev.target.matches('a')) {
         return;
       }
       ev.preventDefault();
       // При клике по ссылке - обновлять URL
       let url = ev.target.getAttribute('href');
       window.location.hash = url;
     });
    
    Пример
    
    var counter = document.querySelector('#counter');
     var content = document.querySelector('#content');
     setInterval(() => counter.innerText = new Date(), 1000);
     
     // Создать обработчик URL
     function handleUrl(url) {
       document.querySelectorAll('a.active').forEach(el => el.classList.remove('active'));
       document.querySelectorAll('a[href="' + url.split('#').pop() + '"]').forEach(el => el.classList.add('active'));
       
       content.innerHTML = url;
     }
     
     // Подписаться на изменения URL
     window.addEventListener('hashchange', (ev) => handleUrl(ev.newURL));
     
     // При загрузке страницы - считать состояние и запустить обработчик
     handleUrl(window.location.href);
     
     // Переопределить поведение внутренних ссылок
     document.body.addEventListener('click', (ev) => {
       if(!ev.target.matches('a')) {
         return;
       }
       ev.preventDefault();
       // При клике по ссылке - обновлять URL
       let url = ev.target.getAttribute('href');
       window.location.hash = url;
     });
    
                

    Вопросы?