javascript - 对如何重构和整理这个脚本感到困惑
问题描述
我有一个大的 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
有任何想法吗?
解决方案
如果不了解整个代码库,就很难帮助重构代码。也就是说,我有一些关于如何改进代码的有用提示:
- 这些
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
推荐阅读
- c++ - 声明但不定义未使用的函数是否合法?
- php - 此代码是否有错误,因为它正确输出成功消息但数据库中没有任何更新
- python - KeyError: 1 问题,有什么问题?
- java - 代码完成后输出一堆零
- google-maps - 在 Private Google Access 环境中为我们限制 Google Maps API 密钥
- c# - 成功身份验证后,客户端浏览器被重定向回 ADFS 服务
- php - CRUD 中的 PHP 删除不会从数据库中删除项目
- java - 添加工具栏后android应用程序崩溃
- asp.net-identity - IdentityServer4 - 谷歌浏览器没有删除所有 cookie
- javascript - 使用 Lodash 通过指定属性值将数据转换为对象属性而不是集合