2021년 프런트 엔드 성능 체크리스트(PDF, Apple Pages, MS Word)
게시 됨: 2022-03-10이 가이드는 프론트엔드 성능 모니터링 , 세션 재생 및 제품 분석을 결합하여 더 나은 고객 경험을 구축하는 데 도움이 되는 서비스인 LogRocket의 친구들로부터 친절하게 지원되었습니다. LogRocket 은 다음을 포함한 주요 지표를 추적합니다. DOM 완료, 첫 번째 바이트까지의 시간, 첫 번째 입력 지연, 클라이언트 CPU 및 메모리 사용량. 지금 LogRocket의 무료 평가판을 받으십시오.
웹 성능은 까다로운 짐승이죠, 그렇죠? 성능 측면에서 우리의 위치와 성능 병목 현상이 정확히 무엇인지 어떻게 알 수 있습니까? 비싼 JavaScript, 느린 웹 글꼴 전달, 무거운 이미지 또는 느린 렌더링입니까? 트리 흔들기, 범위 호이스팅, 코드 분할, 교차 관찰자, 점진적 수화, 클라이언트 힌트, HTTP/3, 서비스 워커, 에지 워커를 사용한 모든 멋진 로딩 패턴으로 충분히 최적화했습니까? 그리고 가장 중요한 것은 어디에서 성과 향상을 시작하고 장기적으로 성과 문화를 구축할 수 있느냐는 것입니다.
그 당시에는 성능이 단순히 사후 고려 사항 에 불과한 경우가 많았습니다. 종종 프로젝트가 끝날 때까지 연기되며 축소, 연결, 자산 최적화 및 잠재적으로 서버의 config
파일에 대한 몇 가지 미세 조정으로 귀결됩니다. 지금 돌이켜보면 상황이 상당히 많이 변한 것 같다.
성능은 단지 기술적인 문제가 아닙니다. 성능은 접근성에서 사용성, 검색 엔진 최적화에 이르기까지 모든 것에 영향을 미치며, 이를 워크플로에 적용할 때 성능에 미치는 영향을 고려하여 설계 결정을 내려야 합니다. 성능을 지속적으로 측정, 모니터링 및 개선해야 하며 , 웹의 복잡성이 증가하면 메트릭을 추적하기 어렵게 만드는 새로운 문제가 발생합니다. 데이터는 장치, 브라우저, 프로토콜, 네트워크 유형 및 대기 시간에 따라 크게 달라지기 때문입니다( CDN, ISP, 캐시, 프록시, 방화벽, 로드 밸런서 및 서버는 모두 성능에 중요한 역할을 합니다.
따라서 프로젝트 시작부터 웹 사이트의 최종 릴리스까지 성능을 개선할 때 염두에 두어야 하는 모든 사항에 대한 개요를 만들면 어떻게 될까요? 아래 에서 2021년에 대한 (편향 없고 객관적인) 프런트 엔드 성능 체크리스트를 찾을 수 있습니다. 응답 시간이 빠르고 사용자 상호 작용이 원활하며 사이트가 사용자의 대역폭을 소모합니다.
목차
- 모두 별도의 페이지에 있습니다.
- 준비하기: 계획 및 측정항목
성능 문화, 핵심 Web Vitals, 성능 프로필, CrUX, Lighthouse, FID, TTI, CLS, 장치. - 현실적인 목표 설정
성능 예산, 성능 목표, RAIL 프레임워크, 170KB/30KB 예산. - 환경 정의
프레임워크 선택, 기본 성능 비용, Webpack, 종속성, CDN, 프런트 엔드 아키텍처, CSR, SSR, CSR + SSR, 정적 렌더링, 사전 렌더링, PRPL 패턴. - 자산 최적화
Brotli, AVIF, WebP, 반응형 이미지, AV1, 적응형 미디어 로딩, 비디오 압축, 웹 글꼴, Google 글꼴. - 빌드 최적화
JavaScript 모듈, 모듈/모듈 패턴, 트리 쉐이킹, 코드 분할, 범위 호이스팅, Webpack, 차등 서비스, 웹 작업자, WebAssembly, JavaScript 번들, React, SPA, 부분 수화, 상호 작용 시 가져오기, 타사, 캐시. - 배달 최적화
지연 로딩, 교차 관찰자, 렌더링 및 디코딩 지연, 중요한 CSS, 스트리밍, 리소스 힌트, 레이아웃 변경, 서비스 작업자. - 네트워킹, HTTP/2, HTTP/3
OCSP 스테이플링, EV/DV 인증서, 패키징, IPv6, QUIC, HTTP/3. - 테스트 및 모니터링
감사 워크플로, 프록시 브라우저, 404 페이지, GDPR 쿠키 동의 프롬프트, 성능 진단 CSS, 접근성. - 빠른 승리
- 체크리스트 다운로드(PDF, Apple Pages, MS Word)
- 오프 위 고!
(체크리스트 PDF(166KB)를 다운로드하거나 편집 가능한 Apple Pages 파일(275KB) 또는 .docx 파일(151KB)을 다운로드할 수도 있습니다. 모두 즐거운 최적화 작업을 하세요!)
준비하기: 계획 및 측정항목
미세 최적화는 성과를 추적하는 데 유용하지만 프로세스 전반에 걸쳐 내려진 모든 결정에 영향을 미칠 수 있는 측정 가능한 목표를 염두에 두고 명확하게 정의된 목표를 염두에 두는 것이 중요합니다. 몇 가지 다른 모델이 있으며 아래에 논의된 모델은 상당히 독단적입니다. 초기에 우선 순위를 설정해야 합니다.
- 공연문화를 정착시킨다.
많은 조직에서 프론트엔드 개발자는 일반적인 기본 문제가 무엇인지, 이를 해결하기 위해 어떤 전략을 사용해야 하는지 정확히 알고 있습니다. 그러나 성과 문화에 대한 확고한 지지가 없는 한 각 결정은 부서 간의 전쟁터가 되어 조직을 사일로로 분해할 것입니다. 비즈니스 이해 관계자의 동의가 필요하며 이를 얻으려면 속도(특히 나중에 자세히 다룰 핵심 성능 지표)에 대한 사례 연구 또는 개념 증명을 수립해야 메트릭 및 핵심 성과 지표에 이점이 있습니다. ( KPI ) 그들은 관심을 가지고 있습니다.예를 들어, 성능을 보다 가시적으로 만들기 위해 전환율과 응용 프로그램 로드 시간 간의 상관 관계와 렌더링 성능을 표시하여 수익 성능에 미치는 영향을 노출할 수 있습니다. 또는 검색 봇 크롤링 속도(PDF, 27–50페이지).
개발/디자인 팀과 비즈니스/마케팅 팀 간의 긴밀한 연계 없이는 성과가 장기적으로 유지될 수 없습니다. 고객 서비스 및 영업 팀에 들어오는 일반적인 불만 사항을 연구하고 높은 이탈률 및 전환율 하락에 대한 분석을 연구합니다. 성능 향상이 이러한 일반적인 문제 중 일부를 완화하는 데 어떻게 도움이 되는지 알아보십시오. 이야기하고 있는 이해 관계자 그룹에 따라 논쟁을 조정하십시오.
모바일과 데스크톱(예: Google Analytics 사용) 모두에서 성능 실험을 실행하고 결과를 측정합니다. 실제 데이터를 사용하여 회사 맞춤형 사례 연구를 구축하는 데 도움이 됩니다. 또한 WPO 통계에 게시된 사례 연구 및 실험의 데이터를 사용하면 성능이 중요한 이유와 성능이 사용자 경험 및 비즈니스 메트릭에 미치는 영향에 대한 비즈니스의 민감도를 높이는 데 도움이 됩니다. 하지만 성과가 중요하다고 말하는 것만으로는 충분하지 않습니다. 또한 측정 가능하고 추적 가능한 목표를 설정하고 시간이 지남에 따라 이를 관찰해야 합니다.
거기에 도착하는 방법? 장기적 성과 구축에 대한 강연에서 Allison McKnight는 Etsy에서 공연 문화를 구축하는 데 그녀가 어떻게 도왔는지에 대한 포괄적인 사례 연구를 공유합니다(슬라이드). 보다 최근에 Tammy Everts는 크고 작은 조직 모두에서 매우 효과적인 성과 팀의 습관에 대해 이야기했습니다.
조직에서 이러한 대화를 나누는 동안 UX가 경험의 스펙트럼인 것처럼 웹 성능은 분포라는 점을 명심하는 것이 중요합니다. Karolina Szczur가 말했듯이 "하나의 숫자가 열망하는 등급을 제공할 수 있을 것으로 기대하는 것은 잘못된 가정입니다." 따라서 성과 목표는 세분화되고 추적 가능하며 유형적이어야 합니다.
- 목표: 가장 빠른 경쟁자보다 최소 20% 더 빠릅니다.
심리학 연구에 따르면 사용자가 귀하의 웹사이트가 경쟁업체의 웹사이트보다 빠르다고 느끼도록 하려면 최소 20% 빨라야 합니다. 주요 경쟁업체를 연구하고 모바일 및 데스크톱에서 수행하는 방법에 대한 메트릭을 수집하고 경쟁업체를 능가하는 데 도움이 되는 임계값을 설정합니다. 하지만 정확한 결과와 목표를 얻으려면 먼저 분석을 연구하여 사용자 경험을 철저히 파악해야 합니다. 그런 다음 테스트를 위해 90번째 백분위수의 경험을 모방할 수 있습니다.경쟁업체의 성과에 대한 좋은 첫인상을 얻으려면 Chrome UX 보고서( CrUX , 기성품 RUM 데이터 세트, Ilya Grigorik의 비디오 소개 및 Rick Viscomi의 세부 가이드) 또는 RUM 모니터링 도구인 Treo를 사용할 수 있습니다. Chrome UX 보고서에서 제공합니다. 데이터는 Chrome 브라우저 사용자로부터 수집되므로 보고서는 Chrome 전용이지만 광범위한 방문자에 대해 성능, 가장 중요한 핵심 성능 평가 점수를 상당히 철저하게 분포시킵니다. 새로운 CrUX 데이터 세트는 매월 두 번째 화요일에 발표됩니다.
또는 다음을 사용할 수도 있습니다.
- Addy Osmani의 Chrome UX 보고서 비교 도구,
- Speed Scorecard(수익 영향 추정기 제공),
- 실제 사용자 경험 테스트 비교 또는
- SiteSpeed CI(합성 테스트 기반).
참고 : Page Speed Insights 또는 Page Speed Insights API(아니요, 더 이상 사용되지 않습니다!)를 사용하는 경우 집계가 아닌 특정 페이지에 대한 CrUX 성능 데이터를 얻을 수 있습니다. 이 데이터는 "랜딩 페이지" 또는 "제품 목록"과 같은 자산의 실적 목표를 설정하는 데 훨씬 더 유용할 수 있습니다. 그리고 CI를 사용하여 예산을 테스트하는 경우 대상 설정에 CrUX를 사용한 경우 테스트된 환경이 CrUX와 일치하는지 확인해야 합니다( Patrick Meenan에게 감사드립니다! ).
속도의 우선 순위를 정하는 이유를 보여주기 위해 도움이 필요하거나, 느린 성능과 함께 전환율 감소 또는 이탈률 증가를 시각화하고 싶거나, 조직에서 RUM 솔루션을 옹호해야 하는 경우, Sergey Chernyshev는 데이터를 시뮬레이션하고 시각화하여 요점을 파악하는 데 도움이 되는 오픈 소스 도구인 UX 속도 계산기를 구축했습니다.
때로는 CrUX에서 가져온 데이터를 다른 데이터와 결합하여 속도 저하, 사각 지대 및 비효율이 있는 곳(경쟁업체 또는 프로젝트)을 빠르게 해결해야 하는 경우가 있습니다. Harry Roberts는 작업에서 주요 페이지 유형별로 성능을 분류하고 주요 메트릭이 어떻게 다른지 추적하는 데 사용하는 Site-Speed Topography Spreadsheet를 사용하고 있습니다. 스프레드시트를 Google 스프레드시트, Excel, OpenOffice 문서 또는 CSV로 다운로드할 수 있습니다.
그리고 끝까지 진행하고 싶다면 사이트의 모든 페이지에서 Lighthouse 성능 감사를 실행할 수 있습니다(Lightouse Parade를 통해). CSV로 저장한 출력과 함께. 이렇게 하면 경쟁업체의 특정 페이지(또는 페이지 유형)가 더 나쁘거나 더 나은 성과를 거두고 무엇에 집중해야 하는지 식별하는 데 도움이 됩니다. (자신의 사이트의 경우 데이터를 분석 엔드포인트로 보내는 것이 더 나을 수 있습니다!).
이러한 방식으로 데이터를 수집하고, 스프레드시트를 설정하고, 20%를 줄이고, 목표( 성과 예산 )를 설정합니다. 이제 테스트할 측정 가능한 항목이 있습니다. 예산을 염두에 두고 대화형 시간을 단축하기 위해 최소한의 페이로드만 제공하려는 경우 합리적인 경로에 있는 것입니다.
시작하는 데 리소스가 필요하십니까?
- Addy Osmani는 성능 예산 책정을 시작하는 방법, 새로운 기능의 영향을 수량화하는 방법, 예산이 초과되었을 때 시작하는 방법에 대한 매우 상세한 글을 작성했습니다.
- 성능 예산으로 설계에 접근하는 방법에 대한 Lara Hogan의 가이드는 설계자에게 유용한 지침을 제공할 수 있습니다.
- Harry Roberts는 Request Map,
- Jonathan Fielding의 Performance Budget Calculator, Katie Hempenius의 perf-budget-calculator 및 Browser Calories는 예산을 만드는 데 도움이 될 수 있습니다.
- 많은 회사에서 성과 예산은 열망이 아니라 오히려 실용적이어야 하며, 특정 지점을 지나치지 않도록 유지 신호 역할을 합니다. 이 경우 지난 2주 동안 최악의 데이터 포인트를 임계값으로 선택하고 거기에서 가져올 수 있습니다. Performance Budgets는 이를 달성하기 위한 전략을 실용적으로 보여줍니다.
- 또한 빌드 크기를 보고하는 그래프로 대시보드를 설정하여 성능 예산과 현재 성능을 모두 볼 수 있습니다 . 이를 가능하게 하는 많은 도구가 있습니다. SiteSpeed.io 대시보드(오픈 소스), SpeedCurve 및 Calibre는 그 중 일부에 불과하며 perf.rocks에서 더 많은 도구를 찾을 수 있습니다.
예산이 확보되면 Webpack Performance Hints 및 Bundlesize, Lighthouse CI, PWMetrics 또는 Sitespeed CI를 사용하여 빌드 프로세스에 통합하여 pull 요청에 예산을 적용하고 PR 주석에 점수 기록을 제공하십시오.
전체 팀에 성능 예산을 공개하려면 Lightwallet을 통해 Lighthouse에 성능 예산을 통합하거나 빠른 Github Actions 통합을 위해 LHCI Action을 사용하십시오. 그리고 사용자 정의가 필요한 경우 엔드포인트 API인 webpagetest-charts-api를 사용하여 WebPagetest 결과에서 차트를 작성할 수 있습니다.
그러나 성과 인식은 성과 예산만으로 이루어지면 안 됩니다. Pinterest와 마찬가지로 종속성이 많고 번들을 부풀릴 수 있는 파일 및 디렉터리에서 가져오기를 허용하지 않는 사용자 지정 eslint 규칙을 만들 수 있습니다. 전체 팀에서 공유할 수 있는 "안전한" 패키지 목록을 설정합니다.
또한 귀하의 비즈니스에 가장 유익한 중요한 고객 작업에 대해 생각해 보십시오. 중요한 작업에 대한 허용 가능한 시간 임계값을 연구, 논의 및 정의하고 전체 조직이 승인한 "UX 준비" 사용자 타이밍 표시를 설정합니다. 대부분의 경우 사용자 여정은 여러 부서의 작업과 관련이 있으므로 허용 가능한 시간에 맞춰 조정하면 향후 성능 논의를 지원하거나 방지하는 데 도움이 됩니다. 추가된 리소스 및 기능에 대한 추가 비용이 표시되고 이해되는지 확인합니다.
구축 중인 제품의 새로운 기능에서 리팩토링, 새로운 글로벌 청중에게 다가가기까지 다양한 기술 이니셔티브와 성과 노력을 조정합니다. 따라서 추가 개발에 대한 대화가 발생할 때마다 성능도 대화의 일부입니다. 코드 기반이 최신이거나 리팩토링되는 중일 때 성능 목표에 훨씬 더 쉽게 도달할 수 있습니다.
또한 Patrick Meenan이 제안한 것처럼 설계 과정 에서 로딩 순서와 절충안을 계획하는 것이 좋습니다. 어떤 부분이 더 중요한지 조기에 우선 순위를 지정하고 표시 순서를 정의하면 지연될 수 있는 부분도 알게 됩니다. 이상적으로는 해당 순서가 CSS 및 JavaScript 가져오기의 순서도 반영하므로 빌드 프로세스 중에 처리하기가 더 쉬울 것입니다. 또한 페이지가 로드되는 동안(예: 웹 글꼴이 아직 로드되지 않은 경우) "중간" 상태에 있어야 하는 시각적 경험을 고려하십시오.
조직에 강력한 성과 문화를 구축했으면 시간이 흘러도 우선 순위를 그대로 유지하기 위해 이전 자신보다 20% 더 빠른 것을 목표로 삼으십시오( Guy Podjarny에게 감사드립니다! ). 그러나 봇 트래픽 및 계절성 효과와 함께 고객의 다양한 유형 및 사용 행동(Tobias Baldauf가 케이던스 및 집단이라고 부름)을 고려하십시오.
기획, 기획, 기획. 초기에 빠른 "낮은 성과" 최적화에 들어가고 싶은 마음이 들 수도 있고 빠른 승리를 위한 좋은 전략일 수도 있습니다. - 맞춤형 성능 목표.
- 올바른 측정항목을 선택하세요.
모든 지표가 똑같이 중요한 것은 아닙니다. 어떤 메트릭이 애플리케이션에 가장 중요한지 연구하십시오. 일반적으로 인터페이스의 가장 중요한 픽셀 을 렌더링하기 시작할 수 있는 속도와 렌더링된 픽셀에 대한 입력 응답 성을 얼마나 빨리 제공할 수 있는지에 따라 정의됩니다. 이 지식은 지속적인 노력을 위한 최상의 최적화 목표를 제공할 것입니다. 결국 경험을 정의하는 것은 로드 이벤트나 서버 응답 시간이 아니라 인터페이스가 얼마나 빠른지 인지하는 것 입니다.무슨 뜻이에요? 전체 페이지 로드 시간(예: onLoad 및 DOMContentLoaded 타이밍 사용)에 초점을 맞추는 대신 고객이 인식하는 페이지 로드의 우선 순위를 지정하십시오. 이는 약간 다른 측정항목 집합에 초점을 맞추는 것을 의미합니다. 사실, 올바른 메트릭을 선택하는 것은 명백한 승자가 없는 과정입니다.
Tim Kadlec의 연구와 그의 강연에서 Marcos Iglesias의 메모를 기반으로 하면 기존 측정항목 을 몇 개의 집합으로 그룹화할 수 있습니다. 일반적으로 성능에 대한 완전한 그림을 얻으려면 이들 모두가 필요하며 특정 경우에는 그 중 일부가 다른 것보다 더 중요합니다.
- 수량 기반 메트릭 은 요청 수, 가중치 및 성능 점수를 측정합니다. 경보를 발생시키고 시간이 지남에 따라 변경 사항을 모니터링하는 데는 좋지만 사용자 경험을 이해하는 데는 좋지 않습니다.
- 마일스톤 메트릭 은 로드 프로세스의 수명 동안 상태를 사용합니다(예: Time To First Byte 및 Time To Interactive ). 사용자 경험 및 모니터링을 설명하는 데 적합하지만 이정표 사이에 어떤 일이 발생하는지 알기에는 적합하지 않습니다.
- 렌더링 메트릭 은 콘텐츠가 얼마나 빨리 렌더링되는지에 대한 추정치를 제공합니다(예: 시작 렌더링 시간, 속도 인덱스 ). 렌더링 성능을 측정하고 조정하는 데는 좋지만 중요한 콘텐츠가 표시되고 상호 작용할 수 있는 시점을 측정하는 데에는 적합하지 않습니다.
- 사용자 지정 메트릭 은 사용자에 대한 특정 사용자 지정 이벤트(예: Twitter의 첫 트윗 시간 및 Pinterest의 PinnerWaitTime)를 측정합니다. 사용자 경험을 정확하게 설명하는 데는 좋지만 메트릭을 확장하고 경쟁자와 비교하는 데는 좋지 않습니다.
그림을 완성하기 위해 일반적으로 이러한 모든 그룹에서 유용한 메트릭을 찾습니다. 일반적으로 가장 구체적이고 관련성이 높은 항목은 다음과 같습니다.
- 인터랙티브 시간 (TTI)
레이아웃이 안정화 되고 주요 웹 글꼴이 표시되고 기본 스레드가 사용자 입력을 처리할 수 있을 만큼 충분히 사용 가능한 지점(기본적으로 사용자가 UI와 상호 작용할 수 있는 시간 표시)입니다. 사용자가 지연 없이 사이트를 사용하기 위해 경험해야 하는 대기 시간을 이해하기 위한 주요 측정항목입니다. Boris Schapira는 TTI를 안정적으로 측정하는 방법에 대한 자세한 게시물을 작성했습니다. - 첫 번째 입력 지연 (FID) 또는 입력 응답 성
사용자가 귀하의 사이트와 처음 상호작용할 때부터 브라우저가 실제로 해당 상호작용 에 응답할 수 있을 때까지의 시간입니다. 사용자가 실제로 사이트와 상호 작용할 때 발생하는 그림의 누락된 부분을 설명하므로 TTI를 매우 잘 보완합니다. RUM 지표로만 사용됩니다. 브라우저에서 FID를 측정하기 위한 JavaScript 라이브러리가 있습니다. - 최대 함량 페인트 (LCP)
페이지의 중요한 콘텐츠 가 로드되었을 가능성이 있는 페이지 로드 타임라인의 지점을 표시합니다. 페이지의 가장 중요한 요소는 사용자의 뷰포트에서 볼 수 있는 가장 큰 요소라고 가정합니다. 요소가 접히는 부분 위와 아래 모두에 렌더링되면 보이는 부분만 관련성이 있는 것으로 간주됩니다. - 총 차단 시간( 미정 )
페이지가 안정적으로 상호작용하기 전에 페이지가 얼마나 비대화식인지의 심각도를 수량화하는 데 도움이 되는 측정항목입니다(즉, 기본 스레드에 최소 5초 동안 50ms( 긴 작업 ) 이상 실행되는 작업이 없음). 이 메트릭은 입력 응답을 방지할 만큼 충분히 오랫동안 메인 스레드가 차단된 첫 번째 페인트와 TTI(Time to Interactive) 사이의 총 시간을 측정합니다. 따라서 낮은 TBT가 좋은 성과를 나타내는 좋은 지표라는 것은 놀라운 일이 아닙니다. (고마워, Artem, Phil) - 누적 레이아웃 이동( CLS )
이 측정항목은 사용자가 사이트에 액세스할 때 예상치 못한 레이아웃 변경 ( 리플로우 )을 얼마나 자주 경험하는지 강조합니다. 불안정한 요소와 전반적인 경험에 미치는 영향을 조사합니다. 점수가 낮을수록 좋습니다. - 속도 지수
페이지 콘텐츠가 시각적으로 채워지는 속도를 측정합니다. 점수가 낮을수록 좋습니다. 속도 지수 점수는 시각적 진행 속도를 기반으로 계산되지만 계산된 값일 뿐입니다. 또한 뷰포트 크기에 민감하므로 대상 고객과 일치하는 테스트 구성 범위를 정의해야 합니다. LCP가 더 관련성 있는 메트릭이 되면서 중요성이 줄어들고 있다는 점에 유의하십시오( Boris, Artem에게 감사드립니다! ). - CPU 소요 시간
페인팅, 렌더링, 스크립팅 및 로드 작업에서 메인 스레드가 차단 되는 빈도 와 기간 을 보여주는 메트릭입니다. 높은 CPU 시간은 버벅 거림 경험의 분명한 지표입니다. 즉, 사용자가 작업과 응답 사이에 눈에 띄는 지연을 경험할 때입니다. WebPageTest를 사용하면 "Chrome" 탭에서 "개발 도구 타임라인 캡처"를 선택하여 WebPageTest를 사용하는 모든 장치에서 실행되는 기본 스레드의 분석을 노출할 수 있습니다. - 구성 요소 수준 CPU 비용
CPU 소요 시간 과 마찬가지로 Stoyan Stefanov가 제안한 이 지표 는 JavaScript가 CPU에 미치는 영향을 조사합니다. 아이디어는 개별적으로 전체 경험에 미치는 영향을 이해하기 위해 구성 요소당 CPU 명령 수를 사용하는 것입니다. Puppeteer 및 Chrome을 사용하여 구현할 수 있습니다. - 좌절 지수
위에 나온 많은 메트릭이 특정 이벤트가 발생하는 시기를 설명하지만 Tim Vereecke의 FrustrationIndex는 메트릭을 개별적으로 보는 대신 메트릭 사이의 간격 을 봅니다. 제목이 표시됨, 첫 번째 콘텐츠가 표시됨, 시각적으로 준비됨 및 페이지가 준비된 모양과 같이 최종 사용자가 인식하는 주요 이정표를 보고 페이지를 로드하는 동안 좌절감을 나타내는 수준을 나타내는 점수를 계산합니다. 격차가 클수록 사용자가 불만을 가질 가능성이 커집니다. 사용자 경험을 위한 잠재적으로 좋은 KPI입니다. Tim은 FrustrationIndex와 작동 방식에 대한 자세한 게시물을 게시했습니다. - 광고 가중치 영향
사이트가 광고에 의해 생성된 수익에 의존하는 경우 광고 관련 코드의 가중치를 추적하는 것이 유용합니다. Paddy Ganti의 스크립트는 두 개의 URL(하나는 일반, 다른 하나는 광고 차단)을 구성하고 WebPageTest를 통해 비디오 비교 생성을 요청하고 델타를 보고합니다. - 편차 측정항목
Wikipedia 엔지니어가 언급한 바와 같이 결과에 얼마나 많은 편차 가 존재하는지에 대한 데이터는 도구가 얼마나 신뢰할 수 있는지, 편차 및 이상값에 얼마나 주의를 기울여야 하는지 알려줄 수 있습니다. 큰 편차는 설정에 필요한 조정의 지표입니다. 또한 특정 페이지를 안정적으로 측정하기가 더 어려운지(예: 상당한 변동을 일으키는 타사 스크립트로 인해)를 이해하는 데 도움이 됩니다. 새 브라우저 버전이 출시될 때 성능의 충돌을 이해하기 위해 브라우저 버전을 추적하는 것도 좋은 생각일 수 있습니다. - 맞춤 측정항목
사용자 지정 메트릭은 비즈니스 요구 사항과 고객 경험에 따라 정의됩니다. 중요한 픽셀, 중요한 스크립트, 필요한 CSS 및 관련 자산을 식별하고 사용자에게 얼마나 빨리 전달되는지 측정해야 합니다. 이를 위해 Hero Rendering Times를 모니터링하거나 Performance API를 사용하여 비즈니스에 중요한 이벤트의 특정 타임스탬프를 표시할 수 있습니다. 또한 테스트가 끝날 때 임의의 JavaScript를 실행하여 WebPagetest로 사용자 정의 메트릭을 수집할 수 있습니다.
첫 번째 의미 있는 페인트 (FMP) 는 위의 개요에 나타나지 않습니다. 서버 가 데이터를 얼마나 빨리 출력하는지에 대한 통찰력을 제공하는 데 사용되었습니다. 긴 FMP는 일반적으로 메인 스레드를 차단하는 JavaScript를 나타내지만 백엔드/서버 문제와도 관련될 수 있습니다. 그러나 이 측정항목은 케이스의 약 20%에서 정확하지 않은 것으로 나타나 최근에 더 이상 사용되지 않습니다. 보다 안정적이고 추론하기 쉬운 LCP로 효과적으로 대체되었습니다. Lighthouse에서는 더 이상 지원되지 않습니다. 안전한 페이지에 있는지 확인하기 위해 최신 사용자 중심 성능 메트릭 및 권장 사항을 다시 확인하십시오( 감사합니다, Patrick Meenan ).
Steve Souders는 이러한 많은 지표에 대해 자세히 설명합니다. Time-To-Interactive는 소위 랩 환경 에서 자동화된 감사를 실행하여 측정되지만 First Input Delay는 실제 사용자 경험을 나타내며 실제 사용자는 눈에 띄게 지연됩니다. 일반적으로 항상 두 가지를 모두 측정하고 추적하는 것이 좋습니다.
애플리케이션의 컨텍스트에 따라 선호하는 메트릭이 다를 수 있습니다. 예를 들어 Netflix TV UI의 경우 키 입력 응답성, 메모리 사용량 및 TTI가 더 중요하고 Wikipedia의 경우 처음/마지막 시각적 변경 및 CPU 소요 시간 메트릭이 더 중요합니다.
참고 : FID와 TTI는 모두 스크롤 동작을 고려하지 않습니다. 스크롤링은 오프 메인 스레드이기 때문에 독립적으로 발생할 수 있으므로 많은 콘텐츠 소비 사이트에서 이러한 메트릭은 훨씬 덜 중요할 수 있습니다( 감사합니다, Patrick! ).
- 핵심 Web Vitals를 측정하고 최적화합니다 .
오랫동안 성능 메트릭은 서버가 응답하는 속도와 브라우저가 로드되는 속도에 대한 엔지니어링 관점에 초점을 맞춘 상당히 기술적인 것이었습니다. 메트릭은 서버 타이밍보다 실제 사용자 경험을 캡처하는 방법을 찾으려고 시도하면서 수년에 걸쳐 변경되었습니다. 2020년 5월, Google은 각각 사용자 경험의 고유한 측면을 나타내는 새로운 사용자 중심 성능 측정항목인 핵심 성능 지표를 발표했습니다.각각에 대해 Google은 허용 가능한 속도 목표 범위를 권장합니다. 이 평가를 통과하려면 전체 페이지 조회수의 75% 이상이 좋음 범위 를 초과해야 합니다. 이러한 측정항목은 빠르게 주목을 받았으며 2021년 5월에 핵심 성능 평가가 Google 검색의 순위 신호가 되면서( 페이지 경험 순위 알고리즘 업데이트 ) 많은 회사에서 성과 점수에 관심을 돌렸습니다.
이러한 지표를 염두에 두고 경험을 최적화하기 위한 유용한 기술 및 도구 와 함께 각각의 핵심 핵심 성능을 하나씩 분석해 보겠습니다. (이 기사의 일반적인 조언을 따르면 더 나은 Core Web Vitals 점수를 얻을 수 있습니다.)
- 가장 큰 내용이 포함된 페인트 ( LCP ) < 2.5초
페이지 로드 를 측정하고 뷰포트 내에서 볼 수 있는 가장 큰 이미지 또는 텍스트 블록 의 렌더링 시간을 보고합니다. 따라서 LCP는 중요한 정보의 렌더링을 지연시키는 모든 것의 영향을 받습니다. 느린 서버 응답 시간, CSS 차단, 인플라이트 JavaScript(자사 또는 타사), 웹 글꼴 로드, 값비싼 렌더링 또는 페인팅 작업, 게으름 -로드된 이미지, 스켈레톤 화면 또는 클라이언트 측 렌더링.
좋은 경험을 위해 LCP는 페이지가 처음 로드되기 시작한 후 2.5초 이내에 발생해야 합니다. 즉, 페이지의 첫 번째 보이는 부분을 가능한 한 빨리 렌더링해야 합니다. 이를 위해서는 각 템플릿에 대해 맞춤형 중요 CSS가 필요하고<head>
주문을 조정하고 중요한 자산을 미리 가져옵니다(나중에 다룰 것입니다).낮은 LCP 점수의 주요 원인은 일반적으로 이미지입니다. 잘 최적화된 서버에서 호스팅되는 Fast 3G에서 2.5초 미만으로 LCP를 제공하려면 클라이언트 측 렌더링이 없고 전용 이미지 CDN에서 가져온 이미지가 모두 정적이며 이론상 최대 이미지 크기는 약 144KB에 불과합니다 . 이것이 반응형 이미지가 중요한 이유이며 중요한 이미지를 조기에 미리 로드하는 것(
preload
포함)입니다.빠른 팁 : 페이지에서 LCP로 간주되는 항목을 찾으려면 DevTools에서 성능 패널의 "타이밍" 아래에 있는 LCP 배지 위로 마우스를 가져갈 수 있습니다( 감사합니다, Tim Kadlec !).
- 첫 번째 입력 지연 ( FID ) < 100ms.
UI의 응답 성을 측정합니다. 즉, 탭이나 클릭과 같은 개별 사용자 입력 이벤트에 반응할 수 있기 전에 브라우저가 다른 작업으로 얼마나 시간 을 많이 썼는지 측정합니다. 특히 페이지 로드 중에 메인 스레드가 사용 중이어서 발생하는 지연을 캡처하도록 설계되었습니다.
목표는 모든 상호 작용에 대해 50–100ms 이내로 유지하는 것입니다. 거기에 도달하려면 긴 작업을 식별하고(>50ms 동안 기본 스레드 차단) 이를 분할하고 번들을 여러 청크로 코드 분할하고 JavaScript 실행 시간을 줄이고 데이터 가져오기를 최적화하고 타사의 스크립트 실행을 연기해야 합니다. , JavaScript를 웹 작업자와 함께 백그라운드 스레드로 이동하고 점진적 수화를 사용하여 SPA에서 재수화 비용을 줄입니다.빠른 팁 : 일반적으로 더 나은 FID 점수를 얻기 위한 신뢰할 수 있는 전략은 더 큰 번들을 더 작은 번들로 나누고 사용자가 필요할 때 필요로 하는 것을 제공 하여 기본 스레드에서 작업을 최소화하여 사용자 상호 작용이 지연되지 않도록 하는 것입니다. . 이에 대해서는 아래에서 자세히 다루겠습니다.
- 누적 레이아웃 이동 ( CLS ) < 0.1.
부드럽고 자연스러운 상호 작용을 보장하기 위해 UI의 시각적 안정성 을 측정합니다. 즉, 페이지 수명 동안 발생하는 모든 예상치 못한 레이아웃 이동에 대한 모든 개별 레이아웃 이동 점수의 합계입니다. 개별 레이아웃 이동은 이미 표시되었던 요소가 페이지에서 위치를 변경할 때마다 발생합니다. 콘텐츠의 크기와 이동 거리에 따라 점수가 매겨집니다.
따라서 시프트가 나타날 때마다 - 예를 들어 대체 글꼴과 웹 글꼴이 서로 다른 글꼴 메트릭을 사용하거나 광고, 포함 또는 iframe이 늦게 제공되거나 이미지/비디오 크기가 예약되지 않거나 늦은 CSS가 강제로 다시 칠하거나 변경 사항이 주입되는 경우 늦은 JavaScript — CLS 점수에 영향을 미칩니다. 좋은 경험을 위한 권장 값은 CLS < 0.1입니다.
Core Web Vitals는 예측 가능한 연간 주기로 시간이 지남에 따라 진화해야 합니다. 첫 해 업데이트의 경우 First Contentful Paint가 Core Web Vitals로 승격되어 FID 임계값이 감소하고 단일 페이지 애플리케이션에 대한 지원이 향상될 것으로 예상할 수 있습니다. We might also see the responding to user inputs after load gaining more weight, along with security, privacy and accessibility (!) considerations.
Related to Core Web Vitals, there are plenty of useful resources and articles that are worth looking into:
- Web Vitals Leaderboard allows you to compare your scores against competition on mobile, tablet, desktop, and on 3G and 4G.
- Core SERP Vitals, a Chrome extension that shows the Core Web Vitals from CrUX in the Google Search Results.
- Layout Shift GIF Generator that visualizes CLS with a simple GIF (also available from the command line).
- web-vitals library can collect and send Core Web Vitals to Google Analytics, Google Tag Manager or any other analytics endpoint.
- Analyzing Web Vitals with WebPageTest, in which Patrick Meenan explores how WebPageTest exposes data about Core Web Vitals.
- Optimizing with Core Web Vitals, a 50-min video with Addy Osmani, in which he highlights how to improve Core Web Vitals in an eCommerce case-study.
- Cumulative Layout Shift in Practice and Cumulative Layout Shift in the Real World are comprehensive articles by Nic Jansma, which cover pretty much everything about CLS and how it correlates with key metrics such as Bounce Rate, Session Time or Rage Clicks.
- What Forces Reflow, with an overview of properties or methods, when requested/called in JavaScript, that will trigger the browser to synchronously calculate the style and layout.
- CSS Triggers shows which CSS properties trigger Layout, Paint and Composite.
- Fixing Layout Instability is a walkthrough of using WebPageTest to identify and fix layout instability issues.
- Cumulative Layout Shift, The Layout Instability Metric, another very detailed guide by Boris Schapira on CLS, how it's calcualted, how to measure and how to optimize for it.
- How To Improve Core Web Vitals, a detailed guide by Simon Hearne on each of the metrics (including other Web Vitals, such as FCP, TTI, TBT), when they occur and how they are measured.
So, are Core Web Vitals the ultimate metrics to follow ? 좀 빠지는. They are indeed exposed in most RUM solutions and platforms already, including Cloudflare, Treo, SpeedCurve, Calibre, WebPageTest (in the filmstrip view already), Newrelic, Shopify, Next.js, all Google tools (PageSpeed Insights, Lighthouse + CI, Search Console etc.) and many others.
However, as Katie Sylor-Miller explains, some of the main problems with Core Web Vitals are the lack of cross-browser support, we don't really measure the full lifecycle of a user's experience, plus it's difficult to correlate changes in FID and CLS with business outcomes.
As we should be expecting Core Web Vitals to evolve, it seems only reasonable to always combine Web Vitals with your custom-tailored metrics to get a better understanding of where you stand in terms of performance.
- 가장 큰 내용이 포함된 페인트 ( LCP ) < 2.5초
- Gather data on a device representative of your audience.
To gather accurate data, we need to thoroughly choose devices to test on. In most companies, that means looking into analytics and creating user profiles based on most common device types. Yet often, analytics alone doesn't provide a complete picture. A significant portion of the target audience might be abandoning the site (and not returning back) just because their experience is too slow, and their devices are unlikely to show up as the most popular devices in analytics for that reason. So, additionally conducting research on common devices in your target group might be a good idea.Globally in 2020, according to the IDC, 84.8% of all shipped mobile phones are Android devices. An average consumer upgrades their phone every 2 years, and in the US phone replacement cycle is 33 months. Average bestselling phones around the world will cost under $200.
A representative device, then, is an Android device that is at least 24 months old , costing $200 or less, running on slow 3G, 400ms RTT and 400kbps transfer, just to be slightly more pessimistic. This might be very different for your company, of course, but that's a close enough approximation of a majority of customers out there. In fact, it might be a good idea to look into current Amazon Best Sellers for your target market. ( Thanks to Tim Kadlec, Henri Helvetica and Alex Russell for the pointers! ).
What test devices to choose then? The ones that fit well with the profile outlined above. It's a good option to choose a slightly older Moto G4/G5 Plus, a mid-range Samsung device (Galaxy A50, S8), a good middle-of-the-road device like a Nexus 5X, Xiaomi Mi A3 or Xiaomi Redmi Note 7 and a slow device like Alcatel 1X or Cubot X19, perhaps in an open device lab. For testing on slower thermal-throttled devices, you could also get a Nexus 4, which costs just around $100.
Also, check the chipsets used in each device and do not over-represent one chipset : a few generations of Snapdragon and Apple as well as low-end Rockchip, Mediatek would be enough (thanks, Patrick!) .
If you don't have a device at hand, emulate mobile experience on desktop by testing on a throttled 3G network (eg 300ms RTT, 1.6 Mbps down, 0.8 Mbps up) with a throttled CPU (5× slowdown). Eventually switch over to regular 3G, slow 4G (eg 170ms RTT, 9 Mbps down, 9Mbps up), and Wi-Fi. To make the performance impact more visible, you could even introduce 2G Tuesdays or set up a throttled 3G/4G network in your office for faster testing.
Keep in mind that on a mobile device, we should be expecting a 4×–5× slowdown compared to desktop machines. Mobile devices have different GPUs, CPU, memory and different battery characteristics. That's why it's important to have a good profile of an average device and always test on such a device.
- Synthetic testing tools collect lab data in a reproducible environment with predefined device and network settings (eg Lighthouse , Calibre , WebPageTest ) and
- Real User Monitoring ( RUM ) tools evaluate user interactions continuously and collect field data (eg SpeedCurve , New Relic — the tools provide synthetic testing, too).
- use Lighthouse CI to track Lighthouse scores over time (it's quite impressive),
- run Lighthouse in GitHub Actions to get a Lighthouse report alongside every PR,
- run a Lighthouse performance audit on every page of a site (via Lightouse Parade), with an output saved as CSV,
- use Lighthouse Scores Calculator and Lighthouse metric weights if you need to dive into more detail.
- Lighthouse is available for Firefox as well, but under the hood it uses the PageSpeed Insights API and generates a report based on a headless Chrome 79 User-Agent.
Luckily, there are many great options that help you automate the collection of data and measure how your website performs over time according to these metrics. Keep in mind that a good performance picture covers a set of performance metrics, lab data and field data:
The former is particularly useful during development as it will help you identify, isolate and fix performance issues while working on the product. The latter is useful for long-term maintenance as it will help you understand your performance bottlenecks as they are happening live — when users actually access the site.
By tapping into built-in RUM APIs such as Navigation Timing, Resource Timing, Paint Timing, Long Tasks, etc., synthetic testing tools and RUM together provide a complete picture of performance in your application. You could use Calibre, Treo, SpeedCurve, mPulse and Boomerang, Sitespeed.io, which all are great options for performance monitoring. Furthermore, with Server Timing header, you could even monitor back-end and front-end performance all in one place.
Note : It's always a safer bet to choose network-level throttlers, external to the browser, as, for example, DevTools has issues interacting with HTTP/2 push, due to the way it's implemented ( thanks, Yoav, Patrick !). For Mac OS, we can use Network Link Conditioner, for Windows Windows Traffic Shaper, for Linux netem, and for FreeBSD dummynet.
As it's likely that you'll be testing in Lighthouse, keep in mind that you can:
- Set up "clean" and "customer" profiles for testing.
While running tests in passive monitoring tools, it's a common strategy to turn off anti-virus and background CPU tasks, remove background bandwidth transfers and test with a clean user profile without browser extensions to avoid skewed results (in Firefox, and in Chrome).However, it's also a good idea to study which browser extensions your customers use frequently, and test with dedicated "customer" profiles as well. In fact, some extensions might have a profound performance impact (2020 Chrome Extension Performance Report) on your application, and if your users use them a lot, you might want to account for it up front. Hence, "clean" profile results alone are overly optimistic and can be crushed in real-life scenarios.
- 동료들과 성과 목표를 공유하십시오.
성과 목표가 팀의 모든 구성원에게 친숙한지 확인하여 선에서 오해를 피하십시오. 디자인, 마케팅 또는 그 사이의 모든 결정은 성능에 영향 을 미치며 전체 팀에 책임과 소유권을 분산하면 나중에 성능 중심 의사 결정이 간소화됩니다. 성능 예산 및 초기에 정의된 우선 순위에 따라 설계 결정을 매핑합니다.
현실적인 목표 설정
- 100밀리초 응답 시간, 60fps.
상호 작용이 원활하게 느껴지도록 인터페이스는 사용자 입력에 응답하는 데 100ms가 있습니다. 그보다 길면 사용자는 앱을 랙으로 인식합니다. 사용자 중심의 성능 모델인 RAIL은 건강한 목표 를 제공합니다. <100밀리초 응답을 허용하려면 페이지는 늦어도 매 <50밀리초 후에 기본 스레드에 제어를 되돌려야 합니다. 예상 입력 대기 시간은 임계값에 도달했는지 알려주며 이상적으로는 50ms 미만이어야 합니다. 애니메이션과 같은 고압 포인트의 경우 할 수 있는 곳에서는 다른 작업을 하지 않는 것이 가장 좋고, 할 수 없는 곳에서는 최소한의 작업을 수행하는 것이 가장 좋습니다.또한 애니메이션의 각 프레임은 16밀리초 미만으로 완료되어야 하므로 초당 60프레임(1초 ÷ 60 = 16.6밀리초), 바람직하게는 10밀리초 미만을 달성해야 합니다. 브라우저가 새 프레임을 화면에 칠하는 데 시간이 필요하기 때문에 코드 실행이 16.6밀리초 표시에 도달하기 전에 완료되어야 합니다. 우리는 120fps(예: iPad Pro의 화면은 120Hz에서 실행됨)에 대한 대화를 시작했으며 Surma는 120fps에 대한 몇 가지 렌더링 성능 솔루션을 다루었지만 아직 우리가 보고 있는 목표는 아닙니다.
성능 기대치는 비관적이지만 인터페이스 디자인은 낙관적이며 유휴 시간을 현명하게 사용하십시오(idlize, idle-until-urgent 및 react-idle 확인). 분명히 이러한 목표는 로드 성능이 아니라 런타임 성능에 적용됩니다.
- FID < 100ms, LCP < 2.5s, TTI < 5s on 3G, 중요 파일 크기 예산 < 170KB(gzipped).
달성하기가 매우 어려울 수 있지만 좋은 궁극적인 목표는 5초 미만의 상호 작용 시간이고 반복 방문의 경우 2초 미만을 목표로 하는 것입니다(서비스 작업자만 가능). 2.5초 미만의 가장 큰 콘텐츠가 포함된 페인트 를 목표로 하고 총 차단 시간 및 누적 레이아웃 이동 을 최소화합니다. 허용 가능한 첫 번째 입력 지연 은 100ms–70ms 미만입니다. 위에서 언급했듯이 우리는 400ms RTT 및 400kbps 전송 속도로 에뮬레이트된 느린 3G 네트워크의 200달러 Android 전화(예: Moto G4)를 기준으로 고려하고 있습니다.웹에서 콘텐츠를 신속하게 전달하기 위한 합리적인 목표를 효과적으로 형성하는 두 가지 주요 제약 조건이 있습니다. 한편으로는 TCP 느린 시작으로 인한 네트워크 전달 제약 이 있습니다. HTML의 처음 14KB(각 1460바이트, 약 14.25KB를 만드는 TCP 패킷 10개)는 문자 그대로 받아들여지지는 않지만 가장 중요한 페이로드 청크이며 첫 번째 왕복에서 전달할 수 있는 예산의 유일한 부분입니다( 모바일 깨우기 시간으로 인해 400ms RTT에서 1초 안에 얻을 수 있는 모든 것입니다.
( 참고 : TCP는 일반적으로 네트워크 연결을 상당히 활용하지 못하기 때문에 Google은 TCP 지연 제어 TCP 흐름 제어 알고리즘인 TCP Bottleneck Bandwidth 및 RRT( BBR )를 개발했습니다. 최신 웹용으로 설계되어 실제 혼잡에 응답합니다. TCP와 같은 패킷 손실 대신 처리량이 더 높고 대기 시간이 짧고 훨씬 빠르며 알고리즘은 다르게 작동합니다.( 감사합니다, Victor, Barry! )
반면에 JavaScript 구문 분석 및 실행 시간으로 인해 메모리와 CPU에 대한 하드웨어 제약 이 있습니다(나중에 자세히 설명하겠습니다). 첫 번째 단락에서 설명한 목표를 달성하려면 JavaScript의 중요한 파일 크기 예산을 고려해야 합니다. 예산이 얼마인지에 대한 의견은 다양하지만(프로젝트의 특성에 따라 크게 다릅니다), 170KB JavaScript gzip으로 압축된 예산은 이미 중급 전화기에서 구문 분석하고 컴파일하는 데 최대 1초가 소요됩니다. 170KB가 압축 해제 시 해당 크기의 3배로 확장된다고 가정하면(0.7MB), 이는 이미 Moto G4/G5 Plus에서 "적절한" 사용자 경험의 죽음의 신호일 수 있습니다.
Wikipedia 웹사이트의 경우 2020년에 전 세계적으로 Wikipedia 사용자의 코드 실행이 19% 빨라졌습니다. 따라서 연간 웹 성능 지표가 안정적으로 유지된다면 환경이 계속 개선됨에 따라 실제로 퇴행 하고 있다는 경고 신호입니다(자세한 내용은 Gilles Dubuc의 블로그 게시물 참조).
동남아시아, 아프리카 또는 인도와 같이 성장하는 시장을 목표로 하려면 매우 다른 제약 조건을 살펴봐야 합니다. Addy Osmani는 소수의 저가, 고품질 장치, 고품질 네트워크 사용 불가, 고가의 모바일 데이터와 같은 주요 피처폰 제약 조건을 이러한 환경에 대한 PRPL-30 예산 및 개발 지침과 함께 다룹니다.
실제로 Google의 Alex Russell은 합리적인 상한선으로 gzip으로 압축된 130~170KB를 목표로 할 것을 권장합니다. 실제 시나리오에서 대부분의 제품은 가깝지 않습니다. 오늘날 중간 번들 크기는 약 452KB 로, 이는 2015년 초에 비해 53.6% 증가한 것입니다. -대화형 .
그러나 번들 크기 예산을 초과할 수도 있습니다. 예를 들어, 우리는 브라우저의 메인 쓰레드의 활동을 기반으로 성능 예산을 설정할 수 있습니다. Calibre, SpeedCurve 및 Bundlesize와 같은 도구를 사용하면 예산을 확인하고 빌드 프로세스에 통합할 수 있습니다.
마지막으로 성능 예산 은 고정된 값이 되어서는 안 됩니다. 네트워크 연결에 따라 성능 예산이 조정되어야 하지만 느린 연결의 페이로드는 사용 방식에 관계없이 훨씬 더 "비쌉니다".
참고 : HTTP/2가 널리 보급되고 5G 및 HTTP/3가 출시되고 휴대폰이 빠르게 발전하고 SPA가 번창하는 시대에 이처럼 경직된 예산을 설정하는 것이 이상하게 들릴 수 있습니다. 그러나 혼잡한 네트워크에서 천천히 발전하는 인프라, 데이터 한도, 프록시 브라우저, 데이터 저장 모드 및 교활한 로밍 요금에 이르기까지 모든 것을 포함하여 네트워크 및 하드웨어의 예측할 수 없는 특성을 처리할 때 합리적으로 들립니다.
환경 정의
- 빌드 도구를 선택하고 설정합니다.
요즘 멋지다고 생각하는 것에 너무 많은 관심을 기울이지 마십시오. Grunt, Gulp, Webpack, Parcel 또는 도구 조합 등 빌드 환경에 따라 달라집니다. 필요한 결과를 얻고 빌드 프로세스를 유지 관리하는 데 문제가 없다면 잘 하고 있는 것입니다.빌드 도구 중에서 Rollup이 계속 인기를 얻고 있고 Snowpack도 마찬가지지만 Webpack은 말 그대로 수백 개의 플러그인을 사용하여 빌드 크기를 최적화할 수 있는 가장 확실한 도구인 것 같습니다. Webpack 로드맵 2021을 주목하십시오.
최근 등장한 가장 주목할만한 전략 중 하나는 중복 코드를 최소화하기 위해 Next.js와 Gatsby에서 Webpack을 사용한 Granular 청킹입니다. 기본적으로 모든 진입점에서 공유되지 않는 모듈은 이를 사용하지 않는 경로에 대해 요청할 수 있습니다. 필요한 것보다 더 많은 코드가 다운로드되므로 결국 오버헤드가 됩니다. Next.js의 세분화된 청크를 사용하면 서버 측 빌드 매니페스트 파일 을 사용하여 다른 진입점에서 사용되는 출력 청크를 결정할 수 있습니다.
SplitChunksPlugin을 사용하면 여러 경로에서 중복 코드를 가져오는 것을 방지하기 위해 여러 조건에 따라 여러 분할 청크가 생성됩니다. 이렇게 하면 탐색 중 페이지 로드 시간과 캐싱이 향상됩니다. Next.js 9.2 및 Gatsby v2.20.7에서 제공됩니다.
Webpack을 시작하는 것은 어려울 수 있습니다. 따라서 Webpack에 대해 자세히 알아보려면 다음과 같은 훌륭한 리소스가 있습니다.
- Webpack 문서는 — 분명히 — 좋은 출발점이며 Webpack — Raja Rao의 The Confusing Bits 및 Andrew Welch의 An Annotated Webpack Config도 마찬가지입니다.
- Sean Larkin은 Webpack: The Core Concepts에 대한 무료 과정을 제공하고 Jeffrey Way는 모두를 위한 Webpack에 대한 환상적인 무료 과정을 공개했습니다. 둘 다 Webpack에 뛰어들기 위한 훌륭한 입문서입니다.
- Webpack Fundamentals는 FrontendMasters에서 출시한 Sean Larkin과 함께 하는 매우 포괄적인 4시간 과정입니다.
- Webpack 예제에는 주제 및 목적별로 분류된 수백 가지의 바로 사용할 수 있는 Webpack 구성이 있습니다. 보너스: 기본 구성 파일을 생성하는 Webpack 구성 구성자도 있습니다.
- Awesome-webpack은 Angular, React 및 프레임워크에 구애받지 않는 프로젝트에 대한 기사, 비디오, 코스, 책 및 예제를 포함하여 유용한 Webpack 리소스, 라이브러리 및 도구의 선별된 목록입니다.
- Webpack을 사용한 빠른 프로덕션 자산 빌드로의 여정은 팀이 RequireJS 기반 JavaScript 빌드 시스템 사용에서 Webpack 사용으로 전환한 방법과 평균 4분 만에 13,200개 이상의 자산을 관리하여 빌드를 최적화한 방법에 대한 Etsy의 사례 연구입니다.
- Webpack 성능 팁은 Ivan Akulov의 금광 스레드로, Webpack에 특별히 초점을 맞춘 팁을 포함하여 많은 성능 중심 팁을 제공합니다.
- Awesome-webpack-perf는 성능을 위한 유용한 Webpack 도구 및 플러그인이 포함된 금광 GitHub 리포지토리입니다. Ivan Akulov도 관리합니다.
- 점진적 향상을 기본값으로 사용합니다.
하지만 세월이 흘러도 프론트 엔드 아키텍처 및 배포의 기본 원칙으로 점진적인 향상을 유지하는 것이 안전한 방법입니다. 먼저 핵심 경험을 설계 및 구축한 다음, 지원 가능한 브라우저를 위한 고급 기능으로 경험을 향상시켜 탄력적인 경험을 만드십시오. 웹사이트가 최적이 아닌 네트워크의 열악한 브라우저에서 열악한 화면을 가진 느린 컴퓨터에서 빠르게 실행되는 경우 적절한 네트워크에서 좋은 브라우저가 있는 빠른 컴퓨터에서만 더 빠르게 실행됩니다.사실, 적응형 모듈 서비스를 통해 우리는 다른 수준으로 점진적인 향상을 가져오고, 저가형 장치에 "라이트" 핵심 경험을 제공하고 고급형 장치에 대한 보다 정교한 기능으로 향상시키는 것 같습니다. 점진적인 향상은 곧 사라지지 않을 것입니다.
- 강력한 성능 기준을 선택하십시오.
네트워크, 열 제한, 캐시 제거, 타사 스크립트, 파서 차단 패턴, 디스크 I/O, IPC 대기 시간, 설치된 확장, 바이러스 백신 소프트웨어 및 방화벽, 백그라운드 CPU 작업, 하드웨어 및 메모리 제약 등 로드에 영향을 미치는 많은 알려지지 않은 사항이 있습니다. L2/L3 캐싱의 차이점, RTTS — JavaScript는 기본적으로 렌더링을 차단하는 웹 글꼴과 너무 많은 메모리를 사용하는 이미지 다음으로 경험 비용이 가장 높습니다. 성능 병목 현상이 서버에서 클라이언트로 이동함에 따라 개발자로서 우리는 이러한 알려지지 않은 모든 사항을 훨씬 더 자세히 고려해야 합니다.이미 임계 경로 HTML/CSS/JavaScript, 라우터, 상태 관리, 유틸리티, 프레임워크 및 애플리케이션 로직이 포함된 170KB 예산으로 네트워크 전송 비용, 구문 분석/컴파일 시간 및 런타임 비용을 철저히 조사해야 합니다. 우리가 선택한 프레임워크. 운 좋게도 지난 몇 년 동안 브라우저가 스크립트를 구문 분석하고 컴파일하는 속도가 크게 향상되었습니다. 그러나 JavaScript 실행은 여전히 주요 병목 현상이므로 스크립트 실행 시간과 네트워크에 세심한 주의를 기울이는 것이 영향을 미칠 수 있습니다.
Tim Kadlec은 최신 프레임워크의 성능에 대한 환상적인 연구를 수행했으며 "JavaScript 프레임워크에는 비용이 있습니다"라는 기사에 요약했습니다. 우리는 종종 독립 실행형 프레임워크의 영향에 대해 이야기하지만 Tim이 언급한 것처럼 실제로 여러 프레임워크를 사용하는 것은 드문 일이 아닙니다. 아마도 Angular의 이전 버전을 사용하는 몇 가지 레거시 애플리케이션과 함께 최신 프레임워크로 천천히 마이그레이션되는 이전 버전의 jQuery일 수 있습니다. 따라서 고급 장치에서도 사용자 경험을 거의 사용할 수 없게 만들 수 있는 JavaScript 바이트 및 CPU 실행 시간의 누적 비용 을 탐색하는 것이 더 합리적입니다.
일반적으로 최신 프레임워크 는 덜 강력한 장치에 우선순위를 두지 않으므로 휴대전화와 데스크톱에서의 경험은 성능 면에서 극적으로 다른 경우가 많습니다. 연구에 따르면 React 또는 Angular를 사용하는 사이트는 다른 사이트보다 CPU에서 더 많은 시간을 소비합니다(물론 React가 Vue.js보다 CPU에서 더 비싸다고 말하는 것은 아닙니다).
Tim에 따르면 한 가지 분명한 사실은 "사이트를 구축하기 위해 프레임워크를 사용하는 경우 최상의 시나리오에서도 초기 성능 측면에서 절충안을 만들고 있는 것입니다."
- 프레임워크 및 종속성을 평가합니다.
이제 모든 프로젝트에 프레임워크가 필요한 것은 아니며 단일 페이지 애플리케이션의 모든 페이지에서 프레임워크를 로드해야 하는 것도 아닙니다. Netflix의 경우 "클라이언트 측에서 React, 여러 라이브러리 및 해당 앱 코드를 제거하면 JavaScript의 총량이 200KB 이상 감소하여 로그아웃한 홈페이지에 대한 Netflix의 상호 작용 시간이 50% 이상 감소했습니다. ." 그런 다음 팀은 사용자가 방문 페이지에서 보낸 시간을 활용하여 사용자가 방문할 가능성이 높은 후속 페이지에 대해 React를 미리 가져왔습니다(자세한 내용은 참조).중요한 페이지에서 기존 프레임워크를 모두 제거하면 어떻게 될까요? Gatsby를 사용하면 정적 HTML 파일에서 Gatsby가 생성한 모든 JavaScript 파일을 제거하는 gatsby-plugin-no-javascript를 확인할 수 있습니다. Vercel에서는 특정 페이지에 대해 프로덕션에서 런타임 JavaScript 비활성화를 허용할 수도 있습니다(실험적).
일단 프레임워크가 선택되면 우리는 최소 몇 년 동안 그 프레임워크를 유지하게 되므로, 프레임워크를 사용해야 하는 경우 선택 사항이 정보에 입각하여 잘 고려되었는지 확인해야 합니다. 신경써.
데이터에 따르면 기본적으로 프레임워크는 상당히 비쌉니다. React 페이지의 58.6%는 1MB 이상의 JavaScript를 제공하고 Vue.js 페이지 로드의 36%는 1.5초 미만의 First Contentful Paint를 사용합니다. Ankur Sethi의 연구에 따르면 "당신의 React 애플리케이션 은 아무리 최적화하더라도 인도의 평균 전화에서 약 1.1초보다 빠르게 로드 되지 않습니다. Angular 앱은 부팅하는 데 항상 최소 2.7초가 걸립니다. Vue 앱 사용자는 사용을 시작하기 전에 최소 1초를 기다려야 합니다." 어쨌든 인도를 주요 시장으로 타겟팅하지 않을 수 있지만 최적이 아닌 네트워크 조건으로 사이트에 액세스하는 사용자는 비슷한 경험을 할 것입니다.
물론 SPA를 빠르게 만드는 것은 가능하지만 즉시 사용 가능한 것은 아니므로 빠르게 만들고 유지 하는 데 필요한 시간과 노력을 고려해야 합니다. 초기에 가벼운 기본 성능 비용을 선택하면 더 쉬울 것입니다.
그렇다면 프레임워크를 선택하는 방법은 무엇입니까? 옵션을 선택하기 전에 크기 에 대한 총 비용 + 초기 실행 시간을 고려하는 것이 좋습니다. Preact, Inferno, Vue, Svelte, Alpine 또는 Polymer와 같은 경량 옵션은 작업을 잘 수행할 수 있습니다. 기준선의 크기는 애플리케이션 코드의 제약 조건을 정의합니다.
Seb Markbage가 언급한 것처럼 프레임워크의 시작 비용을 측정하는 좋은 방법은 먼저 뷰를 렌더링한 다음 뷰를 삭제한 다음 프레임워크가 확장되는 방식을 알려줄 수 있으므로 다시 렌더링 하는 것입니다. 첫 번째 렌더는 지연 컴파일된 코드를 워밍업하는 경향이 있으며, 이는 더 큰 트리가 확장될 때 이점을 얻을 수 있습니다. 두 번째 렌더링은 기본적으로 페이지의 코드 재사용이 페이지의 복잡성이 증가함에 따라 성능 특성에 영향을 미치는 방식에 대한 에뮬레이션입니다.
기능, 접근성, 안정성, 성능, 패키지 생태계 , 커뮤니티, 학습 곡선, 문서, 도구, 추적 기록을 탐색하여 Sacha Greif의 12점 척도 점수 시스템에서 후보자(또는 일반적으로 모든 JavaScript 라이브러리)를 평가할 수 있습니다. , 팀, 호환성, 보안 등.
또한 더 오랜 기간 동안 웹에서 수집된 데이터에 의존할 수 있습니다. 예를 들어, Perf Track은 프레임워크 성능을 대규모로 추적하여 Angular, React, Vue, Polymer, Preact, Ember, Svelte 및 AMP로 구축된 웹사이트에 대한 오리진 집계 Core Web Vitals 점수 를 보여줍니다. Gatsby, Next.js 또는 Create React App으로 구축된 웹사이트와 Nuxt.js(Vue) 또는 Sapper(Svelte)로 구축된 웹사이트를 지정하고 비교할 수도 있습니다.
좋은 출발점은 애플리케이션에 적합한 기본 스택 을 선택하는 것입니다. Gatsby(React), Next.js(React), Vuepress(Vue), Preact CLI 및 PWA 스타터 키트는 평균적인 모바일 하드웨어에서 즉시 로드할 수 있는 합리적인 기본값을 제공합니다. 또한, React 및 Angular에 대한 web.dev 프레임워크별 성능 지침을 살펴보세요( 감사합니다, Phillip! ).
그리고 아마도 단일 페이지 애플리케이션을 모두 구축하는 데 약간 더 신선한 접근 방식을 취할 수 있습니다. Turbolinks는 JSON 대신 HTML을 사용하여 뷰를 렌더링하는 15KB JavaScript 라이브러리입니다. 따라서 링크를 따라갈 때 Turbolinks는 전체 페이지 로드 비용을 발생시키지 않고 자동으로 페이지를 가져오고
<body>
를 바꾸고<head>
를 병합합니다. 스택(Hotwire)에 대한 빠른 설명과 전체 문서를 확인할 수 있습니다.
- 클라이언트 측 렌더링 또는 서버 측 렌더링? 둘 다!
꽤 열띤 대화입니다. 궁극적인 접근 방식은 일종의 프로그레시브 부팅을 설정하는 것입니다. 서버 측 렌더링을 사용하여 빠른 First Contentful Paint를 얻되, First Contentful Paint에 가깝게 대화식 시간을 유지하는 데 필요한 최소한의 JavaScript도 포함합니다. JavaScript가 FCP 이후에 너무 늦게 오는 경우 브라우저는 늦게 발견된 JavaScript를 구문 분석, 컴파일 및 실행하는 동안 기본 스레드를 잠그므로 사이트 또는 응용 프로그램의 상호 작용을 방해합니다.이를 방지하려면 항상 함수 실행 을 별도의 비동기 작업으로 나누고 가능한 경우
requestIdleCallback
을 사용하십시오. WebPack의 동적import()
지원을 사용하여 UI의 지연 로드 부분을 고려하여 사용자가 실제로 필요할 때까지 로드, 구문 분석 및 컴파일 비용을 피하십시오( Addy에게 감사드립니다! ).위에서 언급했듯이 TTI(Time to Interactive)는 탐색과 상호 작용 사이의 시간을 알려줍니다. 구체적으로, 메트릭은 초기 콘텐츠가 렌더링된 후 첫 5초 창을 보고 정의되며, JavaScript 작업은 50ms ( 긴 작업 )보다 오래 걸리지 않습니다. 50ms를 초과하는 작업이 발생하면 5초 창 검색이 다시 시작됩니다. 결과적으로 브라우저는 먼저 Interactive 에 도달했다고 가정하고 Frozen 으로 전환하고 결국에는 Interactive 로 다시 전환합니다.
Interactive 에 도달하면 필요에 따라 또는 시간이 허락하는 한 앱의 중요하지 않은 부분을 부팅할 수 있습니다. 불행히도 Paul Lewis가 지적했듯이 프레임워크에는 일반적으로 개발자에게 표시될 수 있는 우선 순위에 대한 간단한 개념이 없으므로 점진적 부팅은 대부분의 라이브러리 및 프레임워크에서 구현하기가 쉽지 않습니다.
그래도 우리는 거기에 도달하고 있습니다. 요즘에는 우리가 탐색할 수 있는 몇 가지 선택 사항이 있으며 Houssein Djirdeh와 Jason Miller는 Rendering on the Web과 Jason과 Addy의 Modern Front-End Architectures에 대한 글에서 이러한 옵션에 대한 훌륭한 개요를 제공합니다. 아래의 개요는 그들의 뛰어난 작업을 기반으로 합니다.
- 전체 서버 측 렌더링 (SSR)
WordPress와 같은 클래식 SSR에서는 모든 요청이 전적으로 서버에서 처리됩니다. 요청된 콘텐츠는 완성된 HTML 페이지로 반환되며 브라우저는 즉시 이를 렌더링할 수 있습니다. 따라서 예를 들어 SSR 앱은 실제로 DOM API를 사용할 수 없습니다. First Contentful Paint와 Time to Interactive 사이의 간격은 일반적으로 작으며 HTML이 브라우저로 스트리밍되는 즉시 페이지를 렌더링할 수 있습니다.이것은 브라우저가 응답을 받기 전에 처리되기 때문에 클라이언트에서 데이터 가져오기 및 템플릿을 위한 추가 왕복을 방지합니다. 그러나 우리는 더 긴 서버 생각 시간 과 결과적으로 Time To First Byte로 끝나고 최신 응용 프로그램의 반응형 및 풍부한 기능을 사용하지 않습니다.
- 정적 렌더링
제품을 단일 페이지 애플리케이션으로 구축하지만 모든 페이지는 구축 단계로 최소한의 JavaScript를 사용하여 정적 HTML로 미리 렌더링됩니다. 이는 정적 렌더링을 사용하여 가능한 모든 URL에 대해 개별 HTML 파일을 미리 생성한다는 것을 의미합니다. 이는 많은 응용 프로그램에서 감당할 수 없는 것입니다. 그러나 페이지의 HTML을 즉석에서 생성할 필요가 없기 때문에 일관되게 빠른 첫 번째 바이트까지의 시간을 달성할 수 있습니다. 따라서 랜딩 페이지를 빠르게 표시한 다음 후속 페이지에 대한 SPA 프레임워크를 미리 가져올 수 있습니다. Netflix는 이 접근 방식을 채택하여 로딩 및 상호 작용 시간을 50%까지 줄였습니다. - (Re)Hydration을 사용한 서버 측 렌더링 (범용 렌더링, SSR + CSR)
우리는 SSR과 CSR 접근 방식의 두 가지 장점을 모두 사용하려고 노력할 수 있습니다. 혼합된 수화와 함께 서버에서 반환된 HTML 페이지에는 완전한 클라이언트 측 애플리케이션을 로드하는 스크립트도 포함됩니다. 이상적으로는 (SSR과 같은) 빠른 First Contentful Paint를 달성한 다음 (재)수화로 렌더링을 계속합니다. 불행히도 그런 경우는 드뭅니다. 더 자주 페이지는 준비된 것처럼 보이지만 사용자의 입력에 응답할 수 없어 분노의 클릭과 이탈이 발생합니다.React를 사용하면 Express와 같은 노드 서버에서
ReactDOMServer
모듈을 사용한 다음renderToString
메서드를 호출하여 최상위 구성 요소를 정적 HTML 문자열로 렌더링할 수 있습니다.Vue.js에서는 vue-server-renderer를 사용하여
renderToString
을 사용하여 Vue 인스턴스를 HTML로 렌더링할 수 있습니다. Angular에서는@nguniversal
을 사용하여 클라이언트 요청을 완전히 서버에서 렌더링된 HTML 페이지로 전환할 수 있습니다. 완전히 서버에서 렌더링된 경험은 Next.js(React) 또는 Nuxt.js(Vue)를 사용하여 즉시 달성할 수도 있습니다.접근 방식에는 단점이 있습니다. 결과적으로 더 빠른 서버 측 렌더링을 제공하면서 클라이언트 측 앱의 완전한 유연성을 얻을 수 있지만 First Contentful Paint와 Time To Interactive 사이의 간격이 더 길어 지고 First Input Delay가 증가하게 됩니다. 재수화는 비용이 많이 들고 일반적으로 이 전략만으로는 Time To Interactive를 크게 지연시키기 때문에 충분하지 않습니다.
- 점진적 수화(SSR + CSR)를 사용한 스트리밍 서버 측 렌더링
Time To Interactive와 First Contentful Paint 사이의 간격을 최소화하기 위해 한 번에 여러 요청을 렌더링하고 생성되는 대로 콘텐츠를 청크로 보냅니다 . 따라서 콘텐츠를 브라우저로 보내기 전에 전체 HTML 문자열을 기다릴 필요가 없으므로 Time To First Byte가 향상됩니다.React에서는
renderToString()
을 사용하여 응답을 파이프하고 HTML을 청크로 보낼 수 있습니다. Vue에서는 파이프 및 스트리밍이 가능한 renderToStream()을 사용할 수 있습니다. React Suspense를 사용하면 그 목적으로도 비동기식 렌더링을 사용할 수 있습니다.클라이언트 측에서는 전체 응용 프로그램을 한 번에 부팅하는 대신 구성 요소를 점진적으로 부팅합니다 . 애플리케이션의 섹션은 먼저 코드 분할을 통해 독립 실행형 스크립트로 분할된 다음 점진적으로 수화됩니다(우선 순위에 따라). 사실, 우리는 중요한 구성 요소를 먼저 수화할 수 있고 나머지는 나중에 수화할 수 있습니다. 그런 다음 클라이언트 측 및 서버 측 렌더링의 역할을 구성 요소별로 다르게 정의할 수 있습니다. 그런 다음 일부 구성 요소가 표시되거나 사용자 상호 작용에 필요하거나 브라우저가 유휴 상태일 때까지 일부 구성 요소의 수화를 연기할 수도 있습니다.
Vue의 경우 Markus Oberlehner는 가시성 또는 특정 사용자 상호 작용 시 구성 요소 수화를 가능하게 하는 초기 단계 플러그인인 vue-lazy-hydration뿐만 아니라 사용자 상호 작용 시 수화를 사용하여 SSR 앱의 대화형 시간을 줄이는 방법에 대한 가이드를 게시했습니다. Angular 팀은 Ivy Universal과 함께 점진적인 수분 공급을 위해 노력합니다. Preact 및 Next.js를 사용하여 부분 수화를 구현할 수도 있습니다.
- 삼형제 렌더링
서비스 워커가 있으면 초기/비JS 탐색에 스트리밍 서버 렌더링 을 사용할 수 있으며, 그런 다음 서비스 워커가 설치된 후 탐색을 위해 HTML 렌더링을 수행하도록 할 수 있습니다. 이 경우 서비스 작업자는 콘텐츠를 미리 렌더링하고 동일한 세션에서 새 보기를 렌더링하기 위해 SPA 스타일 탐색을 활성화합니다. 서버, 클라이언트 페이지 및 서비스 작업자 간에 동일한 템플릿 및 라우팅 코드를 공유할 수 있을 때 잘 작동합니다.
- 사전 렌더링이 포함된 CSR
사전 렌더링은 서버 측 렌더링과 유사하지만 서버에서 페이지를 동적으로 렌더링하는 대신 빌드 시 애플리케이션을 정적 HTML로 렌더링합니다. 정적 페이지는 클라이언트 측 JavaScript가 많이 없어도 완전히 대화식이지만 사전 렌더링은 다르게 작동합니다 . 기본적으로 클라이언트 측 애플리케이션의 초기 상태를 빌드 시 정적 HTML로 캡처하는 반면 사전 렌더링에서는 페이지가 대화형이 되도록 클라이언트에서 애플리케이션을 부팅해야 합니다.Next.js를 사용하면 앱을 정적 HTML로 미리 렌더링하여 정적 HTML 내보내기를 사용할 수 있습니다. React를 사용하는 오픈 소스 정적 사이트 생성기인 Gatsby에서는 기본 JS 청크가 미리 로드되고 향후 경로가 미리 가져오는 빌드 중에
renderToStaticMarkup
메서드 대신renderToString
메서드를 사용합니다. 간단한 정적 페이지에는 필요하지 않은 DOM 속성이 없습니다.Vue의 경우 Vuepress를 사용하여 동일한 목표를 달성할 수 있습니다. Webpack과 함께 prerender-loader를 사용할 수도 있습니다. Navi는 정적 렌더링도 제공합니다.
결과적으로 Time To First Byte 및 First Contentful Paint가 향상되었으며 Time To Interactive와 First Contentful Paint 사이의 간격이 줄어듭니다. 내용이 많이 변경될 것으로 예상되는 경우 접근 방식을 사용할 수 없습니다. 또한 모든 페이지를 생성하려면 모든 URL을 미리 알아야 합니다. 따라서 일부 구성 요소는 사전 렌더링을 사용하여 렌더링될 수 있지만 동적인 것이 필요한 경우 콘텐츠를 가져오기 위해 앱에 의존해야 합니다.
- 전체 클라이언트 측 렌더링 (CSR)
모든 로직, 렌더링 및 부팅은 클라이언트에서 수행됩니다. 결과는 일반적으로 Time To Interactive와 First Contentful Paint 사이에 큰 차이가 있습니다. 결과적으로 모든 것을 렌더링하려면 전체 앱을 클라이언트에서 부팅해야 하므로 애플리케이션 이 느려지는 경우가 많습니다 .JavaScript에는 성능 비용이 있으므로 응용 프로그램과 함께 JavaScript의 양이 증가함에 따라 JavaScript의 영향을 길들이기 위해서는 JavaScript의 공격적인 코드 분할 및 지연이 절대적으로 필요합니다. 이러한 경우에는 상호 작용이 많이 필요하지 않은 경우 일반적으로 서버 측 렌더링 이 더 나은 접근 방식입니다. 옵션이 아닌 경우 앱 셸 모델을 사용하는 것이 좋습니다.
일반적으로 SSR은 CSR보다 빠릅니다. 그러나 여전히 많은 앱에서 매우 자주 구현됩니다.
그래서, 클라이언트 측 또는 서버 측? 일반적으로 완전한 클라이언트 측 프레임워크의 사용을 절대적으로 필요한 페이지로 제한 하는 것이 좋습니다. 고급 응용 프로그램의 경우 서버 측 렌더링에만 의존하는 것도 좋은 생각이 아닙니다. 서버 렌더링과 클라이언트 렌더링 모두 제대로 수행되지 않으면 재앙이 됩니다.
CSR이든 SSR이든 상관없이 중요한 픽셀을 가능한 한 빨리 렌더링하고 해당 렌더링과 Time To Interactive 사이의 간격을 최소화해야 합니다. 페이지가 많이 변경되지 않으면 사전 렌더링을 고려하고 가능한 경우 프레임워크 부팅을 연기합니다. 서버 측 렌더링을 사용하여 HTML을 청크로 스트리밍 하고 클라이언트 측 렌더링을 위해 점진적인 수화 를 구현하고 가시성, 상호 작용 또는 유휴 시간에 수화하여 두 가지 장점을 모두 얻을 수 있습니다.
- 전체 서버 측 렌더링 (SSR)
- 우리는 얼마나 정적으로 봉사할 수 있습니까?
큰 응용 프로그램에서 작업하든 작은 사이트에서 작업하든 간에, 즉석에서 동적으로 생성되는 것보다 CDN(예: JAM 스택)에서 정적으로 제공 할 수 있는 콘텐츠를 고려하는 것이 좋습니다. 개인화 옵션이 많은 수천 개의 제품과 수백 개의 필터가 있더라도 여전히 중요한 방문 페이지를 정적으로 제공하고 선택한 프레임워크에서 이러한 페이지를 분리하고 싶을 수 있습니다.많은 정적 사이트 생성기가 있으며 생성되는 페이지는 종종 매우 빠릅니다. The more content we can pre-build ahead of time instead of generating page views on a server or client at request time, the better performance we will achieve.
In Building Partially Hydrated, Progressively Enhanced Static Websites, Markus Oberlehner shows how to build out websites with a static site generator and an SPA, while achieving progressive enhancement and a minimal JavaScript bundle size. Markus uses Eleventy and Preact as his tools, and shows how to set up the tools, add partial hydration, lazy hydration, client entry file, configure Babel for Preact and bundle Preact with Rollup — from start to finish.
With JAMStack used on large sites these days, a new performance consideration appeared: the build time . In fact, building out even thousands of pages with every new deploy can take minutes, so it's promising to see incremental builds in Gatsby which improve build times by 60 times , with an integration into popular CMS solutions like WordPress, Contentful, Drupal, Netlify CMS and others.
Also, Next.js announced ahead-of-time and incremental static generation, which allows us to add new static pages at runtime and update existing pages after they've been already built, by re-rendering them in the background as traffic comes in.
Need an even more lightweight approach? In his talk on Eleventy, Alpine and Tailwind: towards a lightweight Jamstack, Nicola Goutay explains the differences between CSR, SSR and everything-in-between, and shows how to use a more lightweight approach — along with a GitHub repo that shows the approach in practice.
- Consider using PRPL pattern and app shell architecture.
Different frameworks will have different effects on performance and will require different strategies of optimization, so you have to clearly understand all of the nuts and bolts of the framework you'll be relying on. When building a web app, look into the PRPL pattern and application shell architecture. The idea is quite straightforward: Push the minimal code needed to get interactive for the initial route to render quickly, then use service worker for caching and pre-caching resources and then lazy-load routes that you need, asynchronously.
- Have you optimized the performance of your APIs?
APIs are communication channels for an application to expose data to internal and third-party applications via endpoints . When designing and building an API, we need a reasonable protocol to enable the communication between the server and third-party requests. Representational State Transfer ( REST ) is a well-established, logical choice: it defines a set of constraints that developers follow to make content accessible in a performant, reliable and scalable fashion. Web services that conform to the REST constraints, are called RESTful web services .As with good ol' HTTP requests, when data is retrieved from an API, any delay in server response will propagate to the end user, hence delaying rendering . When a resource wants to retrieve some data from an API, it will need to request the data from the corresponding endpoint. A component that renders data from several resources, such as an article with comments and author photos in each comment, may need several roundtrips to the server to fetch all the data before it can be rendered. Furthermore, the amount of data returned through REST is often more than what is needed to render that component.
If many resources require data from an API, the API might become a performance bottleneck. GraphQL provides a performant solution to these issues. Per se, GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. Unlike REST, GraphQL can retrieve all data in a single request , and the response will be exactly what is required, without over or under -fetching data as it typically happens with REST.
In addition, because GraphQL is using schema (metadata that tells how the data is structured), it can already organize data into the preferred structure, so, for example, with GraphQL, we could remove JavaScript code used for dealing with state management, producing a cleaner application code that runs faster on the client.
If you want to get started with GraphQL or encounter performance issues, these articles might be quite helpful:
- A GraphQL Primer: Why We Need A New Kind Of API by Eric Baer,
- A GraphQL Primer: The Evolution Of API Design by Eric Baer,
- Designing a GraphQL server for optimal performance by Leonardo Losoviz,
- GraphQL performance explained by Wojciech Trocki.
- Will you be using AMP or Instant Articles?
Depending on the priorities and strategy of your organization, you might want to consider using Google's AMP or Facebook's Instant Articles or Apple's Apple News. You can achieve good performance without them, but AMP does provide a solid performance framework with a free content delivery network (CDN), while Instant Articles will boost your visibility and performance on Facebook.The seemingly obvious benefit of these technologies for users is guaranteed performance , so at times they might even prefer AMP-/Apple News/Instant Pages-links over "regular" and potentially bloated pages. For content-heavy websites that are dealing with a lot of third-party content, these options could potentially help speed up render times dramatically.
Unless they don't. According to Tim Kadlec, for example, "AMP documents tend to be faster than their counterparts, but they don't necessarily mean a page is performant. AMP is not what makes the biggest difference from a performance perspective."
A benefit for the website owner is obvious: discoverability of these formats on their respective platforms and increased visibility in search engines.
Well, at least that's how it used to be. As AMP is no longer a requirement for Top Stories , publishers might be moving away from AMP to a traditional stack instead ( thanks, Barry! ).
Still, you could build progressive web AMPs, too, by reusing AMPs as a data source for your PWA. 단점? Obviously, a presence in a walled garden places developers in a position to produce and maintain a separate version of their content, and in case of Instant Articles and Apple News without actual URLs (thanks Addy, Jeremy!) .
- Choose your CDN wisely.
As mentioned above, depending on how much dynamic data you have, you might be able to "outsource" some part of the content to a static site generator, pushing it to a CDN and serving a static version from it, thus avoiding requests to the server. In fact, some of those generators are actually website compilers with many automated optimizations provided out of the box. As compilers add optimizations over time, the compiled output gets smaller and faster over time.Notice that CDNs can serve (and offload) dynamic content as well. So, restricting your CDN to static assets is not necessary. Double-check whether your CDN performs compression and conversion (eg image optimization and resizing at the edge), whether they provide support for servers workers, A/B testing, as well as edge-side includes, which assemble static and dynamic parts of pages at the CDN's edge (ie the server closest to the user), and other tasks. Also, check if your CDN supports HTTP over QUIC (HTTP/3).
Katie Hempenius has written a fantastic guide to CDNs that provides insights on how to choose a good CDN , how to finetune it and all the little things to keep in mind when evaluating one. In general, it's a good idea to cache content as aggressively as possible and enable CDN performance features like Brotli, TLS 1.3, HTTP/2, and HTTP/3.
Note : based on research by Patrick Meenan and Andy Davies, HTTP/2 prioritization is effectively broken on many CDNs, so be careful when choosing a CDN. Patrick has more details in his talk on HTTP/2 Prioritization ( thanks, Barry! ).
When choosing a CDN, you can use these comparison sites with a detailed overview of their features:
- Cloudfront, Aazure, KeyCDN, Fastly, Verizon, Stackpach, Akamai 및 기타 여러 업체에 대한 CDN 비교 매트릭스인 CDN 비교.
- CDN Perf는 매일 3억 건의 테스트를 수집 및 분석하여 CDN에 대한 쿼리 속도를 측정하며 모든 결과는 전 세계 사용자의 RUM 데이터를 기반으로 합니다. 또한 DNS 성능 비교 및 클라우드 성능 비교를 확인하십시오.
- CDN Planet Guides는 Serve Stale, Purge, Origin Shield, Prefetch 및 Compression과 같은 특정 주제에 대한 CDN 개요를 제공합니다.
- Web Almanac: CDN 채택 및 사용은 최고의 CDN 제공업체, RTT 및 TLS 관리, TLS 협상 시간, HTTP/2 채택 등에 대한 통찰력을 제공합니다. (안타깝게도 데이터는 2019년의 것입니다.)
자산 최적화
- 일반 텍스트 압축에는 Brotli를 사용하십시오.
2015년에 Google은 모든 최신 브라우저에서 지원되는 새로운 오픈 소스 무손실 데이터 형식인 Brotli를 도입했습니다. Brotli용 인코더와 디코더를 구현하는 오픈 소스 Brotli 라이브러리에는 인코더에 대해 미리 정의된 11가지 품질 수준이 있으며 품질 수준이 높을수록 더 나은 압축률을 위해 더 많은 CPU가 필요합니다. 느린 압축은 궁극적으로 더 높은 압축률로 이어지지만 여전히 Brotli는 빠르게 압축을 풉니다. 압축 수준이 4인 Brotli는 Gzip보다 작고 압축 속도가 빠릅니다.실제로 Brotli는 Gzip보다 훨씬 더 효과적인 것으로 보입니다. 의견과 경험은 다르지만 사이트가 이미 Gzip으로 최적화되어 있는 경우 크기 축소 및 FCP 타이밍에서 최소 한 자릿수 개선과 기껏해야 두 자릿수 개선을 기대할 수 있습니다. 사이트에 대한 Brotli 압축 절감 효과를 추정할 수도 있습니다.
브라우저는 사용자가 HTTPS를 통해 웹사이트를 방문하는 경우에만 Brotli를 허용합니다. Brotli는 광범위하게 지원되며 많은 CDN(Akamai, Netlify Edge, AWS, KeyCDN, Fastly(현재는 패스스루), Cloudflare, CDN77)에서 지원하며 아직 지원하지 않는 CDN에서도 Brotli를 활성화할 수 있습니다. (서비스 워커와 함께).
문제는 높은 압축 수준에서 Brotli를 사용하여 모든 자산을 압축하는 데 비용이 많이 들기 때문에 많은 호스팅 제공자가 생성하는 막대한 비용 오버헤드 때문에 전체 자산을 사용할 수 없다는 것입니다. 사실, 가장 높은 압축 수준에서 Brotli는 너무 느려서 서버가 자산을 동적으로 압축할 때까지 응답을 보내기 시작하는 데 걸리는 시간으로 인해 파일 크기의 잠재적인 이득이 무효화될 수 있습니다. (그러나 정적 압축으로 빌드 시간에 시간이 있다면 물론 더 높은 압축 설정이 선호됩니다.)
그러나 이것은 바뀔 수 있습니다. Brotli 파일 형식에는 내장된 정적 사전 이 포함되어 있으며 여러 언어로 된 다양한 문자열을 포함할 뿐만 아니라 해당 단어에 여러 변환을 적용하는 옵션도 지원하여 다양성을 높입니다. 그의 연구에서
Content-Type
Hanau는 "기본값보다 사전의 더 전문화된 하위 집합"을 사용하고 압축기에 HTML, JavaScript 또는 CSS용 사전. 결과는 "제한된 사전 사용 접근 방식을 사용하여 높은 압축 수준에서 웹 콘텐츠를 압축할 때 무시할 수 있는 성능 영향(일반적으로 12%에 비해 1%에서 3% 더 많은 CPU)"이었습니다.게다가 Elena Kirilenko의 연구를 통해 이전 압축 아티팩트를 사용하여 빠르고 효율적인 Brotli 재압축 을 달성할 수 있습니다. Elena에 따르면 " 일단 Brotli를 통해 압축된 자산이 있고 콘텐츠가 미리 사용 가능한 콘텐츠와 유사한 동적 콘텐츠를 즉석에서 압축하려고 하면 압축 시간을 크게 개선할 수 있습니다. "
얼마나 자주 경우입니까? 예를 들어 JavaScript 번들 하위 집합 을 전달하는 경우(예: 코드의 일부가 이미 클라이언트에 캐시되어 있거나 WebBundles와 함께 제공되는 동적 번들이 있는 경우). 또는 미리 알려진 템플릿을 기반으로 하는 동적 HTML 또는 동적으로 부분집합된 WOFF2 글꼴 을 사용합니다. Elena에 따르면 콘텐츠의 10%를 제거하면 압축률이 5.3%, 압축 속도가 39% 향상되며, 콘텐츠의 50%를 제거하면 압축률이 3.2%, 압축률이 26% 빨라집니다.
Brotli 압축이 점점 더 좋아지고 있으므로 정적 자산을 동적으로 압축하는 비용을 우회할 수 있다면 노력할 가치가 있습니다. Brotli는 HTML, CSS, SVG, JavaScript, JSON 등 모든 일반 텍스트 페이로드에 사용할 수 있습니다.
참고 : 2021년 초 현재 HTTP 응답의 약 60%가 텍스트 기반 압축 없이 전달되며, 30.82%는 Gzip으로, 9.1%는 Brotli로 압축합니다(모바일 및 데스크톱 모두). 예를 들어, Angular 페이지의 23.4%는 압축되지 않습니다(gzip 또는 Brotli를 통해). 그러나 종종 압축을 켜는 것은 스위치를 살짝 눌러 성능을 향상시키는 가장 쉬운 방법 중 하나입니다.
전략? 최고 수준에서 Brotli+Gzip을 사용하여 정적 자산을 사전 압축하고 수준 4-6에서 Brotli를 사용하여 즉석에서 (동적) HTML을 압축합니다. 서버가 Brotli 또는 Gzip에 대한 콘텐츠 협상을 올바르게 처리하는지 확인하십시오.
- 적응형 미디어 로딩과 클라이언트 힌트를 사용합니까?
그것은 오래된 뉴스의 땅에서srcset
,sizes
및<picture>
요소와 함께 반응형 이미지를 사용하는 것은 항상 좋은 일입니다. 특히 미디어 풋프린트가 많은 사이트의 경우 적응형 미디어 로딩(이 예에서는 React + Next.js)을 사용하여 한 단계 더 나아가 느린 네트워크 및 저메모리 장치에 가벼운 경험을 제공하고 빠른 네트워크 및 높은 - 기억 장치. React의 맥락에서 우리는 서버의 클라이언트 힌트와 클라이언트의 react-adaptive-hooks를 사용하여 이를 달성할 수 있습니다.반응형 이미지의 미래는 클라이언트 힌트의 광범위한 채택으로 극적으로 바뀔 수 있습니다. 클라이언트 힌트는
DPR
,Viewport-Width
,Width
,Save-Data
,Accept
(이미지 형식 기본 설정 지정용) 등의 HTTP 요청 헤더 필드입니다. 그들은 사용자의 브라우저, 화면, 연결 등의 세부 사항에 대해 서버에 알려야 합니다.결과적으로 서버는 적절한 크기의 이미지 로 레이아웃을 채우는 방법을 결정할 수 있으며 이러한 이미지만 원하는 형식으로 제공할 수 있습니다. 클라이언트 힌트를 사용하여 리소스 선택을 HTML 마크업에서 클라이언트와 서버 간의 요청-응답 협상으로 이동합니다.
Ilya Grigorik이 얼마 전에 언급했듯이 클라이언트 힌트는 그림을 완성합니다. 반응형 이미지의 대안이 아닙니다. "
<picture>
요소는 HTML 마크업에서 필요한 아트 방향 제어를 제공합니다. 클라이언트 힌트는 리소스 선택 자동화를 가능하게 하는 결과 이미지 요청에 대한 주석을 제공합니다. Service Worker는 클라이언트에서 전체 요청 및 응답 관리 기능을 제공합니다."서비스 작업자는 예를 들어 요청에 새 클라이언트 힌트 헤더 값을 추가 하고, URL을 다시 작성하고 이미지 요청을 CDN으로 가리키고, 연결 및 사용자 기본 설정 등에 따라 응답을 조정할 수 있습니다. 이는 이미지 자산뿐만 아니라 거의 모든 다른 요청에 대해서도 마찬가지입니다.
클라이언트 힌트를 지원하는 클라이언트의 경우 이미지에서 42% 바이트 절약을 측정할 수 있고 70번째 백분위수 이상에 대해 1MB 이상 더 적은 바이트를 측정할 수 있습니다. Smashing Magazine에서도 19-32% 개선을 측정할 수 있습니다. 클라이언트 힌트는 Chromium 기반 브라우저에서 지원되지만 Firefox에서는 아직 고려 중입니다.
그러나 일반 반응형 이미지 마크업과 클라이언트 힌트에 대한
<meta>
태그를 모두 제공하는 경우 지원 브라우저는 반응형 이미지 마크업을 평가하고 클라이언트 힌트 HTTP 헤더를 사용하여 적절한 이미지 소스를 요청합니다. - 배경 이미지에 반응형 이미지를 사용합니까?
우리는 반드시 해야 합니다! 이제 Safari 14와 Firefox를 제외한 대부분의 최신 브라우저에서 지원되는image-set
을 사용하여 반응형 배경 이미지도 제공할 수 있습니다.background-image: url("fallback.jpg"); background-image: image-set( "photo-small.jpg" 1x, "photo-large.jpg" 2x, "photo-print.jpg" 600dpi);
기본적으로 우리는 조건부로
1x
설명자가 있는 저해상도 배경 이미지,2x
설명자가 있는 고해상도 이미지,600dpi
설명자가 있는 인쇄 품질 이미지를 제공할 수 있습니다. 그러나 브라우저는 보조 기술에 배경 이미지에 대한 특별한 정보를 제공하지 않으므로 이상적으로는 이러한 사진이 단지 장식일 것입니다. - WebP를 사용합니까?
이미지 압축은 종종 빠른 승리로 간주되지만 실제로는 여전히 많이 활용되지 않습니다. 물론 이미지는 렌더링을 차단하지 않지만 낮은 LCP 점수에 크게 기여하며 매우 자주 사용되는 장치에 비해 너무 무겁고 너무 큽니다.따라서 최소한 이미지에 WebP 형식을 사용하여 탐색할 수 있습니다. 사실, WebP 사가는 작년에 Apple이 Safari 14에서 WebP에 대한 지원을 추가하면서 거의 끝나가고 있습니다. 따라서 수년간의 토론과 토론 끝에 오늘 현재 WebP는 모든 최신 브라우저에서 지원됩니다. 따라서 필요한 경우(Andreas Bovens의 코드 스니펫 참조) 또는 콘텐츠 협상(
Accept
헤더 사용)을 사용하여<picture>
요소와 JPEG 폴백을 사용하여 WebP 이미지를 제공할 수 있습니다.WebP에도 단점 이 없는 것은 아닙니다. WebP 이미지 파일 크기는 동등한 Guetzli 및 Zopfli와 비교되지만 형식은 JPEG와 같은 프로그레시브 렌더링을 지원하지 않습니다. 따라서 WebP 이미지는 네트워크를 통해 더 빨라질 수 있지만 사용자는 좋은 ol' JPEG로 완성된 이미지를 더 빨리 볼 수 있습니다. JPEG를 사용하면 WebP의 경우와 같이 절반이 비어 있는 이미지가 아니라 데이터의 절반 또는 4분의 1로 "적절한" 사용자 경험을 제공하고 나머지는 나중에 로드할 수 있습니다.
결정은 목표에 따라 달라집니다. WebP를 사용하면 페이로드를 줄이고 JPEG를 사용하면 인지된 성능을 개선할 수 있습니다. WebP에 대한 자세한 내용은 Google Pascal Massimino의 WebP Rewind 토크에서 확인할 수 있습니다.
WebP로 변환하려면 WebP Converter, cwebp 또는 libwebp를 사용할 수 있습니다. Ire Aderinokun은 이미지를 WebP로 변환하는 방법에 대한 매우 자세한 자습서도 제공합니다. Josh Comeau도 현대 이미지 형식 수용에 대한 그의 글에서 그렇습니다.
Sketch는 기본적으로 WebP를 지원하며 Photoshop용 WebP 플러그인을 사용하여 Photoshop에서 WebP 이미지를 내보낼 수 있습니다. 그러나 다른 옵션도 사용할 수 있습니다.
WordPress 또는 Joomla를 사용하는 경우 WordPress용 Optimus 및 Cache Enabler 및 Joomla의 자체 지원 확장(Cody Arsenault를 통해)과 같이 WebP 지원을 쉽게 구현하는 데 도움이 되는 확장이 있습니다. React, 스타일이 지정된 구성 요소 또는 gatsby-image를 사용하여
<picture>
요소를 추상화할 수도 있습니다.아 - 뻔뻔한 플러그! — Jeremy Wagner는 WebP에 관한 모든 것에 관심이 있는지 확인하고 싶을 수도 있는 WebP에 대한 Smashing 책을 출판했습니다.
- AVIF를 사용합니까?
큰 소식을 들었을 수도 있습니다. AVIF가 출시되었습니다. AV1 비디오의 키프레임에서 파생된 새로운 이미지 형식입니다. 손실 및 무손실 압축, 애니메이션, 손실 알파 채널을 지원하고 선명한 선과 단색(JPEG의 문제)을 처리할 수 있는 동시에 더 나은 결과를 제공하는 개방형 로열티 프리 형식입니다.사실, WebP 및 JPEG와 비교하여 AVIF는 훨씬 더 나은 성능 을 보여 동일한 DSSIM에서 최대 50%까지 파일 크기를 중앙값으로 절약합니다(인간의 시각을 근사하는 알고리즘을 사용하는 두 개 이상의 이미지 간의 유사성). 실제로 Malte Ubl은 이미지 로딩 최적화에 대한 철저한 게시물에서 AVIF가 "매우 중요한 방식으로 JPEG보다 일관되게 성능이 뛰어납니다. 이것은 JPEG보다 항상 더 작은 이미지를 생성하지 않으며 실제로는 실제 네트워크일 수 있는 WebP와 다릅니다. 점진적 로딩에 대한 지원 부족으로 인한 손실"
아이러니하게도 AVIF는 대형 SVG보다 훨씬 더 나은 성능을 발휘할 수 있지만 물론 SVG를 대체하는 것으로 보여서는 안 됩니다. 또한 HDR 색상 지원을 지원하는 최초의 이미지 형식 중 하나입니다. 더 높은 밝기, 색 비트 깊이 및 색 영역을 제공합니다. 유일한 단점은 현재 AVIF가 프로그레시브 이미지 디코딩(아직?)을 지원하지 않고 Brotli와 유사하게 높은 압축률 인코딩이 디코딩은 빠르지만 현재 상당히 느리다는 것입니다.
AVIF는 현재 Chrome, Firefox 및 Opera에서 지원되며 Safari에서도 곧 지원될 예정입니다(Apple이 AV1을 만든 그룹의 구성원이기 때문에).
그렇다면 요즘 이미지를 제공하는 가장 좋은 방법 은 무엇입니까? 일러스트레이션 및 벡터 이미지의 경우 (압축) SVG가 의심할 여지 없이 최고의 선택입니다. 사진의 경우
picture
요소와 콘텐츠 협상 방법을 사용합니다. AVIF가 지원되는 경우 AVIF 이미지를 보냅니다. 그렇지 않은 경우 먼저 WebP로 폴백하고 WebP가 지원되지 않는 경우 폴백으로 JPEG 또는 PNG로 전환합니다(필요한 경우@media
조건 적용).<picture> <source type="image/avif"> <source type="image/webp"> <img src="image.jpg" alt="Photo" width="450" height="350"> </picture>
솔직히 말해서
picture
요소 내에서 몇 가지 조건을 사용할 가능성이 더 큽니다.<picture> <source type="image/avif" /> <source type="image/webp" /> <source type="image/jpeg" /> <img src="fallback-image.jpg" alt="Photo" width="450" height="350"> </picture>
<picture> <source type="image/avif" /> <source type="image/webp" /> <source type="image/jpeg" /> <img src="fallback-image.jpg" alt="Photo" width="450" height="350"> </picture>
애니메이션 이미지를 정적 이미지로 교체하여 보다 적은 움직임을
prefers-reduced-motion
고객을 위해 더 나아갈 수 있습니다.<picture> <source media="(prefers-reduced-motion: reduce)" type="image/avif"></source> <source media="(prefers-reduced-motion: reduce)" type="image/jpeg"></source> <source type="image/avif"></source> <img src="motion.jpg" alt="Animated AVIF"> </picture>
<picture> <source media="(prefers-reduced-motion: reduce)" type="image/avif"></source> <source media="(prefers-reduced-motion: reduce)" type="image/jpeg"></source> <source type="image/avif"></source> <img src="motion.jpg" alt="Animated AVIF"> </picture>
몇 달 동안 AVIF는 상당한 주목을 받았습니다.
- DevTools의 렌더링 패널에서 WebP/AVIF 폴백을 테스트할 수 있습니다.
- Squoosh, AVIF.io 및 libavif를 사용하여 AVIF 파일을 인코딩, 디코딩, 압축 및 변환할 수 있습니다.
- 작업자의 AVIF 파일을 디코딩하고 캔버스에 결과를 표시하는 Jake Archibald의 AVIF Preact 구성 요소를 사용할 수 있습니다.
- 지원하는 브라우저에만 AVIF를 제공하기 위해 PostCSS 플러그인을 315B 스크립트와 함께 사용하여 CSS 선언에서 AVIF를 사용할 수 있습니다.
- CSS 및 Cloudlare 작업자를 통해 새로운 이미지 형식을 점진적으로 제공하여 반환된 HTML 문서를 동적으로 변경하고
accept
헤더에서 정보를 추론한 다음 적절하게webp/avif
등의 클래스를 추가할 수 있습니다. - AVIF는 이미 Cloudinary에서 사용할 수 있으며(사용 제한 있음) Cloudflare는 이미지 크기 조정에서 AVIF를 지원하며 Netlify에서 사용자 지정 AVIF 헤더로 AVIF를 활성화할 수 있습니다.
- 애니메이션과 관련하여 AVIF는 Safari의
<img src=mp4>
만큼 성능이 뛰어나 GIF 및 WebP보다 성능이 우수하지만 MP4는 여전히 더 나은 성능을 제공합니다. - 일반적으로 애니메이션의 경우 Chromium 기반 브라우저가
<img src=mp4>
를 지원할 것이라고 가정하고 AVC1(h264) > HVC1 > WebP > AVIF > GIF입니다. - AVIF에 대한 자세한 내용은 Netflix의 Aditya Mavlankar의 차세대 이미지 코딩을 위한 AVIF 강연과 Cloudflare의 Kornel Lesinski의 The AVIF Image Format 강연에서 확인할 수 있습니다.
- 모든 AVIF에 대한 훌륭한 참조: AVIF에 대한 Jake Archibald의 포괄적인 게시물이 도착했습니다.
그렇다면 미래의 AVIF는 ? Jon Sneyers는 동의하지 않습니다. AVIF는 Google과 Cloudinary에서 개발한 또 다른 무료 공개 형식인 JPEG XL보다 성능이 60% 더 낮습니다. 사실, JPEG XL은 전반적으로 훨씬 더 나은 성능을 보이는 것 같습니다. 그러나 JPEG XL은 아직 표준화의 마지막 단계에 불과하며 아직 어떤 브라우저에서도 작동하지 않습니다. (좋은 ol' Internet Explorer 9에서 나오는 Microsoft의 JPEG-XR과 혼동하지 마십시오).
- JPEG/PNG/SVG가 제대로 최적화되어 있습니까?
영웅 이미지가 엄청나게 빠르게 로드되는 것이 중요한 랜딩 페이지에서 작업할 때 JPEG가 프로그레시브이고 mozJPEG(스캔 수준을 조작하여 시작 렌더링 시간을 개선함) 또는 Google의 오픈 소스인 Guetzli로 압축되었는지 확인하십시오. 지각 성능에 중점을 두고 Zopfli 및 WebP의 학습을 활용하는 인코더입니다. 유일한 단점: 느린 처리 시간(메가픽셀당 1분의 CPU).PNG의 경우 Pingo를 사용할 수 있고 SVG의 경우 SVGO 또는 SVGOMG를 사용할 수 있습니다. 웹 사이트에서 모든 SVG 자산을 빠르게 미리 보고 복사하거나 다운로드해야 하는 경우 svg-grabber도 이를 수행할 수 있습니다.
모든 단일 이미지 최적화 기사에 명시되어 있지만 벡터 자산을 깨끗하고 긴밀하게 유지하는 것은 항상 언급할 가치가 있습니다. 사용하지 않는 자산을 정리하고 불필요한 메타데이터를 제거하고 아트웍(및 SVG 코드)의 경로 포인트 수를 줄이십시오. ( 고마워, 제레미! )
다음과 같은 유용한 온라인 도구도 있습니다.
- Squoosh를 사용하여 최적의 압축 수준(손실 또는 무손실)에서 이미지를 압축, 크기 조정 및 조작합니다.
- Guetzli.it을 사용하여 Guetzli로 JPEG 이미지를 압축하고 최적화하십시오. 이는 가장자리가 선명하고 색상이 단색인 이미지에 적합합니다(그러나 약간 느릴 수 있음).
- 반응형 이미지 중단점 생성기 또는 Cloudinary 또는 Imgix와 같은 서비스를 사용하여 이미지 최적화를 자동화합니다. 또한 많은 경우에
srcset
및sizes
만 사용하면 상당한 이점을 얻을 수 있습니다. - 반응형 마크업의 효율성을 확인하려면 뷰포트 크기 및 장치 픽셀 비율에 대한 효율성을 측정하는 명령줄 도구인 이미징 힙을 사용할 수 있습니다.
- GitHub 워크플로에 자동 이미지 압축을 추가할 수 있으므로 압축되지 않은 이미지가 프로덕션에 도달할 수 없습니다. 이 작업은 PNG 및 JPG와 함께 작동하는 mozjpeg 및 libvips를 사용합니다.
- 저장 공간을 내부적으로 최적화하기 위해 Dropbox의 새로운 Lepton 형식을 사용하여 JPEG를 평균 22%까지 손실 없이 압축할 수 있습니다.
- 자리 표시자 이미지를 일찍 표시하려면 BlurHash를 사용하세요. BlurHash는 이미지를 가져오고 이 이미지의 자리 표시자를 나타내는 짧은 문자열(20-30자만!)을 제공합니다. 문자열은 JSON 객체의 필드로 쉽게 추가할 수 있을 만큼 충분히 짧습니다.
이미지 최적화만으로는 효과가 없는 경우가 있습니다. 중요한 이미지의 렌더링을 시작하는 데 필요한 시간을 개선하려면 덜 중요한 이미지를 지연 로드 하고 중요한 이미지가 이미 렌더링된 후에 로드할 스크립트를 연기합니다. 가장 확실한 방법은 하이브리드 지연 로딩입니다. 네이티브 지연 로딩과 지연 로드를 사용할 때 사용자 상호 작용을 통해 트리거된 가시성 변경을 감지하는 라이브러리(나중에 살펴볼 IntersectionObserver 사용)입니다. 추가로:
- 중요한 이미지를 미리 로드하여 브라우저가 너무 늦게 발견하지 않도록 하십시오. 배경 이미지의 경우 그보다 더 공격적으로 이미지를 추가하려면
<img src>
를 사용하여 이미지를 일반 이미지로 추가한 다음 화면에서 숨길 수 있습니다. - 예를 들어 돋보기 구성 요소에서 소스를 교환하기 위해
sizes
를 조작하기 위해 미디어 쿼리에 따라 다른 이미지 표시 치수를 지정하여 Sizes 속성으로 이미지 교환을 고려하십시오. - 전경 및 배경 이미지에 대한 예기치 않은 다운로드를 방지하기 위해 이미지 다운로드 불일치를 검토합니다. 기본적으로 로드되지만 표시되지 않을 수 있는 이미지(예: 회전 목마, 아코디언 및 이미지 갤러리)에 주의하십시오.
- 이미지의
width
와height
를 항상 설정해야 합니다. CSS의aspect-ratio
속성과 이미지의 가로 세로 비율과 크기를 설정할 수 있는intrinsicsize
크기 속성에 주의하십시오. 따라서 브라우저는 페이지 로드 중 레이아웃 점프를 피하기 위해 미리 정의된 레이아웃 슬롯을 조기에 예약할 수 있습니다.
모험심을 느낀다면 기본적으로 CDN에 있는 실시간 필터인 Edge 작업자를 사용하여 HTTP/2 스트림을 자르고 재정렬하여 네트워크를 통해 이미지를 더 빠르게 보낼 수 있습니다. 에지 작업자는 사용자가 제어할 수 있는 청크를 사용하는 JavaScript 스트림을 사용하므로(기본적으로 스트리밍 응답을 수정할 수 있는 CDN 에지에서 실행되는 JavaScript임) 이미지 전달을 제어할 수 있습니다.
서비스 워커를 사용하면 유선상의 항목을 제어할 수 없으므로 너무 늦었지만 Edge 워커에서는 작동합니다. 따라서 특정 방문 페이지에 대해 점진적으로 저장된 정적 JPEG 위에 이를 사용할 수 있습니다.
충분하지? 음, 다중 배경 이미지 기술을 사용하여 이미지에 대한 인식 성능을 향상시킬 수도 있습니다. 대비를 사용하여 재생하고 불필요한 세부 사항을 흐리게 처리(또는 색상 제거)하면 파일 크기도 줄일 수 있습니다. 아, 화질을 잃지 않고 작은 사진을 확대 해야합니까? Letsenhance.io 사용을 고려하십시오.
지금까지 이러한 최적화는 기본 사항만 다룹니다. Addy Osmani는 이미지 압축 및 색상 관리의 세부 사항에 대해 매우 자세히 설명하는 Essential Image Optimization에 대한 매우 상세한 가이드를 게시했습니다. 예를 들어, 이미지의 불필요한 부분을 흐리게 처리 하여(가우스 흐림 효과 필터를 적용하여) 파일 크기를 줄일 수 있으며, 결국에는 색상을 제거하거나 사진을 흑백으로 변환하여 크기를 더욱 줄일 수도 있습니다. . 배경 이미지의 경우 Photoshop에서 0~10% 품질로 사진을 내보내는 것도 절대적으로 허용될 수 있습니다.
Smashing Magazine에서는 이미지 이름에 접미사
-opt
를 사용합니다(예:brotli-compression-opt.png
; 이미지에 해당 접미어가 포함될 때마다 팀의 모든 사람들은 이미지가 이미 최적화되었음을 압니다.아, 그리고 웹에서 JPEG-XR을 사용하지 마십시오. "CPU에서 JPEG-XR의 소프트웨어 측 디코딩 처리는 특히 SPA의 맥락에서 바이트 크기 절약의 잠재적인 긍정적 영향을 무효화하고 심지어 더 큽니다." 그러나 Cloudinary/Google의 JPEG XL과 혼합).
- 동영상이 제대로 최적화되어 있습니까?
지금까지 이미지를 다루었지만 좋은 GIF에 대한 대화는 피했습니다. GIF에 대한 우리의 사랑에도 불구하고 이제는 영원히 (적어도 우리 웹사이트와 앱에서) GIF를 버려야 할 때입니다. 렌더링 성능과 대역폭 모두에 영향을 미치는 무거운 애니메이션 GIF를 로드하는 대신 애니메이션 WebP(GIF가 대체)로 전환하거나 반복되는 HTML5 비디오로 완전히 교체하는 것이 좋습니다.이미지와 달리 브라우저는
<video>
콘텐츠를 미리 로드하지 않지만 HTML5 비디오는 GIF보다 훨씬 가볍고 작은 경향이 있습니다. 옵션이 아닌가요? 글쎄, 우리는 적어도 Lossy GIF, gifsicle 또는 giflossy를 사용하여 GIF에 손실 압축을 추가할 수 있습니다.Colin Bendell의 테스트에 따르면 Safari Technology Preview의
img
태그 내의 인라인 비디오는 파일 크기가 매우 작을 뿐만 아니라 GIF에 상응하는 것보다 최소 20배 더 빠르게 표시하고 7배 더 빠르게 디코딩합니다. 단, 다른 브라우저에서는 지원하지 않습니다.좋은 소식의 나라에서 비디오 형식은 수년에 걸쳐 크게 발전 했습니다. 오랫동안 우리는 WebM이 모든 것을 지배하는 형식이 되기를 바랐고 WebP(기본적으로 WebM 비디오 컨테이너 내부에 있는 하나의 정지 이미지)가 오래된 이미지 형식을 대체할 것입니다. 실제로 Safari는 현재 WebP를 지원하고 있지만, 요즘 WebP와 WebM이 지원을 받고 있음에도 불구하고 획기적인 발전은 일어나지 않았습니다.
그래도 우리는 대부분의 최신 브라우저에 WebM을 사용할 수 있습니다.
<!-- By Houssein Djirdeh. https://web.dev/replace-gifs-with-videos/ --> <!-- A common scenartio: MP4 with a WEBM fallback. --> <video autoplay loop muted playsinline> <source src="my-animation.webm" type="video/webm"> <source src="my-animation.mp4" type="video/mp4"> </video>
그러나 아마도 우리는 그것을 완전히 다시 방문할 수 있을 것입니다. 2018년, Alliance of Open Media는 AV1 이라는 새로운 유망한 비디오 형식을 출시했습니다. AV1은 H.265 코덱(H.264의 발전)과 유사한 압축을 갖지만 후자와 달리 AV1은 무료입니다. H.265 라이선스 가격 책정으로 인해 브라우저 공급업체는 성능이 비슷한 AV1을 대신 채택하게 되었습니다. AV1(H.265와 마찬가지로)은 WebM보다 두 배 더 압축 합니다.
실제로 Apple은 현재 HEIF 형식과 HEVC(H.265)를 사용하고 있으며 최신 iOS의 모든 사진과 동영상은 JPEG가 아닌 이러한 형식으로 저장됩니다. HEIF 및 HEVC(H.265)가 웹에 제대로 노출되지 않은 반면(아직?), AV1은 — 그리고 브라우저 지원을 얻고 있습니다. 따라서
<video>
태그에AV1
소스를 추가하는 것이 합리적입니다. 모든 브라우저 공급업체가 참여하는 것처럼 보이기 때문입니다.현재 가장 널리 사용 및 지원되는 인코딩은 MP4 파일에서 제공하는 H.264이므로 파일을 제공하기 전에 MP4가 다중 패스 인코딩으로 처리되고 frei0r iirblur 효과(해당되는 경우)로 흐려지고 moov 원자 메타데이터는 파일의 헤드로 이동되는 반면 서버는 바이트 제공을 허용합니다. Boris Schapira는 FFmpeg가 비디오를 최대로 최적화하기 위한 정확한 지침을 제공합니다. 물론 대안으로 WebM 형식을 제공하는 것도 도움이 될 것입니다.
비디오 렌더링을 더 빠르게 시작해야 하지만 비디오 파일이 여전히 너무 큽 니까? 예를 들어 방문 페이지에 큰 배경 비디오가 있을 때마다? 사용하는 일반적인 기술은 가장 첫 번째 프레임을 먼저 정지 이미지로 표시하거나 비디오의 일부로 해석될 수 있는 크게 최적화된 짧은 반복 세그먼트를 표시한 다음 비디오가 충분히 버퍼링될 때마다 재생을 시작하는 것입니다. 실제 영상. Doug Sillars는 이 경우에 도움이 될 수 있는 배경 비디오 성능에 대한 자세한 가이드를 작성했습니다. ( 고마워, Guy Podjarny! ).
위 시나리오의 경우 반응형 포스터 이미지 를 제공할 수 있습니다. 기본적으로
video
요소는 하나의 이미지만 포스터로 허용하며 이것이 반드시 최적일 필요는 없습니다. 반응형 비디오 포스터(Responsive Video Poster)를 사용할 수 있습니다. JavaScript 라이브러리를 사용하면 다른 화면에 다른 포스터 이미지를 사용할 수 있으며 전환 오버레이와 비디오 자리 표시자의 전체 스타일 제어를 추가할 수 있습니다.연구에 따르면 비디오 스트림 품질은 시청자 행동에 영향을 미칩니다. 실제로 시청자는 시작 지연이 약 2초를 초과하면 비디오를 포기하기 시작합니다. 그 지점을 넘어서면 지연이 1초 증가하면 포기율이 약 5.8% 증가합니다. 따라서 중간 비디오 시작 시간이 12.8초인 것은 놀라운 일이 아닙니다. 비디오의 40%는 최소 1번의 지연이 있고 20%는 최소 2초의 지연된 비디오 재생이 있습니다. 사실 3G에서는 네트워크가 콘텐츠를 제공할 수 있는 것보다 더 빠르게 비디오가 재생되기 때문에 비디오 스톨은 피할 수 없습니다.
그래서, 해결책은 무엇입니까? 일반적으로 작은 화면 장치는 데스크탑을 제공하는 720p 및 1080p를 처리할 수 없습니다. Doug Sillars에 따르면 비디오의 더 작은 버전을 만들고 Javascript를 사용하여 더 작은 화면의 소스를 감지하여 이러한 장치에서 빠르고 부드러운 재생 을 보장할 수 있습니다. 또는 스트리밍 비디오를 사용할 수 있습니다. HLS 비디오 스트림은 적절한 크기의 비디오를 장치에 전달하여 다양한 화면에 대해 다른 비디오를 생성할 필요성을 추상화합니다. 또한 네트워크 속도를 협상하고 사용 중인 네트워크 속도에 맞게 비디오 비트 전송률을 조정합니다.
대역폭 낭비를 피하기 위해 실제로 비디오를 잘 재생할 수 있는 장치에 대해서만 비디오 소스를 추가할 수 있었습니다. 또는
video
태그에서autoplay
속성을 완전히 제거하고 JavaScript를 사용하여 더 큰 화면에 대한autoplay
을 삽입할 수 있습니다. 또한video
에preload="none"
을 추가하여 실제로 파일이 필요할 때까지 비디오 파일을 다운로드 하지 않도록 브라우저에 지시해야 합니다.<!-- Based on Doug Sillars's post. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ --> <video preload="none" playsinline muted loop width="1920" height="1080" poster="poster.jpg"> <source src="video.webm" type="video/webm"> <source src="video.mp4" type="video/mp4"> </video>
그런 다음 실제로 AV1을 지원하는 브라우저를 구체적으로 타겟팅할 수 있습니다.
<!-- Based on Doug Sillars's post. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ --> <video preload="none" playsinline muted loop width="1920" height="1080" poster="poster.jpg"> <source src="video.av1.mp4" type="video/mp4; codecs=av01.0.05M.08"> <source src="video.hevc.mp4" type="video/mp4; codecs=hevc"> <source src="video.webm" type="video/webm"> <source src="video.mp4" type="video/mp4"> </video>
그런 다음 특정 임계값(예: 1000px)에
autoplay
을 다시 추가할 수 있습니다./* By Doug Sillars. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ */ <script> window.onload = addAutoplay(); var videoLocation = document.getElementById("hero-video"); function addAutoplay() { if(window.innerWidth > 1000){ videoLocation.setAttribute("autoplay",""); }; } </script>
비디오 재생 성능은 그 자체로 하나의 이야기이며, 이에 대해 자세히 알아보려면 비디오 전송 메트릭에 대한 세부 정보가 포함된 현재 상태의 비디오 및 비디오 전송 모범 사례에 대한 다른 Doug Sillars 시리즈를 살펴보십시오. , 비디오 사전 로드, 압축 및 스트리밍. 마지막으로 스트리밍 여부를 사용하여 비디오 스트리밍이 얼마나 느리거나 빠른지 확인할 수 있습니다.
- 웹 글꼴 전달이 최적화되어 있습니까?
The first question that's worth asking is if we can get away with using UI system fonts in the first place — we just need to make sure to double check that they appear correctly on various platforms. If it's not the case, chances are high that the web fonts we are serving include glyphs and extra features and weights that aren't being used. We can ask our type foundry to subset web fonts or if we are using open-source fonts, subset them on our own with Glyphhanger or Fontsquirrel. We can even automate our entire workflow with Peter Muller's subfont, a command line tool that statically analyses your page in order to generate the most optimal web font subsets, and then inject them into our pages.WOFF2 support is great, and we can use WOFF as fallback for browsers that don't support it — or perhaps legacy browsers could be served system fonts. There are many, many, many options for web font loading, and we can choose one of the strategies from Zach Leatherman's "Comprehensive Guide to Font-Loading Strategies," (code snippets also available as Web font loading recipes).
Probably the better options to consider today are Critical FOFT with
preload
and "The Compromise" method. Both of them use a two-stage render for delivering web fonts in steps — first a small supersubset required to render the page fast and accurately with the web font, and then load the rest of the family async. The difference is that "The Compromise" technique loads polyfill asynchronously only if font load events are not supported, so you don't need to load the polyfill by default. Need a quick win? Zach Leatherman has a quick 23-min tutorial and case study to get your fonts in order.In general, it might be a good idea to use the
preload
resource hint to preload fonts, but in your markup include the hints after the link to critical CSS and JavaScript. Withpreload
, there is a puzzle of priorities, so consider injectingrel="preload"
elements into the DOM just before the external blocking scripts. According to Andy Davies, "resources injected using a script are hidden from the browser until the script executes, and we can use this behaviour to delay when the browser discovers thepreload
hint." Otherwise, font loading will cost you in the first render time.It's a good idea to be selective and choose files that matter most, eg the ones that are critical for rendering or that would help you avoiding visible and disruptive text reflows. In general, Zach advises to preload one or two fonts of each family — it also makes sense to delay some font loading if they are less critical.
It has become quite common to use
local()
value (which refers to a local font by name) when defining afont-family
in the@font-face
rule:/* Warning! Not a good idea! */ @font-face { font-family: Open Sans; src: local('Open Sans Regular'), local('OpenSans-Regular'), url('opensans.woff2') format ('woff2'), url('opensans.woff') format('woff'); }
The idea is reasonable: some popular open-source fonts such as Open Sans are coming pre-installed with some drivers or apps, so if the font is available locally, the browser doesn't need to download the web font and can display the local font immediately. As Bram Stein noted, "though a local font matches the name of a web font, it most likely isn't the same font . Many web fonts differ from their "desktop" version. The text might be rendered differently, some characters may fall back to other fonts, OpenType features can be missing entirely, or the line height may be different."
Also, as typefaces evolve over time, the locally installed version might be very different from the web font, with characters looking very different. So, according to Bram, it's better to never mix locally installed fonts and web fonts in
@font-face
rules. Google Fonts has followed suit by disablinglocal()
on the CSS results for all users, other than Android requests for Roboto.Nobody likes waiting for the content to be displayed. With the
font-display
CSS descriptor, we can control the font loading behavior and enable content to be readable immediately (withfont-display: optional
) or almost immediately (with a timeout of 3s, as long as the font gets successfully downloaded — withfont-display: swap
). (Well, it's a bit more complicated than that.)However, if you want to minimize the impact of text reflows, we could use the Font Loading API (supported in all modern browsers). Specifically that means for every font, we'd creata a
FontFace
object, then try to fetch them all, and only then apply them to the page. This way, we group all repaints by loading all fonts asynchronously, and then switch from fallback fonts to the web font exactly once. Take a look at Zach's explanation, starting at 32:15, and the code snippet):/* Load two web fonts using JavaScript */ /* Zach Leatherman: https://noti.st/zachleat/KNaZEg/the-five-whys-of-web-font-loading-performance#sWkN4u4 */ // Remove existing @font-face blocks // Create two let font = new FontFace("Noto Serif", /* ... */); let fontBold = new FontFace("Noto Serif, /* ... */); // Load two fonts let fonts = await Promise.all([ font.load(), fontBold.load() ]) // Group repaints and render both fonts at the same time! fonts.forEach(font => documents.fonts.add(font));
/* Load two web fonts using JavaScript */ /* Zach Leatherman: https://noti.st/zachleat/KNaZEg/the-five-whys-of-web-font-loading-performance#sWkN4u4 */ // Remove existing @font-face blocks // Create two let font = new FontFace("Noto Serif", /* ... */); let fontBold = new FontFace("Noto Serif, /* ... */); // Load two fonts let fonts = await Promise.all([ font.load(), fontBold.load() ]) // Group repaints and render both fonts at the same time! fonts.forEach(font => documents.fonts.add(font));
To initiate a very early fetch of the fonts with Font Loading API in use, Adrian Bece suggests to add a non-breaking space
nbsp;
at the top of thebody
, and hide it visually witharia-visibility: hidden
and a.hidden
class:<body class="no-js"> <!-- ... Website content ... --> <div aria-visibility="hidden" class="hidden"> <!-- There is a non-breaking space here --> </div> <script> document.getElementsByTagName("body")[0].classList.remove("no-js"); </script> </body>
<body class="no-js"> <!-- ... Website content ... --> <div aria-visibility="hidden" class="hidden"> <!-- There is a non-breaking space here --> </div> <script> document.getElementsByTagName("body")[0].classList.remove("no-js"); </script> </body>
This goes along with CSS that has different font families declared for different states of loading, with the change triggered by Font Loading API once the fonts have successfully loaded:
body:not(.wf-merriweather--loaded):not(.no-js) { font-family: [fallback-system-font]; /* Fallback font styles */ } .wf-merriweather--loaded, .no-js { font-family: "[web-font-name]"; /* Webfont styles */ } /* Accessible hiding */ .hidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: 0; }
body:not(.wf-merriweather--loaded):not(.no-js) { font-family: [fallback-system-font]; /* Fallback font styles */ } .wf-merriweather--loaded, .no-js { font-family: "[web-font-name]"; /* Webfont styles */ } /* Accessible hiding */ .hidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: 0; }
If you ever wondered why despite all your optimizations, Lighthouse still suggests to eliminate render-blocking resources (fonts), in the same article Adrian Bece provides a few techniques to make Lighthouse happy, along with a Gatsby Omni Font Loader, a performant asynchronous font loading and Flash Of Unstyled Text (FOUT) handling plugin for Gatsby.
Now, many of us might be using a CDN or a third-party host to load web fonts from. In general, it's always better to self-host all your static assets if you can, so consider using google-webfonts-helper, a hassle-free way to self-host Google Fonts. And if it's not possible, you can perhaps proxy the Google Font files through the page origin.
It's worth noting though that Google is doing quite a bit of work out of the box, so a server might need a bit of tweaking to avoid delays ( thanks, Barry! )
This is quite important especially as since Chrome v86 (released October 2020), cross-site resources like fonts can't be shared on the same CDN anymore — due to the partitioned browser cache. This behavior was a default in Safari for years.
But if it's not possible at all, there is a way to get to the fastest possible Google Fonts with Harry Roberts' snippet:
<!-- By Harry Roberts. https://csswizardry.com/2020/05/the-fastest-google-fonts/ - 1. Preemptively warm up the fonts' origin. - 2. Initiate a high-priority, asynchronous fetch for the CSS file. Works in - most modern browsers. - 3. Initiate a low-priority, asynchronous fetch that gets applied to the page - only after it's arrived. Works in all browsers with JavaScript enabled. - 4. In the unlikely event that a visitor has intentionally disabled - JavaScript, fall back to the original method. The good news is that, - although this is a render-blocking request, it can still make use of the - preconnect which makes it marginally faster than the default. --> <!-- [1] --> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <!-- [2] --> <link rel="preload" as="style" href="$CSS&display=swap" /> <!-- [3] --> <link rel="stylesheet" href="$CSS&display=swap" media="print" onload="this.media='all'" /> <!-- [4] --> <noscript> <link rel="stylesheet" href="$CSS&display=swap" /> </noscript>
Harry's strategy is to pre-emptively warm up the fonts' origin first. Then we initiate a high-priority, asynchronous fetch for the CSS file. Afterwards, we initiate a low-priority, asynchronous fetch that gets applied to the page only after it's arrived (with a print stylesheet trick). Finally, if JavaScript isn't supported, we fall back to the original method.
Ah, talking about Google Fonts: you can shave up to 90% of the size of Google Fonts requests by declaring only characters you need with
&text
. Plus, the support for font-display was added recently to Google Fonts as well, so we can use it out of the box.그래도 조심스러운 한마디.
font-display: optional
을 사용하는 경우 웹 글꼴 요청을 일찍 트리거하므로preload
도 사용하는 것이 차선책일 수 있습니다(가져와야 하는 다른 중요한 경로 리소스가 있는 경우 네트워크 정체 가 발생함). 더 빠른 교차 출처 글꼴 요청을 위해preconnect
을 사용하지만 다른 출처에서 글꼴을 사전 로드하면 네트워크 경합이 발생할 수 있으므로 사전preload
에 주의하십시오. 이러한 모든 기술은 Zach의 웹 글꼴 로딩 레시피에서 다룹니다.반면에 사용자가 접근성 기본 설정에서 동작 줄이기를 활성화했거나 데이터 절약 모드 를 선택한 경우 웹 글꼴(또는 최소한 두 번째 단계 렌더링)을 선택 해제하는 것이 좋습니다(
Save-Data
헤더 참조). , 또는 사용자의 연결 속도가 느린 경우(네트워크 정보 API를 통해).사용자가 데이터 저장 모드를 선택한 경우(다른 사용 사례도 있음) 글꼴 선언을 정의 하지 않기 위해
prefers-reduced-data
CSS 미디어 쿼리를 사용할 수도 있습니다. 미디어 쿼리는 기본적으로 클라이언트 힌트 HTTP 확장의Save-Data
요청 헤더가 CSS와 함께 사용할 수 있도록 설정/해제되어 있는지 노출합니다. 현재 플래그 뒤의 Chrome 및 Edge에서만 지원됩니다.측정항목? 웹 글꼴 로드 성능을 측정하려면 모든 텍스트 표시 메트릭(모든 글꼴이 로드되고 모든 콘텐츠가 웹 글꼴로 표시되는 순간), 실제 기울임꼴까지의 시간 및 첫 번째 렌더링 후 웹 글꼴 리플로우 수 를 고려하십시오. 분명히 두 메트릭이 모두 낮을수록 성능이 더 좋습니다.
가변 글꼴 은 어떻습니까? 가변 글꼴에는 상당한 성능 고려 사항이 필요할 수 있습니다. 그것들은 우리에게 인쇄상의 선택을 위한 훨씬 더 넓은 디자인 공간을 제공하지만, 많은 개별 파일 요청에 반대되는 단일 직렬 요청의 비용이 듭니다.
가변 글꼴은 글꼴 파일의 전체 결합 파일 크기를 크게 줄이지 만 단일 요청이 느려 페이지의 모든 콘텐츠 렌더링을 차단할 수 있습니다. 따라서 글꼴을 문자 집합으로 하위 설정하고 분할하는 것은 여전히 중요합니다. 그러나 좋은 측면에서는 가변 글꼴을 사용하면 기본적으로 정확히 하나의 리플로우가 발생하므로 다시 그리기를 그룹화하는 데 JavaScript가 필요하지 않습니다.
그렇다면 방탄 웹폰트 로딩 전략 은 무엇일까요? 글꼴을 하위 집합으로 만들고 2단계 렌더링을 위해 준비하고
font-display
설명자로 선언하고 글꼴 로드 API를 사용하여 다시 그리기를 그룹화하고 글꼴을 영구 서비스 작업자의 캐시에 저장합니다. 첫 번째 방문 시 차단 외부 스크립트 직전에 스크립트 사전 로드를 삽입합니다. 필요한 경우 Bram Stein의 Font Face Observer로 대체할 수 있습니다. 글꼴 로드 성능을 측정하는 데 관심이 있는 경우 Andreas Marschke는 Font API 및 UserTiming API를 사용한 성능 추적을 살펴봅니다.마지막으로
unicode-range
를 포함하여 큰 글꼴을 더 작은 언어별 글꼴로 나누고 Monica Dinculescu의 글꼴 스타일 일치자를 사용하여 폴백과 글꼴 간의 크기 불일치로 인한 레이아웃 변화를 최소화합니다. 웹 글꼴.또는 대체 글꼴에 대한 웹 글꼴을 에뮬레이트하기 위해 @font-face 설명자를 사용하여 글꼴 메트릭을 재정의할 수 있습니다(데모, Chrome 87에서 활성화됨). (그러나 복잡한 글꼴 스택으로 인해 조정이 복잡합니다.)
미래가 밝습니까? 점진적 글꼴 강화를 통해 궁극적으로 "특정 페이지에서 글꼴의 필수 부분만 다운로드하고 해당 글꼴에 대한 후속 요청에 대해 후속 페이지에서 요구되는 추가 글리프 세트로 원본 다운로드를 동적으로 '패치'할 수 있습니다. 보기"로 Jason Pamental이 설명합니다. 증분 전송 데모는 이미 사용 가능하며 작업이 진행 중입니다.
빌드 최적화
- 우선 순위를 정의했습니까?
무엇을 다루고 있는지 먼저 파악하는 것이 좋습니다. 모든 자산(JavaScript, 이미지, 글꼴, 타사 스크립트 및 캐러셀, 복잡한 인포그래픽 및 멀티미디어 콘텐츠와 같은 페이지의 "고가" 모듈)의 인벤토리 를 실행하고 그룹으로 분류합니다.스프레드시트를 설정합니다 . 레거시 브라우저에 대한 기본 핵심 경험(즉, 완전히 액세스 가능한 핵심 콘텐츠), 유능한 브라우저에 대한 향상된 경험(즉, 풍부하고 완전한 경험) 및 추가 기능 (절대적으로 필요하지 않으며 다음과 같이 지연 로드될 수 있는 자산)을 정의합니다. 웹 글꼴, 불필요한 스타일, 캐러셀 스크립트, 비디오 플레이어, 소셜 미디어 위젯, 큰 이미지). 몇 년 전에 우리는 이 접근 방식을 자세히 설명하는 "Smashing Magazine의 성능 향상"에 대한 기사를 게시했습니다.
성능을 최적화할 때 우선 순위를 반영해야 합니다. 핵심 경험을 즉시 로드한 다음 개선 사항 을 로드한 다음 추가 기능을 로드하십시오.
- 프로덕션에서 기본 JavaScript 모듈을 사용합니까?
핵심 경험을 레거시 브라우저로 보내고 향상된 경험을 최신 브라우저로 보내는 최신 기술을 기억하십니까? 이 기술의 업데이트된 변형은 모듈/노모듈 패턴이라고도 하는 ES2017+<script type="module">
을 사용할 수 있습니다(Jeremy Wagner가 차등 서비스 로 도입하기도 함).아이디어는 두 개의 개별 JavaScript 번들 을 컴파일하고 제공하는 것입니다. "일반" 빌드, Babel-transforms 및 polyfills가 있고 실제로 필요로 하는 레거시 브라우저에만 제공하고 변환 또는 변환이 없는 다른 번들(동일한 기능) 폴리필.
결과적으로 브라우저가 처리해야 하는 스크립트의 양을 줄여 메인 스레드의 차단을 줄이는 데 도움이 됩니다. Jeremy Wagner는 Babel 설정부터 Webpack에서 수정해야 할 사항, 이 모든 작업의 이점에 이르기까지 차등 서비스와 빌드 파이프라인에서 설정하는 방법에 대한 포괄적인 기사를 게시했습니다.
기본 JavaScript 모듈 스크립트는 기본적으로 지연되므로 HTML 구문 분석이 수행되는 동안 브라우저는 기본 모듈을 다운로드합니다.
하지만 한 가지 경고 사항: 모듈/노모듈 패턴 은 일부 클라이언트에서 역효과를 일으킬 수 있으므로 해결 방법을 고려할 수 있습니다. 그러나 Jeremy의 덜 위험한 차등 서빙 패턴은 사전 로드 스캐너를 우회하여 성능에 영향을 줄 수 있습니다. 예상하다. ( 고마워요, 제레미! )
실제로 Rollup은 모듈을 출력 형식으로 지원하므로 프로덕션 환경에서 코드를 번들링하고 모듈을 배포할 수 있습니다. Parcel은 Parcel 2에서 모듈을 지원합니다. Webpack의 경우 module-nomodule-plugin은 모듈/nomodule 스크립트 생성을 자동화합니다.
참고 : 기능 감지만으로는 해당 브라우저에 제공할 페이로드에 대한 정보에 입각한 결정을 내리기에 충분하지 않습니다. 그 자체로는 브라우저 버전에서 장치 기능을 추론할 수 없습니다. 예를 들어 개발 도상국의 저렴한 Android 휴대전화는 대부분 Chrome을 실행하며 제한된 메모리와 CPU 기능에도 불구하고 겨자를 줄입니다.
결국 Device Memory Client Hints Header를 사용하여 저가형 장치를 보다 안정적으로 타겟팅할 수 있습니다. 작성하는 시점에서 헤더는 Blink에서만 지원됩니다(일반적으로 클라이언트 힌트에 적용됨). Device Memory에는 Chrome에서 사용할 수 있는 JavaScript API도 있으므로 한 가지 옵션은 API를 기반으로 기능을 감지하고 지원되지 않는 경우 모듈/노모듈 패턴 으로 폴백하는 것입니다( 감사합니다, Yoav! ).
- 트리 흔들기, 범위 호이스팅 및 코드 분할을 사용하고 있습니까?
트리 쉐이킹은 실제로 프로덕션에서 사용되는 코드만 포함하여 빌드 프로세스를 정리하고 Webpack에서 사용하지 않는 가져오기를 제거하는 방법입니다. Webpack 및 Rollup을 사용하면 두 도구 모두import
체인을 평면화하고 코드를 손상시키지 않고 하나의 인라인 함수로 변환할 수 있는 위치를 감지할 수 있는 범위 호이스팅도 있습니다. Webpack을 사용하면 JSON Tree Shaking도 사용할 수 있습니다.코드 분할은 요청 시 로드되는 "청크"로 코드베이스를 분할하는 또 다른 Webpack 기능입니다. 모든 JavaScript를 즉시 다운로드, 구문 분석 및 컴파일해야 하는 것은 아닙니다. 코드에서 분할 지점을 정의하면 Webpack이 종속성과 출력된 파일을 처리할 수 있습니다. 이를 통해 초기 다운로드를 작게 유지하고 애플리케이션에서 요청할 때 요청 시 코드를 요청할 수 있습니다. Alexander Kondrov는 Webpack 및 React를 사용한 코드 분할에 대한 환상적인 소개를 제공합니다.
코드 분할 경로를 취한 다음
<link rel="preload">
또는<link rel="prefetch">
를 사용하여 경로를 미리 로드하도록 브라우저에 프롬프트하는 preload-webpack-plugin 사용을 고려하십시오. Webpack 인라인 지시문은 또한preload
/prefetch
에 대한 제어를 제공합니다. (하지만 우선순위 문제에 주의하십시오.)분할점을 정의할 위치는 어디입니까? 사용되는 CSS/JavaScript 청크와 사용되지 않는 청크를 추적합니다. Umar Hansa는 Devtools의 Code Coverage를 사용하여 이를 달성하는 방법을 설명합니다.
단일 페이지 애플리케이션을 다룰 때 페이지를 렌더링하기 전에 앱을 초기화하는 데 약간의 시간이 필요합니다. 설정에는 사용자 정의 솔루션이 필요하지만 초기 렌더링 시간을 단축하기 위한 모듈과 기술에 주의할 수 있습니다. 예를 들어 React 성능을 디버그하고 일반적인 React 성능 문제를 제거하는 방법과 Angular에서 성능을 개선하는 방법은 다음과 같습니다. 일반적으로 대부분의 성능 문제는 앱을 부트스트랩하는 초기 시간부터 발생합니다.
그렇다면 공격적으로 코드를 분할하지만 너무 공격적이지는 않은 가장 좋은 방법은 무엇입니까? Phil Walton에 따르면 "동적 가져오기를 통한 코드 분할 외에도 [우리는] 패키지 수준에서 코드 분할을 사용할 수도 있습니다. 여기서 가져온 각 노드 모듈은 패키지 이름을 기반으로 한 청크에 배치됩니다." Phil은 빌드 방법에 대한 자습서도 제공합니다.
- Webpack의 출력을 향상시킬 수 있습니까?
Webpack은 종종 불가사의한 것으로 간주되기 때문에 Webpack의 출력을 더욱 줄이는 데 유용할 수 있는 Webpack 플러그인이 많이 있습니다. 다음은 좀 더 주의가 필요할 수 있는 좀 더 모호한 것들입니다.흥미로운 것 중 하나는 Ivan Akulov의 스레드에서 가져온 것입니다. 한 번 호출하고 그 결과를 변수에 저장한 다음 해당 변수를 사용하지 않는 함수가 있다고 상상해 보십시오. Tree-shaking은 변수를 제거하지만, 그렇지 않으면 사용될 수 있기 때문에 함수는 제거 하지 않습니다 . 그러나 함수가 어디에도 사용되지 않는 경우 제거하는 것이 좋습니다. 그렇게 하려면 Uglify와 Terser가 지원하는
/*#__PURE__*/
를 함수 호출 앞에 붙입니다.Ivan이 권장하는 다른 도구는 다음과 같습니다.
- purgecss-webpack-plugin은 특히 Bootstrap 또는 Tailwind를 사용할 때 사용하지 않는 클래스를 제거합니다.
- split-chunks-plugin을 사용하여
optimization.splitChunks: 'all'
을 활성화합니다. 이렇게 하면 웹팩이 더 나은 캐싱을 위해 항목 번들을 자동으로 코드 분할합니다. -
optimization.runtimeChunk: true
. 이렇게 하면 webpack의 런타임이 별도의 청크로 이동되며 캐싱도 향상됩니다. - google-fonts-webpack-plugin은 글꼴 파일을 다운로드하므로 서버에서 제공할 수 있습니다.
- workbox-webpack-plugin을 사용하면 모든 웹팩 자산에 대한 사전 캐싱 설정으로 서비스 작업자를 생성할 수 있습니다. 또한 바로 적용할 수 있는 모듈의 종합 가이드인 Service Worker Packages를 확인하십시오. 또는 preload-webpack-plugin을 사용하여 모든 JavaScript 청크에 대한
preload
/prefetch
를 생성합니다. - speed-measure-webpack-plugin은 웹팩 빌드 속도를 측정하여 빌드 프로세스의 어느 단계가 가장 시간이 많이 걸리는지 통찰력을 제공합니다.
- duplicate-package-checker-webpack-plugin은 번들에 동일한 패키지의 여러 버전이 포함되어 있으면 경고합니다.
- 범위 격리를 사용하고 컴파일 시 동적으로 CSS 클래스 이름을 줄입니다.
- JavaScript를 웹 워커로 오프로드할 수 있습니까?
Time-to-Interactive에 대한 부정적인 영향을 줄이려면 무거운 JavaScript를 웹 작업자로 오프로드하는 방법을 살펴보는 것이 좋습니다.코드 기반이 계속 증가함에 따라 UI 성능 병목 현상이 나타나 사용자 경험이 느려집니다. DOM 작업이 기본 스레드에서 JavaScript와 함께 실행되고 있기 때문입니다. 웹 작업자를 사용하면 이러한 값비싼 작업을 다른 스레드에서 실행 중인 백그라운드 프로세스로 이동할 수 있습니다. 웹 작업자의 일반적인 사용 사례는 데이터를 미리 가져오고 나중에 필요할 때 사용할 수 있도록 일부 데이터를 미리 로드하고 저장하는 Progressive Web Apps입니다. 그리고 Comlink를 사용하여 기본 페이지와 작업자 간의 통신을 간소화할 수 있습니다. 아직 해야 할 일이 있지만 우리는 거기에 도달하고 있습니다.
웹 작업자에게 프레임워크 및 앱 로직을 이동하는 다양한 접근 방식을 보여주는 웹 작업자에 대한 몇 가지 흥미로운 사례 연구가 있습니다. 결론: 일반적으로 여전히 몇 가지 문제가 있지만 이미 좋은 사용 사례가 있습니다( Ivan Akulov에게 감사드립니다! ).
Chrome 80부터 JavaScript 모듈의 성능 이점이 있는 웹 작업자를 위한 새로운 모드 인 모듈 작업자가 출시되었습니다.
script type="module"
과 일치하도록 스크립트 로드 및 실행을 변경할 수 있으며 작업자 실행을 차단하지 않고 지연 로드 코드에 동적 가져오기를 사용할 수도 있습니다.시작하는 방법? 다음은 살펴볼 가치가 있는 몇 가지 리소스입니다.
- Surma는 브라우저의 메인 스레드에서 JavaScript를 실행하는 방법과 웹 작업자를 언제 사용해야 하는지에 대한 훌륭한 가이드를 게시했습니다.
- 또한 메인 스레드 아키텍처에 대한 Surma의 이야기를 확인하십시오.
- A Quest to Guarantee Responsiveness(Shubhie Panicker 및 Jason Miller)는 웹 작업자를 사용하는 방법과 웹 작업자를 피해야 하는 경우에 대한 자세한 통찰력을 제공합니다.
- 사용자의 길에서 벗어나기: 웹 작업자를 사용한 버벅거림 감소에서는 웹 작업자 작업에 유용한 패턴, 작업자 간의 효과적인 통신 방법, 메인 스레드에서 복잡한 데이터 처리 처리, 테스트 및 디버그를 강조합니다.
- Workerize를 사용하면 모듈을 웹 작업자로 이동하여 내보낸 기능을 비동기식 프록시로 자동 반영할 수 있습니다.
- Webpack을 사용하는 경우 workerize-loader를 사용할 수 있습니다. 또는 작업자 플러그인을 사용할 수도 있습니다.
DOM은 "스레드로부터 안전"하지 않으며 실행하는 코드는 별도의 파일에 포함되어야 하기 때문에 웹 작업자는 DOM에 액세스할 수 없습니다.
- WebAssembly에 "핫 경로"를 오프로드할 수 있습니까?
C/C++/Rust와 같은 고급 언어 컴파일을 위한 이식 가능한 대상으로 설계된 바이너리 명령 형식인 WebAssembly( WASM )로 계산적으로 많은 작업을 오프로드할 수 있습니다. 브라우저 지원이 놀랍고 최근 JavaScript와 WASM 간의 함수 호출이 빨라짐에 따라 실행 가능하게 되었습니다. 또한 Fastly의 에지 클라우드에서도 지원됩니다.물론 WebAssembly가 JavaScript를 대체해서는 안 되지만 CPU 사용량이 많은 경우 이를 보완할 수 있습니다. 대부분의 웹 앱에서 JavaScript가 더 적합하고 WebAssembly는 게임과 같이 계산 집약적인 웹 앱 에 가장 잘 사용됩니다.
WebAssembly에 대해 더 알고 싶다면:
- Lin Clark은 WebAssembly에 대한 철저한 시리즈를 작성했으며 Milica Mihajlija는 브라우저에서 기본 코드를 실행하는 방법, 그렇게 하려는 이유, JavaScript와 웹 개발의 미래에 대한 모든 의미에 대한 일반적인 개요를 제공합니다.
- WebAssembly를 사용하여 20X까지 웹 앱 속도를 높인 방법(사례 연구)에서는 느린 JavaScript 계산이 컴파일된 WebAssembly로 대체되어 상당한 성능 향상을 가져온 방법에 대한 사례 연구를 강조합니다.
- Patrick Hamann은 WebAssembly의 성장하는 역할에 대해 이야기해 왔으며 WebAssembly에 대한 몇 가지 신화를 폭로하고 문제를 탐구하며 오늘날 애플리케이션에서 실제로 사용할 수 있습니다.
- Google Codelabs는 C에서 네이티브 코드를 가져와 WebAssembly로 컴파일한 다음 JavaScript에서 직접 호출하는 방법을 배우는 60분 과정인 Introduction to WebAssembly를 제공합니다.
- Alex Danilo는 Google I/O 강연에서 WebAssembly와 이것이 어떻게 작동하는지 설명했습니다. 또한 Benedek Gagyi는 WebAssembly에 대한 실제 사례 연구, 특히 팀이 iOS, Android 및 웹사이트에 대한 C++ 코드베이스의 출력 형식으로 WebAssembly를 사용하는 방법을 공유했습니다.
언제 웹 작업자, 웹 어셈블리, 스트림 또는 WebGL JavaScript API를 사용하여 GPU에 액세스 해야 하는지 아직 확실하지 않습니까? JavaScript 가속은 언제 무엇을 사용해야 하는지 설명하는 짧지만 유용한 안내서입니다. 또한 편리한 순서도와 많은 유용한 리소스가 함께 제공됩니다.
- 레거시 브라우저에만 레거시 코드를 제공합니까?
ES2017이 최신 브라우저에서 매우 잘 지원되기 때문에babelEsmPlugin
을 사용하여 대상으로 하는 최신 브라우저에서 지원하지 않는 ES2017+ 기능만 트랜스파일할 수 있습니다.Houssein Djirdeh와 Jason Miller는 최근 JavaScript를 Webpack 및 Rollup과 함께 작동하게 하는 방법과 필요한 도구에 대해 자세히 설명하는 최신 및 레거시 JavaScript를 변환하고 제공하는 방법에 대한 포괄적인 가이드를 게시했습니다. 또한 사이트 또는 앱 번들에서 얼마나 많은 JavaScript를 줄일 수 있는지 예측할 수 있습니다.
JavaScript 모듈은 모든 주요 브라우저에서 지원되므로
script type="module"
을 사용하여 ES 모듈을 지원하는 브라우저가 파일을 로드하도록 하는 반면 이전 브라우저는script nomodule
로 레거시 빌드를 로드할 수 있습니다.요즘에는 변환기나 번들러 없이 브라우저에서 기본적으로 실행되는 모듈 기반 JavaScript를 작성할 수 있습니다.
<link rel="modulepreload">
헤더는 모듈 스크립트의 초기(높은 우선 순위) 로드를 시작하는 방법을 제공합니다. 기본적으로 브라우저가 긴 왕복 시간 동안 할 일이 없도록 가져와야 하는 항목에 대해 알려줌으로써 대역폭 사용을 최대화하는 데 도움이 되는 멋진 방법입니다. 또한 Jake Archibald는 읽을 가치가 있는 ES 모듈에 대해 염두에 두어야 할 문제와 주의할 사항이 포함된 자세한 기사를 게시했습니다.
- 증분 분리로 레거시 코드를 식별하고 다시 작성 합니다.
오래 지속되는 프로젝트는 먼지와 오래된 코드를 모으는 경향이 있습니다. 종속성을 다시 방문하여 최근에 문제를 일으킨 레거시 코드를 리팩토링하거나 다시 작성하는 데 얼마나 많은 시간이 필요한지 평가하십시오. 물론 항상 큰 작업이지만 레거시 코드의 영향을 알게 되면 점진적 분리로 시작할 수 있습니다.먼저, 레거시 코드 호출의 비율이 일정하게 유지되는지 또는 증가하지 않고 감소하는지 추적하는 메트릭을 설정합니다. 공개적으로 팀이 라이브러리를 사용하지 못하도록 하고 풀 요청에 사용되는 경우 CI가 개발자에게 경고하도록 하십시오. 폴리필은 레거시 코드에서 표준 브라우저 기능을 사용하는 재작성된 코드베이스로 전환하는 데 도움이 될 수 있습니다.
- 사용하지 않는 CSS/JS를 식별하고 제거 합니다.
Chrome의 CSS 및 JavaScript 코드 적용을 통해 어떤 코드가 실행/적용되었는지 알 수 있습니다. 적용 범위 기록을 시작하고 페이지에서 작업을 수행한 다음 코드 적용 범위 결과를 탐색할 수 있습니다. 사용하지 않는 코드를 감지했다면 해당 모듈을 찾아import()
로 지연 로드하십시오(전체 스레드 참조). 그런 다음 적용 범위 프로필을 반복하고 이제 초기 로드 시 더 적은 코드를 제공하는지 확인합니다.Puppeteer를 사용하여 프로그래밍 방식으로 코드 적용 범위를 수집할 수 있습니다. Chrome을 사용하면 코드 검사 결과도 내보낼 수 있습니다. Andy Davies가 언급했듯이 최신 브라우저와 레거시 브라우저 모두에 대한 코드 적용 범위를 수집할 수 있습니다.
더 많은 노출이 필요할 수 있는 Puppetter의 다른 많은 사용 사례와 도구가 있습니다.
- 예를 들어 모든 빌드에서 사용되지 않는 CSS를 자동으로 시각적으로 비교하거나 모니터링하는 것과 같은 Puppeteer의 사용 사례,
- Puppeteer를 사용한 웹 성능 레시피,
- Pupeeteer 및 Playwright 스크립트를 기록하고 생성하는 데 유용한 도구,
- 또한 DevTools에서 바로 테스트를 기록할 수도 있습니다.
- Nitay Neeman의 Puppeteer에 대한 포괄적인 개요와 예제 및 사용 사례.
또한 purgecss, UnCSS 및 Helium은 CSS에서 사용하지 않는 스타일을 제거하는 데 도움이 될 수 있습니다. 그리고 의심스러운 코드 조각이 어딘가에서 사용되었는지 확실하지 않다면 Harry Roberts의 조언을 따를 수 있습니다. 특정 클래스에 대해 1x1px 투명 GIF를 만들어
dead/
디렉토리(예:/assets/img/dead/comments.gif
)에 놓으십시오./assets/img/dead/comments.gif
.그런 다음 CSS의 해당 선택기에서 특정 이미지를 배경으로 설정하고 파일이 로그에 표시될 경우 몇 개월 동안 기다리십시오. 항목이 없으면 화면에 해당 레거시 구성 요소가 렌더링되지 않은 것입니다. 계속해서 모두 삭제할 수 있습니다.
I-feel-adventurous -department의 경우 DevTools를 사용하여 DevTools를 모니터링하여 페이지 세트를 통해 사용하지 않는 CSS 수집을 자동화할 수도 있습니다.
- JavaScript 번들의 크기를 자릅니다.
Addy Osmani가 언급했듯이, 일부만 필요할 때 전체 JavaScript 라이브러리를 제공할 가능성이 높으며, 필요하지 않거나 코드가 중복되는 브라우저를 위한 오래된 폴리필과 함께 제공됩니다. 오버헤드를 방지하려면 빌드 프로세스 중에 사용하지 않는 메서드와 폴리필을 제거하는 webpack-libs-optimizations를 사용하는 것이 좋습니다.레거시 브라우저와 최신 브라우저로 보내는 폴리필 을 확인하고 검토하고 이에 대해 보다 전략적인 태도를 취하세요. 브라우저 기능 세트에 대한 요청을 수락하고 요청한 브라우저에 필요한 폴리필만 반환하는 서비스인 polyfill.io를 살펴보십시오.
일반 워크플로에도 번들 감사 를 추가합니다. 몇 년 전에 추가한 무거운 라이브러리에 대한 가벼운 대안이 있을 수 있습니다. 예를 들어 Moment.js(현재 중단됨)는 다음으로 대체될 수 있습니다.
- 네이티브 국제화 API,
- 친숙한 Moment.js API 및 패턴을 사용하는 Day.js,
- date-fns 또는
- 룩슨.
- 사람이 검토한 패키지 권장 사항과 품질 중심 검색을 결합한 Skypack Discover를 사용할 수도 있습니다.
Benedikt Rotsch의 연구에 따르면 Moment.js에서 date-fns로의 전환은 3G 및 저가형 휴대전화의 First paint에 대해 약 300ms를 단축할 수 있습니다.
번들 감사의 경우 Bundlephobia는 npm 패키지를 번들에 추가하는 비용을 찾는 데 도움이 될 수 있습니다. size-limit는 JavaScript 실행 시간에 대한 세부 정보로 기본 번들 크기 검사를 확장합니다. 이러한 비용을 Lighthouse Custom Audit와 통합할 수도 있습니다. 이것은 프레임워크에도 적용됩니다. Vue MDC 어댑터(Vue용 재료 구성요소)를 제거하거나 트리밍하면 스타일이 194KB에서 10KB로 떨어집니다.
종속성과 실행 가능한 대안의 영향에 대해 정보에 입각한 결정을 내리는 데 도움이 되는 추가 도구가 많이 있습니다.
- 웹팩 번들 분석기
- 소스 맵 탐색기
- 번들 버디
- 번들 공포증
- Webpack 분석은 특정 모듈이 번들에 포함된 이유를 보여줍니다.
- 번들 마법사는 또한 전체 페이지에 대한 종속성 맵을 빌드합니다.
- Webpack 크기 플러그인
- 비주얼 코드 가져오기 비용
전체 프레임워크를 제공하는 대신 프레임워크를 다듬고 추가 코드가 필요하지 않은 원시 JavaScript 번들 로 컴파일할 수 있습니다. Svelte는 이를 수행하고 React.js 구성 요소를 빌드 시 기본 DOM 작업으로 변환하는 Rawact Babel 플러그인도 수행합니다. 왜요? 글쎄요, 유지보수 담당자가 설명하듯이, "react-dom에는 증분 렌더링, 스케줄링, 이벤트 처리 등을 위한 코드를 포함하여 렌더링될 수 있는 모든 가능한 구성 요소/HTMLElement에 대한 코드가 포함되어 있습니다. 그러나 이러한 모든 기능이 필요하지 않은 응용 프로그램이 있습니다(초기에는 페이지 로드). 이러한 응용 프로그램의 경우 기본 DOM 작업을 사용하여 대화형 사용자 인터페이스를 구축하는 것이 합리적일 수 있습니다."
- 우리는 부분 수분을 사용합니까?
응용 프로그램에서 사용되는 JavaScript의 양으로 인해 클라이언트에 가능한 한 적게 보낼 수 있는 방법을 찾아야 합니다. 그렇게 하는 한 가지 방법(우리는 이미 간략하게 다루었습니다)은 부분적인 수분 공급입니다. 아이디어는 매우 간단합니다. SSR을 수행한 다음 전체 앱을 클라이언트로 보내는 대신 앱의 JavaScript 중 작은 부분만 클라이언트로 보낸 다음 수화됩니다. 정적 웹사이트에 여러 렌더링 루트가 있는 여러 개의 작은 React 앱으로 생각할 수 있습니다."부분 수화의 경우(Next 및 Preact 사용)" 기사에서 Lukas Bombach는 독일의 뉴스 매체 중 하나인 Welt.de의 팀이 부분 수화로 더 나은 성과를 달성한 방법을 설명합니다. 설명과 코드 스니펫을 통해 차세대 초고성능 GitHub 리포지토리를 확인할 수도 있습니다.
다음과 같은 대체 옵션을 고려할 수도 있습니다.
- Preact와 Eleventy로 부분적인 수분 공급,
- React GitHub 리포지토리의 점진적 수화,
- Vue.js(GitHub repo)의 지연 수화,
- 사용자가 이를 필요로 하는 UI와 상호 작용할 때 중요하지 않은 리소스(예: 구성 요소, 포함)를 지연 로드하려면 상호 작용 패턴에서 가져옵니다.
Jason Miller는 React로 점진적 수화를 구현하는 방법에 대한 작업 데모를 게시했습니다. 따라서 즉시 사용할 수 있습니다: 데모 1, 데모 2, 데모 3(GitHub에서도 사용 가능). 또한 react-prerendered-component 라이브러리를 살펴볼 수 있습니다.
- React/SPA에 대한 전략을 최적화했습니까?
단일 페이지 애플리케이션 애플리케이션의 성능에 어려움을 겪고 계십니까? Jeremy Wagner는 클라이언트 측 프레임워크 성능이 다양한 장치에 미치는 영향을 조사하여 하나를 사용할 때 알아야 할 몇 가지 의미와 지침을 강조했습니다.결과적으로 다음은 Jeremy가 React 프레임워크에 사용하도록 제안한 SPA 전략입니다(그러나 다른 프레임워크에서는 크게 변경되지 않아야 함).
- 가능하면 상태 저장 구성 요소를 상태 비저장 구성 요소로 리팩터링하십시오.
- 서버 응답 시간을 최소화하기 위해 가능한 경우 상태 비저장 구성 요소를 미리 렌더링합니다. 서버에서만 렌더링합니다.
- 간단한 상호 작용이 있는 상태 저장 구성 요소의 경우 해당 구성 요소를 사전 렌더링 또는 서버 렌더링하는 것을 고려하고 상호 작용을 프레임워크 독립 이벤트 리스너로 교체합니다.
- 클라이언트에서 상태 저장 구성 요소를 수화해야 하는 경우 가시성 또는 상호 작용에 지연 수화를 사용하십시오.
- 지연 수화 구성 요소의 경우
requestIdleCallback
을 사용하여 주 스레드 유휴 시간 동안 수화를 예약합니다.
추구하거나 검토할 수 있는 몇 가지 다른 전략이 있습니다.
- React 앱의 CSS-in-JS에 대한 성능 고려 사항
- 동적 가져오기 및 지연 수화를 사용하여 필요할 때만 폴리필을 로드하여 Next.js 번들 크기를 줄입니다.
- JavaScript의 비밀: React, 성능 최적화 및 다중 스레딩에 대한 이야기, React로 사용자 인터페이스 문제 개선에 대한 긴 7부 시리즈,
- React 성능을 측정하는 방법과 React 애플리케이션을 프로파일링하는 방법.
- Alex Holachek의 환상적인 강연, 슬라이드 및 GitHub 리포지토리와 함께 React로 모바일 우선 웹 애니메이션 만들기( Addy, 팁 감사합니다! )
- webpack-libs-optimizations는 유용한 Webpack별 성능 관련 최적화가 많이 포함된 환상적인 GitHub 리포지토리입니다. Ivan Akulov가 유지 관리합니다.
- Ivan Akulov가 React의 성능을 개선하는 방법에 대한 가이드인 Notion의 React 성능 개선과 앱을 약 30% 더 빠르게 만드는 유용한 포인터를 제공합니다.
- React Refresh Webpack Plugin(실험적)은 구성 요소 상태를 유지하고 후크 및 기능 구성 요소를 지원하는 핫 리로딩을 허용합니다.
- 번들 크기에 영향을 주지 않는 새로 제안된 구성 요소인 번들 크기가 없는 React Server 구성 요소에 주의하십시오. 이 프로젝트는 현재 개발 중이지만 커뮤니티의 모든 피드백에 감사드립니다(Sophie Alpert의 훌륭한 설명자).
- JavaScript 청크에 대해 예측 프리페치를 사용하고 있습니까?
우리는 발견적 방법을 사용하여 JavaScript 청크를 미리 로드할 시기를 결정할 수 있습니다. 추측.js는 Google 애널리틱스 데이터를 사용하여 사용자가 지정된 페이지에서 다음에 방문할 가능성이 가장 높은 페이지를 결정하는 도구 및 라이브러리 세트입니다. Google 애널리틱스 또는 기타 소스에서 수집한 사용자 탐색 패턴을 기반으로 하는Guess.js는 머신 러닝 모델을 구축하여 각 후속 페이지에 필요한 JavaScript를 예측하고 미리 가져옵니다.따라서 모든 대화형 요소는 참여 확률 점수를 받고 이 점수를 기반으로 클라이언트 측 스크립트는 미리 리소스를 미리 가져오기로 결정합니다. 이 기술을 Next.js 애플리케이션, Angular 및 React에 통합할 수 있으며 설정 프로세스를 자동화하는 Webpack 플러그인도 있습니다.
분명히, 불필요한 데이터를 소비하고 원하지 않는 페이지를 미리 가져오도록 브라우저에 메시지를 표시할 수 있으므로 미리 가져오는 요청 수를 상당히 보수적으로 유지하는 것이 좋습니다. 좋은 사용 사례는 체크아웃에 필요한 유효성 검사 스크립트를 미리 가져오거나 중요한 클릭 유도문안이 뷰포트에 들어올 때 추측적인 미리 가져오는 것입니다.
덜 세련된 것이 필요하십니까? DNStradamus는 아웃바운드 링크가 뷰포트에 나타날 때 DNS 프리페치를 수행합니다. Quicklink, InstantClick 및 Instant.page는 다음 페이지 탐색을 더 빠르게 로드하기 위해 유휴 시간 동안 뷰포트에서 자동으로 링크를 미리 가져 오는 작은 라이브러리입니다. Quicklink를 사용하면 React Router 경로와 Javascript를 미리 가져올 수 있습니다. 또한 데이터를 고려하므로 2G 또는
Data-Saver
가 켜져 있는 경우 프리페치하지 않습니다. 모드가 뷰포트 프리페칭 (기본값)을 사용하도록 설정된 경우 Instant.page도 마찬가지입니다.예측 프리페치의 과학을 자세히 살펴보고 싶다면 Divya Tagtachian이 처음부터 끝까지 모든 옵션을 다루는 예측 프리페치 기술에 대한 훌륭한 강연을 들을 수 있습니다.
- 대상 JavaScript 엔진에 대한 최적화를 활용하십시오.
사용자 기반에서 어떤 JavaScript 엔진이 지배적인지 연구한 다음 이를 최적화하는 방법을 탐색하십시오. 예를 들어, Blink-browsers, Node.js 런타임 및 Electron에서 사용되는 V8에 대해 최적화할 때 모놀리식 스크립트에 대한 스크립트 스트리밍을 사용합니다.스크립트 스트리밍을 사용하면 다운로드가 시작되면
async
또는defer scripts
를 별도의 백그라운드 스레드에서 구문 분석할 수 있으므로 경우에 따라 페이지 로드 시간이 최대 10%까지 향상됩니다. Practically, use<script defer>
in the<head>
, so that the browsers can discover the resource early and then parse it on the background thread.Caveat : Opera Mini doesn't support script deferment, so if you are developing for India or Africa,
defer
will be ignored, resulting in blocking rendering until the script has been evaluated (thanks Jeremy!) .You could also hook into V8's code caching as well, by splitting out libraries from code using them, or the other way around, merge libraries and their uses into a single script, group small files together and avoid inline scripts. Or perhaps even use v8-compile-cache.
When it comes to JavaScript in general, there are also some practices that are worth keeping in mind:
- Clean Code concepts for JavaScript, a large collection of patterns for writing readable, reusable, and refactorable code.
- You can Compress data from JavaScript with the CompressionStream API, eg to gzip before uploading data (Chrome 80+).
- Detached window memory leaks and Fixing memory leaks in web apps are detailed guides on how to find and fix tricky JavaScript memory leaks. Plus, you can use queryObjects(SomeConstructor) from the DevTools Console ( thanks, Mathias! ).
- Reexports are bad for loading and runtime performance, and avoiding them can help reduce the bundle size significantly.
- We can improve scroll performance with passive event listeners by setting a flag in the
options
parameter. So browsers can scroll the page immediately, rather than after the listener has finished. (via Kayce Basques). - If you have any
scroll
ortouch*
listeners, passpassive: true
to addEventListener. This tells the browser you're not planning to callevent.preventDefault()
inside, so it can optimize the way it handles these events. (via Ivan Akulov) - We can achieve better JavaScript scheduling with isInputPending(), a new API that attempts to bridge the gap between loading and responsiveness with the concepts of interrupts for user inputs on the web, and allows for JavaScript to be able to check for input without yielding to the browser.
- You can also automatically remove an event listener after it has executed.
- Firefox's recently released Warp, a significant update to SpiderMonkey (shipped in Firefox 83), Baseline Interpreter and there are a few JIT Optimization Strategies available as well.
- Always prefer to self-host third-party assets.
Yet again, self-host your static assets by default. It's common to assume that if many sites use the same public CDN and the same version of a JavaScript library or a web font, then the visitors would land on our site with the scripts and fonts already cached in their browser, speeding up their experience considerably. However, it's very unlikely to happen.For security reasons, to avoid fingerprinting, browsers have been implementing partitioned caching that was introduced in Safari back in 2013, and in Chrome last year. So if two sites point to the exact same third-party resource URL, the code is downloaded once per domain , and the cache is "sandboxed" to that domain due to privacy implications ( thanks, David Calhoun! ). Hence, using a public CDN will not automatically lead to better performance.
Furthermore, it's worth noting that resources don't live in the browser's cache as long as we might expect, and first-party assets are more likely to stay in the cache than third-party assets. Therefore, self-hosting is usually more reliable and secure, and better for performance, too.
- Constrain the impact of third-party scripts.
With all performance optimizations in place, often we can't control third-party scripts coming from business requirements. Third-party-scripts metrics aren't influenced by end-user experience, so too often one single script ends up calling a long tail of obnoxious third-party scripts, hence ruining a dedicated performance effort. To contain and mitigate performance penalties that these scripts bring along, it's not enough to just defer their loading and execution and warm up connections via resource hints, iedns-prefetch
orpreconnect
.Currently 57% of all JavaScript code excution time is spent on third-party code. The median mobile site accesses 12 third-party domains , with a median of 37 different requests (or about 3 requests made to each third party).
Furthermore, these third-parties often invite fourth-party scripts to join in, ending up with a huge performance bottleneck, sometimes going as far as to the eigth-party scripts on a page. So regularly auditing your dependencies and tag managers can bring along costly surprises.
Another problem, as Yoav Weiss explained in his talk on third-party scripts, is that in many cases these scripts download resources that are dynamic. The resources change between page loads, so we don't necessarily know which hosts the resources will be downloaded from and what resources they would be.
Deferring, as shown above, might be just a start though as third-party scripts also steal bandwidth and CPU time from your app. We could be a bit more aggressive and load them only when our app has initialized.
/* Before */ const App = () => { return <div> <script> window.dataLayer = window.dataLayer || []; function gtag(){...} gtg('js', new Date()); </script> </div> } /* After */ const App = () => { const[isRendered, setRendered] = useState(false); useEffect(() => setRendered(true)); return <div> {isRendered ? <script> window.dataLayer = window.dataLayer || []; function gtag(){...} gtg('js', new Date()); </script> : null} </div> }
In a fantastic post on "Reducing the Site-Speed Impact of Third-Party Tags", Andy Davies explores a strategy of minimizing the footprint of third-parties — from identifying their costs towards reducing their impact.
According to Andy, there are two ways tags impact site-speed — they compete for network bandwidth and processing time on visitors' devices, and depending on how they're implemented, they can delay HTML parsing as well. So the first step is to identify the impact that third-parties have, by testing the site with and without scripts using WebPageTest. With Simon Hearne's Request Map, we can also visualize third-parties on a page along with details on their size, type and what triggered their load.
Preferably self-host and use a single hostname, but also use a request map to exposes fourth-party calls and detect when the scripts change. You can use Harry Roberts' approach for auditing third parties and produce spreadsheets like this one (also check Harry's auditing workflow).
Afterwards, we can explore lightweight alternatives to existing scripts and slowly replace duplicates and main culprits with lighter options. Perhaps some of the scripts could be replaced with their fallback tracking pixel instead of the full tag.
If it's not viable, we can at least lazy load third-party resources with facades, ie a static element which looks similar to the actual embedded third-party, but is not functional and therefore much less taxing on the page load. The trick, then, is to load the actual embed only on interaction .
For example, we can use:
- lite-vimeo-embed for the Vimeo player,
- lite-vimeo for the Vimeo player,
- lite-youtube-embed for the YouTube player,
- react-live-chat-loader for a live chat (case study, and another case-study),
- lazyframe for iframes.
One of the reasons why tag managers are usually large in size is because of the many simultaneous experiments that are running at the same time, along with many user segments, page URLs, sites etc., so according to Andy, reducing them can reduce both the download size and the time it takes to execute the script in the browser.
And then there are anti-flicker snippets. Third-parties such as Google Optimize, Visual Web Optimizer (VWO) and others are unanimous in using them. These snippets are usually injected along with running A/B tests : to avoid flickering between the different test scenarios, they hide the
body
of the document withopacity: 0
, then adds a function that gets called after a few seconds to bring theopacity
back. This often results in massive delays in rendering due to massive client-side execution costs.Therefore keep track how often the anti-flicker timeout is triggered and reduce the timeout. Default blocks display of your page by up to 4s which will ruin conversion rates. According to Tim Kadlec, "Friends don't let friends do client side A/B testing". Server-side A/B testing on CDNs (eg Edge Computing, or Edge Slice Rerendering) is always a more performant option.
If you have to deal with almighty Google Tag Manager , Barry Pollard provides some guidelines to contain the impact of Google Tag Manager. Also, Christian Schaefer explores strategies for loading ads.
주의: 일부 타사 위젯은 감사 도구로부터 자신을 숨기므로 발견하고 측정하기가 더 어려울 수 있습니다. 제3자를 스트레스 테스트하려면 DevTools의 성능 프로필 페이지에서 상향식 요약을 검토하고 요청이 차단되거나 시간 초과된 경우 어떤 일이 발생하는지 테스트합니다. 후자의 경우 WebPageTest의 Blackhole 서버
blackhole.webpagetest.org
를 사용할 수 있습니다.hosts
파일에서 특정 도메인을 가리킬 수 있습니다.그러면 어떤 옵션이 있습니까? 시간 초과로 리소스 다운로드를 경주하여 서비스 워커를 사용하는 것을 고려하고 리소스가 특정 시간 초과 내에 응답하지 않으면 빈 응답을 반환하여 브라우저에 페이지 구문 분석을 계속하도록 지시합니다. 또한 성공하지 못하거나 특정 기준을 충족하지 않는 타사 요청을 기록하거나 차단할 수 있습니다. 가능하면 공급업체 서버가 아닌 자체 서버에서 타사 스크립트를 로드하고 지연 로드하십시오.
또 다른 옵션은 콘텐츠 보안 정책(CSP) 을 설정하여 오디오 또는 비디오 다운로드 금지와 같은 타사 스크립트의 영향을 제한하는 것입니다. 가장 좋은 방법은
<iframe>
을 통해 스크립트를 포함하여 스크립트가 iframe의 컨텍스트에서 실행되므로 페이지의 DOM에 액세스할 수 없고 도메인에서 임의의 코드를 실행할 수 없도록 하는 것입니다. iframe은sandbox
속성을 사용하여 추가로 제한할 수 있으므로 iframe이 수행할 수 있는 모든 기능(예: 스크립트 실행 방지, 경고 방지, 양식 제출, 플러그인, 상단 탐색 액세스 등)을 비활성화할 수 있습니다.기능 정책을 사용하여 브라우저 내 성능 린팅을 통해 타사를 확인할 수도 있습니다.
사이트의 특정 브라우저 기능을 선택하거나 선택 해제합니다. (참고로, 크기가 크거나 최적화되지 않은 이미지, 크기가 조정되지 않은 미디어, 동기화 스크립트 등을 피하는 데 사용할 수도 있습니다.) 현재 Blink 기반 브라우저에서 지원됩니다. /* Via Tim Kadlec. https://timkadlec.com/remembers/2020-02-20-in-browser-performance-linting-with-feature-policies/ */ /* Block the use of the Geolocation API with a Feature-Policy header. */ Feature-Policy: geolocation 'none'
/* Via Tim Kadlec. https://timkadlec.com/remembers/2020-02-20-in-browser-performance-linting-with-feature-policies/ */ /* Block the use of the Geolocation API with a Feature-Policy header. */ Feature-Policy: geolocation 'none'
많은 타사 스크립트가 iframe에서 실행되기 때문에 허용량을 철저히 제한해야 합니다. 샌드박스 처리된 iframe은 항상 좋은 생각이며
sandbox
속성의 여러allow
값을 통해 각 제한을 해제할 수 있습니다. 샌드박싱은 거의 모든 곳에서 지원되므로 타사 스크립트를 허용해야 하는 최소한의 작업으로 제한합니다.교차로 관찰기 사용을 고려하십시오. 이벤트를 전달하거나 DOM에서 필요한 정보(예: 광고 가시성)를 가져오는 동안 광고를 iframe할 수 있습니다 . 기능 정책, 리소스 크기 제한 및 CPU/대역폭 우선 순위와 같은 새로운 정책에 주의하여 유해한 웹 기능 및 브라우저 속도를 늦추는 스크립트(예: 동기 스크립트, 동기 XHR 요청, document.write 및 오래된 구현)를 제한하십시오.
마지막으로, 타사 서비스를 선택할 때 Patrick Hulce의 ThirdPartyWeb.Today를 확인하는 것이 좋습니다. 이 서비스는 모든 타사 스크립트를 범주(분석, 소셜, 광고, 호스팅, 태그 관리자 등)별로 그룹화 하고 엔터티의 스크립트가 얼마나 긴지 시각화하는 서비스입니다. (평균적으로) 실행합니다. 분명히 가장 큰 엔터티는 그들이 있는 페이지에 최악의 성능 영향을 미칩니다. 페이지를 훑어보는 것만으로도 예상해야 하는 성능 발자국에 대한 아이디어를 얻을 수 있습니다.
아, 그리고 일반적인 용의자를 잊지 마십시오. 공유를 위한 타사 위젯 대신 정적 소셜 공유 버튼(예: SSBG)과 대화형 맵 대신 대화형 맵에 대한 정적 링크를 사용할 수 있습니다.
- HTTP 캐시 헤더를 올바르게 설정하십시오.
캐싱은 해야 할 일인 것처럼 보이지만 제대로 하기는 꽤 어려울 수 있습니다.expires
,max-age
,cache-control
및 기타 HTTP 캐시 헤더가 올바르게 설정되었는지 다시 확인해야 합니다. 적절한 HTTP 캐시 헤더가 없으면 브라우저는last-modified
이후 경과 시간의 10%에 자동으로 설정하여 잠재적인 과소 및 과소 캐싱으로 끝납니다.일반적으로 리소스는 매우 짧은 시간(변경될 가능성이 있는 경우) 또는 무기한(정적인 경우) 캐시할 수 있어야 합니다. 필요할 때 URL에서 버전을 변경할 수 있습니다. Cache-Forever 전략이라고 부를 수 있습니다. 이 전략에서는
Cache-Control
및Expires
헤더를 브라우저에 전달하여 자산이 1년 내에만 만료되도록 할 수 있습니다. 따라서 브라우저는 자산이 캐시에 있는 경우 자산을 요청하지도 않습니다.API 응답은 예외입니다(예:
/api/user
). 캐싱을 방지하기 위해max-age=0, no-store
아닌private, no store
를 사용할 수 있습니다.Cache-Control: private, no-store
Cache-control: immutable
을 사용하여 사용자가 다시 로드 버튼을 눌렀을 때 긴 명시적 캐시 수명의 재검증을 방지합니다. 다시 로드의 경우immutable
은 HTTP 요청을 저장하고 동적 HTML이 더 이상 다수의 304 응답과 경쟁하지 않으므로 로드 시간을 개선합니다.immutable
수 없는 것을 사용하려는 일반적인 예는 이름에 해시가 있는 CSS/JavaScript 자산입니다. 그들을 위해 우리는 아마도 가능한 한 오랫동안 캐시하고 그들이 재검증되지 않도록 하기를 원할 것입니다.Cache-Control: max-age: 31556952, immutable
Colin Bendell의 연구에 따르면
immutable
은max-age
를 사용하는 경우에도 304 리디렉션을 약 50%만큼 줄입니다. 클라이언트는 여전히 새로 고침 시 다시 유효성을 검사하고 차단합니다. Firefox, Edge 및 Safari에서 지원되며 Chrome은 여전히 이 문제에 대해 논의 중입니다.Web Almanac에 따르면 "사용률이 3.5%로 증가했으며 Facebook 및 Google 타사 응답에서 널리 사용됩니다."
오래된 오래된 재검증을 기억하십니까?
Cache-Control
응답 헤더(예:Cache-Control: max-age=604800
)로 캐싱 시간을 지정하면max-age
가 만료된 후 브라우저가 요청한 콘텐츠를 다시 가져와 페이지 로드 속도를 느리게 합니다. 이 속도 저하를stale-while-revalidate
로 피할 수 있습니다. 기본적으로 캐시가 백그라운드에서 비동기식으로 재검증하는 한 오래된 자산을 사용할 수 있는 추가 시간 창을 정의합니다. 따라서 클라이언트의 대기 시간(네트워크 및 서버 모두에서)을 "숨깁니다".2019년 6~7월에 Chrome과 Firefox는 HTTP Cache-Control 헤더에서
stale-while-revalidate
지원을 시작했습니다. 따라서 결과적으로 오래된 자산이 더 이상 중요한 경로에 있지 않으므로 후속 페이지 로드 지연 시간을 개선해야 합니다. 결과: 반복 보기에 대한 RTT가 0입니다.특히 CDN과 관련하여 가변 헤더를 조심하고 새로운 요청이 이전 요청과 약간(그러나 크게 다르지는 않음) 다를 때마다 유효성 검사를 위한 추가 왕복을 방지하는 데 도움이 되는 HTTP 표현 변형에 주의하십시오( 감사합니다, Guy 및 Mark ) .
또한 불필요한 헤더(예:
x-powered-by
,pragma
,x-ua-compatible
,expires
,X-XSS-Protection
등)를 보내고 있지 않고 유용한 보안 및 성능 헤더(예:Content-Security-Policy
,X-Content-Type-Options
등). 마지막으로 단일 페이지 애플리케이션에서 CORS 요청의 성능 비용을 염두에 두십시오.참고 : 우리는 종종 캐시된 자산을 즉시 검색한다고 가정하지만 연구에 따르면 캐시에서 개체를 검색하는 데 수백 밀리초가 걸릴 수 있습니다. 실제로 Simon Hearne에 따르면 "때로는 네트워크가 캐시보다 빠를 수 있으며 캐시에서 자산을 검색하는 것은 많은 수의 캐시된 자산(파일 크기 아님)과 사용자 기기로 인해 비용이 많이 들 수 있습니다. 예: Chrome OS 평균 캐시 검색 5개의 캐시된 리소스가 있는 ~50ms에서 25개의 리소스가 있는 최대 ~100ms로 두 배가 됩니다."
게다가 우리는 종종 번들 크기가 큰 문제가 아니며 사용자가 한 번 다운로드한 다음 캐시된 버전을 사용한다고 가정합니다. 동시에 CI/CD를 사용하여 하루에 여러 번 코드를 프로덕션으로 푸시하고 캐시가 매번 무효화되므로 캐싱 문제에 대해 전략적입니다.
캐싱과 관련하여 읽을 가치가 있는 리소스가 많이 있습니다.
- 민간인을 위한 캐시 제어, Harry Roberts와 함께 모든 캐싱에 대해 자세히 알아보십시오.
- HTTP 캐싱 헤더에 대한 Heroku의 입문서,
- Jake Archibald의 캐싱 모범 사례,
- Ilya Grigorik의 HTTP 캐싱 입문서,
- Jeff Posnick의 stale-while-revalidate로 최신 상태를 유지합니다.
- CS Visualized: Lydia Hallie의 CORS는 CORS, 작동 방식 및 이해 방법에 대한 훌륭한 설명입니다.
- CORS에 대해 이야기하면서 여기 Eric Portis의 Same-Origin Policy에 대한 복습이 있습니다.
배달 최적화
- 중요한 JavaScript를 비동기식으로 로드하기 위해
defer
를 사용합니까?
사용자가 페이지를 요청하면 브라우저는 HTML을 가져와서 DOM을 구성한 다음 CSS를 가져와서 CSSOM을 구성한 다음 DOM과 CSSOM을 일치시켜 렌더링 트리를 생성합니다. JavaScript를 해결해야 하는 경우 브라우저는 해결될 때까지 페이지 렌더링을 시작하지 않으므로 렌더링 이 지연됩니다. 개발자로서 우리는 브라우저에게 기다리지 말고 페이지 렌더링을 시작하라고 명시적으로 알려야 합니다. 스크립트에 대해 이 작업을 수행하는 방법은 HTML의defer
및async
속성을 사용하는 것입니다.실제로는
async
대신defer
를 사용하는 것이 좋습니다. 아, 또 뭐가 달라 ? Steve Souders에 따르면async
스크립트가 도착하면 스크립트가 준비되는 즉시 즉시 실행됩니다. 예를 들어 스크립트가 캐시에 이미 있는 경우와 같이 매우 빠르게 발생하면 실제로 HTML 파서를 차단할 수 있습니다.defer
를 사용하면 HTML이 구문 분석될 때까지 브라우저가 스크립트를 실행하지 않습니다. 따라서 렌더링을 시작하기 전에 JavaScript를 실행해야 하는 경우가 아니면defer
를 사용하는 것이 좋습니다. 또한 여러 비동기 파일이 비결정적 순서로 실행됩니다.async
및defer
에 대한 몇 가지 오해가 있다는 점은 주목할 가치가 있습니다. 가장 중요한 것은async
가 스크립트가 준비될 때마다 코드가 실행된다는 것을 의미하지 않는다는 것입니다. 이는 스크립트가 준비 되고 이전의 모든 동기화 작업이 완료될 때마다 실행됨을 의미합니다. Harry Roberts의 말에 따르면 "동기화 스크립트 뒤에async
스크립트를 넣으면async
스크립트는 가장 느린 동기화 스크립트만큼 빠릅니다."또한
async
와defer
를 모두 사용하지 않는 것이 좋습니다. 최신 브라우저는 둘 다 지원하지만 두 속성이 모두 사용될 때마다async
가 항상 이깁니다.더 자세한 내용을 알고 싶다면 Milica Mihajlija가 DOM을 더 빠르게 구축에 대한 매우 상세한 가이드를 작성하여 추측적 구문 분석, 비동기 및 지연에 대한 세부 정보로 이동합니다.
- IntersectionObserver 및 우선 순위 힌트를 사용하여 값비싼 구성 요소를 지연 로드합니다.
일반적으로 무거운 JavaScript, 비디오, iframe, 위젯 및 잠재적인 이미지와 같은 모든 값비싼 구성 요소를 지연 로드하는 것이 좋습니다. 기본 지연 로딩은 이미loading
속성이 있는 이미지 및 iframe에 사용할 수 있습니다(Chromium만 해당). 내부적으로 이 속성은 뷰포트에서 계산된 거리에 도달할 때까지 리소스 로드를 연기합니다.<!-- Lazy loading for images, iframes, scripts. Probably for images outside of the viewport. --> <img loading="lazy" ... /> <iframe loading="lazy" ... /> <!-- Prompt an early download of an asset. For critical images, eg hero images. --> <img loading="eager" ... /> <iframe loading="eager" ... />
이 임계값은 가져오는 이미지 리소스 유형에서 효과적인 연결 유형에 이르기까지 몇 가지 사항에 따라 다릅니다. 그러나 Android에서 Chrome을 사용하여 수행한 실험에 따르면 4G에서 지연 로드된 스크롤 없이 볼 수 있는 이미지의 97.5%가 표시되고 10ms 이내에 완전히 로드되었으므로 안전해야 합니다.
<script>
,<img>
또는<link>
요소에importance
속성(high
또는low
)을 사용할 수도 있습니다(Blink만 해당). 사실, 캐러셀에서 이미지의 우선 순위를 낮추고 스크립트의 우선 순위를 다시 지정하는 좋은 방법입니다. 그러나 때로는 좀 더 세분화된 제어가 필요할 수 있습니다.<!-- When the browser assigns "High" priority to an image, but we don't actually want that. --> <img src="less-important-image.svg" importance="low" ... /> <!-- We want to initiate an early fetch for a resource, but also deprioritize it. --> <link rel="preload" importance="low" href="/script.js" as="script" />
약간 더 정교한 지연 로딩을 수행하는 가장 효과적인 방법은 대상 요소와 상위 요소 또는 최상위 문서의 뷰포트와의 교차점에서 변경 사항을 비동기식으로 관찰 하는 방법을 제공하는 Intersection Observer API를 사용하는 것입니다. 기본적으로 콜백 함수와 옵션 세트를 수신하는 새로운
IntersectionObserver
객체를 생성해야 합니다. 그런 다음 관찰할 대상을 추가합니다.콜백 함수는 대상 이 보이 거나 보이지 않을 때 실행되므로 뷰포트를 가로챌 때 요소가 표시되기 전에 몇 가지 작업을 시작할 수 있습니다. 사실, 우리는
rootMargin
(루트 주변 여백) 및threshold
(목표 가시성의 백분율을 나타내는 단일 숫자 또는 숫자 배열)을 사용하여 관찰자의 콜백이 호출되어야 하는 시기를 세부적으로 제어할 수 있습니다.Alejandro Garcia Anglada는 실제로 구현하는 방법에 대한 편리한 자습서를 게시했으며 Rahul Nanwani는 지연 로딩 전경 및 배경 이미지에 대한 자세한 게시물을 작성했으며 Google Fundamentals는 Intersection Observer를 사용하여 지연 로딩 이미지 및 비디오에 대한 자세한 자습서도 제공합니다.
움직이는 물체와 끈끈한 물체를 사용한 예술 주도의 스토리텔링을 기억하십니까? Intersection Observer를 사용하여 고성능 스크롤링을 구현할 수도 있습니다.
지연 로드할 수 있는 다른 항목을 다시 확인하십시오. 지연 로딩 번역 문자열 과 이모티콘도 도움이 될 수 있습니다. 그렇게 함으로써 Mobile Twitter는 새로운 국제화 파이프라인에서 80% 더 빠른 JavaScript 실행을 달성할 수 있었습니다.
하지만 주의할 점은 지연 로딩이 규칙이 아니라 예외여야 한다는 점입니다. 제품 페이지 이미지, 영웅 이미지 또는 기본 탐색이 대화형이 되는 데 필요한 스크립트와 같이 실제로 사람들이 빨리 보기를 원하는 모든 것을 지연 로드하는 것은 합리적이지 않을 수 있습니다.
- 이미지를 점진적으로 로드합니다.
페이지에 프로그레시브 이미지 로딩을 추가하여 지연 로딩을 한 단계 더 끌어올릴 수도 있습니다. Facebook, Pinterest, Medium 및 Wolt와 유사하게 먼저 낮은 품질 또는 흐릿한 이미지를 로드한 다음 페이지가 계속 로드될 때 BlurHash 기술 또는 LQIP(Low Quality Image Placeholders)를 사용하여 전체 품질 버전으로 교체할 수 있습니다. 기술.이러한 기술이 사용자 경험을 개선하는지 여부에 따라 의견이 다르지만, First Contentful Paint에 대한 시간은 확실히 단축됩니다. SVG 플레이스홀더로 이미지의 낮은 품질 버전을 생성하는 SQIP 또는 CSS 선형 그라디언트가 있는 그라디언트 이미지 플레이스홀더를 사용하여 자동화할 수도 있습니다.
이러한 자리 표시자는 텍스트 압축 방법으로 자연스럽게 압축되므로 HTML 내에 포함될 수 있습니다. Dean Hume은 자신의 기사에서 Intersection Observer를 사용하여 이 기술을 구현하는 방법을 설명했습니다.
대체? 브라우저가 교차 관찰자를 지원하지 않는 경우에도 폴리필을 지연 로드하거나 이미지를 즉시 로드할 수 있습니다. 그리고 그것을 위한 도서관도 있습니다.
더 멋지게 가고 싶습니까? 이미지를 추적하고 기본 모양과 가장자리를 사용하여 가벼운 SVG 자리 표시자를 만들고 먼저 로드한 다음 자리 표시자 벡터 이미지에서 (로드된) 비트맵 이미지로 전환할 수 있습니다.
-
content-visibility
렌더링을 연기합니까?
콘텐츠 블록, 이미지 및 비디오가 많은 복잡한 레이아웃의 경우 데이터 디코딩 및 픽셀 렌더링은 특히 저가형 장치에서 상당히 비용이 많이 드는 작업일 수 있습니다.content-visibility: auto
를 사용하면 컨테이너가 뷰포트 외부에 있는 동안 자식 레이아웃을 건너뛰도록 브라우저에 프롬프트를 표시할 수 있습니다.예를 들어, 초기 로드에서 바닥글 및 후기 섹션의 렌더링을 건너뛸 수 있습니다.
footer { content-visibility: auto; contain-intrinsic-size: 1000px; /* 1000px is an estimated height for sections that are not rendered yet. */ }
내용 가시성: auto; 오버플로처럼 작동합니다. 숨김; , 하지만 기본 margin-left 대신
padding-left
및padding-right
를 적용하여 수정할 수 있습니다margin-left: auto;
,margin-right: auto;
그리고 선언된 너비. 패딩은 기본적으로 요소가 내용 상자를 오버플로하고 상자 모델을 전체적으로 벗어나지 않고 패딩 상자에 들어갈 수 있도록 합니다.또한 새 콘텐츠가 결국 렌더링될 때 일부 CLS를 도입할 수 있으므로 적절한 크기의 자리 표시자와 함께
contain-intrinsic-size
를 사용하는 것이 좋습니다( 감사합니다, Una! ).Thijs Terluin은 두 속성 및 브라우저에서
contain-intrinsic-size
를 계산하는 방법에 대해 자세히 설명하고 Malte Ubl은 계산 방법을 보여주고 Jake와 Surma의 간단한 비디오 설명자는 모든 작동 방식을 설명합니다.CSS Containment를 사용하여 좀 더 세분화해야 하는 경우 다른 요소에 대해 크기, 정렬 또는 계산된 스타일만 필요한 경우 DOM 노드의 자손에 대한 레이아웃, 스타일 및 페인트 작업을 수동으로 건너뛸 수 있습니다. 캔버스 밖에서.
-
decoding="async"
로 디코딩을 연기합니까?
때때로 콘텐츠가 화면 밖에 나타나지만 고객이 필요할 때 사용할 수 있도록 하고 싶습니다. 이상적으로는 중요한 경로에서 어떤 것도 차단하지 않고 비동기적으로 디코딩 및 렌더링하는 것입니다.decoding="async"
를 사용하여 브라우저에 기본 스레드에서 이미지를 디코딩할 수 있는 권한을 부여하여 이미지 디코딩에 사용되는 CPU 시간의 사용자 영향을 방지할 수 있습니다(Malte Ubl을 통해).<img decoding="async" … />
또는 오프스크린 이미지의 경우 먼저 자리 표시자를 표시하고 이미지가 뷰포트 내에 있을 때 IntersectionObserver를 사용하여 백그라운드에서 다운로드할 이미지에 대한 네트워크 호출을 트리거할 수 있습니다. 또한 img.decode()로 디코딩할 때까지 렌더링을 연기하거나 Image Decode API를 사용할 수 없는 경우 이미지를 다운로드할 수 있습니다.
예를 들어 이미지를 렌더링할 때 페이드 인 애니메이션을 사용할 수 있습니다. Katie Hempenius와 Addy Osmani는 Speed at Scale: Web Performance Tips and Tricks from Trenches에서 더 많은 통찰력을 공유합니다.
- 중요한 CSS를 생성하고 제공합니까?
브라우저가 가능한 한 빨리 페이지 렌더링을 시작할 수 있도록 페이지의 첫 번째 표시 부분("중요한 CSS" 또는 "스크롤 없이 볼 수 있는 CSS"라고 함) 렌더링을 시작하는 데 필요한 모든 CSS를 수집하는 것이 일반적입니다. ") 페이지의<head>
에 인라인으로 포함하여 왕복을 줄입니다. 느린 시작 단계에서 교환되는 패키지의 제한된 크기로 인해 중요한 CSS에 대한 예산은 약 14KB입니다.그 이상으로 넘어가면 브라우저는 더 많은 스타일을 가져오기 위해 추가 왕복이 필요합니다. CriticalCSS 및 Critical을 사용하면 사용 중인 모든 템플릿에 대해 중요한 CSS를 출력할 수 있습니다. 하지만 경험상 모든 템플릿에 대해 중요한 CSS를 수동으로 수집하는 것보다 더 나은 자동 시스템은 없었고 실제로 최근에 다시 옮겨온 접근 방식입니다.
그런 다음 중요한 CSS를 인라인하고 나머지는 critters Webpack 플러그인으로 지연 로드할 수 있습니다. 가능하면 Filament Group에서 사용하는 조건부 인라인 접근 방식을 사용하거나 인라인 코드를 즉시 정적 자산으로 변환하십시오.
현재 loadCSS와 같은 라이브러리를 사용하여 전체 CSS를 비동기식으로 로드 하는 경우 실제로는 필요하지 않습니다.
media="print"
를 사용하면 브라우저가 CSS를 비동기적으로 가져오도록 속일 수 있지만 로드되면 화면 환경에 적용됩니다. ( 고마워, 스콧! )<!-- Via Scott Jehl. https://www.filamentgroup.com/lab/load-css-simpler/ --> <!-- Load CSS asynchronously, with low priority --> <link rel="stylesheet" href="full.css" media="print" onload="this.media='all'" />
각 템플릿에 대한 모든 중요한 CSS를 수집할 때 '스크롤 없이 볼 수 있는 부분' 영역만 탐색하는 것이 일반적입니다. 그러나 복잡한 레이아웃의 경우 레이아웃의 기초를 포함하여 막대한 재계산 및 다시 그리기 비용을 방지 하여 결과적으로 핵심 성능 평가 점수를 손상시키는 것을 방지하는 것이 좋습니다.
사용자가 페이지 중간에 직접 연결되는 URL을 받았지만 CSS가 아직 다운로드되지 않은 경우에는 어떻게 됩니까? 이 경우 중요하지 않은 콘텐츠를 숨기는 것이 일반적이 되었습니다. 예를 들어
opacity: 0;
인라인 CSS 및opacity: 1
전체 CSS 파일에 표시하고 CSS를 사용할 수 있을 때 표시합니다. 연결 속도가 느린 사용자는 페이지의 내용을 읽을 수 없기 때문에 큰 단점 이 있습니다. 그렇기 때문에 스타일이 제대로 지정되지 않은 경우에도 콘텐츠를 항상 볼 수 있도록 하는 것이 좋습니다.중요한 CSS(및 기타 중요한 자산)를 루트 도메인의 별도 파일에 넣으면 캐싱으로 인해 인라인보다 더 많은 이점이 있습니다. Chrome은 페이지를 요청할 때 루트 도메인에 대한 두 번째 HTTP 연결을 추측적으로 열어 이 CSS를 가져오기 위해 TCP 연결이 필요하지 않습니다. 이는 중요한 CSS 파일 세트(예: critical-homepage.css , critical-product-page.css 등)를 생성하고 인라인할 필요 없이 루트에서 제공할 수 있음을 의미합니다. ( 고마워요, 필립! )
주의 사항: HTTP/2를 사용하면 중요한 CSS를 별도의 CSS 파일에 저장하고 HTML을 부풀리지 않고 서버 푸시를 통해 전달할 수 있습니다. 문제는 서버 푸시가 브라우저 전반에 걸쳐 많은 문제와 경쟁 조건으로 인해 골칫거리였다는 것입니다. 지속적으로 지원되지 않았고 일부 캐싱 문제가 있었습니다(Hooman Beheshti의 프레젠테이션 슬라이드 114 참조).
실제로 그 효과는 부정적이고 네트워크 버퍼를 부풀려 문서의 진짜 프레임이 전달되는 것을 방해할 수 있습니다. 따라서 당분간 Chrome이 서버 푸시에 대한 지원을 제거할 계획이라는 것은 그리 놀라운 일이 아닙니다.
- CSS 규칙을 다시 그룹화하여 실험합니다.
우리는 중요한 CSS에 익숙해졌지만 그 이상으로 넘어갈 수 있는 몇 가지 최적화가 있습니다. Harry Roberts는 매우 놀라운 결과로 놀라운 연구를 수행했습니다. 예를 들어 기본 CSS 파일을 개별 미디어 쿼리로 분할 하는 것이 좋습니다. 그렇게 하면 브라우저는 우선 순위가 높은 중요한 CSS를 검색하고 우선 순위가 낮은 다른 모든 항목을 검색하여 중요 경로에서 완전히 벗어납니다.또한
async
스니펫 앞에<link rel="stylesheet" />
를 배치하지 마십시오. 스크립트가 스타일시트에 의존하지 않는 경우 차단 스타일 위에 차단 스크립트를 배치하는 것을 고려하십시오. 그렇다면 해당 JavaScript를 둘로 나누고 CSS의 양쪽에 로드하십시오.Scott Jehl은 인라인 CSS 파일을 서비스 워커와 함께 캐싱하여 또 다른 흥미로운 문제를 해결했습니다. 이 문제는 중요한 CSS를 사용하는 경우 익숙한 일반적인 문제입니다. 기본적으로 JavaScript를 사용하여 쉽게 찾을 수 있도록
style
요소에 ID 속성을 추가하면 작은 JavaScript 조각이 해당 CSS를 찾고 Cache API를 사용하여 로컬 브라우저 캐시에 저장합니다(text/css
) 후속 페이지에서 사용합니다. 후속 페이지에서 인라인을 방지하고 캐시된 자산을 외부에서 참조하기 위해 사이트를 처음 방문할 때 쿠키를 설정합니다. 짜잔!동적 스타일 지정도 비용이 많이 들 수 있지만 일반적으로 동시에 렌더링되는 수백 개의 구성 구성 요소에 의존하는 경우에만 해당됩니다. 따라서 CSS-in-JS를 사용하는 경우 CSS가 테마나 소품에 대한 종속성이 없을 때 CSS-in-JS 라이브러리가 실행을 최적화하는지 확인하고 스타일이 지정된 구성 요소를 과도하게 구성하지 마십시오 . Agelos Arvanitakis는 CSS-in-JS의 성능 비용에 대한 더 많은 통찰력을 공유합니다.
- 응답을 스트리밍합니까?
종종 잊혀지고 무시되는 스트림은 비동기 데이터 청크를 읽거나 쓰기 위한 인터페이스를 제공하며, 이 중 일부만 주어진 시간에 메모리에서 사용할 수 있습니다. 기본적으로 데이터의 첫 번째 청크를 사용할 수 있게 되는 즉시 원래 요청을 한 페이지가 응답 작업을 시작하도록 허용하고 스트리밍에 최적화된 파서를 사용하여 콘텐츠를 점진적으로 표시합니다.여러 소스에서 하나의 스트림을 만들 수 있습니다. 예를 들어 빈 UI 셸을 제공하고 JavaScript가 이를 채우도록 하는 대신 서비스 워커가 셸은 캐시에서 가져오고 본문은 네트워크에서 오는 스트림을 구성 하도록 할 수 있습니다. Jeff Posnick이 언급했듯이 웹 앱이 부분 템플릿을 함께 연결하여 서버에서 HTML을 렌더링하는 CMS로 구동되는 경우 해당 모델은 서버 대신 서비스 작업자에서 복제된 템플릿 논리와 함께 스트리밍 응답을 사용하는 것으로 직접 변환됩니다. Jake Archibald의 Year of Web Streams 기사는 이를 정확히 구축할 수 있는 방법을 강조합니다. 성능 향상이 상당히 눈에 띕니다.
전체 HTML 응답을 스트리밍하는 한 가지 중요한 이점은 초기 탐색 요청 중에 렌더링된 HTML이 브라우저의 스트리밍 HTML 파서를 최대한 활용할 수 있다는 것입니다. 페이지가 로드된 후 문서에 삽입되는 HTML 덩어리(JavaScript를 통해 채워진 콘텐츠에서 흔히 볼 수 있는 것처럼)는 이 최적화를 활용할 수 없습니다.
브라우저 지원? 모든 최신 브라우저에서 지원되는 API 및 서비스 워커를 지원하는 Chrome, Firefox, Safari 및 Edge의 부분 지원으로 여전히 거기에 도달하고 있습니다. 그리고 다시 모험심을 느낀다면 스트리밍 요청의 실험적 구현을 확인할 수 있습니다. 이를 통해 본문을 생성하는 동안 요청 전송을 시작할 수 있습니다. Chrome 85에서 사용할 수 있습니다.
- 구성 요소를 연결 인식하도록 만드는 것이 좋습니다.
데이터는 비싸고 페이로드가 증가함에 따라 사이트나 앱에 액세스하는 동안 데이터 절약을 선택하는 사용자를 존중해야 합니다. Save-Data 클라이언트 힌트 요청 헤더를 사용하면 비용 및 성능이 제한된 사용자에게 애플리케이션과 페이로드를 사용자 지정할 수 있습니다.사실, 높은 DPI 이미지에 대한 요청을 낮은 DPI 이미지로 다시 작성하고, 웹 글꼴 제거, 멋진 시차 효과, 미리보기 축소판 및 무한 스크롤, 비디오 자동 재생 끄기, 서버 푸시, 표시된 항목 수 줄이기 및 이미지 품질 저하 또는 마크업을 제공하는 방법을 변경할 수도 있습니다. Tim Vereecke는 데이터 절약을 위한 많은 옵션을 특징으로 하는 데이터 절약 전략에 대한 매우 상세한 기사를 게시했습니다.
누가
save-data
를 사용하고 있는지 궁금할 것입니다. 전 세계 Android Chrome 사용자의 18%가 라이트 모드를 활성화했으며(Save-Data
켜짐), 그 수치는 더 높을 것입니다. Simon Hearne의 연구에 따르면 옵트인 비율은 저렴한 기기에서 가장 높지만 많은 예외가 있습니다. 예를 들어, 캐나다 사용자의 옵트인 비율은 34% 이상(미국의 ~7%)이고 최신 Samsung 플래그십 사용자의 옵트인 비율은 전 세계적으로 거의 18%입니다.Save-Data
모드가 켜져 있으면 Chrome Mobile은 최적화된 환경, 즉 지연된 스크립트, 강제font-display: swap
및 강제 지연 로드가 있는 프록시 웹 환경을 제공합니다. 브라우저에 의존하여 이러한 최적화를 수행하는 것보다 직접 경험을 구축하는 것이 더 합리적입니다.헤더는 현재 Chromium, Android 버전의 Chrome 또는 데스크톱 기기의 Data Saver 확장 프로그램을 통해서만 지원됩니다. 마지막으로 네트워크 정보 API를 사용하여 네트워크 유형에 따라 값비싼 JavaScript 모듈, 고해상도 이미지 및 비디오를 전달할 수도 있습니다. 네트워크 정보 API, 특히
navigator.connection.effectiveType
은RTT
,downlink
,effectiveType
값(및 기타 몇 가지)을 사용하여 사용자가 처리할 수 있는 연결 및 데이터의 표현을 제공합니다.이 맥락에서 Max Bock은 연결 인식 구성 요소에 대해 말하고 Addy Osmani는 적응형 모듈 서비스에 대해 이야기합니다. 예를 들어 React를 사용하여 연결 유형에 따라 다르게 렌더링하는 구성 요소를 작성할 수 있습니다. Max가 제안한 것처럼 뉴스 기사의
<Media />
구성 요소는 다음을 출력할 수 있습니다.-
Offline
:alt
텍스트가 있는 자리 표시자, -
2G
/save-data
모드: 저해상도 이미지, - 비 Retina 화면의
3G
: 중간 해상도 이미지, - Retina 화면의
3G
: 고해상도 Retina 이미지, -
4G
: HD 비디오.
Dean Hume은 서비스 워커를 사용하여 유사한 로직의 실용적인 구현을 제공합니다. 비디오의 경우 기본적으로 비디오 포스터를 표시한 다음 "재생" 아이콘과 비디오 플레이어 쉘, 비디오 메타데이터 등을 더 나은 연결에 표시할 수 있습니다. 지원하지 않는 브라우저에 대한 폴백으로 canplaythrough 이벤트를 수신하고
canplaythrough
이벤트가 2초 이내에 실행되지 않으면canplaythrough
Promise.race()
를 사용하여 소스 로딩 시간을 초과할 수 있습니다.좀 더 자세히 알아보려면 다음과 같이 시작할 수 있는 몇 가지 리소스를 참조하세요.
- Addy Osmani는 React에서 적응형 서빙을 구현하는 방법을 보여줍니다.
- React Adaptive Loading Hooks & Utilities는 React,
- Netanel Basel은 Angular의 연결 인식 구성 요소를 탐색합니다.
- Theodore Vorilas는 Vue에서 네트워크 정보 API를 사용하여 적응형 구성 요소를 제공하는 방법을 공유합니다.
- Umar Hansa는 고가의 JavaScript를 선택적으로 다운로드/실행하는 방법을 보여줍니다.
-
- 구성 요소 장치의 메모리 인식을 고려하십시오.
네트워크 연결은 사용자의 컨텍스트에서 단 하나의 관점을 제공합니다. 더 나아가 Device Memory API를 사용하여 사용 가능한 장치 메모리를 기반으로 리소스를 동적으로 조정할 수도 있습니다.navigator.deviceMemory
returns how much RAM the device has in gigabytes, rounded down to the nearest power of two. The API also features a Client Hints Header,Device-Memory
, that reports the same value.Bonus : Umar Hansa shows how to defer expensive scripts with dynamic imports to change the experience based on device memory, network connectivity and hardware concurrency.
- Warm up the connection to speed up delivery.
Use resource hints to save time ondns-prefetch
(which performs a DNS lookup in the background),preconnect
(which asks the browser to start the connection handshake (DNS, TCP, TLS) in the background),prefetch
(which asks the browser to request a resource) andpreload
(which prefetches resources without executing them, among other things). Well supported in modern browsers, with support coming to Firefox soon.Remember
prerender
? The resource hint used to prompt browser to build out the entire page in the background for next navigation. The implementations issues were quite problematic, ranging from a huge memory footprint and bandwidth usage to multiple registered analytics hits and ad impressions.Unsurprinsingly, it was deprecated, but the Chrome team has brought it back as NoState Prefetch mechanism. In fact, Chrome treats the
prerender
hint as a NoState Prefetch instead, so we can still use it today. As Katie Hempenius explains in that article, "like prerendering, NoState Prefetch fetches resources in advance ; but unlike prerendering, it does not execute JavaScript or render any part of the page in advance."NoState Prefetch only uses ~45MiB of memory and subresources that are fetched will be fetched with an
IDLE
Net Priority. Since Chrome 69, NoState Prefetch adds the header Purpose: Prefetch to all requests in order to make them distinguishable from normal browsing.Also, watch out for prerendering alternatives and portals, a new effort toward privacy-conscious prerendering, which will provide the inset
preview
of the content for seamless navigations.Using resource hints is probably the easiest way to boost performance , and it works well indeed. 언제 무엇을 사용합니까? As Addy Osmani has explained, it's reasonable to preload resources that we know are very likely to be used on the current page and for future navigations across multiple navigation boundaries, eg Webpack bundles needed for pages the user hasn't visited yet.
Addy's article on "Loading Priorities in Chrome" shows how exactly Chrome interprets resource hints, so once you've decided which assets are critical for rendering, you can assign high priority to them. To see how your requests are prioritized, you can enable a "priority" column in the Chrome DevTools network request table (as well as Safari).
Most of the time these days, we'll be using at least
preconnect
anddns-prefetch
, and we'll be cautious with usingprefetch
,preload
andprerender
. Note that even withpreconnect
anddns-prefetch
, the browser has a limit on the number of hosts it will look up/connect to in parallel, so it's a safe bet to order them based on priority ( thanks Philip Tellis! ).Since fonts usually are important assets on a page, sometimes it's a good idea to request the browser to download critical fonts with
preload
. However, double check if it actually helps performance as there is a puzzle of priorities when preloading fonts: aspreload
is seen as high importance, it can leapfrog even more critical resources like critical CSS. ( thanks, Barry! )<!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
<!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
Since
<link rel="preload">
accepts amedia
attribute, you could choose to selectively download resources based on@media
query rules, as shown above.Furthermore, we can use
imagesrcset
andimagesizes
attributes to preload late-discovered hero images faster, or any images that are loaded via JavaScript, eg movie posters:<!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
<!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
We can also preload the JSON as fetch , so it's discovered before JavaScript gets to request it:
<!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="fetch" href="foo.com/api/movies.json" crossorigin>
We could also load JavaScript dynamically, effectively for lazy execution of the script.
/* Adding a preload hint to the head */ var preload = document.createElement("link"); link.href = "myscript.js"; link.rel = "preload"; link.as = "script"; document.head.appendChild(link); /* Injecting a script when we want it to execute */ var script = document.createElement("script"); script.src = "myscript.js"; document.body.appendChild(script);
A few gotchas to keep in mind:
preload
is good for moving the start download time of an asset closer to the initial request, but preloaded assets land in the memory cache which is tied to the page making the request.preload
plays well with the HTTP cache: a network request is never sent if the item is already there in the HTTP cache.Hence, it's useful for late-discovered resources, hero images loaded via
background-image
, inlining critical CSS (or JavaScript) and pre-loading the rest of the CSS (or JavaScript).A
preload
tag can initiate a preload only after the browser has received the HTML from the server and the lookahead parser has found thepreload
tag. Preloading via the HTTP header could be a bit faster since we don't to wait for the browser to parse the HTML to start the request (it's debated though).Early Hints will help even further, enabling preload to kick in even before the response headers for the HTML are sent (on the roadmap in Chromium, Firefox). Plus, Priority Hints will help us indicate loading priorities for scripts.
Beware : if you're using
preload
,as
must be defined or nothing loads, plus preloaded fonts without thecrossorigin
attribute will double fetch. If you're usingprefetch
, beware of theAge
header issues in Firefox.
- Use service workers for caching and network fallbacks.
No performance optimization over a network can be faster than a locally stored cache on a user's machine (there are exceptions though). If your website is running over HTTPS, we can cache static assets in a service worker cache and store offline fallbacks (or even offline pages) and retrieve them from the user's machine, rather than going to the network.As suggested by Phil Walton, with service workers, we can send smaller HTML payloads by programmatically generating our responses. A service worker can request just the bare minimum of data it needs from the server (eg an HTML content partial, a Markdown file, JSON data, etc.), and then it can programmatically transform that data into a full HTML document. So once a user visits a site and the service worker is installed, the user will never request a full HTML page again. The performance impact can be quite impressive.
Browser support? Service workers are widely supported and the fallback is the network anyway. Does it help boost performance ? Oh yes, it does. And it's getting better, eg with Background Fetch allowing background uploads/downloads via a service worker as well.
There are a number of use cases for a service worker. For example, you could implement "Save for offline" feature, handle broken images, introduce messaging between tabs or provide different caching strategies based on request types. In general, a common reliable strategy is to store the app shell in the service worker's cache along with a few critical pages, such as offline page, frontpage and anything else that might be important in your case.
There are a few gotchas to keep in mind though. With a service worker in place, we need to beware range requests in Safari (if you are using Workbox for a service worker it has a range request module). If you ever stumbled upon
DOMException: Quota exceeded.
error in the browser console, then look into Gerardo's article When 7KB equals 7MB.Gerardo는 "프로그레시브 웹 앱을 빌드 중이고 서비스 워커가 CDN에서 제공되는 정적 자산을 캐시할 때 캐시 스토리지가 부풀려지는 경우 원본 간 리소스에 대해 적절한 CORS 응답 헤더가 있는지 확인하고 불투명한 응답을 캐시하지 마십시오. 의도치 않게 서비스 워커와 함께
<img>
태그에crossorigin
속성을 추가하여 교차 출처 이미지 자산을 CORS 모드로 옵트인하게 됩니다."서비스 워커를 시작하는 데 유용한 리소스 가 많이 있습니다.
- 서비스 워커 마인드셋(Service Worker Mindset): 서비스 워커가 배후에서 일하는 방식과 서비스 워커를 구축할 때 이해해야 할 사항을 이해하는 데 도움이 됩니다.
- Chris Ferdinandi는 오프라인 애플리케이션을 만드는 방법을 설명하고 최근에 본 페이지를 오프라인으로 저장하는 것부터 서비스 워커 캐시에 있는 항목의 만료 날짜 설정에 이르기까지 다양한 시나리오를 다루는 서비스 워커에 대한 일련의 훌륭한 기사를 제공합니다.
- 서비스 워커 함정 및 모범 사례, 서비스 워커 등록 지연 및 서비스 워커 캐싱 범위에 대한 몇 가지 팁.
- Ire Aderinokun의 "Offline First"에서 Service Worker와 함께 앱 셸을 사전 캐싱하는 전략에 대한 훌륭한 시리즈입니다.
- 서비스 워커: 풍부한 오프라인 경험, 주기적 백그라운드 동기화 및 푸시 알림을 위해 서비스 워커를 사용하는 방법에 대한 실용적인 팁이 포함된 소개입니다.
- 서비스 워커를 굽는 방법에 대한 다양한 레시피가 포함된 훌륭한 Jake Archibald의 오프라인 요리책을 참조하는 것은 항상 가치가 있습니다.
- Workbox는 프로그레시브 웹 앱을 빌드하기 위해 특별히 빌드된 서비스 워커 라이브러리 세트입니다.
- 예를 들어 A/B 테스트를 위해 CDN/Edge에서 서버 작업자를 실행하고 있습니까?
이 시점에서 우리는 클라이언트에서 서비스 워커를 실행하는 데 상당히 익숙하지만 CDN을 서버에서 구현하면 이를 사용하여 에지에서도 성능을 조정할 수 있습니다.예를 들어 A/B 테스트에서 HTML이 다양한 사용자를 위해 콘텐츠를 변경해야 하는 경우 CDN 서버의 서비스 작업자를 사용하여 논리를 처리할 수 있습니다. 또한 HTML 재작성을 스트리밍하여 Google 글꼴을 사용하는 사이트의 속도를 높일 수 있습니다.
- 렌더링 성능을 최적화합니다.
응용 프로그램이 느려질 때마다 즉시 눈에.니다. 따라서 페이지를 스크롤하거나 요소에 애니메이션을 적용할 때 지연이 없고 지속적으로 초당 60프레임을 기록하고 있는지 확인해야 합니다. 이것이 가능하지 않다면 최소한 초당 프레임 수를 일정하게 유지하는 것이 60에서 15 사이의 혼합 범위보다 바람직합니다. CSS의will-change
를 사용하여 브라우저에 어떤 요소와 속성이 변경되는지 알립니다.발생할 때마다 DevTools에서 불필요한 다시 그리기를 디버그 하세요.
- 런타임 렌더링 성능을 측정합니다. 그것을 이해하는 방법에 대한 몇 가지 유용한 팁을 확인하십시오.
- 시작하려면 브라우저 렌더링 최적화에 대한 Paul Lewis의 무료 Udacity 과정과 웹 성능에 대한 브라우저 페인팅 및 고려 사항에 대한 Georgy Marchuk의 기사를 확인하십시오.
- Firefox DevTools의 "추가 도구 → 렌더링 → 페인트 깜박임"에서 페인트 깜박임을 활성화하십시오.
- React DevTools에서 "업데이트 강조 표시"를 선택하고 "각 구성 요소가 렌더링된 이유 기록"을 활성화합니다.
- 왜 렌더링을 했는가를 사용할 수도 있으므로 구성 요소가 다시 렌더링되면 플래시에서 변경 사항을 알려줍니다.
Masonry 레이아웃을 사용하고 있습니까? 곧 CSS 그리드만으로 Masonry 레이아웃을 구축할 수 있다는 점을 염두에 두십시오.
이 주제에 대해 더 자세히 알고 싶다면 Nolan Lawson이 자신의 기사에서 레이아웃 성능을 정확하게 측정하는 방법을 공유했으며 Jason Miller도 대안 기술을 제안했습니다. GPU 애니메이션을 올바르게 만드는 방법에 대한 Sergey Chikuyonok의 기사도 있습니다.
참고 : GPU 합성 레이어에 대한 변경 사항은 가장 저렴하므로
opacity
및transform
을 통해 합성만 트리거하여 벗어날 수 있다면 올바른 방향으로 가고 있는 것입니다. Anna Migas는 Debugging UI Rendering Performance에 대한 강연에서도 많은 실용적인 조언을 제공했습니다. 그리고 DevTools에서 페인트 성능을 디버그하는 방법을 이해하려면 Umar의 페인트 성능 감사 비디오를 확인하십시오. - 인지된 성능에 최적화했습니까?
구성 요소가 페이지에 표시되는 순서와 브라우저에 자산을 제공하는 전략이 중요하지만 인지된 성능의 역할도 과소평가해서는 안 됩니다. 이 개념은 기본적으로 다른 일이 일어나는 동안 고객을 바쁘게 하거나 참여하게 하는 기다림의 심리적 측면을 다룹니다. 그것이 바로 지각 관리, 선제적 시작, 조기 완료 및 관용 관리가 작동하는 곳입니다.이 모든 것은 무엇을 의미합니까? 자산을 로드하는 동안 우리는 항상 고객 보다 한 발 앞서 려고 노력할 수 있으므로 백그라운드에서 많은 일이 일어나는 동안 경험이 신속하게 느껴집니다. 고객의 참여를 유지하기 위해 표시기를 로드하는 대신 스켈레톤 화면(구현 데모)을 테스트하고, 전환/애니메이션을 추가하고, 더 이상 최적화할 것이 없을 때 기본적으로 UX를 속일 수 있습니다.
Art of UI Skeletons에 대한 사례 연구에서 Kumar McMillan은 동적 목록, 텍스트 및 최종 화면을 시뮬레이션하는 방법과 React로 뼈대 사고 를 고려하는 방법에 대한 몇 가지 아이디어와 기술을 공유합니다.
하지만 조심하십시오. 일부 테스트에서는 스켈레톤 스크린이 모든 메트릭에서 최악의 성능을 보일 수 있음을 보여주었으므로 배포하기 전에 스켈레톤 스크린을 테스트해야 합니다.
- 레이아웃 변경 및 다시 그리기를 방지합니까?
인지된 성능의 영역에서 아마도 더 파괴적인 경험 중 하나는 크기 조정된 이미지 및 비디오, 웹 글꼴, 삽입된 광고 또는 구성 요소를 실제 콘텐츠로 채우는 늦게 발견된 스크립트로 인해 발생하는 레이아웃 이동 또는 리플로우 일 것입니다. 결과적으로 고객은 읽기 영역 위의 레이아웃 점프에 의해 중단되기 위해 기사 읽기를 시작할 수 있습니다. 경험은 종종 갑작스럽고 상당히 혼란스럽습니다. 이는 아마도 재고해야 할 우선 순위를 로드하는 경우일 것입니다.커뮤니티는 리플로우를 방지하기 위해 몇 가지 기술과 해결 방법을 개발했습니다. 일반적으로 사용자 상호 작용에 대한 응답으로 발생하지 않는 한 기존 콘텐츠 위에 새 콘텐츠를 삽입 하지 않는 것이 좋습니다. 항상 이미지에 너비 및 높이 속성을 설정하므로 최신 브라우저는 기본적으로 상자를 할당하고 공간을 예약합니다(Firefox, Chrome).
이미지나 비디오 모두에 대해 SVG 자리 표시자를 사용하여 미디어가 표시될 디스플레이 상자를 예약할 수 있습니다. 즉, 가로 세로 비율도 유지해야 할 때 영역이 적절하게 예약됩니다. 또한 자리 표시자 또는 광고 및 동적 콘텐츠에 대한 대체 이미지를 사용할 수 있으며 레이아웃 슬롯을 미리 할당할 수도 있습니다.
외부 스크립트로 이미지를 지연 로드하는 대신 기본 지연 로딩이 지원되지 않는 경우에만 외부 스크립트를 로드할 때 네이티브 지연 로딩 또는 하이브리드 지연 로딩을 사용하는 것이 좋습니다.
위에서 언급했듯이 항상 웹 글꼴 다시 그리기를 그룹화하고 모든 대체 글꼴에서 한 번에 모든 웹 글꼴로 전환합니다. font-style-matcher를 사용하여 글꼴 사이의 줄 높이와 간격을 조정하여 전환이 너무 갑작스럽지 않은지 확인하십시오. .
웹 글꼴을 에뮬레이트하기 위해 대체 글꼴에 대한 글꼴 메트릭을 재정의 하려면 @font-face 설명자를 사용하여 글꼴 메트릭을 재정의할 수 있습니다(데모, Chrome 87에서 활성화됨). (그러나 복잡한 글꼴 스택으로 인해 조정이 복잡합니다.)
후기 CSS의 경우 레이아웃에 중요한 CSS가 각 템플릿의 헤더에 인라인 되도록 할 수 있습니다. 그보다 훨씬 더: 긴 페이지의 경우 세로 스크롤 막대가 추가되면 기본 콘텐츠가 왼쪽으로 16px 이동합니다. 스크롤바를 일찍 표시하기 위해
overflow-y: scroll
onhtml
을 추가하여 처음 페인트할 때 스크롤바를 적용할 수 있습니다. 후자는 스크롤바가 너비가 변경될 때 스크롤바가 접히지 않는 콘텐츠 리플로우로 인해 사소한 레이아웃 이동을 유발할 수 있기 때문에 도움이 됩니다. Windows와 같은 비 오버레이 스크롤바가 있는 플랫폼에서 대부분 발생해야 합니다. 하지만: breakposition: sticky
는 해당 요소가 컨테이너 밖으로 스크롤되지 않기 때문입니다.스크롤할 때 페이지 상단에 고정되거나 고정되는 헤더를 처리하는 경우 헤더가 파인될 때 헤더를 위한 공간을 확보하십시오(예: 자리 표시자 요소 또는 콘텐츠의
margin-top
사용). 예외는 CLS에 영향을 미치지 않아야 하는 쿠키 동의 배너여야 하지만 때로는 영향을 미치므로 구현에 따라 다릅니다. 이 Twitter 스레드에는 몇 가지 흥미로운 전략과 시사점이 있습니다.다양한 양의 텍스트를 포함할 수 있는 탭 구성 요소의 경우 CSS 그리드 스택을 사용하여 레이아웃 이동을 방지할 수 있습니다. 각 탭의 내용을 동일한 그리드 영역에 배치하고 한 번에 하나씩 숨김으로써 컨테이너가 항상 더 큰 요소의 높이를 사용하도록 할 수 있으므로 레이아웃 이동이 발생하지 않습니다.
아, 물론 목록 아래에 내용(예: 바닥글)이 있는 경우 무한 스크롤 및 "더 로드"로 인해 레이아웃이 변경될 수도 있습니다. CLS를 개선하려면 사용자가 페이지의 해당 부분으로 스크롤 하기 전에 로드될 콘텐츠를 위한 충분한 공간을 예약하고 콘텐츠 로드로 인해 아래로 밀려날 수 있는 페이지 하단의 바닥글 또는 DOM 요소를 제거합니다. 또한, 스크롤 없이 볼 수 있는 콘텐츠에 대한 데이터와 이미지를 미리 가져오므로 사용자가 스크롤할 때 해당 콘텐츠가 이미 존재합니다. react-window와 같은 목록 가상화 라이브러리를 사용하여 긴 목록도 최적화할 수 있습니다( 감사합니다, Addy Osmani! ).
리플로우의 영향이 포함되도록 하려면 Layout Instability API를 사용하여 레이아웃 안정성을 측정하십시오. 이를 통해 CLS (누적 레이아웃 이동) 점수를 계산하고 테스트에 요구 사항으로 포함할 수 있으므로 회귀가 나타날 때마다 추적하고 수정할 수 있습니다.
레이아웃 이동 점수를 계산하기 위해 브라우저는 뷰포트 크기와 두 렌더링된 프레임 사이의 뷰포트에서 불안정한 요소의 움직임을 확인합니다. 이상적으로 점수는
0
에 가까울 것입니다. Milica Mihajlija와 Philip Walton이 CLS가 무엇이며 어떻게 측정하는지에 대한 훌륭한 가이드가 있습니다. 특히 비즈니스 크리티컬 작업의 경우 인지된 성능을 측정 및 유지하고 중단을 피하기 위한 좋은 출발점입니다.빠른 팁 : DevTools에서 레이아웃 이동을 일으킨 원인을 찾으려면 성능 패널의 "경험" 에서 레이아웃 이동을 탐색할 수 있습니다.
보너스 : 리플로우 및 다시 그리기를 줄이고 싶다면 Charis Theodoulou의 DOM Reflow/Layout Thrashing 최소화 가이드와 Paul Irish의 What force layout/reflow 목록과 CSSTriggers.com, 레이아웃, 페인트를 트리거하는 CSS 속성에 대한 참조 테이블을 확인하세요. 그리고 합성.
네트워킹 및 HTTP/2
- OCSP 스테이플링이 활성화되어 있습니까?
서버에서 OCSP 스테이플링을 활성화하면 TLS 핸드셰이크 속도를 높일 수 있습니다. OCSP(온라인 인증서 상태 프로토콜)는 CRL(인증서 해지 목록) 프로토콜의 대안으로 만들어졌습니다. 두 프로토콜 모두 SSL 인증서가 해지되었는지 확인하는 데 사용됩니다.그러나 OCSP 프로토콜은 브라우저가 인증서 정보 목록을 다운로드한 다음 검색하는 데 시간을 할애할 필요가 없으므로 핸드셰이크에 필요한 시간이 줄어듭니다.
- SSL 인증서 해지의 영향을 줄였습니까?
"EV 인증서의 성능 비용"에 대한 기사에서 Simon Hearne은 일반 인증서에 대한 훌륭한 개요와 인증서 선택이 전체 성능에 미칠 수 있는 영향을 제공합니다.Simon이 쓴 것처럼 HTTPS의 세계에는 트래픽을 보호하는 데 사용되는 몇 가지 유형의 인증서 유효성 검사 수준이 있습니다.
- DV( 도메인 유효성 검사 )는 인증서 요청자가 도메인을 소유하고 있는지 확인하고,
- 조직 검증 (OV)은 조직이 도메인을 소유하고 있는지,
- 확장 검증 (EV)은 엄격한 검증을 통해 조직이 도메인을 소유하고 있는지 검증합니다.
이러한 모든 인증서는 기술 측면에서 동일합니다. 인증서에 제공된 정보와 속성만 다릅니다.
EV 인증서는 사람이 인증서를 검토하고 유효성을 확인해야 하므로 비용이 많이 들고 시간이 많이 걸립니다 . 반면에 DV 인증서는 많은 호스팅 제공업체 및 CDN에 잘 통합된 개방형 자동화 인증 기관(예: Let's Encrypt)에서 무료로 제공되는 경우가 많습니다. 사실, 이 글을 쓰는 시점에서 2억 2,500만 개 이상의 웹사이트(PDF)를 지원하지만 페이지의 2.69%(Firefox에서 열림)만 차지합니다.
그렇다면 무엇이 문제인가? 문제는 EV 인증서가 위에서 언급한 OCSP 스테이플링을 완전히 지원하지 않는다는 것 입니다. 스테이플링을 사용하면 서버가 인증서가 해지되었는지 인증 기관에 확인한 다음 이 정보를 인증서에 추가("스테이플")할 수 있지만 스테이플링 없이 클라이언트가 모든 작업을 수행해야 하므로 TLS 협상 중에 불필요한 요청이 발생합니다. . 연결 상태가 좋지 않으면 눈에 띄는 성능 비용(1000ms+)이 발생할 수 있습니다.
EV 인증서는 웹 성능을 위한 좋은 선택이 아니며 DV 인증서보다 성능에 훨씬 더 큰 영향을 미칠 수 있습니다. 최적의 웹 성능을 위해 항상 OCSP 스테이플 DV 인증서를 제공하십시오. 또한 EV 인증서보다 훨씬 저렴하고 번거롭지 않습니다. 글쎄, 적어도 CRLite를 사용할 수 있을 때까지는.
참고 : QUIC/HTTP/3를 사용하면 TLS 인증서 체인이 QUIC 핸드셰이크의 바이트 수를 지배하는 하나의 가변 크기 콘텐츠라는 점에 주목할 가치가 있습니다. 크기는 수백 바이트에서 10KB 이상까지 다양합니다.
따라서 TLS 인증서를 작게 유지하는 것은 QUIC/HTTP/3에서 매우 중요합니다. 큰 인증서는 다중 핸드셰이크를 유발하기 때문입니다. 또한 인증서가 압축되어 있는지 확인해야 합니다. 그렇지 않으면 인증서 체인이 너무 커서 단일 QUIC 항공편에 맞지 않을 수 있습니다.
다음에서 문제와 솔루션에 대한 자세한 내용과 포인터를 찾을 수 있습니다.
- EV 인증서는 웹을 느리고 신뢰할 수 없게 만듭니다. Aaron Peters,
- SSL 인증서 해지가 웹 성능에 미치는 영향 Matt Hobbs,
- Simon Hearne의 EV 인증서 성능 비용,
- QUIC 핸드셰이크가 빠르려면 압축이 필요합니까? 패트릭 맥마누스 지음.
- 아직 IPv6을 채택하셨습니까?
IPv4의 공간이 부족하고 주요 모바일 네트워크에서 IPv6을 빠르게 채택하고 있기 때문에(미국은 거의 50% IPv6 채택 임계값에 도달했습니다), 미래를 대비하기 위해 DNS를 IPv6으로 업데이트하는 것이 좋습니다. 네트워크 전체에 이중 스택 지원이 제공되는지 확인하십시오. IPv6과 IPv4가 동시에 실행될 수 있습니다. 결국 IPv6은 이전 버전과 호환되지 않습니다. 또한 연구에 따르면 IPv6은 NDP(이웃 검색) 및 경로 최적화로 인해 해당 웹 사이트를 10~15% 더 빠르게 만들었습니다. - 모든 자산이 HTTP/2(또는 HTTP/3)를 통해 실행되는지 확인하십시오.
Google이 지난 몇 년 동안 더 안전한 HTTPS 웹을 추진함에 따라 HTTP/2 환경으로의 전환은 확실히 좋은 투자입니다. 실제로 Web Almanac에 따르면 모든 요청의 64%가 이미 HTTP/2를 통해 실행되고 있습니다.HTTP/2는 완벽하지 않고 우선순위 문제가 있지만 매우 잘 지원된다는 점을 이해하는 것이 중요합니다. 그리고 대부분의 경우에 더 좋습니다.
주의 사항: HTTP/2 서버 푸시가 Chrome에서 제거되고 있으므로 구현이 서버 푸시에 의존하는 경우 다시 방문해야 할 수 있습니다. 대신 이미 Fastly에서 실험으로 통합된 Early Hint를 볼 수 있습니다.
여전히 HTTP에서 실행 중인 경우 가장 시간이 많이 걸리는 작업은 먼저 HTTPS로 마이그레이션한 다음 HTTP/2 멀티플렉싱 및 병렬화를 수용하도록 빌드 프로세스를 조정하는 것입니다. HTTP/2를 Gov.uk로 가져오는 것은 CORS, SRI 및 WPT를 통해 방법을 찾는 것과 같은 작업에 대한 환상적인 사례 연구입니다. 이 기사의 나머지 부분에서는 HTTP/2로 전환 중이거나 이미 HTTP/2로 전환했다고 가정합니다.
- HTTP/2를 적절하게 배포합니다.
다시 말하지만, HTTP/2를 통해 자산을 제공하는 것은 지금까지 자산을 제공한 방식을 부분적으로 점검함으로써 이점을 얻을 수 있습니다. 모듈 패키징과 많은 작은 모듈을 병렬로 로드하는 것 사이에서 균형을 잘 찾아야 합니다. 하루가 끝나면 여전히 가장 좋은 요청은 요청이 없는 것이지만 목표는 자산의 빠른 첫 번째 전달과 캐싱 사이에서 적절한 균형을 찾는 것입니다.한편으로는 전체 인터페이스를 여러 개의 작은 모듈로 분할하여 빌드 프로세스의 일부로 압축하고 병렬로 로드하는 대신 자산을 완전히 연결하는 것을 피하고 싶을 수 있습니다. 파일 하나를 변경해도 전체 스타일 시트나 JavaScript를 다시 다운로드할 필요가 없습니다. 또한 구문 분석 시간을 최소화하고 개별 페이지의 페이로드를 낮게 유지합니다.
반면에 포장은 여전히 중요합니다. 많은 작은 스크립트를 사용하면 전체 압축이 저하 되고 캐시에서 개체를 검색하는 비용이 증가합니다. 큰 패키지를 압축하면 사전 재사용의 이점을 얻을 수 있지만 작은 개별 패키지는 그렇지 않습니다. 이를 해결하기 위한 표준 작업이 있지만 현재로서는 아직 멀었습니다. 둘째, 브라우저는 아직 이러한 워크플로에 최적화되어 있지 않습니다 . 예를 들어 Chrome은 리소스 수에 선형적으로 IPC(프로세스 간 통신)를 트리거하므로 수백 개의 리소스를 포함하면 브라우저 런타임 비용이 발생합니다.
그래도 CSS를 점진적으로 로드할 수 있습니다. 사실 인바디 CSS는 더 이상 Chrome용 렌더링을 차단하지 않습니다. 그러나 몇 가지 우선 순위 문제가 있으므로 간단하지는 않지만 실험해 볼 가치가 있습니다.
HTTP/2 연결 병합을 사용하면 HTTP/2의 이점을 누리면서 도메인 샤딩을 사용할 수 있지만 실제로 이를 달성하는 것은 어렵고 일반적으로 좋은 방법으로 간주되지 않습니다. 또한 HTTP/2 및 하위 리소스 무결성이 항상 작동하는 것은 아닙니다.
무엇을 할까요? HTTP/2를 통해 실행하는 경우 약 6-10개의 패키지 를 보내는 것이 적절한 절충안처럼 보입니다(레거시 브라우저에서는 나쁘지 않습니다). 웹사이트에 적합한 균형을 찾기 위해 실험하고 측정합니다.
- 단일 HTTP/2 연결을 통해 모든 자산을 전송합니까?
HTTP/2의 주요 장점 중 하나는 단일 연결을 통해 자산을 전송할 수 있다는 것입니다. 그러나 때로는 우리가 뭔가 잘못했을 수 있습니다. 예를 들어 CORS 문제가 있거나crossorigin
속성을 잘못 구성하여 브라우저가 새 연결을 열도록 강제할 수 있습니다.모든 요청이 단일 HTTP/2 연결을 사용하는지 또는 무언가 잘못 구성되었는지 확인하려면 DevTools → 네트워크에서 "연결 ID" 열을 활성화하십시오. 예를 들어 여기에서 모든 요청은 동일한 연결(286)을 공유합니다. 단, manifest.json은 별도의 연결(451)을 엽니다.
- 서버와 CDN이 HTTP/2를 지원합니까?
다른 서버와 CDN(여전히)은 HTTP/2를 다르게 지원합니다. CDN 비교를 사용하여 옵션을 확인하거나 서버의 성능과 지원되는 기능을 빠르게 찾아보십시오.HTTP/2 우선 순위(비디오) 및 HTTP/2 우선 순위 지정에 대한 테스트 서버 지원에 대한 Pat Meenan의 놀라운 연구를 참조하십시오. Pat에 따르면 Linux 4.9 커널 이상에서 안정적으로 작동하려면 BBR 혼잡 제어를 활성화하고
tcp_notsent_lowat
를 16KB로 설정하는 것이 좋습니다( 감사합니다, Yoav! ). Andy Davies는 브라우저, CDN 및 Cloud Hosting Services에서 HTTP/2 우선 순위 지정에 대해 유사한 연구를 수행했습니다.그 상태에서 커널이 TCP BBR을 지원하는지 다시 확인하고 가능한 경우 활성화하십시오. 현재 Google Cloud Platform, Amazon Cloudfront, Linux(예: Ubuntu)에서 사용됩니다.
- HPACK 압축이 사용 중입니까?
HTTP/2를 사용하는 경우 불필요한 오버헤드를 줄이기 위해 서버가 HTTP 응답 헤더에 대해 HPACK 압축을 구현하는지 다시 확인하십시오. 일부 HTTP/2 서버는 사양을 완전히 지원하지 않을 수 있으며 HPACK이 예입니다. H2spec은 이를 확인하는 훌륭한(기술적으로 세부적인 경우) 도구입니다. HPACK의 압축 알고리즘은 매우 인상적이며 작동합니다. - 서버의 보안이 방탄인지 확인하십시오.
HTTP/2의 모든 브라우저 구현은 TLS를 통해 실행되므로 보안 경고나 페이지의 일부 요소가 작동하지 않는 것을 피하고 싶을 것입니다. 보안 헤더가 올바르게 설정되었는지 다시 확인하고 알려진 취약점을 제거하고 HTTPS 설정을 확인하십시오.또한 모든 외부 플러그인 및 추적 스크립트가 HTTPS를 통해 로드되고 교차 사이트 스크립팅이 불가능하며 HTTP Strict Transport Security 헤더와 콘텐츠 보안 정책 헤더가 모두 올바르게 설정되었는지 확인하십시오.
- 서버와 CDN이 HTTP/3를 지원합니까?
HTTP/2는 웹에 여러 가지 중요한 성능 향상을 가져왔지만 상당한 패킷 손실이 있는 느린 네트워크에서 눈에 띄는 TCP의 head-of-line 차단과 같이 개선해야 할 부분도 많이 남겼습니다. HTTP/3는 이러한 문제를 잘 해결하고 있습니다(기사).HTTP/2 문제를 해결하기 위해 IETF는 Google, Akamai 등과 함께 최근에 HTTP/3으로 표준화된 새로운 프로토콜을 개발하고 있습니다.
Robin Marx는 HTTP/3에 대해 아주 잘 설명했고, 다음 설명은 그 설명을 기반으로 합니다. 핵심적으로 HTTP/3은 기능 측면에서 HTTP/2와 매우 유사 하지만 내부적으로는 매우 다르게 작동합니다. HTTP/3는 더 빠른 핸드셰이크, 더 나은 암호화, 더 안정적인 독립 스트림, 더 나은 암호화 및 흐름 제어와 같은 여러 개선 사항을 제공합니다. 주목할만한 차이점은 HTTP/3이 TCP가 아닌 UDP 다이어그램 위에 캡슐화된 QUIC 패킷과 함께 QUIC를 전송 계층으로 사용한다는 것입니다.
QUIC는 TLS 1.3을 프로토콜에 완전히 통합하는 반면 TCP에서는 맨 위에 계층화됩니다. 일반적인 TCP 스택에서는 TCP와 TLS가 별도의 핸드셰이크를 수행해야 하기 때문에 몇 번의 왕복 오버헤드가 있지만 QUIC를 사용하면 두 가지 모두를 단일 왕복으로 결합하고 완료 할 수 있습니다. TLS 1.3을 사용하면 후속 연결에 대한 암호화 키를 설정할 수 있으므로 두 번째 연결부터 "0-RTT"라고 하는 첫 번째 왕복에서 애플리케이션 계층 데이터를 이미 보내고 받을 수 있습니다.
또한 HTTP/2의 헤더 압축 알고리즘은 우선 순위 시스템과 함께 완전히 재작성되었습니다. 또한 QUIC는 각 QUIC 패킷 헤더의 연결 ID를 통해 Wi-Fi에서 셀룰러 네트워크로의 연결 마이그레이션 을 지원합니다. 대부분의 구현은 커널 공간이 아닌 사용자 공간에서 이루어지므로(TCP에서와 같이) 앞으로 프로토콜이 발전할 것으로 예상해야 합니다.
모든 것이 큰 차이를 만들까요? 아마도 그렇습니다. 특히 모바일에서 로딩 시간에 영향을 미칠 뿐만 아니라 최종 사용자에게 자산을 제공하는 방법에도 영향을 미칩니다. HTTP/2에서는 여러 요청이 연결을 공유하지만 HTTP/3에서는 요청도 연결을 공유하지만 독립적으로 스트리밍하므로 삭제된 패킷이 더 이상 모든 요청에 영향을 미치지 않고 하나의 스트림에만 영향을 줍니다.
즉, 하나의 큰 JavaScript 번들을 사용하면 하나의 스트림이 일시 중지되면 자산 처리 속도가 느려지지만 여러 파일이 병렬로 스트리밍되는 경우(HTTP/3) 영향이 덜 중요합니다. 그래서 포장은 여전히 중요 합니다.
HTTP/3는 아직 진행 중입니다. Chrome, Firefox 및 Safari에는 이미 구현이 있습니다. 일부 CDN은 이미 QUIC 및 HTTP/3을 지원합니다. 2020년 말 Chrome은 HTTP/3 및 IETF QUIC 배포를 시작했으며 실제로 모든 Google 서비스(Google Analytics, YouTube 등)는 이미 HTTP/3를 통해 실행되고 있습니다. LiteSpeed 웹 서버는 HTTP/3를 지원하지만 Apache, nginx 또는 IIS는 아직 지원하지 않지만 2021년에는 빠르게 변경될 것입니다.
결론 : 서버와 CDN에서 HTTP/3를 사용할 수 있는 옵션이 있다면 그렇게 하는 것이 좋습니다. 주요 이점은 특히 대기 시간이 긴 연결에서 여러 개체를 동시에 가져오는 것입니다. 해당 분야에 대한 연구가 많지 않아 아직 확실하지 않지만 첫 번째 결과는 매우 유망합니다.
프로토콜의 세부 사항과 장점에 대해 더 자세히 알아보려면 다음을 확인하는 것이 좋습니다.
- HTTP/3 설명, HTTP/3 및 QUIC 프로토콜을 문서화하기 위한 공동 작업. 다양한 언어로 제공되며 PDF로도 제공됩니다.
- Daniel Stenberg와 함께 HTTP/3으로 웹 성능을 향상시키십시오.
- Robin Marx와 함께 QUIC에 대한 아카데믹 가이드는 QUIC 및 HTTP/3 프로토콜의 기본 개념을 소개하고, HTTP/3가 헤드 오브 라인 차단 및 연결 마이그레이션을 처리하는 방법, HTTP/3이 항상 지속되도록 설계되는 방법을 설명합니다(감사합니다, Simon !).
- HTTP3Check.net에서 서버가 HTTP/3에서 실행 중인지 확인할 수 있습니다.
테스트 및 모니터링
- 감사 워크플로를 최적화했습니까?
별 것 아닌 것처럼 들릴 수도 있지만 손끝에서 올바른 설정을 하면 테스트 시간을 상당히 절약할 수 있습니다. WebPageTest의 공개 인스턴스에 테스트를 제출하기 위해 Tim Kadlec의 Alfred Workflow for WebPageTest를 사용하는 것을 고려하십시오. 실제로 WebPageTest에는 모호한 기능이 많이 있으므로 시간을 내어 WebPageTest Waterfall View 차트를 읽는 방법과 WebPageTest Connection View 차트를 읽는 방법을 배워 성능 문제를 더 빨리 진단하고 해결하십시오.또한 Google 스프레드시트에서 WebPageTest를 구동하고 Lighthouse CI를 사용하여 Travis 설정에 접근성, 성능 및 SEO 점수를 통합하거나 Webpack에 바로 통합할 수 있습니다.
여러 소스에서 성능 데이터를 자동으로 수집할 수 있는 모듈식 도구인 최근 출시된 AutoWebPerf를 살펴보십시오. 예를 들어 중요한 페이지에 대한 일일 테스트를 설정하여 CrUX API의 현장 데이터와 PageSpeed Insights의 Lighthouse 보고서에서 실험실 데이터를 캡처할 수 있습니다.
그리고 무언가를 빠르게 디버그해야 하지만 빌드 프로세스가 현저하게 느린 것 같다면 "공백 제거 및 기호 맹글링은 정교한 코드 변환이 아니라 대부분의 JavaScript에서 축소된 코드의 크기 감소의 95%를 차지합니다. 압축을 비활성화하면 Uglify 빌드 속도가 3~4배 빨라집니다."
- 프록시 브라우저와 레거시 브라우저에서 테스트했습니까?
Chrome 및 Firefox에서 테스트하는 것만으로는 충분하지 않습니다. 웹사이트가 프록시 브라우저와 레거시 브라우저에서 어떻게 작동하는지 살펴보세요. 예를 들어 UC Browser와 Opera Mini는 아시아에서 상당한 시장 점유율을 차지하고 있습니다(아시아에서는 최대 35%). 나중에 큰 놀라움을 피하기 위해 관심 국가의 평균 인터넷 속도를 측정하십시오. 네트워크 조절로 테스트하고 높은 DPI 장치를 에뮬레이트합니다. BrowserStack은 원격 실제 장치에서 테스트하는 데 환상적이며 사무실에 있는 최소한 몇 대의 실제 장치로도 이를 보완합니다. 그만한 가치가 있습니다.
- 404 페이지의 성능을 테스트해 보셨습니까?
일반적으로 우리는 404페이지에 대해 두 번 생각하지 않습니다. 결국 클라이언트가 서버에 존재하지 않는 페이지를 요청하면 서버는 404 상태 코드 및 관련 404 페이지로 응답합니다. 그렇게 많지 않죠?404 응답의 중요한 측면은 브라우저로 전송되는 실제 응답 본문 크기 입니다. Matt Hobbs의 404페이지 연구에 따르면 404 응답의 대다수는 누락된 파비콘, WordPress 업로드 요청, 깨진 JavaScript 요청, 매니페스트 파일, CSS 및 글꼴 파일에서 비롯됩니다. 클라이언트가 존재하지 않는 자산을 요청할 때마다 404 응답을 받게 되며 종종 그 응답은 엄청납니다.
404 페이지에 대한 캐싱 전략 을 검토하고 최적화하십시오. 우리의 목표는 HTML 응답을 예상할 때만 브라우저에 HTML을 제공하고 다른 모든 응답에 대해 작은 오류 페이로드를 반환하는 것입니다. Matt에 따르면 "원점 앞에 CDN을 배치하면 CDN에 404 페이지 응답을 캐시할 수 있습니다. CDN이 없으면 404 페이지를 공격하는 것이 DoS 공격 벡터로 사용될 수 있기 때문에 유용합니다. CDN이 캐시된 버전으로 응답하도록 하는 대신 원본 서버가 모든 404 요청에 응답하도록 강제합니다."
404 오류는 성능을 저하시킬 뿐만 아니라 트래픽 비용도 발생할 수 있으므로 Lighthouse 테스트 제품군에 404 오류 페이지를 포함하고 시간 경과에 따른 점수를 추적하는 것이 좋습니다.
- GDPR 동의 프롬프트의 성능을 테스트했습니까?
GDPR 및 CCPA 시대에는 EU 고객이 추적을 선택하거나 선택 해제할 수 있는 옵션을 제공하기 위해 타사에 의존하는 것이 일반적이 되었습니다. 그러나 다른 타사 스크립트와 마찬가지로 해당 스크립트의 성능은 전체 성능 노력에 상당히 치명적인 영향을 미칠 수 있습니다.물론 실제 동의는 스크립트가 전체 성능에 미치는 영향을 변경할 가능성이 있으므로 Boris Schapira가 언급했듯이 몇 가지 다른 웹 성능 프로필을 연구하고 싶을 수 있습니다.
- 동의를 완전히 거부했으며,
- 동의가 부분적으로 거부되었으며,
- 전적으로 동의했습니다.
- 사용자가 동의 프롬프트에 대해 조치를 취하지 않았습니다(또는 프롬프트가 콘텐츠 차단기에 의해 차단됨).
일반적으로 쿠키 동의 프롬프트는 CLS에 영향을 미치지 않아야 하지만 때때로 영향을 미치므로 무료 및 오픈 소스 옵션인 Osano 또는 cookie-consent-box를 사용하는 것이 좋습니다.
일반적으로 마우스 이벤트의 수평 또는 수직 오프셋을 결정하고 앵커에 상대적으로 팝업을 올바르게 배치해야 하므로 팝업 성능 을 조사할 가치가 있습니다. Noam Rosenthal은 웹 성능 사례 연구: Wikipedia 페이지 미리보기(비디오 및 회의록으로도 사용 가능) 문서에서 Wikimedia 팀의 학습 내용을 공유합니다.
- 성능 진단 CSS를 유지합니까?
성능이 저하된 코드가 배포되었는지 확인하기 위해 모든 종류의 검사를 포함할 수 있지만 쉽게 해결할 수 있는 몇 가지 간단한 결과에 대한 빠른 아이디어를 얻는 것이 유용한 경우가 많습니다. 이를 위해 Tim Kadlec의 뛰어난 성능 진단 CSS(지연 로드 이미지, 크기가 지정되지 않은 이미지, 레거시 형식 이미지 및 동기 스크립트를 강조 표시하는 Harry Roberts의 스니펫에서 영감을 얻었습니다.예를 들어 스크롤 없이 볼 수 있는 이미지가 지연 로드되지 않도록 하고 싶을 수 있습니다. 예를 들어 사용되지 않는 웹 글꼴을 강조 표시하거나 아이콘 글꼴을 감지하기 위해 필요에 따라 스니펫을 사용자 정의할 수 있습니다. 디버깅하는 동안 실수를 확인하거나 현재 프로젝트를 매우 빠르게 감사하기 위한 아주 작은 도구입니다.
/* Performance Diagnostics CSS */ /* via Harry Roberts. https://twitter.com/csswizardry/status/1346477682544951296 */ img[loading=lazy] { outline: 10px solid red; }
- Have you tested the impact on accessibility?
When the browser starts to load a page, it builds a DOM, and if there is an assistive technology like a screen reader running, it also creates an accessibility tree. The screen reader then has to query the accessibility tree to retrieve the information and make it available to the user — sometimes by default, and sometimes on demand. And sometimes it takes time.When talking about fast Time to Interactive, usually we mean an indicator of how soon a user can interact with the page by clicking or tapping on links and buttons. The context is slightly different with screen readers. In that case, fast Time to Interactive means how much time passes by until the screen reader can announce navigation on a given page and a screen reader user can actually hit keyboard to interact.
Leonie Watson has given an eye-opening talk on accessibility performance and specifically the impact slow loading has on screen reader announcement delays. Screen readers are used to fast-paced announcements and quick navigation, and therefore might potentially be even less patient than sighted users.
Large pages and DOM manipulations with JavaScript will cause delays in screen reader announcements. A rather unexplored area that could use some attention and testing as screen readers are available on literally every platform (Jaws, NVDA, Voiceover, Narrator, Orca).
- Is continuous monitoring set up?
Having a private instance of WebPagetest is always beneficial for quick and unlimited tests. However, a continuous monitoring tool — like Sitespeed, Calibre and SpeedCurve — with automatic alerts will give you a more detailed picture of your performance. Set your own user-timing marks to measure and monitor business-specific metrics. Also, consider adding automated performance regression alerts to monitor changes over time.Look into using RUM-solutions to monitor changes in performance over time. For automated unit-test-alike load testing tools, you can use k6 with its scripting API. Also, look into SpeedTracker, Lighthouse and Calibre.
빠른 승리
This list is quite comprehensive, and completing all of the optimizations might take quite a while. So, if you had just 1 hour to get significant improvements, what would you do? Let's boil it all down to 17 low-hanging fruits . Obviously, before you start and once you finish, measure results, including Largest Contentful Paint and Time To Interactive on a 3G and cable connection.
- Measure the real world experience and set appropriate goals. Aim to be at least 20% faster than your fastest competitor. Stay within Largest Contentful Paint < 2.5s, a First Input Delay < 100ms, Time to Interactive < 5s on slow 3G, for repeat visits, TTI < 2s. Optimize at least for First Contentful Paint and Time To Interactive.
- Optimize images with Squoosh, mozjpeg, guetzli, pingo and SVGOMG, and serve AVIF/WebP with an image CDN.
- Prepare critical CSS for your main templates, and inline them in the
<head>
of each template. For CSS/JS, operate within a critical file size budget of max. 170KB gzipped (0.7MB decompressed). - Trim, optimize, defer and lazy-load scripts. Invest in the config of your bundler to remove redundancies and check lightweight alternatives.
- Always self-host your static assets and always prefer to self-host third-party assets. Limit the impact of third-party scripts. Use facades, load widgets on interaction and beware of anti-flicker snippets.
- Be selective when choosing a framework. For single-page-applications, identify critical pages and serve them statically, or at least prerender them, and use progressive hydration on component-level and import modules on interaction.
- Client-side rendering alone isn't a good choice for performance. Prerender if your pages don't change much, and defer the booting of frameworks if you can. If possible, use streaming server-side rendering.
- Serve legacy code only to legacy browsers with
<script type="module">
and module/nomodule pattern. - Experiment with regrouping your CSS rules and test in-body CSS.
- Add resource hints to speed up delivery with faster
dns-lookup
,preconnect
,prefetch
,preload
andprerender
. - Subset web fonts and load them asynchronously, and utilize
font-display
in CSS for fast first rendering. - Check that HTTP cache headers and security headers are set properly.
- Enable Brotli compression on the server. (If that's not possible, at least make sure that Gzip compression is enabled.)
- Enable TCP BBR congestion as long as your server is running on the Linux kernel version 4.9+.
- Enable OCSP stapling and IPv6 if possible. Always serve an OCSP stapled DV certificate.
- Enable HPACK compression for HTTP/2 and move to HTTP/3 if it's available.
- Cache assets such as fonts, styles, JavaScript and images in a service worker cache.
Download The Checklist (PDF, Apple Pages)
With this checklist in mind, you should be prepared for any kind of front-end performance project. Feel free to download the print-ready PDF of the checklist as well as an editable Apple Pages document to customize the checklist for your needs:
- Download the checklist PDF (PDF, 166 KB)
- Download the checklist in Apple Pages (.pages, 275 KB)
- Download the checklist in MS Word (.docx, 151 KB)
If you need alternatives, you can also check the front-end checklist by Dan Rublic, the "Designer's Web Performance Checklist" by Jon Yablonski and the FrontendChecklist.
오프 위 고!
Some of the optimizations might be beyond the scope of your work or budget or might just be overkill given the legacy code you have to deal with. 괜찮아! Use this checklist as a general (and hopefully comprehensive) guide, and create your own list of issues that apply to your context. But most importantly, test and measure your own projects to identify issues before optimizing. Happy performance results in 2021, everyone!
A huge thanks to Guy Podjarny, Yoav Weiss, Addy Osmani, Artem Denysov, Denys Mishunov, Ilya Pukhalski, Jeremy Wagner, Colin Bendell, Mark Zeman, Patrick Meenan, Leonardo Losoviz, Andy Davies, Rachel Andrew, Anselm Hannemann, Barry Pollard, Patrick Hamann, Gideon Pyzer, Andy Davies, Maria Prosvernina, Tim Kadlec, Rey Bango, Matthias Ott, Peter Bowyer, Phil Walton, Mariana Peralta, Pepijn Senders, Mark Nottingham, Jean Pierre Vincent, Philipp Tellis, Ryan Townsend, Ingrid Bergman, Mohamed Hussain SH, Jacob Groß, Tim Swalling, Bob Visser, Kev Adamson, Adir Amsalem, Aleksey Kulikov and Rodney Rehm for reviewing this article, as well as our fantastic community which has shared techniques and lessons learned from its work in performance optimization for everybody to use. 당신은 정말 스매싱입니다!