Merge Request차이 프론트엔드 개요

이 문서는 프론트엔드 디프스 Vue 애플리케이션이 작동하는 방법과 여러 다른 부분을 개요로 제공합니다. 기여자들이 다음을 이해하고 싶어할 때 도움이 될 것입니다:

  • 디프스 Vue 앱이 어떻게 설정되는지 이해합니다.
  • 개선이 필요한 영역을 식별합니다.

이 문서는 계속해서 업데이트됩니다. 디프 애플리케이션에서 중요한 변경 사항이 발생할 때 마다 업데이트하세요.

디프스 Vue 앱

컴포넌트

디프를 렌더링하는 Vue 앱은 다양한 Vue 컴포넌트를 사용하며, 그 중 일부는 GitLab 앱의 다른 영역과 공유됩니다. 아래 차트는 어떤 컴포넌트가 렌더링되는지를 보여줍니다.

이 차트에는 다음과 같은 유형의 항목이 포함되어 있습니다:

범례 항목 해석
xxx~~, ee-xxx~~ 줄임된 디렉터리 경로 이름. [ee]/app/assets/javascripts에서 찾을 수 있으며, 0..n개의 중첩된 폴더를 생략합니다.
직사각형 노드 파일
타원 노드 보다 깊은 개념을 설명하는 일반 언어
이중 사각형 노드 단순화된 코드 브랜치
다이아몬드 및 원 형태 노드 2개 (다이아몬드) 또는 3개 이상 (원) 옵션을 가진 브랜치
펜던트/배너 노드 (왼쪽 노치, 오른쪽 사각형) 중첩된 경로를 줄이기 위한 상위 디렉터리
./ 가장 가까운 부모 디렉터리 펜던트 노드에 대한 상대 경로. 부모 펜던트 노드 아래에 중첩된 비 상대 경로는 해당 디렉터리에 없습니다.
%%{init: { "fontFamily": "GitLab Sans" }}%% flowchart TB accDescr: GitLab 프론트엔드에서 컴포넌트를 렌더링하는 플로차트 classDef 코드 font-family: 고정폭; A["diffs~~app.vue"] descVirtualScroller(["가상 스크롤러"]) 코드단위[["v-for(diffFiles)"]] B["diffs~~diff_file.vue"] C["diffs~~diff_file_header.vue"] D["diffs~~diff_stats.vue"] E["diffs~~diff_content.vue"] isTextFile{텍스트 파일인가요} isWhitespaceOnly{공백만 있는가요} notDiffable{비교 불가능한가요} noPreview{"별도의 미리 보기가 없음"} %% 텍스트 변경 사항 dirDiffViewer>"vue_shared~~diff_viewer"] F["./viewers/not_diffable.vue"] G["./viewers/no_preview.vue"] H["./diff_viewer.vue"] I["diffs~~diff_view.vue"] isRenamed{이름 변경되었나요} isModeChanged{모드가 변경되었나요} hasNewPath{새 경로가 없나요} isImage{이미지 파일인가요} J["./viewers/renamed.vue"] K["./viewers/mode_changed.vue"] descNoViewer(["뷰어가 렌더링되지 않음"]) L["./viewers/image_diff_viewer.vue"] M["./viewers/download.vue"] N["vue_shared~~download_diff_viewer.vue"] isReplaced{이미지가 교체되었나요} O["vue_shared~~image_viewer.vue"] switchImageMode((image_diff_viewer.mode)) P["./viewers/image_diff/onion_skin_viewer.vue"] Q["./viewers/image_diff/swipe_viewer.vue"] R["./viewers/image_diff/two_up_viewer.vue"] S["diffs~~image_diff_overlay.vue"] 댓글단위[["v-for(discussions)"]] T["vue_shared~~design_note_pin.vue"] U["vue_shared~~user_avatar_link.vue"] V["diffs~~diff_discussions.vue"] W["batch_comments~~diff_file_drafts.vue"] 댓글단위(두 개의 파일 비교 댓글)["v-for(discussions)"] 초안단위(두 개의 파일 비교 초안)["v-for(drafts)"] X["notes~~notable_discussion.vue"] %% 텍스트 파일 변경 사항 코드단위(두 개의 파일 비교)["v-for(diffLines)"] Y["diffs~~diff_expansion_cell.vue"] Z["diffs~~diff_row.vue"] AA["diffs~~diff_line.vue"] AB["batch_comments~~draft_note.vue"] AC["diffs~~diff_comment_cell.vue"] AD["diffs~~diff_gutter_avatars.vue"] AE["ee-diffs~~inline_findings_gutter_icon_dropdown.vue"] AF["notes~~noteable_note.vue"] AG["notes~~note_actions.vue"] AH["notes~~note_body.vue"] AI["notes~~note_header.vue"] AJ["notes~~reply_button.vue"] AK["notes~~note_awards_list.vue"] AL["notes~~note_edited_text.vue"] AM["notes~~note_form.vue"] AN["vue_shared~~awards_list.vue"] AO["emoji~~picker.vue"] AP["emoji~~emoji_list.vue"] descEmojiVirtualScroll(["가상 스크롤러"]) AQ["emoji~~category.vue"] AR["emoji~emoji_category.vue"] AS["vue_shared~~markdown_editor.vue"] class 코드단위,댓글단위,초안단위 코드; class 코드단위(두 개의 파일 비교),초안단위(두 개의 파일 비교) 코드; %% 이 스위치 노드에도 코드 스타일을 적용합니다 class switchImageMode 코드; %% 이 boolean 노드에도 코드 스타일을 적용합니다 class isTextFile,isWhitespaceOnly,notDiffable,noPreview 코드; class isRenamed,isModeChanged,hasNewPath,isImage 코드; class isReplaced 코드; A --> descVirtualScroller A -->|"가상 스크롤러는 페이지 검색을 실행하면 비활성화됩니다. (Cmd/Ctrl+f)"|코드단위 descVirtualScroller --> 코드단위 코드단위 --> B --> C --> D B --> E %% 파일 보기 플래그 캐스케이드 E --> isTextFile isTextFile --> |예| I isTextFile --> |아니요| isWhitespaceOnly isWhitespaceOnly --> |예| descShowChanges isWhitespaceOnly --> |아니요| dirDiffViewer dirDiffViewer --> H H --> notDiffable notDiffable --> |예| F notDiffable --> |아니요| noPreview noPreview --> |예| G noPreview --> |아니요| isRenamed isRenamed --> |예| J isRenamed --> |아니요| isModeChanged isModeChanged --> |예| K isModeChanged --> |아니요| hasNewPath hasNewPath --> |예| isImage hasNewPath --> |아니요| descNoViewer isImage --> |예| L isImage --> |아니요| M M --> N %% 이미지 디프 뷰어 L --> isReplaced isReplaced --> |예| switchImageMode isReplaced --> |아니요| O switchImageMode -->|"'twoup' (기본값)"| R switchImageMode -->|'onion'| P switchImageMode -->|'swipe'| Q P & Q --> S S --> 댓글단위 S --> AM R-->|"노트 컨테이너 div에 렌더링됨"|U & W & V %% 상기 "P & Q --> S" 문 아래에 이 노드 관련은 결합하지 마십시오 %% 노드 관계의 순서는 그래프의 레이아웃을 정의하고, 이 순서에 따라야 합니다. R --> S V --> 초안단위 W --> 댓글단위 %% 이 보이지 않는 링크는 `noteable_discussion`이 `design_note_pin` 위에 렌더링되도록 합니다 X ~~~ T 초안단위 --> AB 댓글단위 & 초안단위 & 댓글단위 --> T 초안단위 --> X %% 텍스트 파일 디프 뷰어 I --> 코드단위 코드단위 --> Z 코드단위 -->|"isMatchLine?"| Y 코드단위 -->|"hasCodeQuality?"| AA 코드단위 -->|"hasDraftNote(s)?"| AB Z -->|"hasCodeQuality?"| AE Z -->|"hasDiscussions?"| AD AA --> AC %% 초안 노트들 AB --> AF AF --> AG & AH & AI AG --> AJ AH --> AK & AL & AM AK --> AN --> AO --> AP --> descEmojiVirtualScroll --> AQ --> AR AM --> AS

일부 컴포넌트는 다른 것보다 많이 렌더링되지만, 주요 컴포넌트는 diff_row.vue입니다. 이 컴포넌트는 diff 파일의 모든 diff line을 렌더링합니다. 성능상의 이유로, 이 컴포넌트는 기능적인 컴포넌트입니다. 그러나, Vue 3로 업그레이드하게 되면 더 이상 필요하지 않습니다.

주요 디프 앱 컴포넌트는 디프 앱의 주요 진입점입니다. 이 컴포넌트 중 가장 중요한 부분 중 하나는 논의를 diff line에 할당하는 작업을 디스패치 하는 것입니다. 이 작업은 메타데이터 요청이 완료된 후, 일괄 diff 요청이 완료된 후에 디스패치됩니다. 또한, diff 파일 배열과 노트 배열의 변경 사항을 감시하는 감시자가 설정되어 있습니다. 여기서 변경 사항이 발생할 때마다, 논의 설정 작업이 디스패치됩니다.

DiffRow 컴포넌트는 diff line 데이터를 하나의 형식으로 저장할 수 있는 방식으로 설정되어 있습니다. 이전에는 인라인 및 사이드 바이 사이드를위한 두 가지 다른 형식을 요청해야 했습니다. 그런 다음, 이 표준 형식을 사용하여 DiffRow 컴포넌트는 diff line 데이터를 렌더링합니다. 이 표준 형식을 통해 사용자는 데이터를 다시 가져 오지 않고 인라인 및 사이드 바이 사이드 사이를 전환할 수 있습니다.

note
이 컴포넌트의 경우 사용되고 렌더링 된 데이터의 많은 부분이 조건에 따라 메모이즈되고 캐시됩니다. 간혹 데이터가 각 다른 컴포넌트 렌더링 사이에 캐시될 수 있습니다.

Vuex 스토어

Diffs 앱을 위한 Vuex 스토어는 다음과 같은 3가지 다른 모듈로 구성됩니다:

  • Notes
  • Diffs
  • 일괄 주석

노트 모듈은 토론을 담당하며, 여기에는 차이점 토론이 포함됩니다. 이 모듈에서는 토론이 검색되고, 새로운 토론을 위한 폴링이 설정됩니다. 또한, 이 모듈은 이슈 앱과 공유되며, 이곳의 변경 사항은 이슈 및 Merge Request에서 모두 테스트되어야 합니다.

차이 모듈은 차이점과 관련된 모든 것에 대한 책임이 있습니다. 이는 차이점을 가져오는 것부터 차이점 토론을 라인에 할당하고 차이점 토론을 만드는 것 등을 포함합니다.

마지막으로, 일괄 코멘트 모듈은 복잡하지 않으며, 초안 코멘트 기능에만 책임이 있습니다. 그러나 이 모듈은 초안 코멘트가 게시될 때마다 노트 및 차이 모듈에서 작업을 디스패치합니다.

API 요청

메타데이터

차이점 메타데이터 엔드포인트는 모든 차이 파일을 가져오지 않고도 차이점 앱이 빠르게 필요로 하는 기본 데이터를 가져오는 데 사용됩니다. 이 기본 데이터에는 다음과 같은 것들이 포함됩니다:

  • 차이 파일 이름 및 차이 파일에 대한 추가 메타 데이터
  • 추가 및 삭제된 라인 번호
  • 브랜치 이름
  • 차이 버전

메타데이터 응답의 가장 중요한 부분은 차이 파일 이름입니다. 이 데이터를 통해 차이점 앱은 모든 일괄 차이 요청이 완료될 때까지 기다리지 않고도 차이점 앱 내에서 파일 브라우저를 렌더링할 수 있습니다.

메타데이터 응답을 받으면 차이 파일 데이터가 프론트엔드가 트리 뷰 또는 리스트 뷰에서 파일 브라우저를 렌더링하는 데 필요한 올바른 구조로 처리됩니다.

이 파일 객체의 구조는 다음과 같습니다:

{
  "key": "",
  "path": "",
  "name": "",
  "type": "",
  "tree": [],
  "changed": true,
  "diffLoaded": false,
  "filePaths": {
    "old": file.old_path,
    "new": file.new_path
  },
  "tempFile": false,
  "deleted": false,
  "fileHash": "",
  "addedLines": 1,
  "removedLines": 1,
  "parentPath": "/",
  "submodule": false
}

일괄 차이

차이점 엔드포인트의 응답 크기를 줄이기 위해 이 응답을 다른 요청으로 분할하여 다음을 수행합니다:

  • 각 요청의 응답 크기를 줄임
  • 첫 번째 요청이 완료되는 대로 차이점 앱에서 빠르게 차이점을 렌더링할 수 있게 함

첫 번째 요청을 더 빨리하게 만들기 위해 작은 양의 차이점을 요청하는 방식으로 요청을 보냅니다. 그런 다음 요청하는 차이점의 수가 증가하고, 각 요청당 최대 차이점 수가 30개가 될 때까지 이어집니다.

요청이 완료되면 차이점 앱은 받은 데이터를 형식에 맞게 변환하여 차이점 라인을 렌더링하기 쉬운 형식으로 만듭니다.

%%{init: { "fontFamily": "GitLab Sans" }}%% graph TD accTitle: 차이점 형식 지정 accDescr: 차이점 렌더링시 사용되는 단계의 플로우차트를 포함한 작업 A[fetchDiffFilesBatch] --> B[commit SET_DIFF_DATA_BATCH] --> C[prepareDiffData] --> D[prepareRawDiffFile] --> E[ensureBasicDiffFileLines] --> F[prepareDiffFileLines] --> G[finalizeDiffFile] --> H[deduplicateFilesList]

이 작업이 완료되면 차이점 앱은 이제 차이점 라인을 렌더링할 수 있습니다. 하지만 무엇보다도 렌더링 전에 차이점 앱은 하나의 형식을 더 만듭니다. 차이점 라인 데이터를 가져와 인라인 및 옆쪽 모드 간에 쉽게 전환할 수 있는 형식으로 매핑합니다. 이 형식은 diff_content.vue 컴포넌트 내의 계산된 속성에서 발생합니다.

렌더 대기열

note
이것은 더 이상 필요하지 않을 수 있습니다. 렌더 대기열의 미래를 결정하려면 조사 작업이 필요할 수 있습니다. 생성한 가상 스크롤 바로 인해 이 방법에서 얻은 성능 이점이 사라졌을 수 있습니다.

차이점을 빠르게 렌더링하기 위해, 렌더 대기열을 사용하여 브라우저가 유휴 상태일 때에만 차이점을 렌더링하도록 합니다. 이렇게 함으로써 많은 큰 차이점을 동시에 렌더링할 때 브라우저가 멈추지 않고 총 차단 시간을 줄일 수 있습니다.

이 파일 렌더링의 파이프라인은 다음 각 조건에 대해 차이점이 모두 true이면 발생합니다. 이 중 하나라도 false이면 렌더 대기열은 발생하지 않고 차이점은 예상대로 렌더링됩니다.

  • 이 파일의 차이점은 이미 렌더링되었습니까?
  • 이 차이점에 뷰어가 있습니까? (다운로드가 아닌가요?)
  • 차이점이 확장되었습니까?

다음은 발생하는 파이프라인에 대한 간략한 개요를 제공합니다:

%%{init: { "fontFamily": "GitLab Sans" }}%% graph TD accTitle: 렌더 대기열 파이프라인 accDescr: 렌더 대기열 파이프라인의 단계에 대한 플로우차트 A[startRenderDiffsQueue] -->B B[commit RENDER_FILE current file index] -->C C[canRenderNextFile?] C -->|Yes| D[Render file] -->B C -->|No| E[Re-run requestIdleCallback] -->C

다음과 같은 확인 사항이 발생합니다:

  • 남은 유휴 시간이 5밀리초 미만입니까?
  • 우리는 이미 이 파일을 4번 렌더링을 시도했습니까?

이러한 확인 사항이 완료되면 파일은 Vuex에서 렌더링 가능으로 표시되어 차이점 앱이 차이점 라인과 토론을 시작할 수 있게 됩니다.