Modern Frontend Routing

A presentation at DotJS in December 2019 in Paris, France by Eduardo San Martin Morote

Slide 1

Slide 1

Eduardo San Martin Morote Frontend Nerd @posva 🔗 esm.dev

Slide 2

Slide 2

Modern Routing

Slide 3

Slide 3

Modern Routing

Slide 4

Slide 4

Modern Routing

Slide 5

Slide 5

Modern Frontend Routing

Slide 6

Slide 6

https://movies.com/search?genre=horror&page=1#filters

Slide 7

Slide 7

https://movies.com/search?genre=horror&page=1#filters History

Slide 8

Slide 8

https://movies.com/search?genre=horror&page=1#filters History Router

Slide 9

Slide 9

https://movies.com/search?genre=horror&page=1#filters History Matcher Router

Slide 10

Slide 10

https://movies.com/search?genre=horror&page=1#filters History Matcher Router Components

Slide 11

Slide 11

https://movies.com/search?genre=horror&page=1#filters History Matcher Router Components

Slide 12

Slide 12

History API history.pushState(data, title, url) history.replaceState(data, title, url) window.addEventListener(‘popstate’, handler) history.state history.length posva

Slide 13

Slide 13

https://movies.com history.state history.length null 1 posva

Slide 14

Slide 14

https://movies.com history.state history.length null 1 posva

Slide 15

Slide 15

https://movies.com history.state history.length null 1 history.pushState({ from: null }, ”, ‘/releases’) posva

Slide 16

Slide 16

https://movies.com /releases history.state history.length { from: null } 2 history.pushState({ from: null }, ”, ‘/releases’) posva

Slide 17

Slide 17

https://movies.com /releases history.state history.length { from: null } 2 history.pushState({ from: null }, ”, ‘/releases’) history.pushState({ from: ‘/releases’ }, ”, ‘/search?genre=horror&page=1#filters’) posva

Slide 18

Slide 18

https://movies.com /search?genre=horror&page=1#filters history.state history.length { from: ‘/releases’ } 3 history.pushState({ from: null }, ”, ‘/releases’) history.pushState({ from: ‘/releases’ }, ”, ‘/search?genre=horror&page=1#filters’) posva

Slide 19

Slide 19

https://movies.com /search?genre=horror&page=1#filters history.state history.length { from: ‘/releases’ } 3 history.pushState({ from: null }, ”, ‘/releases’) history.pushState({ from: ‘/releases’ }, ”, ‘/search?genre=horror&page=1#filters’) posva

Slide 20

Slide 20

https://movies.com /releases history.state history.length { from: null } 3 history.pushState({ from: null }, ”, ‘/releases’) history.pushState({ from: ‘/releases’ }, ”, ‘/search?genre=horror&page=1#filters’) posva

Slide 21

Slide 21

https://movies.com /releases history.state history.length { from: null } 3 history.pushState({ from: null }, ”, ‘/releases’) history.pushState({ from: ‘/releases’ }, ”, ‘/search?genre=horror&page=1#filters’) posva

Slide 22

Slide 22

https://movies.com /search?genre=horror&page=1#filters history.state history.length { from: ‘/releases’ } 3 history.pushState({ from: null }, ”, ‘/releases’) history.pushState({ from: ‘/releases’ }, ”, ‘/search?genre=horror&page=1#filters’) posva

Slide 23

Slide 23

https://movies.com/search?genre=horror&page=1#filters posva

Slide 24

Slide 24

https://movies.com/search?genre=horror&page=1#filters Path Query (or search) Hash (or fragment) posva

Slide 25

Slide 25

Path /search Query ?genre=horror&page=1 Hash #filters posva

Slide 26

Slide 26

Path /search Query ?genre=horror&page=1 Hash #filters location posva

Slide 27

Slide 27

Path /search Query ?genre=horror&page=1 Hash #filters location.pathname posva

Slide 28

Slide 28

Path /search Query ?genre=horror&page=1 Hash #filters location.search posva

Slide 29

Slide 29

Path /search Query ?genre=horror&page=1 Hash #filters location.hash posva

Slide 30

Slide 30

Path /search Query ?genre=horror&page=1 Hash #filters location.href posva

Slide 31

Slide 31

/search?genre=horror&page=1#filters posva

Slide 32

Slide 32

/search?genre=horror&page=1#filters { } path: ‘/search’, query: { genre: ‘horror’, page: ‘1’ }, hash: ‘#filters’ posva

Slide 33

Slide 33

?genre=horror&page=1 posva

Slide 34

Slide 34

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 35

Slide 35

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 36

Slide 36

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 37

Slide 37

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 38

Slide 38

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 39

Slide 39

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 40

Slide 40

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 41

Slide 41

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 42

Slide 42

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 43

Slide 43

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 44

Slide 44

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 45

Slide 45

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 46

Slide 46

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 47

Slide 47

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 48

Slide 48

URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

Slide 49

Slide 49

posva

Slide 50

Slide 50

Encoding Character Hex Encoded ” U+0022 %22 < U+003C %3C ñ U+00F1 %C3%B1 posva

Slide 51

Slide 51

Encoding Character Hex Encoded ” U+0022 %22 < U+003C %3C ñ U+00F1 %C3%B1

<p class=”a”> posva

Slide 52

Slide 52

Encoding Character Hex Encoded ” U+0022 %22 < U+003C %3C ñ U+00F1 %C3%B1

<p class=”a”> %3Cp%20class=%22a%22%3E posva

Slide 53

Slide 53

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) posva

Slide 54

Slide 54

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path Query Hash posva

Slide 55

Slide 55

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path Query Hash ␣ ” < > ` posva

Slide 56

Slide 56

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path ␣ ” < > Query Hash ␣ " < > posva

Slide 57

Slide 57

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path ␣ ” < > # ? { } Query Hash ␣ " < > posva

Slide 58

Slide 58

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path ␣ ” < > # ? { } Query ␣ " < > Hash ␣ ” < > ` posva

Slide 59

Slide 59

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path Query Hash ␣ ” < > # ? { } ␣ " < > # & = ␣ " < > posva

Slide 60

Slide 60

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) posva

Slide 61

Slide 61

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) posva

Slide 62

Slide 62

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) location.hash: ‘# “<>`’ posva

Slide 63

Slide 63

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) location.hash: ‘# “<>' https://<…..>#%20"<> posva

Slide 64

Slide 64

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) location.hash: ‘# “<>' https://<…..>#%20"<> location.hash: ‘#%20%22%3C%3E%60’ posva

Slide 65

Slide 65

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) location.hash: ‘# “<>' https://<…..>#%20"<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..># “<>` posva

Slide 66

Slide 66

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) location.hash: ‘# “<>' https://<…..>#%20"<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..># “<>` location.hash: ‘#%20%22%3C%3E%60’ posva

Slide 67

Slide 67

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) location.hash: ‘# “<>' https://<…..>#%20"<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..># “<>` location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 posva

Slide 68

Slide 68

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) location.hash: ‘# “<>' https://<…..>#%20"<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..># “<>location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># “<>` posva

Slide 69

Slide 69

location.hash = ‘# “<>' location.hash = encodeURI('# "<>’) location.hash: ‘# “<>' https://<…..>#%20"<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..># “<>location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># “<>` posva

Slide 70

Slide 70

location.hash = ‘# “<>' location.hash: ‘# "<>’ location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20"<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..># “<>location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># “<>` posva

Slide 71

Slide 71

location.hash = ‘# “<>' location.hash: ‘# "<>’ https://<…..>#%20”<>location.hash = encodeURI('# "<>’) location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60’ https://<…..># “<>location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># “<>` posva

Slide 72

Slide 72

location.hash = ‘# “<>' location.hash: ‘# "<>’ https://<…..>#%20”<>location.hash: ‘#%20%22%3C%3E%60' location.hash = encodeURI('# "<>’) location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60’ https://<…..># “<>location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># “<>` posva

Slide 73

Slide 73

location.hash = ‘# “<>' location.hash: ‘# "<>’ https://<…..>#%20”<>location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘# “<>' https://<…..># "<> posva

Slide 74

Slide 74

location.hash = ‘# “<>' location.hash: ‘# "<>’ https://<…..>#%20”<>location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘# “<>' https://<…..># "<> posva

Slide 75

Slide 75

location.hash = ‘# “<>' location.hash: ‘# "<>’ https://<…..>#%20”<>location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘# “<>' https://<…..># "<> posva

Slide 76

Slide 76

location.hash = ‘# “<>' location.hash: ‘# "<>’ https://<…..>#%20”<>location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘# “<>' https://<…..># "<> location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

Slide 77

Slide 77

location.hash = ‘# “<>' 🤔 location.hash: ‘# "<>’ https://<…..>#%20”<>location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘# “<>' https://<…..># "<> location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

Slide 78

Slide 78

location.hash = ‘# “<>' 🤔 location.hash: ‘# "<>’ 💯 location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘# “<>' https://<…..># "<> location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

Slide 79

Slide 79

location.hash = ‘# “<>' 🤔 location.hash: ‘# "<>’ 💯 location.hash: ‘#%20%22%3C%3E%60’ ✅ location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>https://<…..># "<> https://<…..>#%20”<>%60 location.hash: ‘# “<>' https://<…..># "<> location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

Slide 80

Slide 80

location.hash = ‘# “<>' 🤔 location.hash: ‘# "<>’ 💯 location.hash: ‘#%20%22%3C%3E%60’ ✅ location.hash: ‘#%20%22%3C%3E%60’ 🤪 location.hash: ‘# “<>' https://<…..>#%20"<> https://<…..># “<>https://<…..>#%20"<>%60 https://<…..># "<> location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

Slide 81

Slide 81

location.hash = ‘# “<>' location.hash: ‘# "<>’ https://<…..>#%20”<>location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘# “<>' https://<…..># "<> location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

Slide 82

Slide 82

location.hash = ‘# “<>' location.hash: ‘# "<>’ https://<…..>#%20”<>location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘# “<>' https://<…..># "<> location.hash = encodeURI(‘# “<>') location.hash: ‘#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: ‘#%20%22%3C%3E%60' https://<…..># "<> location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: ‘#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

Slide 83

Slide 83

Encode you URL even if it looks bad encodeURI / decodeURI encodeURIComponent / decodeURIComponent Path and hash Query keys and values posva

Slide 84

Slide 84

History { } path: ‘/movies/493922’, query: {}, hash: ” Communicate with the address bar Handle encoding Handle browser specific bugs/inconsistencies posva

Slide 85

Slide 85

{ { } path: ‘/movies/493922’, query: {}, hash: ” } path: ‘/movies/493922’, params: { id: ‘493922’}, name: ‘MovieDetail’, component: …, query: {}, hash: ” posva

Slide 86

Slide 86

Matcher { { } path: ‘/movies/493922’, query: {}, hash: ” } path: ‘/movies/493922’, params: { id: ‘493922’}, name: ‘MovieDetail’, component: …, query: {}, hash: ” posva

Slide 87

Slide 87

Matcher ‘/home’ /^/home$/i /^/movies/([^/]+)$/i ‘/movies/:id’ /^/movies/(\n+)$/i ‘/movies/:id(\n+)’ /^/movies/new$/i ‘/movies/new’ path-to-regexp posva

Slide 88

Slide 88

Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i ‘/movies/new’ /^/movies/([^/]+)$/i /^/movies/(\n+)$/i /^/movies/new$/i posva

Slide 89

Slide 89

Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i ‘/movies/new’ /^/movies/([^/]+)$/i /^/movies/(\n+)$/i /^/movies/new$/i posva

Slide 90

Slide 90

Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i /^/movies/([^/]+)$/i ‘/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva

Slide 91

Slide 91

Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i /^/movies/([^/]+)$/i ‘/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva

Slide 92

Slide 92

Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i /^/movies/([^/]+)$/i ‘/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva

Slide 93

Slide 93

Matcher 70 ‘/home’ 130 ‘/movies/:id’ 135 ‘/movies/:id(\n+)’ 140 ‘/movies/new’ /^/home$/i /^/movies/([^/]+)$/i ‘/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva

Slide 94

Slide 94

Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ ‘/movies/new’ posva

Slide 95

Slide 95

Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ ‘/movies/new’ posva

Slide 96

Slide 96

Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ posva

Slide 97

Slide 97

Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ 70 posva

Slide 98

Slide 98

Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ 70 + 70 70 posva

Slide 99

Slide 99

Matcher 140 ‘/movies/new’ 70 + 70 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 + 60 70 ‘/home’ 70 posva

Slide 100

Slide 100

Matcher 140 ‘/movies/new’ 70 + 70 135 ‘/movies/:id(\n+)’ 70 + 60 130 ‘/movies/:id’ 70 + 60 70 ‘/home’ 70 + 5 posva

Slide 101

Slide 101

140 70 + 70 135 70 + 60 130 70 + 60 70 70 ‘/movies/new’ + 5 posva

Slide 102

Slide 102

Router { } path: ‘/movies/new’, params: {}, name: ‘MovieNew’, component: …, query: {}, hash: ” posva

Slide 103

Slide 103

Router { } path: ‘/movies/new’, params: {}, name: ‘MovieNew’, component: …, query: {}, hash: ” View Component posva

Slide 104

Slide 104

Router { } path: ‘/movies/new’, params: {}, name: ‘MovieNew’, component: …, query: {}, hash: ” View Component posva

Slide 105

Slide 105

https://movies.com/search?genre=horror&page=1#filters History Matcher Router Components posva

Slide 106

Slide 106

Thank you! posva