세션 재방송 어떻게 작업 제1부분: 서열화

11121 단어 javascript
이 일련의 글에서 나는 회화 재방송 제품(hotjar, logrocket 등)이 어떻게 작동하는지 공유하고 싶다.

나는 또한 이 게시물에 기술된 모든 함수를 포함하는 개원 라이브러리 rrweb을 유지했다.

서열화


브라우저에서 변경 사항을 로컬로 기록하고 재생하기만 하면 DOM 객체를 깊이 복사하여 현재 뷰를 저장할 수 있습니다.예를 들어, 다음 코드 구현 (jQuery의 단순화 예제를 사용하여 바디 부분만 저장)
// record
const snapshot = $('body').clone();
// replay
$('body').replaceWith(snapshot);
이제 전체 DOM 객체를 메모리에 저장하여 스냅샷을 생성합니다.
그러나 객체 자체는 정렬할 수 없습니다. 이것은 JSON과 같은 특정 텍스트 형식으로 저장하여 전송할 수 없다는 것을 의미합니다.원격 기록을 위해서는 DOM 데이터를 정렬하는 방법이 필요합니다.
두 가지 이유로 parse5과 같은 기존 오픈 소스 솔루션을 사용하지 않습니다.
  • 우리는'비표준'서열화 방법을 실현해야 한다. 다음은 상세하게 토론할 것이다.
  • 이 부분의 코드는 기록된 페이지에서 실행되어야 합니다. 우리는 가능한 한 코드의 양을 제어하고 필요한 기능만 보존하기를 바랍니다.
  • 서열화 중의 특수 처리


    우리의 서열화 방법이 비표준적인 이유는 다음과 같은 부분을 실행해야 하기 때문이다.
  • 출력은 묘사성이 필요합니다.처음 녹화된 페이지의 모든 JavaScript는 재방송 시 실행되지 않습니다.rrweb에서, 우리는 스냅샷에서 자리 표시자 script으로 noscript 표시를 대체함으로써 이 점을 실현한다.스크립트의 내용은 더 이상 중요하지 않습니다.반대로 DOM에 대한 스크립트의 변경 사항을 기록합니다.​​원본 웹 페이지에 존재할 수 있는 대량의 스크립트 내용을 완전히 기록할 필요가 없다.
  • 은 HTML에 기록된 뷰 상태를 반영하지 않습니다.예를 들어 <input type="text" />의 값은 HTML에 반영되지 않고 value 속성으로 기록됩니다.서열화할 때, 우리는 값을 읽고 속성으로 저장해야 한다.그래서 <input type="text" value="recordValue" />처럼 보입니다.
  • 상대 경로가 절대 경로로 변환됩니다.재생 기간에 우리는 녹음된 페이지를 <iframe>에 놓을 것이다.이 페이지의 URL은 다시 보기 페이지의 주소입니다.기록된 페이지에 상대 경로가 있으면 사용자가 열 때 오류가 발생하기 때문에 기록할 때 상대 경로를 전환해야 합니다.CSS 스타일시트의 상대 경로도 변환해야 합니다.
  • CSS 스타일시트의 내용을 기록하고 싶습니다.기록된 페이지가 외부 스타일시트에 연결되면 브라우저에서 해결된 CSS 규칙을 가져와 모든 규칙을 포함하는 내연 스타일시트를 생성할 수 있습니다.이렇게 하면 항상 접근할 수 있는 스타일시트 (예를 들어 인트라넷이나localhost에 있기 때문에) 가 녹화에 포함되고 정확하게 재방송될 수 있습니다.
  • 고유 ID


    또한 우리의 서열화는 전체 유형과 증량 유형을 포함해야 한다.완전 정렬은 DOM 트리를 적절한 트리 데이터 구조로 변환합니다.
    예를 들어, 다음 DOM 트리:
    <html>
      <body>
        <header>
        </header>
      </body>
    </html>
    
    다음 데이터 구조로 정렬합니다.
    {
      "type": "Document",
      "childNodes": [
        {
          "type": "Element",
          "tagName": "html",
          "attributes": {},
          "childNodes": [
            {
              "type": "Element",
              "tagName": "head",
              "attributes": {},
              "childNodes": [],
              "id": 3
            },
            {
              "type": "Element",
              "tagName": "body",
              "attributes": {},
              "childNodes": [
                {
                  "type": "Text",
                  "textContent": "\n    ",
                  "id": 5
                },
                {
                  "type": "Element",
                  "tagName": "header",
                  "attributes": {},
                  "childNodes": [
                    {
                      "type": "Text",
                      "textContent": "\n    ",
                      "id": 7
                    }
                  ],
                  "id": 6
                }
              ],
              "id": 4
            }
          ],
          "id": 2
        }
      ],
      "id": 1
    }
    
    이 서열화 결과 중 두 가지를 주의해야 한다.
  • DOM 트리를 훑어볼 때 노드를 단원으로 사용합니다.따라서 DOM의 요소 유형 노드를 제외하고는 텍스트 노드와 주석 노드 등 다른 모든 유형의 노드에 대한 기록을 포함합니다.
  • 우리는 각 노드에 유일한 식별자 id을 추가하여 후속 증량 스냅샷에 사용합니다.
  • 만약 우리가 같은 페이지에서 단추의 클릭을 기록하고 재생한다면, 우리는 다음과 같은 형식으로 조작을 기록할 수 있다. 즉, 우리가 말한 증량 스냅샷이다.
    type clickSnapshot = {
      source: 'MouseInteraction';
      type: 'Click';
      node: HTMLButtonElement;
    }
    
    snapshot.node.click()에서 이 작업을 다시 수행할 수 있습니다.
    그러나 실제 장면에서는 전체 DOM을 재구성했지만 증분 스냅샷의 상호 작용 DOM 노드를 기존 DOM과 연결할 수 없습니다.
    이것이 바로 표지부 id의 원인이다.레코드와 재생 측면에서 완전히 동일한 매핑을 유지하고 DOM 노드를 생성하고 제거할 때 업데이트되는 id -> Node 매핑은 스냅샷에서 고유한 증가 숫자를 사용하고 재생 중에 해당하는 DOM 노드를 찾기 위해 id만 기록해야 합니다.
    상술한 데이터 구조는 다음과 같습니다.
    type clickSnapshot = {
      source: 'MouseInteraction';
      type: 'Click';
      id: Number;
    }
    

    좋은 웹페이지 즐겨찾기