簡體   English   中英

即使導入了我的函數,該函數也無法在另一個函數范圍之外工作

[英]My function not working outside of another function scope even though I have imported it

我試圖找出下面的代碼是怎么回事,我已經使用import { getTodos } from './todos'將函數import { getTodos } from './todos'到另一個名為views.js文件中。 我想打電話給getTodos中的頂級views.js ,但我得到一個錯誤,它不是一個函數(它是undefined )。 但是,一旦我嘗試在同一模塊的函數中使用getTodos ,它就會突然起作用! 為什么突然不再定義它了,我該如何修復代碼,以便將其定義在我希望的位置?

我的todos.js文件包括定義getTodos函數:

import uuidv4 from 'uuid/v4'
import { renderTodos } from './views'

let todos = []

const loadTodos = () => {
    const todosJSON = localStorage.getItem('todos')
    try {
        return todosJSON ? JSON.parse(todosJSON) : []
    } catch (e) {
        return []
    }
}


todos = loadTodos()

const getTodos = () => todos


const saveTodos = () => {
    localStorage.setItem('todos', JSON.stringify(todos))
}


const createTodo = (text) => {

    if (text.length > 0) {
        todos.push({
            id: uuidv4(),
            text,
            completed: false
        })
        saveTodos()
        renderTodos()
    }

}

const removeTodo = (id) => {
    const todoIndex = todos.findIndex((todo) => todo.id === id)

    if (todoIndex > -1) {
        todos.splice(todoIndex, 1)
        saveTodos()
    }
}

const toggleTodo = (id) => {
    const todo = todos.find((todo) => todo.id === id)

    if (todo) {
        todo.completed = !todo.completed
        saveTodos()
    }
}



export { loadTodos, getTodos, createTodo, removeTodo, toggleTodo }


我試圖加載getTodos的我的views.js文件然后使用它:

import { getTodos, saveTodos, toggleTodo, removeTodo  } from './todos'
import { getFilters } from './filters'

const todos = getTodos()
const renderTodos = () => {

    const filters = getFilters()
    const todosEl = document.querySelector('#todos')

    const filteredTodos = todos.filter((todo) => {
        const searchTextMatch = todo.text.toLowerCase().includes(filters.searchText.toLowerCase())
        const hideCompletedMatch = !filters.hideCompleted || !todo.completed

        return searchTextMatch && hideCompletedMatch
    })

    const incompleteTodos = filteredTodos.filter((todo) => !todo.completed)

    todosEl.innerHTML = ''
    todosEl.appendChild(generateSummaryDOM(incompleteTodos))

    if (todos.length > 0) {
        filteredTodos.forEach((todo) => {
            todosEl.appendChild(generateTodoDOM(todo))
        })
    } else {
        const emptyMessage = document.createElement('p')
        emptyMessage.textContent = 'No to-dos to show, go ahead and add some!'
        emptyMessage.classList.add('empty-message')
        todosEl.appendChild(emptyMessage)
    }
}


const generateTodoDOM = (todo) => {

    const todoEl = document.createElement('label')
    const containerEl = document.createElement('div')
    const checkbox = document.createElement('input')
    const removeButton = document.createElement('button')
    const span = document.createElement('span')


    // Setup Container
    todoEl.classList.add('list-item')
    containerEl.classList.add('list-item__container')
    todoEl.appendChild(containerEl)

    // Setup Remove Button
    removeButton.textContent = 'remove'
    removeButton.classList.add('button', 'button--text')
    removeButton.addEventListener('click', () => {
        removeTodo(todo.id)
        renderTodos()
    })
    span.textContent = todo.text


    // Setup Checkbox
    checkbox.setAttribute('type', 'checkbox')
    checkbox.checked = todo.completed
    checkbox.addEventListener('change', (e) => {
        toggleTodo(todo.id)
        renderTodos()
    })
    containerEl.appendChild(checkbox)


    containerEl.appendChild(span)
    todoEl.appendChild(removeButton)

    return todoEl
}

const generateSummaryDOM = (incompleteTodos) => {
    const summary = document.createElement('h2')
    summary.classList.add('list-title')
    const plural =  incompleteTodos.length === 1 ? '' : 's'
    summary.textContent = `You have ${incompleteTodos.length} todo${plural} left`
    return summary
}

// Make sure to set up the exports

export { generateSummaryDOM, renderTodos, generateTodoDOM }

index.js文件:

import { createTodo } from './todos'
import { renderTodos } from './views'
import { setFilters } from './filters'

// Render initial todos

renderTodos()

// Set up search text handler

document.querySelector('#search-todos').addEventListener('input', (e) => {
    setFilters({
        searchText: e.target.value
    })
    renderTodos()
})

// Set up checkbox handler

document.querySelector('#hide-completed').addEventListener('change', (e) => {
    setFilters({
        hideCompleted: e.target.checked
    })
    renderTodos()
})

// Set up form submission handler

document.querySelector('#add-todo').addEventListener('submit', (e) => {
    e.preventDefault()
    const text = e.target.elements.addTodoText.value.trim()
    createTodo(text)
    e.target.elements.addTodoText.value = ''
})

chrome調試器控制台中出現的錯誤是:

views.js:4 Uncaught TypeError: (0 , _todos.getTodos) is not a function
    at Object../src/views.js (views.js:4)
    at __webpack_require__ (bootstrap:19)
    at Object../src/todos.js (todos.js:2)
    at __webpack_require__ (bootstrap:19)
    at Object../src/index.js (index.js:1)
    at __webpack_require__ (bootstrap:19)
    at Object.0 (bundle.js:20254)
    at __webpack_require__ (bootstrap:19)
    at bootstrap:68
    at bootstrap:68

我只是想了解為什么會發生這種奇怪的行為,因為一旦我在renderTodos使用了getTodos,它實際上就可以工作了,而且我沒有收到任何錯誤,例如:

const renderTodos = () => {
    const todos = getTodos()
    // Other stuffs
}

我正在使用Babel和Webpack。

主要問題是您具有循環依賴關系。 對於可以說明問題的更簡單的示例,請考慮:

// foo.js
import bar from './bar';
const x = bar();
export default () => x;

// bar.js
import foo from './foo';
const y = foo();
export default () => y;

在上面的代碼中,就像您的代碼一樣,您有使用導入的模塊,其中所述導入取決於從當前模塊中導入什么。 如果A從B導入,並且B也從A導入,那么您需要確保A和B在它們的兩個頂級代碼完全完成之前都不會使用彼此的任何東西-否則,您不能依賴於當時正在定義其他導入。

最好重構代碼以某種方式消除循環依賴。

我喜歡使用的一種模式是確保模塊中的頂級代碼不會啟動任何東西-而是,當一切最終都可以從入口點開始時 (在這里看起來是index.js ),這很好。 一個快速的解決方案是為每個模塊導出運行初始化代碼的功能 ,而不是將該代碼放在頂層。 例如:

// views.js

import { getTodos, saveTodos, toggleTodo, removeTodo  } from './todos'
import { getFilters } from './filters'

let todos;
const init = () => {
  todos = getTodos();
};
// ... everything else
export { generateSummaryDOM, renderTodos, generateTodoDOM, init }

然后,在導入所有內容之后,立即開始index.js import並調用init

幸運的是,因為todos.js僅調用(跨模塊,進口) renderTodos在其createTodo功能, createTodo出口,但不叫todos.js ,它並不需要改變。

另一個(可能更好)選擇是去除todos.js上的扶養renderTodos todos.js ,您目前只使用renderTodos在其createTodo功能。 考慮更改thign,以便createTodo創建和保存待辦事項,但不使用renderTodos渲染它-而是查找使用createTodo位置(看起來僅在index.js ),並使用index.js代替renderTodos

// todos.js

// do not import renderTodos here

// ...
const createTodo = (text) => {
    if (text.length > 0) {
        todos.push({
            id: uuidv4(),
            text,
            completed: false
        })
        saveTodos()
    }
}

// index.js

// ...
document.querySelector('#add-todo').addEventListener('submit', (e) => {
    e.preventDefault()
    const text = e.target.elements.addTodoText.value.trim()
    createTodo(text)
    renderTodos();
    e.target.elements.addTodoText.value = ''
})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM