Bom dia amigosAplicativos simples de uma página baseados em React, Vue ou JavaScript puro nos cercam por toda parte. Uma boa "página única" pressupõe um mecanismo de roteamento apropriado.Bibliotecas como "navigo" ou "react-router" são muito úteis. Mas como eles funcionam? Precisamos importar a biblioteca inteira? Ou é suficiente, em parte, por exemplo, 10%? De fato, você pode escrever facilmente um roteador rápido e útil, isso levará um tempo e o programa consistirá em menos de 100 linhas de código.Exigências
Nosso roteador deve ser:- escrito em ES6 +
- compatível com história e hash
- biblioteca reutilizável
Normalmente, um aplicativo Web usa uma instância de um roteador, mas em muitos casos precisamos de várias instâncias, portanto não poderemos usar um singleton como modelo. Para funcionar, nosso roteador requer as seguintes propriedades:- roteadores: lista de roteadores registrados
- mode: hash ou histórico
- elemento raiz: o elemento raiz do aplicativo, se estiver no modo de uso histórico
- constructor: a principal função para criar uma nova instância do roteador
class Router {
routes = []
mode = null
root = '/'
constructor(options) {
this.mode = window.history.pushState ? 'history' : 'hash'
if (options.mode) this.mode = options.mode
if (options.root) this.root = options.root
}
}
export default Router
Adicionando e removendo roteadores
A adição e remoção de roteadores é feita adicionando e removendo elementos da matriz:class Router {
routes = []
mode = null
root = '/'
constructor(options) {
this.mode = window.history.pushState ? 'history' : 'hash'
if (options.mode) this.mode = options.mode
if (options.root) this.root = options.root
}
add = (path, cb) => {
this.routes.push({
path,
cb
})
return this
}
remove = path => {
for (let i = 0; i < this.routes.length; i += 1) {
if (this.routes[i].path === path) {
this.routes.slice(i, 1)
return this
}
}
return this
}
flush = () => {
this.routes = []
return this
}
}
export default Router
Obtendo o caminho atual
Precisamos saber onde estamos no aplicativo em um determinado momento.Para isso, precisamos processar os dois modos (histórico e hash). No primeiro caso, precisamos remover o caminho para o elemento raiz de window.location, no segundo - "#". Também precisamos da função (clearSlash) para remover todos os roteadores (linhas do início ao fim):[...]
clearSlashes = path =>
path
.toString()
.replace(/\/$/, '')
.replace(/^\//, '')
getFragment = () => {
let fragment = ''
if (this.mode === 'history') {
fragment = this.clearSlashes(decodeURI(window.location.pathname + window.location.search))
fragment = fragment.replace(/\?(.*)$/, '')
fragment = this.root !== '/' ? fragment.replace(this.root, '') : fragment
} else {
const match = window.location.href.match(/#(.*)$/)
fragment = match ? match[1] : ''
}
return this.clearSlashes(fragment)
}
}
export default Router
Navegação
Ok, temos uma API para adicionar e remover URLs. Também temos a oportunidade de obter o endereço atual. O próximo passo é navegar pelo roteador. Trabalhamos com a propriedade "mode":[...]
getFragment = () => {
let fragment = ''
if (this.mode === 'history') {
fragment = this.clearSlashes(decodeURI(window.location.pathname + window.location.search))
fragment = fragment.replace(/\?(.*)$/, '')
fragment = this.root !== '/' ? fragment.replace(this.root, '') : fragment
} else {
const match = window.location.href.match(/#(.*)$/)
fragment = match ? match[1] : ''
}
return this.clearSlashes(fragment)
}
navigate = (path = '') => {
if (this.mode === 'history') {
window.history.pushState(null, null, this.root + this.clearSlashes(path))
} else {
window.location.href = `${window.location.href.replace(/#(.*)$/, '')}#${path}`
}
return this
}
}
export default Router
Observando as mudanças
Agora precisamos de lógica para rastrear as alterações de endereço, usando um link ou usando o método "navegar" que criamos. Também precisamos garantir que a página correta seja renderizada na primeira visita. Poderíamos usar o estado do aplicativo para registrar alterações, no entanto, para fins de estudo, faremos isso com setInterval:class Router {
routes = [];
mode = null;
root = "/";
constructor(options) {
this.mode = window.history.pushState ? "history" : "hash";
if (options.mode) this.mode = options.mode;
if (options.root) this.root = options.root;
this.listen();
}
[...]
listen = () => {
clearInterval(this.interval)
this.interval = setInterval(this.interval, 50)
}
interval = () => {
if (this.current === this.getFragment()) return
this.current = this.getFragment()
this.routes.some(route => {
const match = this.current.match(route.path)
if (match) {
match.shift()
route.cb.apply({}, match)
return match
}
return false
})
}
}
export default Router
Conclusão
Nossa biblioteca está pronta para uso. Consiste em apenas 84 linhas de código!Exemplo de código e uso no Github .Obrigado pela atenção.