성능 향상을 위해 React 이벤트 탐지기를 캐시합니다.

JavaScript에서 충분히 인식되지 않은 개념은 대상과 함수가 어떻게 인용되는지, 이것이 React 성능에 직접적인 영향을 미친다는 것이다.완전히 같은 두 함수를 만들려면, 그것들은 여전히 같지 않다.직접 해보기:
const functionOne = function() { alert('Hello world!'); };
const functionTwo = function() { alert('Hello world!'); };
functionOne === functionTwo; // false
그러나 기존 함수에 변수를 할당하면 차이점을 봅니다.
const functionThree = function() { alert('Hello world!'); };
const functionFour = functionThree;
functionThree === functionFour; // true
객체의 작업 방식이 동일합니다.
const object1 = {};
const object2 = {};
const object3 = object1;
object1 === object2; // false
object1 === object3; // true
만약 네가 다른 언어의 경험이 있다면, 너는 지침에 익숙해질 것이다.여기서 발생하는 일은 대상을 만들 때마다 장치에 메모리를 분배하는 것이다.내가 object1 = {}이라고 말했을 때, 나는 사용자의 RAM에 object1을 위한 바이트를 만들었다.object1을 RAM에 대한 키 값을 포함하는 주소로 상상할 수 있습니다.object2 = {}이라고 말할 때, 나는 사용자의 RAM에 object2에 전문적으로 사용되는 다른 바이트 블록을 만들었다.object1의 주소와 object2의 주소가 일치합니까?아니오. 이것이 바로 이 두 변수의 등식 검사가 통과되지 않은 이유입니다.그것들의 키 값은 완전히 같을 수 있지만, 메모리에 있는 주소는 다르다. 이것이 바로 비교할 부분이다.
내가 object3 = object1의 값을 부여했을 때, 나는 object3의 값을 object1의 주소로 부여했다.그것은 새로운 대상이 아니다.그것은 메모리의 같은 위치이다.다음과 같이 확인할 수 있습니다.
const object1 = { x: true };
const object3 = object1;
object3.x = false;
object1.x; // false
이 예에서, 나는 메모리에 대상을 만들고 object1에 분배했다.그리고 나는 object3을 메모리의 같은 주소에 분배할 것이다.변이 object3을 통해 나는 메모리에서 이 위치의 값을 바꾸었다. 이것은 메모리에서 이 위치에 대한 모든 다른 인용도 바뀐다는 것을 의미한다.object1은 메모리의 위치를 가리키며 변경된 값이 있습니다.
초급 개발자에게 이것은 매우 흔히 볼 수 있는 잘못으로 자신의 깊이 있는 강좌가 필요할 가능성이 높다.그러나 본 강좌는 React 성능에 관한 것으로 경력이 높은 개발자라도 성능에 영향을 미칠 수 있다. 왜냐하면 그들은 변수 인용의 의미를 전혀 고려하지 않기 때문이다.
이것은 React와 어떤 관계가 있습니까?React는 처리 시간을 절약하여 성능을 향상시키는 스마트 방법이 있습니다. 만약에 Pure Component의 도구와 상태가 변하지 않는다면 render의 출력도 변하지 않을 것입니다.분명히 모든 것이 평등하다면, 모든 것은 변하지 않을 것이다.만약 아무런 변경이 없다면, render은 반드시 같은 출력을 되돌려야 하기 때문에, 우리는 그것을 실행할 필요가 없다.이것이 바로 왜 반응이 신속한가 하는 것이다.필요할 때만 렌더링됩니다.
React에서 도구와 상태가 JavaScript와 동일한지 확인 — == 연산자와 비교하기만 하면 됩니다.React는 객체가 동일한지 여부를 결정하기 위해 얕거나 깊게 비교되지 않습니다.얕은 층 비교는 비교 대상의 키 값 쌍을 설명하는 용어로 메모리 주소를 비교하는 것이 아니다.깊이가 더욱 깊어진다. 만약에 키 값이 중의 어떤 값도 대상이라면 이런 키 값이 맞다는 것도 이상한 느낌이다.React는 둘 다 하지 않습니다. 인용이 같은지 확인하는 것뿐입니다.
구성 요소의 도구를 { x: 1 }에서 다른 대상 { x: 1 }으로 변경하려면 메모리에서 참조하는 위치가 다르기 때문에 React가 다시 렌더링됩니다.구성 요소의 도구를 object1(위쪽)에서 object3으로 변경하려면 두 대상이 같은 참조이기 때문에 React는 다시 렌더링되지 않습니다.
함수는 JavaScript에서 동일하게 처리됩니다.React가 다른 메모리 주소가 있는 동일한 함수를 수신하면 다시 렌더링됩니다.React가 같은 함수 참조를 수신하는 경우에는 그렇지 않습니다.
불행하게도 이것은 내가 코드 심사 기간에 만난 흔한 장면이다.
class SomeComponent extends React.PureComponent {

  get instructions() {
    if (this.props.do) {
      return 'Click the button: ';
    }
    return 'Do NOT click the button: ';
  }

  render() {
    return (
      <div>
        {this.instructions}
        <Button onClick={() => alert('!')} />
      </div>
    );
  }
}
이것은 매우 간단한 구성 요소다.버튼을 누르면 경보가 울린다.do={true}do={false} 또는 SomeComponent 아이템으로 제어할 수 있습니다.
여기서 발생하는 일은 매번 SomeComponent을 다시 렌더링하는 것이다(예를 들어 do에서 true으로 전환), false도 다시 렌더링하는 것이다!Button 프로세서는 완전히 같지만 onClick 호출할 때마다 생성됩니다.렌더링할 때마다 메모리에 새 함수 (렌더링 함수에서 생성된 것이기 때문에) 를 만들고 메모리에 있는 새 주소에 대한 새 인용을 render에 전달하며 <Button /> 구성 요소를 다시 렌더링합니다. 출력에 변경된 것이 전혀 없지만.

고치다


함수가 구성 요소에 의존하지 않으면 (Button 상하문이 없음) 구성 요소 외부에서 정의할 수 있습니다.구성 요소의 모든 실례는 함수가 모든 상황에서 같기 때문에 같은 함수를 사용합니다.
const createAlertBox = () => alert('!');

class SomeComponent extends React.PureComponent {

  get instructions() {
    if (this.props.do) {
      return 'Click the button: ';
    }
    return 'Do NOT click the button: ';
  }

  render() {
    return (
      <div>
        {this.instructions}
        <Button onClick={createAlertBox} />
      </div>
    );
  }
}
이전 예와 달리 thiscreateAlertBox 동안 메모리의 동일한 위치에 대한 동일한 참조를 유지합니다.따라서 render을 다시 렌더링할 필요가 없습니다.Button은 작고 빠르게 렌더링되는 구성 요소일 수 있지만, 대형, 복잡하고 렌더링 속도가 느린 구성 요소에서 이러한 내연 정의를 볼 수 있으며, React 응용 프로그램을 곤경에 빠뜨릴 수 있습니다.렌더링 방법에서 이 함수를 정의하지 않는 것이 좋습니다.
함수가 구성 요소에 의존하므로 구성 요소 외부에서 정의할 수 없는 경우 구성 요소의 방법을 이벤트 프로세서로 전달할 수 있습니다.
class SomeComponent extends React.PureComponent {

  createAlertBox = () => {
    alert(this.props.message);
  };

  get instructions() {
    if (this.props.do) {
      return 'Click the button: ';
    }
    return 'Do NOT click the button: ';
  }

  render() {
    return (
      <div>
        {this.instructions}
        <Button onClick={this.createAlertBox} />
      </div>
    );
  }
}
이런 상황에서 Button의 실례마다 다른 경보 상자가 있다.SomeComponent의 클릭 사건 감청기는 Button의 유일한 감청기가 필요하다.SomeComponent을 전달하는 방법을 통해 createAlertBox의 재렌더링 여부는 중요하지 않습니다.SomeComponent 아이템 변경 여부는 중요하지 않습니다!message 메모리의 주소는 변하지 않습니다. 이것은 createAlertBox을 다시 렌더링할 필요가 없고 처리 시간을 절약하고 응용 프로그램의 렌더링 속도를 높일 수 있음을 의미합니다.
그런데 만약에 제 함수가 동적이라면요?

수정 (고급)


저자 주: 나는 기억에서 같은 기능을 반복적으로 인용하는 방법으로 아래의 예를 썼다.이런 예들은 참고 문헌의 이해를 쉽게 하는 데 목적을 두고 있다.참고 문헌을 이해하기 위해 이 절을 읽을 것을 건의하지만 마지막으로 더 좋은 실현을 소개했습니다. 이것은 Chris Ryan이 평론을 통해 아낌없이 제공한 것입니다.그의 해결 방안은 캐시 실효와 React의 내장 메모리 관리를 고려했다.
하나의 구성 요소에 많은 독특한 동적 이벤트 탐지기가 있는데, 예를 들어 맵 그룹을 만들 때.
class SomeComponent extends React.PureComponent {
  render() {
    return (
      <ul>
        {this.props.list.map(listItem =>
          <li key={listItem.text}>
            <Button onClick={() => alert(listItem.text)} />
          </li>
        )}
      </ul>
    );
  }
}
이 경우 이벤트 감청기의 수를 변경할 수 있는 수량 변경 단추가 있습니다. 모든 감청기는 독특한 기능을 가지고 있습니다. Button을 만들 때 무엇인지 알 수 없습니다.너는 어떻게 이 난제를 해결할 수 있니?
memoization을 입력하거나caching이라고 부르기 쉬운 내용을 입력하십시오.모든 유일한 값에 대해 함수를 만들고 캐시합니다.나중에 이 유일한 값에 대한 모든 인용은 이전에 캐시된 함수로 되돌려주십시오.
이것이 바로 내가 어떻게 상술한 예시를 실현할 것인가이다.
class SomeComponent extends React.PureComponent {

  // Each instance of SomeComponent has a cache of click handlers
  // that are unique to it.
  clickHandlers = {};

  // Generate and/or return a click handler,
  // given a unique identifier.
  getClickHandler(key) {

    // If no click handler exists for this unique identifier, create one.
    if (!Object.prototype.hasOwnProperty.call(this.clickHandlers, key)) {
      this.clickHandlers[key] = () => alert(key);
    }
    return this.clickHandlers[key];
  }

  render() {
    return (
      <ul>
        {this.props.list.map(listItem =>
          <li key={listItem.text}>
            <Button onClick={this.getClickHandler(listItem.text)} />
          </li>
        )}
      </ul>
    );
  }
}
수조의 모든 항목은 SomeComponent 방법을 통해 전달된다.처음 값을 사용하여 이 방법을 호출할 때, 이 방법은 이 값의 유일한 함수를 만들고 이 함수를 되돌려줍니다.앞으로 이 값을 사용하여 이 방법에 대한 호출은 새 함수를 만들지 않습니다.반대로 메모리에 생성된 함수에 대한 인용을 되돌려줍니다.
따라서 getClickHandler을 다시 렌더링하면 SomeComponent이 다시 렌더링되지 않습니다.이와 유사하게 Button 아이템에 항목을 추가하면 버튼마다 이벤트 탐지기를 동적으로 만들 수 있습니다.
각 프로세서가 여러 변수에 의해 결정될 때, 당신은 자신의 총명한 재능을 사용하여 유일한 식별자를 생성해야 할 수도 있지만, 이것은 단순히 매핑 결과의 모든 JSX 대상에 유일한 list 도구를 생성하는 것보다 그리 어렵지 않습니다.key을 식별자로 사용하는 경고: 목록이 순서를 변경하거나 항목을 삭제하면 잘못된 결과를 얻을 수 있습니다.그룹 수가 index에서 [ 'soda', 'pizza' ]으로 변경되고 이벤트 탐지기를 [ 'pizza' ]으로 캐시하면 사용자가 피자의now-index-0 단추를 눌렀을 때 listeners[0] = () => alert('soda')에 경보를 보냅니다.이것은 React가 관건적인 도구에 그룹 인덱스를 사용하지 말라고 건의한 이유와 같습니다.

더 나은 구현



중간 사용자인 Chris Ryan이 제공합니다.

결론


만약 당신이 이 문장을 좋아한다면, 마음대로 그것에게 마음이나 딱정벌레를 주십시오.그것은 매우 빠르고, 매우 쉬우며, 게다가 무료이다.질문이나 조언이 있으면 아래 댓글로 남겨주세요.
제 칼럼을 더 많이 읽으려면 Medium과, 또는 check out my portfolio on CharlesStover.com을 따르세요.

좋은 웹페이지 즐겨찾기