[JS] jsx with diff (2-실패)

jiseong·2022년 5월 24일
0

T I Learned

목록 보기
253/291
post-custom-banner

이전 포스팅에 이어서 jsx에 diff를 적용해보는 과정으로 이번에는 컴포넌트 타입에 대한 처리를 해주려고 한다.

예시코드

그전에 테스트를 위한 예시코드의 수정이 필요하며 테스트해볼 상황은 크게 두가지이다.

  1. 루트 컴포넌트 리렌더링시 자식 컴포넌트도 리렌더링 (추가적인 최적화가 필요)
  2. 자식 컴포넌트만 리렌더링
class Todo extends Dj.Component {
  constructor(props: AttributeType){
    super(props);
    this.state = {
      value: ''
    }
    this.handleInput = this.handleInput.bind(this);
  }

  handleInput(event: Event){
    const { value } = event.target as HTMLInputElement;
    this.setState({
      ...this.state,
      value
    })
  }

  render() {
    const {books} = this.props;

    return (
      <div class="my-component">
        <input type='text' onInput={this.handleInput} value={this.state.value} />
        <button onClick={this.props.handleClick}>책추가</button>
        <ul>
          {books.map((book: IBook) => (
            <li>
              <input type="checkbox" class="toggle" checked={book.completed} />
              {book.content}
            </li>
          ))}
        </ul>
      </div>
    )
  }
}

class App extends Dj.Component {
  constructor(props: AttributeType){
    super(props);
    this.state = {
      books: [
        { id: 1, completed: false, content: 'star' },
        { id: 2, completed: true, content: 'rain' },
      ]
    }
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(event: MouseEvent) {
    this.setState({
      ...this.state,
      books: [
        { id: 1, completed: true, content: 'star' },
        { id: 2, completed: true, content: 'rain' },
        { id: 3, completed: false, content: 'you Dont know JS' },
      ]
    })
  }

  render() {
    const {books} = this.state;
    return (
      <div>
        <p>이것은 컴포넌트 리렌더링을 위한 것</p>
        <div>
          <Todo books={books} handleClick={this.handleClick} />
        </div>
      </div>
    )
  }
}

Dj.render(<App />, document.querySelector('#root'));

컴포넌트인 경우

우선, 컴포넌트인 경우 새로운 가상DOM으로 해당 컴포넌트 인스턴스를 생성하여 이전의 컴포넌트와 비교하는 함수를 호출하였다.

// Core/Dj/diff.ts
function nodeCompare(vDOM: IDom, container: Node | null , realDOM?: INode , idx: number = 0){
  // 생략...
  
  if(isComponentType(vDOM)){
    componentCompare(vDOM, realDOM, idx);
    return;
  }
  // 생략...
}

componentCompare함수는 새로운 가상DOM인 vDOM, 현재 화면에 렌더링되어있는 실제DOM인 realDOM 그리고 몇번째 자식인지를 구분하기 위한 idx를 매개변수를 받는 함수로 바벨에 의해 변환된 vDOM 객체를 컴포넌트 vDOM 객체로 한번 더 변환해주는 함수라고 생각하면 된다.

// componentCompare 함수
function componentCompare(vDOM: IDom, realDOM: INode, idx: number = 0){
  // 과거랑 현재랑 같은 컴포넌트라면
  const nextComponentVDOM = createComponent(vDOM);

  nodeCompare(nextComponentVDOM, realDOM.parentNode, realDOM, idx);
}

vDOM: 바벨에 의해 변환된 객체로 해당 정보를 이용하여 컴포넌트 vDOM 객체로 만들어야 한다.
nextComponentVDOM: 실제 보여져야 할 노드들의 정보를 담고 있는 객체

// 컴포넌트용 vDOM 객체를 만드는 함수
export function createComponent(vDOM: IDom) {
  if(typeof vDOM.type === 'string') return;

  const C =  vDOM.type;
  const component = new C(vDOM.attributes || {});
  const componentVDOM = component.render();
  componentVDOM.DJ_COMPONENT = component; // component 식별을 위한 것

  return componentVDOM;
}

함수 내부에서 컴포넌트 vDOM 객체로 변환해주는 작업을 거친 후 nodeCompare를 다시 호출해주면 책추가 버튼을 눌렀을 때, 정상적으로 diff알고리즘을 거친 후 필요한 요소만 변경할 수 있게된다.

결과

한참 부족한 내용들이 많지만 일단 여기까지 작성해주고나면 내가 고려했던 상황들은 정상적으로 처리되는 것을 확인할 수 있었다.

post-custom-banner

0개의 댓글