Biasanya, kinerja dipahami sebagai jumlah operasi untuk interval waktu tertentu, dan semakin banyak, semakin baik. Tetapi definisi seperti itu, dan pendekatan secara keseluruhan, memiliki sedikit penerapan ke frontend, karena setiap pengguna akan memiliki "frontend" mereka sendiri. Itulah yang ingin saya bicarakan, apa yang terjadi "di sana", dengan pengguna, di sisi lain, pada kenyataannya, dan bukan pada MacBook teratas Anda.
Selain itu, saya akan mencoba mempertimbangkan secara singkat aturan umum untuk mengoptimalkan kode dan beberapa kesalahan yang perlu diperhatikan. Saya juga akan memberi tahu Anda tentang
alat yang membantu tidak hanya dalam pembuatan profil, tetapi juga di luar kotak mengumpulkan sekelompok metrik dasar tentang kinerja aplikasi Anda (dan saya harap Anda membaca posting ini sampai akhir).
Pertama-tama, kita akan menentukan apa kinerja front-end, dan kemudian kita akan beralih ke bagaimana mengukurnya. Jadi, seperti yang saya katakan, kami tidak akan mengukur beberapa ops / detik, kami membutuhkan data nyata yang dapat menjawab pertanyaan tentang apa yang sebenarnya terjadi dengan proyek kami di setiap tahap pekerjaannya. Untuk melakukan ini, kita memerlukan serangkaian metrik berikut:
- kecepatan unduh;
- waktu render pertama dan interaktivitas (Time To Interactive);
- kecepatan reaksi terhadap tindakan pengguna;
- FPS untuk pengguliran dan animasi;
- inisialisasi aplikasi;
- jika Anda memiliki SPA, maka Anda perlu mengukur waktu yang dihabiskan untuk beralih antar rute;
- konsumsi memori dan lalu lintas;
- dan ... cukup untuk saat ini.
Semua ini adalah metrik dasar, yang tanpanya tidak mungkin untuk memahami apa yang sebenarnya terjadi di front-end. Dan tidak hanya di ujung depan, tetapi pada kenyataannya, dengan pengguna akhir. Tetapi untuk mulai mengumpulkan metrik ini, pertama-tama Anda perlu belajar cara mengukurnya, jadi mari kita ingat metode apa yang ada untuk analisis kinerja.
Hal pertama untuk memulai tentu saja adalah API Kinerja. Yaitu,
performance.timing , yang melaluinya Anda dapat mengetahui berapa lama pengguna untuk membuka proyek Anda. Tetapi API Kinerja hanya mencakup sebagian dari metrik, sisanya harus diukur sendiri, dan untuk ini kami memiliki alat berikut:
Pada saat itu, saya menyadari bahwa Anda perlu melihat alat yang akan menggabungkan kelebihan di atas dan, jika mungkin, tidak memiliki minus. Jadi ada
PerfKeeper .
Perfkeeper
- Kontrol penuh atas awal dan akhir.
- Anda dapat mengirim ke server.
- Ini ditampilkan di konsol.
- Mendukung DevTools -> Kinerja -> Waktu Pengguna.
- Ada pengelompokan.
- Ada pengkodean warna (serta unit pengukuran, mis., Anda dapat mengukur tidak hanya waktu).
- Mendukung ekstensi.
Sekarang saya tidak akan melukis API di sini, saya tidak menulis
dokumentasi untuk ini, dan artikelnya bukan tentang itu, tetapi saya akan melanjutkan tentang cara mengumpulkan metrik.
Kecepatan Unduhan Halaman
Seperti yang sudah saya katakan, Anda dapat mengetahui kecepatan unduhan dari
performance.timing , yang akan memungkinkan Anda untuk mengetahui siklus penuh dari awal pemuatan halaman (waktu untuk menyelesaikan DNS, menginstal Handshake HTTP, memproses permintaan) dan sampai halaman tersebut terisi penuh (DomReady dan OnLoad):

Akibatnya, Anda harus mendapatkan serangkaian metrik berikut:
Contoh ekstensi navigasi untuk @ perf-tools / keeper .Tapi ini tidak cukup, kami hanya mendapatkan nilai-nilai dasar dan masih belum tahu apa yang sebenarnya memakan banyak waktu. Dan untuk mengetahuinya, Anda perlu mengisi metrik HTML juga.
Seperti yang sudah saya katakan, saya akan menunjukkan contoh menggunakan
PerfKeeper , jadi hal pertama yang harus dilakukan adalah inline di
<hed/>
PerfKeeper itu sendiri (2,5 Kb) dan selanjutnya:

Hasilnya, Anda akan melihat keindahan seperti itu di konsol:

Ini adalah metode pengukuran kakek klasik, 100% berhasil. Tetapi dunia tidak tinggal diam, dan untuk pengukuran yang lebih akurat kami sekarang memiliki
API Timing Sumber Daya (dan jika sumber daya berada pada domain
Timing-Allow-Origin yang terpisah untuk membantu Anda).
Dan di sini ada baiknya berbicara tentang kesalahan klasik selama pemuatan awal halaman, yaitu:
- kekurangan GZip dan HTTP / 2 (ya, ini masih umum);
- penggunaan font yang tidak masuk akal (terkadang font terhubung hanya untuk satu tajuk atau bahkan nomor telepon di footer 0_o);
- Kumpulan CSS / JS terlalu umum.
Cara mengoptimalkan pemuatan halaman:
- gunakan Brotli (atau bahkan SDCH) sebagai ganti GZip, aktifkan HTTP / 2;
- Kumpulkan hanya CSS yang diperlukan (kritis) dan jangan lupa tentang CSSO ;
- meminimalkan ukuran bundel JS dengan memisahkan bundel INTI minimum, dan memuat sisanya sesuai permintaan, mis. tidak serempak;
- memuat JS dan CSS dalam mode non-blocking, secara dinamis membuat
/> <sript src="..."/>
, idealnya memuat JS setelah konten utama; - gunakan SVG sebagai ganti PNG, dan jika dikombinasikan dengan JS, itu akan menghilangkan XML yang berlebihan (misalnya, seperti font-mengagumkan );
- gunakan lazy loading untuk kedua gambar dan iframe (di samping ini, dukungan asli akan muncul dalam waktu dekat).
Waktu Rendering Pertama dan Interaktivitas (TTI)
Tahap selanjutnya setelah memuat adalah saat ketika pengguna melihat hasilnya, dan antarmuka beralih ke mode interaktif. Untuk ini, kita memerlukan
Timing Kinerja Paint dan
PerformanceObserver .
Yang pertama adalah sederhana, kami memanggil
performance.getEntriesByType('paint')
dan kami mendapatkan dua metrik:
- cat pertama - rendering pertama;
- first-contentful-paint - dan render pertama penuh.
Contoh ekstensi cat untuk @ perf-tools / keeper .Tetapi dengan metrik berikutnya, Time To Interactive, ini sedikit lebih menarik. Tidak ada cara pasti untuk menentukan kapan aplikasi Anda menjadi interaktif, mis. dapat diakses oleh pengguna, tetapi ini dapat secara tidak langsung dipahami oleh tidak adanya
longtasks :
Contoh ekstensi kinerja untuk @ perf-tools / keeper .Selain metrik dasar ini, metrik kesiapan aplikasi Anda juga diperlukan, mis. di suatu tempat dalam kode Anda harus seperti ini:
Import { system } from '@perf-tools/keeper'; export function applicationBoot(el, data) { const app = new Application(el, data);
Tingkat respons terhadap tindakan pengguna
Ada bidang besar untuk metrik dan sangat individual, jadi saya akan berbicara tentang dua bidang dasar yang cocok untuk proyek apa pun, yaitu:
peristiwa pertama - waktu kejadian pertama, misalnya, klik pertama (membagi tempat pengguna menusuk), metrik ini sangat relevan untuk semua jenis hasil pencarian, daftar produk, umpan berita, dll. Dengannya, Anda dapat mengontrol bagaimana waktu reaksi dan pengguna mengalir dari tindakan Anda (perubahan dalam: desain / fitur baru / optimasi, dll.) Berubah
Contoh ekstensi kinerja untuk @ perf-tools / keeper .latensi - tunda saat memproses beberapa acara, misalnya:
click
,
input
,
submit
,
scroll
, dll.
Untuk mengukur penundaan, cukup gantung pengendali acara di
window
dengan
capture = true
dan gunakan
requestAnimationFrame
menghitung perbedaannya, ini akan menjadi penundaan:
window.addEventListener(eventType, ({target}) => { const start = now(); requestAnimationFrame(() => { const latency = now() - start; if (latency >= minLatency) {
Contoh ekstensi kinerja untuk @ perf-tools / keeper berfungsi ketika angka Fibonacci dihitung pada klik.FPS saat menggulir dan menghidupkan
Ini adalah metrik yang paling menarik, biasanya diukur melalui
requestAnimationFrame
, dan jika Anda perlu melakukan pengukuran FPS konstan, maka
FPSMeter klasik akan melakukannya (walaupun terlalu optimis). Tetapi itu tidak berfungsi sama sekali jika Anda perlu mengukur kelancaran pengguliran halaman, karena dia butuh pemanasan. Dan kemudian saya menemukan cara yang sangat
menarik .
Sebenarnya, kami hanya membuat div transparan (1x1px), menambahkan
transition: left 300ms linear
dan menjalankannya dari satu sudut ke sudut lainnya, dan ketika sedang dianimasikan, melalui
requestAnimationFrame
memeriksa kirinya yang asli, dan jika panjang baru berbeda dari yang sebelumnya, kemudian tingkatkan jumlah frame yang diberikan (jika tidak kita memiliki drawdown FPS).
Dan itu tidak semua, jika Anda menggunakan FF, maka ada
mozPaintCount , yang bertanggung jawab atas jumlah frame yang diberikan, mis. kita ingat "DO", dan pada
transitionend
kita menghitung perbedaannya.
Total, tanpa pemanasan apa pun, kami tahu pasti apakah browser menggambar ulang bingkai atau tidak.
Mereka segera menjanjikan API normal:
http://wicg.imtqy.com/frame-timing/Contoh ekstensi fps untuk @ perf-tools / keeper .Optimisasi bergulir:
- hal yang paling sederhana adalah tidak melakukan apa pun pada scroll, atau menunda eksekusi melalui
requestAnimationFrame
, atau bahkan requestIdleCallback
; - sangat hati-hati menggunakan
pointer-events: none
, menyalakan dan mematikannya dapat memiliki efek sebaliknya, jadi lebih baik untuk melakukan percobaan A / B menggunakan pointer-events
dan tanpa; - jangan lupa tentang daftar tervirtualisasi, hampir semua mesin Lihat sekarang memiliki komponen seperti itu, tetapi sekali lagi, berhati-hatilah, elemen daftar seperti itu harus sesederhana mungkin, atau menggunakan "boneka" yang akan diganti dengan elemen nyata setelah pengguliran selesai. Jika Anda menulis sendiri daftar tervirtualisasi, maka tidak ada HTML dalam dan jangan lupa tentang DOM Recycling (ini adalah saat Anda tidak membuat elemen DOM untuk setiap bersin, tetapi menggunakannya kembali).
Inisialisasi aplikasi
Hanya ada satu aturan: detail sehingga Anda bisa menjawab dengan tepat apa yang telah menghabiskan waktu dari menginisialisasi aplikasi ke peluncuran akhir. Akibatnya, Anda harus mendapatkan setidaknya metrik berikut:
- berapa banyak waktu yang dibutuhkan untuk menyelesaikan setiap kecanduan;
- waktu untuk menerima dan menyiapkan data untuk aplikasi;
- membuat aplikasi dengan merinci berdasarkan blok.
Yaitu pada output, Anda harus mendapatkan metrik yang dengannya Anda dapat melacak secara akurat fase apa yang sedang terjadi drawdown Anda.
Contoh kerjaKonsol
Waktu pengguna 
Jika Anda memiliki SPA, Anda perlu mengukur waktu perutean
Pertama, harus ada metrik umum untuk mengevaluasi kinerja (waktu transit pada rute) secara keseluruhan, tetapi juga perlu untuk memiliki metrik untuk setiap rute (misalnya, kami memiliki "Daftar utas", "Membaca utas", "Cari", dll. d.), metrik itu sendiri harus dibagi menjadi metrik:
- Menerima data (dengan rincian yang mana)
- Render
- Total aplikasi
- Blok (misalnya, bersama kami, itu akan menjadi: "Kolom kiri" (alias "Daftar folder"), "Bilah pencarian pintar", "Daftar surat" dan sejenisnya)
Tanpa semua ini, tidak mungkin untuk memahami di mana masalah dimulai, jadi kami memiliki banyak modul di luar kotak dengan pengaturan waktu (misalnya, modul yang sama untuk XHR memiliki
startTime
dan
endTime
, yang dicatat secara otomatis).
Tetapi metrik ini tidak cukup untuk menilai secara memadai apa yang terjadi. Mereka terlalu umum karena kita berbicara tentang SPA, maka Anda pasti memiliki semacam Runtime Cache (agar tidak pergi ke server lagi jika Anda sudah ada di sana), sehingga metrik kami dibagi lagi menjadi perutean dengan dan tanpa cache. Namun, khususnya dalam kasus kami, kami membagi metrik dengan jumlah entitas di dalamnya. Dengan kata lain, Anda tidak dapat menambahkan tampilan "Utas" dengan 1, 5, 10 atau 100+ huruf dalam satu metrik, jadi jika Anda memiliki daftar yang ditampilkan, Anda perlu memilih breakpoints dan selanjutnya memisahkan metrik.
Konsumsi memori dan lalu lintas
Mari kita mulai dengan memori . Dan di sini kita menunggu kekecewaan besar. Saat ini hanya ada kinerja.memory yang tidak standar (Chrome saja), yang memberikan angka sangat rendah. Tetapi mereka masih perlu diukur dan perhatikan bagaimana aplikasi "mengalir" dari waktu ke waktu:
Contoh ekstensi memori untuk @ perf-tools / keeperLalu lintas Untuk menghitung lalu lintas, Anda perlu
Timing-Allow-Origin (jika sumber daya berada di domain terpisah) dan
API Timing Sumber Daya , ini akan membantu tidak hanya untuk menghitung lalu lintas, tetapi juga untuk memerincinya:
- protokol apa yang digunakan (HTTP / 1, HTTP / 2, dll.);
- jenis sumber daya yang dimuat;
- berapa lama untuk mengunduhnya;
- ukuran, apalagi, Anda dapat memahami apakah sumber daya dimuat di jaringan atau diambil dari cache.
Contoh ekstensi sumber daya untuk @ perf-tools / keeper .Apa yang menghitung traffic?
- Yang paling penting adalah memungkinkan Anda untuk melihat gambar asli, dan tidak seperti biasanya dengan CSS + JS dan lebih dari itu, bagaimana "gambar" ini berubah seiring waktu.
- Kemudian Anda dapat menganalisis apa sebenarnya yang dimuat, membagi sumber daya ke dalam kelompok, dll.
- Seberapa baik caching bekerja untuk Anda.
- Apakah ada anomali, misalnya, setelah 15 menit operasi, misalnya, kode mengalami rekursi dan memuat beberapa sumber daya tanpa henti, memantau lalu lintas akan membantu dalam hal ini.
Nah, laporan catch-up dari rekan saya
Igor Druzhinin tentang topik ini:
Mengevaluasi kualitas aplikasi - memantau konsumsi lalu lintasAnalisis
Kami menyiapkan metrik, lalu apa? Dan kemudian mereka perlu dikirim ke suatu tempat. Dan di sini Anda dapat mengambil beberapa
Graphite dari Anda , atau, sebagai permulaan, Anda dapat menggunakan
Google Analytics atau sejenisnya untuk pengumpulan data untuk keuntungan pribadi.
Dan jangan lupa, itu tidak cukup hanya untuk mendapatkan grafik, untuk semua metrik penting harus ada persentil yang memungkinkan Anda untuk memahami, misalnya, berapa persen audiens yang memuat proyek untuk <1s, <2s, <3s, <5s, 5s + +, dll.
Menulis kode kinerja tinggi
Pada awalnya, saya ingin menulis sesuatu yang bermakna di sini, mereka mengatakan menggunakan WebWorker, jangan lupa
requestIdleCallback
atau sesuatu yang eksotis, misalnya, melalui Runtime Cache melalui tab browser menggunakan SharedWorker atau ServiceWorker (yang tidak hanya tentang caching, jika itu). Tapi ini semua sangat abstrak, dan banyak topik dikalahkan hingga tidak mungkin, jadi tulis saja yang berikut ini:
- Mulailah tutup kode Anda dengan metrik yang akan mengukur kinerjanya.
- Jangan percaya tolok ukur dengan jsperf. Sebagian besar dari mereka ditulis dengan buruk, dan hanya diambil di luar konteks. Tolok ukur terbaik adalah metrik nyata pada proyek, yang dengannya Anda akan melihat efek tindakan Anda.
- Ingatlah tentang persepsi produktivitas, atau lebih tepatnya Hukum Weber-Fechner. Yaitu, jika Anda memulai pengoptimalan, maka jangan meluncurkan perubahan sampai menjadi lebih baik setidaknya 20%, jika tidak, pengguna tidak akan melihat. Hukum juga bekerja berlawanan arah.
- Ketakutan tetap, terutama yang dihasilkan. Mereka tidak hanya dapat menggantung browser, tetapi juga mendapatkan XSS, itulah sebabnya di Mail kami dilarang mem-parsing HTML menggunakan mereka, hanya melalui bypass DOM.
- Anda tidak perlu menggunakan array untuk memasukkan nilai dalam satu atau grup lain, untuk ini ada
object
atau Set
(misalnya, successSteps.includes(currentStep)
diperlukan successSteps.hasOwnProperty(currentStep)
), O (1) adalah segalanya. - Ungkapan "Optimalisasi prematur adalah akar dari semua kejahatan" bukanlah tentang menulis apa pun yang Anda inginkan. Jika Anda tahu cara terbaik, tulislah secara optimal.
Saya akan menulis beberapa paragraf tentang kode dan pengoptimalannyaDOM Sangat sering saya mendengar "Masalah di DOM" - ini, tentu saja, benar, tetapi mengingat bahwa hampir semua orang sekarang memiliki abstraksi atasnya. Dialah yang menjadi penghambat, atau lebih tepatnya kode Anda, yang bertanggung jawab untuk pembentukan pandangan dan logika bisnis.
Tetapi jika kita berbicara tentang DOM, misalnya, daripada menghapus sebuah fragmen dari DOM, lebih baik menyembunyikannya atau melepaskannya. Jika Anda masih perlu menghapus, maka buat operasi ini di
requestIdleCallback
(jika mungkin), atau bagi proses penghancuran menjadi dua fase: sinkron dan asinkron.
Saya akan segera melakukan reservasi, gunakan pendekatan ini dengan bijak, jika tidak Anda bisa menembak lutut Anda.
Kami juga menggunakan teknik menarik lainnya pada daftar, misalnya, "Daftar utas". Inti dari teknik ini adalah bahwa alih-alih satu "Daftar" global dan memperbarui datanya, kami menghasilkan "Daftar utas" untuk setiap "Folder". Akibatnya, ketika pengguna menavigasi antara "Folder", satu daftar dihapus dari DOM (tidak dihapus), dan yang lainnya diperbarui sebagian atau tidak sama sekali. Dan tidak semua, seperti halnya dengan "Daftar Tunggal".
Semua ini memberikan respons instan terhadap tindakan pengguna.
Matematika Kami dengan mudah menghapus semua matematika di Worker atau WebAssembly, ini sudah bekerja sejak lama.
Transpillers . Oh, banyak yang bahkan tidak berpikir bahwa kode yang mereka tulis melewati transpiler. Ya, mereka tahu tentang dia, tapi itu saja. Tapi apa yang dia ubah menjadi mereka tidak lagi peduli. Memang, di DevTools mereka melihat hasil peta sumber.
Oleh karena itu, pelajari alat yang Anda gunakan, misalnya, babel yang sama di
taman bermain memiliki kesempatan untuk melihat apa yang menghasilkan kode tergantung pada preset yang dipilih, lihat saja
yeild
sama,
await
atau
for of
.
Seluk-beluk lidah . Bahkan lebih sedikit orang yang tahu tentang monomorfisme dari kode tersebut, atau klise mengapa
handleEvent
lambat dan ... Anda akhirnya menggunakan
handleEvent
!
Data dan prapemanjangan . Lebih sedikit permintaan, lebih banyak caching. Selain itu, sangat sering kita menggunakan teknik "tinjauan ke masa depan", inilah saat di latar belakang kita memuat data. Misalnya, setelah merender "Daftar Thread", kami mulai memuat utas yang belum dibaca dalam "Folder" saat ini, sehingga ketika Anda mengkliknya, pengguna segera beralih ke "Baca" daripada "loader" lainnya. Kami menggunakan teknik serupa tidak hanya untuk Data, tetapi juga untuk JS. Misalnya, "Menulis Surat" adalah bundel besar (karena editor), dan tidak semua orang menulis surat sekaligus, jadi kami memuatnya di latar belakang, setelah aplikasi diinisialisasi.
Louders Saya tidak tahu mengapa, tetapi saya tidak melihat artikel yang mengajarkan bagaimana tidak membuat loader, melainkan mengambil presentasi dari "masa depan" Bereaksi, di mana banyak waktu dicurahkan untuk masalah ini dalam rangka Suspense. Tetapi bagaimanapun juga, aplikasi yang ideal adalah tanpa loader, kami telah mencoba sejak lama di Mail untuk menunjukkannya hanya dalam situasi darurat.
Secara umum, kami memiliki kebijakan seperti itu, tidak ada data, tidak ada tampilan, tidak ada yang menggambar semi-antarmuka, pertama kita memuat data dan hanya kemudian "menggambar". Itulah sebabnya kami menggunakan "tinjauan ke masa depan" di mana pengguna akan pergi dan memuat data ini sehingga pengguna tidak melihat loader. Selain itu, lapisan data kami, yang persisten, banyak membantu dalam tugas ini. jika Anda meminta "Utas" di suatu tempat di satu tempat, maka pada saat Anda meminta dari tempat lain atau tempat yang sama, tidak akan ada permintaan, kami akan mengambil data dari Runtime Cache (lebih tepatnya, tautan ke data). Jadi dalam segala hal, koleksi utas juga hanya tautan ke data.
Tetapi jika Anda masih memutuskan untuk membuat loader, maka jangan lupa aturan dasar yang akan membuat loader Anda tidak terlalu mengganggu:
- tidak perlu menunjukkan loader segera, pada saat mengirim permintaan, harus ada penundaan setidaknya 300-500 ms sebelum pertunjukan;
- Setelah menerima data, Anda tidak perlu menghapus loader dengan tajam, di sini sekali lagi harus ada penundaan.
Aturan sederhana ini diperlukan agar pemuat hanya muncul saat permintaan berat dan tidak "berkedip" setelah selesai. Namun yang terpenting, loader terbaik adalah loader yang tidak muncul.
Terima kasih atas perhatian Anda, itu saja, ukur, analisis, dan gunakan
PerfKeeper (
contoh langsung ), serta
github dan
twitter saya , jika ada pertanyaan!