A(nother) todo: 버전 1.1

19314 단어
참고 이 게시물은 this branch을 참조합니다.

프로젝트의 초기 버전은 작동하지만 상황이 좋지 않습니다.

우선, 응용 프로그램 전체가 main.js 파일 안에 있으며 지금은 문제가 되지 않을 수 있지만 응용 프로그램이 상대적으로 단순할 때는 나중에 추가할 때 악몽이 될 것입니다. 점점 더 많은 기능. 나는 README.md 에 좋은 요약을 보관해 두었지만 진행 상황에 대해 이야기할 수 있도록 복사/붙여넣기를 할 것입니다.

This branch is meant to be "I've been working in this project for awhile now, and I would like to refactor it to do some things differently."

The things that are different here

  1. The application now does not all live inside the main.js file. Instead all of the functionality has been put into it's own files, with the main.js file simply serving as the aggregator of those events.
  2. The renderer and the store have been largely separated. The render function here is what would be called a "Pure" function -- it keeps no track of state and will render the todos based solely off their values into the DOM.
  3. Largely there are no direct interactions with the todo variable outside of the todo.store.js file with 1 exception. I could imagine that a slightly more knowledgeable engineer would want to persist information between browser refreshes, and as such takes advantage of the localStorage. However, trying to pull this data from storage and use our todos store API was likely a bit more complex. I imagine someone having had a bit of time to fiddle around with adding the items in a loop, but "this way was faster and just worked" is definitely something I could see myself in my earlier years saying.

However, this definitely has some downsides. The largest thing is that since we are no longer doing this all in the main.js file there are multiple places where we need to call document.querySelector('#todo-list-container'), which even for a relatively small app was rather annoying to write.

The question then is: how do we make it so that we only have to call the query selector once? There are a few ways, if you are starting off with Javascript and ESM think of a few and test them out.

Beyond that, I think a lot of developers after looking at this code will say something like "I just feel like there should be a way to get the rendering function to run when the data updates... there has to be some way to do that, right?"

Also at this point many people are probably feeling the itch of wanting to make the UI a bit prettier, with a navbar and things...



이제 src/ 디렉토리가 어떻게 생겼는지 살펴보겠습니다.



글쎄, 확실히 더 많은 파일이 있습니다!

각 파일은 좀 더 구체적입니다. todos 로 시작하는 두 개의 파일이 있지만 하나는 "저장소"( todos.store.js )이고 다른 하나는 "렌더링"( todos.render.js )입니다. 저장소는 이제 실제 todos 배열과 관련된 작업을 처리합니다. 즉, 할일 추가, 제거 및 완료 표시가 모두 하나의 논리적 위치에 저장됩니다.

// todos.store.js
import renderTodos, { itemCache } from './todos.render';

/** @type {import('./todos.render').todo[]} */
const todos = [];
// Get an item from local storage
const stored = localStorage.getItem('todos__visited');

// if we do not have a value from our localStorage, add an example to the array.
if (!stored) {
  todos.push({
    title: 'Example todo',
    done: false,
    description: 'Extra details you may want the user to know.'
  });
  // make sure to set the storage so that other people do not see it again.
  localStorage.setItem('todos__visited', 'true');
}

export default todos;
/**
 * 
 * @param {string} title
 * @param {stirng} description
 * @returns {boolean} true if added / false if not.
 */
export function addTodo(title, description = null) {
  try {
    todos.push({ title, description: description || '', done: false });
    // render tick
    renderTodos(document.querySelector('#todo-list-container'), todos);
    return true;
  } catch (e) {
    return false;
  }

}

/**
 * 
 * @param {number} todo the index of the todo
 * @returns {-1 | 0 | 1} 1 for success, 0 for failure, -1 if the item could not be found. Helpful for debugging and not much else.
 */
export function removeTodo(todo) {
  // if we do not have this todo, don't try anything further
  if (!todos[todo]) return -1;
  // try to do this
  try {
    (itemCache.get(todos[todo]) || []).forEach(el => el && el.remove && el.remove());
    todos.splice(todo, 1);
    renderTodos(document.querySelector('#todo-list-container'), todos);
    return 1;
  } catch (e) {
    return 0;
  }
  // re-render
}
/**
 * 
 * @param {number} index 
 * @returns {boolean} True if toggled, false if there was an error.
 */
export function toggleTodo(index) {
  if (!todos[index]) return false;
  todos[index].done = !todos[index].done;
  renderTodos(document.querySelector('#todo-list-container'), todos);
}

.render. 파일은 데이터를 가져와 제공된 <dl> 요소로 렌더링하는 데에만 집중합니다.

그 외에는 이 지점에 대해 완전히 다른 점은 없습니다. "하지만 작동합니다!""글쎄, 이제 상황이 약간 구분되고 우려 사항이 분리되었습니다."

// todos.render.js
import styles from './styles.module.css';
/**
 * @typedef todo
 * @property {string} title
 * @property {boolean} done
 * @property {string} description
 */

/** @type {Map<todo, [HTMLElement, HTMLElement]} */
export const itemCache = new Map();

/**
 * @param {HTMLDListElement} todo
 * @param {todo[]} todos 
 * @returns {void} 
 */
export default function renderTodos(todo, todos) {
  const els = todos.flatMap((todo, index) => {
    // either we can grab this item from the cache OR we create a new one.
    const [dt, dd] = itemCache.get(todo) || [document.createElement('dt'), document.createElement('dd')];
    if (!itemCache.has(todo)) {
      // set the innerHTML, along with creating the delete span.
      const span = document.createElement('span');
      // Very generic close button
      span.innerHTML = '&times;'
      span.setAttribute('role', 'delete');
      dt.append(
        document.createTextNode(todo.title),
        span
      );

      // Add necessary classes.
      dt.classList.add(styles['todo-list-item']);
      dt.classList.toggle(styles['todo-list-complete'], todo.done);

      //update description element if we have a description
      if (todo.description) {
        dd.innerHTML = todo.description;
      }
      itemCache.set(todo, [dt, dd]);
    }

    dt.dataset.key = dd.dataset.key = index;
    dt.classList.toggle(styles['todo-list-complete'], todo.done);

    return [dt, dd];
  });
  // store the data so that it persists between sessions.
  localStorage.setItem('todos__data', JSON.stringify(todos));
  // append the items to the list.
  todo.append(...els);
}



gitlab 저장소를 확인하십시오. 나는 "앱"의 실시간 미리보기를 시작하기 위해 노력하고 있지만 여기의 작업 흐름은 약간 이상합니다.

좋은 웹페이지 즐겨찾기