Skip to content

Commit 8c324a0

Browse files
authored
Merge pull request #12 from JavaScript-Basic-OTUS/lesson27
Add lesson 27
2 parents f4a5796 + d9464f2 commit 8c324a0

File tree

9 files changed

+619
-1
lines changed

9 files changed

+619
-1
lines changed

lessons/lesson27/lesson.md

Lines changed: 365 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,365 @@
1-
# Lesson 27
1+
---
2+
title: Занятие 27
3+
description: Многостраничные и одностраничные приложения - работа с URL
4+
---
5+
6+
# OTUS
7+
8+
## Javascript Basic
9+
10+
<!-- v -->
11+
12+
## Клиентский роутинг
13+
14+
Как строится одностраничное приложение (SPA)
15+
16+
<!-- v -->
17+
18+
### Проверка
19+
20+
- Хорошо ли видно и слышно?
21+
- Проверить идёт ли запись
22+
23+
<!-- s -->
24+
25+
## Клиентский роутинг
26+
27+
Как строится одностраничное приложение (SPA)
28+
29+
<!-- v -->
30+
31+
### Цели занятия
32+
33+
- Разобраться какие API можно использовать для организации SPA
34+
35+
- Научиться создавать клиентский роутинг
36+
37+
<!-- v -->
38+
39+
### Маршрут вебинара
40+
41+
- Введение
42+
- Hash API
43+
- History API
44+
- Router
45+
- Практика
46+
- Итоги
47+
48+
<!-- s -->
49+
50+
## Введение
51+
52+
<!-- v -->
53+
54+
**Что такое URL**
55+
56+
<!-- v -->
57+
58+
> Единый указатель ресурса (англ. [**Uniform Resource Locator**](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL), URL) — единообразный локатор (определитель местонахождения) ресурса.
59+
60+
> Ранее назывался Universal Resource Locator — универсальный указатель ресурса. URL служит стандартизированным способом записи **адреса ресурса в сети Интернет**.
61+
62+
<!-- v -->
63+
64+
```
65+
scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
66+
```
67+
68+
```
69+
https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL#anchor
70+
```
71+
72+
<!-- v -->
73+
74+
[**Что происходит, когда вы вводите URL в браузере**](http://wsvincent.com/what-happens-when-url/)
75+
76+
<!-- v -->
77+
78+
**Что такое клиентский роутинг**
79+
80+
<!-- v -->
81+
82+
> Клиентский роутинг (**client-side routing**) это, когда пользователь перемещается по приложению/веб-сайту, и при этом **не происходит полной перезагрузки страницы**, даже если URL-адрес страницы изменяется. Вместо этого **используется JavaScript для обновления URL-адреса**, а также для извлечения и отображения нового содержимого.
83+
84+
<!-- v -->
85+
86+
**Способы управления URL на клиенте**
87+
88+
<!-- v -->
89+
90+
- Hash API
91+
- History API
92+
93+
<!-- s -->
94+
95+
## `Hash API`
96+
97+
<!-- v -->
98+
99+
\*Старый способ. До появления HTML5.
100+
101+
<!-- v -->
102+
103+
> Способ управления состояниям фрагмента URL
104+
105+
```
106+
scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
107+
```
108+
109+
<!-- v -->
110+
111+
- window.location.hash
112+
- `window.onhashchange` / `"hashchange"` event
113+
114+
<!-- v -->
115+
116+
[Пример](https://codesandbox.io/s/vigorous-black-vzbit?file=/index.html)
117+
118+
```js
119+
document.body.addEventListener("click", (event) => {
120+
if (!event.target.matches("a")) {
121+
return;
122+
}
123+
event.preventDefault();
124+
let url = event.target.getAttribute("href");
125+
location.hash = url; // <= set only hash or URL
126+
});
127+
128+
window.addEventListener("hashchange", () => {
129+
// <= handle/catch hash changes
130+
console.log(`hashchange: ${location.hash}`);
131+
});
132+
```
133+
134+
<!-- s -->
135+
136+
## `History API`
137+
138+
<!-- v -->
139+
140+
[Новое API](https://caniuse.com/?search=history) HTML5
141+
142+
<!-- v -->
143+
144+
> [**History API**](https://developer.mozilla.org/en-US/docs/Web/API/History_API) опирается на один DOM интерфейс — объект **History**
145+
146+
> Каждая вкладка браузера имеет уникальный объект History, который находится в `window.history`
147+
148+
<!-- v -->
149+
150+
> [**History**](https://developer.mozilla.org/en-US/docs/Web/API/History) имеет несколько методов, событий и свойств, которыми мы можем **управлять из JavaScript**.
151+
152+
<!-- v -->
153+
154+
<!-- eslint-skip -->
155+
156+
```js
157+
/* Количество записей в текущей сессии истории */
158+
window.history.length
159+
160+
/* Возвращает текущий объект состояния истории */
161+
window.history.state
162+
163+
/* Метод, позволяющий гулять по истории.
164+
* В качестве аргумента передается смещение, относительно текущей позиции.
165+
* Если передан 0, то будет обновлена текущая страница.
166+
* Если индекс выходит за пределы истории, то ничего не произойдет. */
167+
window.history.go(n)
168+
169+
/* Метод, идентичный вызову go(-1) */
170+
window.history.back()
171+
172+
/* Метод, идентичный вызову go(1) */
173+
window.history.forward()
174+
175+
/* Добавляет элемент истории */
176+
window.history.pushState(data, title [, url])
177+
178+
/* Обновляет текущий элемент истории */
179+
window.history.replaceState(data, title [, url])
180+
```
181+
182+
<!-- v -->
183+
184+
```js
185+
/* Триггерится при `history.go/back/forward` или при браузерных кликах */
186+
window.addEventListener("popstate", (event) => console.log(event.state));
187+
```
188+
189+
<!-- v -->
190+
191+
[Пример](https://codesandbox.io/s/vigorous-black-vzbit?file=/index.html)
192+
193+
```js
194+
document.body.addEventListener("click", (event) => {
195+
if (!event.target.matches("a")) {
196+
return;
197+
}
198+
event.preventDefault();
199+
let url = event.target.getAttribute("href");
200+
history.pushState({}, url, url); // <--
201+
});
202+
203+
/* Триггерится при `history.go/back/forward` или при браузерных кликах */
204+
window.addEventListener("popstate", (event) => {
205+
console.log(
206+
"location: " + document.location + ", state: " + JSON.stringify(event.state)
207+
);
208+
});
209+
```
210+
211+
<!-- v -->
212+
213+
> \*Нужна настройка сервера, т.к. при обновлении / передаче ссылки должна загрузиться начальная страница
214+
215+
<!-- s -->
216+
217+
## `Router`
218+
219+
<!-- v -->
220+
221+
> Обработчик URL - называется роутером (**Router**)
222+
223+
> Router определяет какой код должен выполняться в зависимости от адреса.
224+
> Логика `router`'а может быть завязана на параметры.
225+
226+
<!-- v -->
227+
228+
\*Бывает серверный и **браузерный** роутинг/роутер.
229+
230+
<!-- v -->
231+
232+
\*Router **не встроенные API**, а скорее общепринятый термин.
233+
234+
<!-- v -->
235+
236+
Очень много готовых библиотек/статей:
237+
238+
- [Pilot: многофункциональный JavaScript роутер](https://habrahabr.ru/company/mailru/blog/172333/)
239+
- [Роутер на JavaScript](http://blog.byndyu.ru/2009/09/javascript.html)
240+
- [A modern JavaScript router in 100 lines](http://krasimirtsonev.com/blog/article/A-modern-JavaScript-router-in-100-lines-history-api-pushState-hash-url)
241+
- [A simple minimalistic JavaScript router with a fallback for older browsers.](https://github.com/krasimir/navigo)
242+
- [router.js](https://github.com/tildeio/router.js/)
243+
244+
<!-- v -->
245+
246+
Example. Simple Route Interface
247+
248+
```sh
249+
# Interface of Route
250+
IRoute {
251+
match # String | RegExp | function
252+
onEnter([data]) # function
253+
}
254+
```
255+
256+
```js
257+
// Example
258+
const route = {
259+
match: "/",
260+
onEnter: () => console.log("onEnter index"),
261+
};
262+
```
263+
264+
<!-- v -->
265+
266+
Example. Advanced Route Interface
267+
268+
```sh
269+
# Interface of Route
270+
IRoute {
271+
match # String | RegExp | function
272+
onEnter([data]) # function
273+
onLeave([data]) # function
274+
onBeforeEnter([data]) # function
275+
}
276+
```
277+
278+
```js
279+
// Example
280+
const route = {
281+
match: "/",
282+
onEnter: () => console.log("onEnter index"),
283+
onLeave: () => console.log("onLeave index"),
284+
};
285+
```
286+
287+
<!-- v -->
288+
289+
Example. Router Interface 1
290+
291+
```sh
292+
# Interface of Route
293+
IRouter {
294+
add(route) # function
295+
remove(route) # function
296+
go(url, [param]) # function
297+
}
298+
```
299+
300+
<!-- v -->
301+
302+
Example. Router Interface 2
303+
304+
```sh
305+
# Interface of Route
306+
IRouter {
307+
on(match, onEnter) # function
308+
go(url, [params]) # function
309+
}
310+
```
311+
312+
<!-- v -->
313+
314+
[Пример](https://codesandbox.io/s/vigorous-black-vzbit?file=/index.html)
315+
316+
<!-- s -->
317+
318+
## Практика
319+
320+
<!-- v -->
321+
322+
To-do:
323+
324+
1. Fork sandbox
325+
1. Implement unsubscribe/remove functionality
326+
1. Add support for "onLeave" callback
327+
328+
```sh
329+
# Interface of Route
330+
IRouter {
331+
on(match, onEnter, [onLeave]) # function -> function
332+
go(url, [params]) # function
333+
}
334+
```
335+
336+
<!-- v -->
337+
338+
[codesandbox](https://codesandbox.io/s/vigorous-black-vzbit?file=/examples/practice.js)
339+
340+
<!-- s -->
341+
342+
## Итоги
343+
344+
> 1. **Клиентский роутинг** - навигацию по приложению/веб-сайту без **перезагрузки страницы**
345+
346+
> 2. Способы управления URL на клиенте:
347+
> (old) **Hash API** и (new) **History API**
348+
349+
> 3. **Router** (термин)- обработчик URL, определяет какой код должен выполняться в зависимости от адреса. **\*Не встроённое API**
350+
351+
<!-- s -->
352+
353+
### Вопросы?
354+
355+
<!-- s -->
356+
357+
## Спасибо за внимание!
358+
359+
[Ссылка на опрос]()
360+
361+
<!-- s -->
362+
363+
### Ссылки
364+
365+
- [Understanding client side routing by implementing a router in Vanilla JS](https://www.willtaylor.blog/client-side-routing-in-vanilla-js/#:~:text=What%20is%20client%20side%20routing,fetch%20and%20display%20new%20content.)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# vanilla-client-side-routing-examples
2+
3+
Created with CodeSandbox
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* eslint-disable */
2+
3+
const render = () => {
4+
const route = location.hash.replace("#", "") || "/";
5+
document.getElementById("root").innerHTML = `<h2>"${route}" page</h2>`;
6+
};
7+
8+
// 1. Handle initial page load
9+
window.addEventListener("load", () => {
10+
render(); // 👈
11+
});
12+
13+
// 2. Handle hash changes
14+
window.addEventListener("hashchange", () => {
15+
render(); // 👈
16+
});
17+
18+
// 3. Catch <a> tag clicks
19+
document.body.addEventListener("click", (event) => {
20+
if (!event.target.matches("a")) {
21+
return;
22+
}
23+
event.preventDefault();
24+
const url = event.target.getAttribute("href");
25+
location.hash = url; // doesn't reload page
26+
// location.href = url; // reloads page
27+
// location.replace(url); // reloads page
28+
});

0 commit comments

Comments
 (0)