Menulis Router JavaScript Modern

Selamat siang teman!

Aplikasi sederhana, satu halaman berdasarkan React, Vue, atau JavaScript murni mengelilingi kami di mana-mana. "Satu halaman" yang bagus mengasumsikan mekanisme perutean yang tepat.

Perpustakaan seperti "navigo" atau "react-router" sangat berguna. Tetapi bagaimana cara kerjanya? Apakah kita perlu mengimpor seluruh perpustakaan? Atau cukupkah sebagian, katakanlah, 10%? Bahkan, Anda dapat dengan mudah menulis sendiri router yang cepat dan bermanfaat, itu akan membutuhkan sedikit waktu, dan program ini akan terdiri dari kurang dari 100 baris kode.

Persyaratan


Router kami harus:

  • ditulis dalam ES6 +
  • kompatibel dengan histori dan hash
  • perpustakaan dapat digunakan kembali

Biasanya, aplikasi web menggunakan satu instance router, tetapi dalam banyak kasus kami membutuhkan beberapa instance, jadi kami tidak akan dapat menggunakan singleton sebagai templat. Agar berfungsi, router kami membutuhkan properti berikut:

  • router: daftar router terdaftar
  • mode: hash atau histori
  • elemen root: elemen root aplikasi, jika kita berada dalam mode riwayat penggunaan
  • constructor: fungsi utama untuk membuat instance router baru

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

Menambah dan Menghapus Router


Menambah dan menghapus router dilakukan dengan menambahkan dan menghapus elemen array:

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

Mendapatkan jalur saat ini


Kita perlu tahu di mana kita berada dalam aplikasi pada titik waktu tertentu.

Untuk ini, kita perlu memproses kedua mode (sejarah dan hash). Dalam kasus pertama, kita perlu menghapus path ke elemen root dari window.location, di yang kedua - "#". Kami juga membutuhkan fungsi (clearSlash) untuk menghapus semua router (garis dari awal hingga selesai):

[...]

    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

Navigasi


Oke, kami memiliki API untuk menambah dan menghapus URL. Kami juga memiliki kesempatan untuk mendapatkan alamat saat ini. Langkah selanjutnya adalah menavigasi router. Kami bekerja dengan properti "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

Menyaksikan perubahan


Sekarang kita perlu logika untuk melacak perubahan alamat, baik menggunakan tautan atau menggunakan metode "navigasi" yang kita buat. Kami juga perlu memastikan bahwa halaman yang benar diberikan pada kunjungan pertama. Kita dapat menggunakan status aplikasi untuk mendaftarkan perubahan, namun, untuk tujuan belajar, kita akan melakukan ini dengan 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

Kesimpulan


Perpustakaan kami siap digunakan. Ini terdiri dari hanya 84 baris kode!

Contoh kode dan penggunaan di Github .

Terima kasih atas perhatian Anda.

All Articles