Eduardo San Martin Morote Frontend Nerd @posva 🔗 esm.dev
A presentation at DotJS in December 2019 in Paris, France by Eduardo San Martin Morote
 
                Eduardo San Martin Morote Frontend Nerd @posva 🔗 esm.dev
 
                Modern Routing
 
                Modern Routing
 
                Modern Routing
 
                Modern Frontend Routing
 
                https://movies.com/search?genre=horror&page=1#filters
 
                https://movies.com/search?genre=horror&page=1#filters History
 
                https://movies.com/search?genre=horror&page=1#filters History Router
 
                https://movies.com/search?genre=horror&page=1#filters History Matcher Router
 
                https://movies.com/search?genre=horror&page=1#filters History Matcher Router Components
 
                https://movies.com/search?genre=horror&page=1#filters History Matcher Router Components
 
                History API history.pushState(data, title, url) history.replaceState(data, title, url) window.addEventListener(‘popstate’, handler) history.state history.length posva
 
                https://movies.com history.state history.length null 1 posva
 
                https://movies.com history.state history.length null 1 posva
 
                https://movies.com history.state history.length null 1 history.pushState({ from: null }, ”, ‘/releases’) posva
 
                https://movies.com /releases history.state history.length { from: null } 2 history.pushState({ from: null }, ”, ‘/releases’) posva
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                https://movies.com/search?genre=horror&page=1#filters posva
 
                https://movies.com/search?genre=horror&page=1#filters Path Query (or search) Hash (or fragment) posva
 
                Path /search Query ?genre=horror&page=1 Hash #filters posva
 
                Path /search Query ?genre=horror&page=1 Hash #filters location posva
 
                Path /search Query ?genre=horror&page=1 Hash #filters location.pathname posva
 
                Path /search Query ?genre=horror&page=1 Hash #filters location.search posva
 
                Path /search Query ?genre=horror&page=1 Hash #filters location.hash posva
 
                Path /search Query ?genre=horror&page=1 Hash #filters location.href posva
 
                /search?genre=horror&page=1#filters posva
 
                /search?genre=horror&page=1#filters { } path: ‘/search’, query: { genre: ‘horror’, page: ‘1’ }, hash: ‘#filters’ posva
 
                ?genre=horror&page=1 posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                URLSearchParams API const q = new URLSearchParams(‘genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva
 
                posva
 
                Encoding Character Hex Encoded ” U+0022 %22 < U+003C %3C ñ U+00F1 %C3%B1 posva
 
                Encoding Character Hex Encoded ” U+0022 %22 < U+003C %3C ñ U+00F1 %C3%B1
<p class=”a”> posva 
                Encoding Character Hex Encoded ” U+0022 %22 < U+003C %3C ñ U+00F1 %C3%B1
<p class=”a”> %3Cp%20class=%22a%22%3E posva 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                location.hash = ‘# “<>' location.hash = encodeURI('# "<>’)
posva
 
                location.hash = ‘# “<>' location.hash = encodeURI('# "<>’)
posva
 
                location.hash = ‘# “<>' location.hash = encodeURI('# "<>’)
location.hash: ‘# “<>`’
posva
 
                location.hash = ‘# “<>' location.hash = encodeURI('# "<>’)
location.hash: ‘# “<>' https://<…..>#%20"<>
posva
 
                location.hash = ‘# “<>' location.hash = encodeURI('# "<>’)
location.hash: ‘# “<>' https://<…..>#%20"<> location.hash: ‘#%20%22%3C%3E%60’
posva
 
                location.hash = ‘# “<>' location.hash = encodeURI('# "<>’)
location.hash: ‘# “<>' https://<…..>#%20"<> location.hash: ‘#%20%22%3C%3E%60’
https://<…..># “<>`
posva
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                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
 
                Encode you URL even if it looks bad encodeURI / decodeURI encodeURIComponent / decodeURIComponent Path and hash Query keys and values posva
 
                History { } path: ‘/movies/493922’, query: {}, hash: ” Communicate with the address bar Handle encoding Handle browser specific bugs/inconsistencies posva
 
                { { } path: ‘/movies/493922’, query: {}, hash: ” } path: ‘/movies/493922’, params: { id: ‘493922’}, name: ‘MovieDetail’, component: …, query: {}, hash: ” posva
 
                Matcher { { } path: ‘/movies/493922’, query: {}, hash: ” } path: ‘/movies/493922’, params: { id: ‘493922’}, name: ‘MovieDetail’, component: …, query: {}, hash: ” posva
 
                Matcher ‘/home’ /^/home$/i /^/movies/([^/]+)$/i ‘/movies/:id’ /^/movies/(\n+)$/i ‘/movies/:id(\n+)’ /^/movies/new$/i ‘/movies/new’ path-to-regexp posva
 
                Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i ‘/movies/new’ /^/movies/([^/]+)$/i /^/movies/(\n+)$/i /^/movies/new$/i posva
 
                Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i ‘/movies/new’ /^/movies/([^/]+)$/i /^/movies/(\n+)$/i /^/movies/new$/i posva
 
                Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i /^/movies/([^/]+)$/i ‘/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva
 
                Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i /^/movies/([^/]+)$/i ‘/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva
 
                Matcher ‘/home’ ‘/movies/:id’ ‘/movies/:id(\n+)’ ‘/movies/new’ /^/home$/i /^/movies/([^/]+)$/i ‘/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva
 
                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
 
                Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ ‘/movies/new’ posva
 
                Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ ‘/movies/new’ posva
 
                Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ posva
 
                Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ 70 posva
 
                Matcher 140 ‘/movies/new’ 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 ‘/home’ 70 + 70 70 posva
 
                Matcher 140 ‘/movies/new’ 70 + 70 135 ‘/movies/:id(\n+)’ 130 ‘/movies/:id’ 70 + 60 70 ‘/home’ 70 posva
 
                Matcher 140 ‘/movies/new’ 70 + 70 135 ‘/movies/:id(\n+)’ 70 + 60 130 ‘/movies/:id’ 70 + 60 70 ‘/home’ 70 + 5 posva
 
                140 70 + 70 135 70 + 60 130 70 + 60 70 70 ‘/movies/new’ + 5 posva
 
                Router { } path: ‘/movies/new’, params: {}, name: ‘MovieNew’, component: …, query: {}, hash: ” posva
 
                Router { } path: ‘/movies/new’, params: {}, name: ‘MovieNew’, component: …, query: {}, hash: ” View Component posva
 
                Router { } path: ‘/movies/new’, params: {}, name: ‘MovieNew’, component: …, query: {}, hash: ” View Component posva
 
                https://movies.com/search?genre=horror&page=1#filters History Matcher Router Components posva
 
                Thank you! posva
