首页 > 解决方案 > 对如何重构和整理这个脚本感到困惑

问题描述

我有一个大的 JS 文件,我将在下面发布。基本上,我遇到了一个需要解决的问题,但我觉得我需要在找到问题的过程中重构和整理它。我的问题是,我不太确定如何整理它并可能将其拆分为更易于管理的块以及每个块如何相互交互的最佳选择是什么。

任何人都可以就我如何解决这个问题提供任何建议吗?

我知道我可以将每个部分移出到单独的文件等中,但我不太确定如何让每个文件以与它们都在一个文件中相同的方式与其他文件交互。

一些建议和指示会大大帮助我改进我的脚本。

谢谢。

我的脚本:

import URI from 'urijs'
import Handlebars from 'handlebars'
import InfiniteScroll from 'infinite-scroll'
import Flickity from 'flickity'
import { getOffsetTop } from './get-offset-top.js'
import MicroModal from 'micromodal'
import SlimSelect from 'slim-select'

// Define global variables
let infScroll = false
let infiniteScrollEnabled = false
let object = false
let selector = false
let url = false

function debounce(func, wait, immediate) {
    var timeout
    return function () {
        var context = this, args = arguments
        var later = function () {
            timeout = null
            if (!immediate) func.apply(context, args)
        }
        var callNow = immediate && !timeout
        clearTimeout(timeout)
        timeout = setTimeout(later, wait)
        if (callNow) func.apply(context, args)
    }
}



function toggleViewMoreButton() {
    if (document.querySelector('.pagination')) {
        document.querySelector('.view-more-button').style.display = 'inline-flex'
    } else {
        document.querySelector('.view-more-button').style.display = 'none'
    }
}



function refreshModals() {
    // Use whatever selector you're using for MicroModal triggers.
    const modalTrigger = 'data-micromodal-trigger'

    // Get all triggers.
    const modalTriggers = document.querySelectorAll(`[${modalTrigger}]`)

    modalTriggers.forEach(trigger => {
        // Get the attribute to save.
        const triggerElement = trigger.getAttribute(modalTrigger)

        // Remove the attribute briefly to clear memory/existing modals.
        trigger.removeAttribute(modalTrigger)

        // Immediately add it back.
        trigger.setAttribute(modalTrigger, triggerElement)

        // Re-initialize.
        MicroModal.init()
    })
}



function doInfiniteScroll() {
    if (document.querySelector(`.paginator--${object}s .next_page a`)) {
        let gridElement = document.querySelector(`.${object}-grid`)
        let requestPath = `.paginator--${object}s .next_page a`

        infScroll = new InfiniteScroll(gridElement, {
            path: requestPath,
            append: `.${object}-top-level`,
            scrollThreshold: false,
            status: '.page-load-status',
            button: '.view-more-button',
            history: false
        })

        infScroll.on('append', (response, path, items) => {
            // Refresh Modals
            refreshModals()

            // Manually push URL to the Turbolinks history
            Turbo.navigator.history.push(new URL(path));

            console.log(path)
        })

        infScroll.on('request', function (path) {
            console.log(path)
        })

        infiniteScrollEnabled = true
    }
}



function endInfiniteScroll() {
    if (infiniteScrollEnabled) {
        infScroll.destroy()

        infiniteScrollEnabled = false
    }
}



function updateGrid(url) {
    fetch(url)
        .then(response => response.json())
        .then(data => {
            let template = document.getElementById('park-and-coaster-grid-template').innerHTML
            let templateScript = Handlebars.compile(template)
            let html = templateScript(data)

            document.querySelector('.wacky-worm').innerHTML = html

            // Re enable InfiniteScroll
            doInfiniteScroll()

            // Refresh Modals
            refreshModals()

            // Show or Hide View More button
            toggleViewMoreButton()
        })

    // Add current filter settings to URL history (Remove page query if on first page)
    let queries = url.search(true)
    if (queries.page == 1) {
        url.removeSearch('page')
    }

    url.suffix('')

    Turbo.navigator.history.push(new URL(url));
}









document.addEventListener("turbo:load", function (e) {
    // Figure out whether we are dealing with Parks or Coasters
    if (document.body.classList.contains('parks-index') || document.body.classList.contains('coasters-index') || document.body.classList.contains('rides-index')) {
        object = document.querySelector('.wacky-worm').getAttribute('data-object')

        if (document.querySelector('.item-coaster_attributes')) {
            const slim_select = new SlimSelect({
                select: '.item-coaster_attributes',
                placeholder: 'All attributes',
                allowDeselectOption: true
            });
        }

        doInfiniteScroll()

        toggleViewMoreButton()

        // Pagination
        document.querySelector('.wacky-worm').addEventListener('click', (e) => {
            if (e.target.matches('.pagination li a')) {
                e.preventDefault()

                endInfiniteScroll()

                url = new URI(e.target.getAttribute('href'))
                // params = url.search(true)
                url.suffix('json')

                updateGrid(url)
            }
        })

        // If a year is selected then show the "New in <year>" checkbox automatically
        var url = new URI();
        let queries = url.search(true)
        if (queries.page == 1) {
            url.removeSearch('page')
        }

        if (queries.ridden_in) {
            let year = queries.ridden_in
            let new_in_ridden_in_year_element = document.querySelector('.item-new-in-ridden-in-year')

            new_in_ridden_in_year_element.querySelector('.update-year-here').innerHTML = 'New in ' + year
            new_in_ridden_in_year_element.style.visibility = 'visible'
        }





        // Order
        document.querySelector('select.item-sort').addEventListener('change', (e) => {
            let selected = e.target.options[e.target.selectedIndex].value
            url = new URI()

            endInfiniteScroll()

            url.removeQuery('sort')
            url.addQuery('sort', selected)
            url.removeSearch('page')
            url.addQuery('page', 1)
            url.suffix('json')

            updateGrid(url)
        })




        // Material
        selector = 'select.item-material'
        if (document.querySelector(selector)) {
            document.querySelectorAll(selector).forEach(radio => {
                radio.addEventListener('change', (e) => {
                    let selected = e.target.value
                    url = new URI()

                    endInfiniteScroll()

                    if (selected == 'all') {
                        url.removeSearch('material')
                        url.suffix('json')

                        updateGrid(url)
                    } else {
                        url.removeSearch('material')
                        url.addQuery('material', selected)
                        url.removeSearch('page')
                        url.addQuery('page', 1)
                        url.suffix('json')

                        updateGrid(url)
                    }
                })
            })
        }



        // Milestones
        selector = '[type="checkbox"][name="milestones"]'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                url = new URI()

                endInfiniteScroll()

                if (e.target.checked) {
                    url.removeSearch('milestones')
                    url.addQuery('milestones', 1)
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeSearch('milestones')
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }



        // Soundtracks
        selector = '[type="checkbox"][name="soundtracks"]'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                url = new URI()

                endInfiniteScroll()

                if (e.target.checked) {
                    url.removeSearch('soundtracks')
                    url.addQuery('soundtracks', 1)
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeSearch('soundtracks')
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }



        // Reverse
        selector = '[type="checkbox"][name="reverse"]'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                url = new URI()

                endInfiniteScroll()

                if (e.target.checked) {
                    url.removeSearch('reverse')
                    url.addQuery('reverse', 1)
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeSearch('reverse')
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }



        // Countries
        selector = 'select.item-countries'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                let selected = e.target.options[e.target.selectedIndex].value
                url = new URI()

                endInfiniteScroll()

                if (selected == 'all') {
                    url.removeSearch('country')
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeSearch('country')
                    url.addQuery('country', selected)
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }



        // Parks
        selector = 'select.item-parks'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                let selected = e.target.options[e.target.selectedIndex].value
                url = new URI()

                endInfiniteScroll()

                if (selected == 'all') {
                    url.removeSearch('park')
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeSearch('park')
                    url.addQuery('park', selected)
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }



        // Manufacturers
        selector = 'select.item-manufacturers'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                let selected = e.target.options[e.target.selectedIndex].value
                url = new URI()

                endInfiniteScroll()

                if (selected == 'all') {
                    url.removeSearch('manufacturer')
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeSearch('manufacturer')
                    url.addQuery('manufacturer', selected)
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }



        // Park Chains
        selector = 'select.item-parkchains'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                let selected = e.target.options[e.target.selectedIndex].value
                url = new URI()

                endInfiniteScroll()

                if (selected == 'all') {
                    url.removeSearch('parkchain')
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeSearch('parkchain')
                    url.addQuery('parkchain', selected)
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }



        // Letter
        selector = '[type="radio"][name="letter"]'
        if (document.querySelector(selector)) {
            document.querySelectorAll(selector).forEach(radio => {
                radio.addEventListener('change', (e) => {
                    let selected = e.target.value
                    url = new URI()

                    endInfiniteScroll()

                    if (selected == 'all') {
                        url.removeSearch('letter')
                        url.suffix('json')

                        updateGrid(url)
                    } else {
                        url.removeSearch('letter')
                        url.addQuery('letter', selected)
                        url.removeSearch('page')
                        url.addQuery('page', 1)
                        url.suffix('json')

                        updateGrid(url)
                    }
                })
            })
        }



        // Ridden in Year
        selector = 'select.item-ridden-in'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                let selected = e.target.options[e.target.selectedIndex].value
                url = new URI()

                let new_in_ridden_in_year_element = document.querySelector('.item-new-in-ridden-in-year')

                // Set New in RIY Checkbox back to unchecked
                new_in_ridden_in_year_element.querySelector('[type="checkbox"]').checked = false

                endInfiniteScroll()

                url.removeQuery('new_in_riy')

                if (selected == 'all') {
                    new_in_ridden_in_year_element.style.visibility = 'hidden'

                    url.removeQuery('ridden_in')
                    url.suffix('json')

                    updateGrid(url)
                    console.log(url)
                } else {
                    new_in_ridden_in_year_element.style.visibility = 'visible'
                    new_in_ridden_in_year_element.querySelector('.update-year-here').innerHTML = 'New in ' + selected

                    url.removeQuery('ridden_in')
                    url.addQuery('ridden_in', selected)
                    url.removeQuery('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                    console.log(url)
                }
            })
        }



        // New in Ridden in Year
        selector = '[type="checkbox"][name="new_in_riy"]'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                url = new URI()

                endInfiniteScroll()

                if (e.target.checked) {
                    url.removeQuery('new_in_riy')
                    url.addQuery('new_in_riy', 1)
                    url.removeQuery('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeQuery('new_in_riy')
                    url.removeQuery('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }

        // Coaster Styles
        selector = 'select.item-coaster_styles'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                let selected = e.target.options[e.target.selectedIndex].value
                url = new URI()

                endInfiniteScroll()

                if (selected == 'all') {
                    url.removeSearch('coaster_styles')
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeSearch('coaster_styles')
                    url.addQuery('coaster_styles', selected)
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }

        // Coaster Attributes
        selector = 'select.item-coaster_attributes'
        if (document.querySelector(selector)) {
            document.querySelector(selector).addEventListener('change', (e) => {
                // let selected = e.target.options[e.target.selectedIndex].value

                // Convert all of the options to an array
                // Then, get an array of only the options that are selected
                // Then, get an array of the selected option values
                let selected = Array.from(document.querySelector(selector).options).filter(function (option) {
                    return option.selected;
                }).map(function (option) {
                    return option.value;
                })

                selected = selected.join('|');

                url = new URI()

                endInfiniteScroll()

                if (selected == '') {
                    url.removeSearch('coaster_attributes')
                    url.suffix('json')

                    updateGrid(url)
                } else {
                    url.removeSearch('coaster_attributes')
                    url.addQuery('coaster_attributes', selected)
                    url.removeSearch('page')
                    url.addQuery('page', 1)
                    url.suffix('json')

                    updateGrid(url)
                }
            })
        }
    }
})

更新:

我的主文件中有这段代码:

export let infScroll = false
export let infiniteScrollEnabled = false

// Enable Infinite Scroll on grid items
if (!(infiniteScrollEnabled)) {
  startInfiniteScroll()
}

我的 startInfiniteScroll 文件如下所示:

import InfiniteScroll from 'infinite-scroll'
import { object } from '../park-and-coaster-filters'
import { infScroll } from '../park-and-coaster-filters'
import { infiniteScrollEnabled } from '../park-and-coaster-filters'

export default function startInfiniteScroll() {
    console.log('start startInfiniteScroll')

    console.log(object)

    infScroll = new InfiniteScroll(`.${object}-grid`, {
        path: '.next_page a',
        append: `.${object}-top-level`,
        scrollThreshold: false,
        status: '.page-load-status',
        button: '.view-more-button',
        history: 'push'
    })

    console.log('end startInfiniteScroll')

    infiniteScrollEnabled = true
}

基本上,在运行此程序时,我在控制台中收到错误消息:

未捕获的类型错误:无法设置 # 的属性 infScroll,它在 startInfiniteScroll 处只有一个 getter

有任何想法吗?

标签: javascript

解决方案


如果不了解整个代码库,就很难帮助重构代码。也就是说,我有一些关于如何改进代码的有用提示:

  • 这些selector =块是相同的,应该写成一个函数。
  • 作为一般规则:如果您多次编写相同的代码,则它属于一个函数。
  • 尝试将您的代码拆分为更小的函数。每个功能应该只做一件事。这也使得调试和管理变得更加容易。
  • 写评论。您可以使用块注释来描述每个函数的用途、参数和返回值:
/**
 * Multiplies a number by two.
 * @param {Number} val - Number to be doubled
 * @return {Number} The number multiplied by two
 */
function double(val) {
  return val * 2
}

这是一篇关于使用块评论的精彩文章

  • 考虑学习 TypeScript,因为它可以帮助您在编写代码时调试代码。

希望这将帮助您从现在开始使您的代码变得更好。干杯!


编辑

如果您希望将部分代码移动到不同的文件中,则必须使用export...import.

// utilityFunctions.js

export function double (val) {
  return val * 2
}

在你的主脚本中导入这个函数

// index.js

import { double } from './utilityFunctions'

console.log(double(2))
// returns 4

推荐阅读