Jadual Kandungan
Apa itu Google Lighthouse?
Persediaan
Membuka krom dengan node.js
Running Lighthouse Programmatically
Menyelamatkan laporan rumah api
Mewujudkan direktori
Menyimpan laporan
Membandingkan laporan rumah api
Bandingkan laporan baru terhadap laporan sebelumnya
Bandingkan dua laporan
Logik perbandingan
Complete source code
Next steps
Rumah hujung hadapan web tutorial css Bina alat Node.js untuk merakam dan membandingkan laporan Google Lighthouse

Bina alat Node.js untuk merakam dan membandingkan laporan Google Lighthouse

Apr 09, 2025 am 09:18 AM

Bina alat Node.js untuk merakam dan membandingkan laporan Google Lighthouse

Dalam tutorial ini, saya akan menunjukkan kepada anda langkah demi langkah bagaimana untuk membuat alat mudah dalam Node.js untuk menjalankan audit Google Lighthouse melalui baris arahan, simpan laporan yang mereka hasilkan dalam format JSON dan kemudian membandingkannya supaya prestasi web dapat dipantau apabila laman web berkembang dan berkembang.

Saya berharap ini dapat berfungsi sebagai pengenalan yang baik untuk mana -mana pemaju yang berminat untuk belajar tentang cara bekerja dengan Google Lighthouse secara programatik.

Tetapi pertama, untuk yang tidak dikenali ...

Apa itu Google Lighthouse?

Google Lighthouse adalah salah satu alat automatik terbaik yang terdapat pada tali pinggang utiliti pemaju web. Ia membolehkan anda dengan cepat mengaudit laman web dalam beberapa bidang utama yang bersama -sama dapat membentuk ukuran kualiti keseluruhannya. Ini adalah:

  • Prestasi
  • Kebolehcapaian
  • Amalan terbaik
  • Seo
  • Aplikasi Web Progresif

Sebaik sahaja audit selesai, laporan kemudiannya dijana pada laman web anda dengan baik ... dan tidak begitu baik, dengan yang terakhir berniat untuk berfungsi sebagai penunjuk untuk langkah seterusnya anda untuk memperbaiki halaman.

Inilah laporan penuh.

Bersama-sama dengan diagnostik umum dan metrik prestasi web, ciri yang sangat berguna dalam laporan ini adalah bahawa setiap bidang utama diagregatkan ke dalam skor berkod warna antara 0-100.

Bukan sahaja ini membolehkan pemaju dengan cepat mengukur kualiti laman web tanpa analisis lanjut, tetapi ia juga membolehkan rakyat bukan teknikal seperti pihak berkepentingan atau pelanggan untuk memahami juga.

Sebagai contoh, ini bermakna, lebih mudah untuk berkongsi kemenangan dengan Heather dari pemasaran selepas menghabiskan masa meningkatkan kebolehcapaian laman web kerana dia lebih dapat menghargai usaha selepas melihat skor akses rumah api naik 50 mata ke dalam hijau.

Tetapi sama, Simon pengurus projek mungkin tidak memahami apa yang dimaksudkan dengan kelajuan indeks atau cat yang pertama, tetapi apabila dia melihat laporan mercusuar yang menunjukkan lutut skor prestasi laman web jauh di merah, dia tahu anda masih mempunyai kerja untuk dilakukan.

Jika anda berada di Chrome atau versi terkini Edge, anda boleh menjalankan audit rumah api untuk diri sendiri sekarang menggunakan DevTools. Inilah Caranya:

Anda juga boleh menjalankan audit rumah api dalam talian melalui pandangan halaman atau melalui alat prestasi popular, seperti WebpageTest.

Walau bagaimanapun, hari ini, kami hanya berminat dengan mercusuar sebagai modul nod, kerana ini membolehkan kami menggunakan alat ini secara programatik untuk mengaudit, merekodkan dan membandingkan metrik prestasi web.

Mari cari bagaimana.

Persediaan

Pertama, jika anda belum memilikinya, anda akan memerlukan Node.js. Terdapat satu juta cara yang berbeza untuk memasangnya. Saya menggunakan Pengurus Pakej Homebrew, tetapi anda juga boleh memuat turun pemasang terus dari laman web Node.js jika anda lebih suka. Tutorial ini ditulis dengan Node.js v10.17.0 dalam fikiran, tetapi kemungkinan besar akan berfungsi dengan baik pada versi yang paling dikeluarkan dalam beberapa tahun kebelakangan.

Anda juga akan memerlukan Chrome yang dipasang, kerana itulah kami akan menjalankan audit rumah api.

Seterusnya, buat direktori baru untuk projek itu dan kemudian CD ke dalamnya di konsol. Kemudian jalankan NPM init untuk mula membuat fail Package.json. Pada ketika ini, saya akan mengesyorkan hanya bashing kunci Enter berulang kali untuk melangkau sebanyak mungkin sehingga fail dibuat.

Sekarang, mari buat fail baru dalam direktori projek. Saya memanggil lh.js saya, tetapi berasa bebas untuk memanggilnya apa sahaja yang anda mahukan. Ini akan mengandungi semua JavaScript untuk alat ini. Buka di editor teks pilihan anda, dan buat masa ini, tulis pernyataan konsol.log.

 Console.log ('Hello World');
Salin selepas log masuk

Kemudian dalam konsol, pastikan CWD anda (direktori kerja semasa) adalah direktori projek anda dan jalankan nod LH.JS, menggantikan nama fail saya untuk apa sahaja yang anda gunakan.

Anda mesti melihat:

 $ node lh.js
Helo Dunia
Salin selepas log masuk

Jika tidak, semak pemasangan nod anda berfungsi dan anda pasti dalam direktori projek yang betul.

Sekarang ini tidak dapat dilakukan, kita boleh meneruskan untuk membangunkan alat itu sendiri.

Membuka krom dengan node.js

Mari pasang kebergantungan pertama projek kami: Mercusuar sendiri.

 NPM Pasang Lighthouse--Save-DeV
Salin selepas log masuk

Ini mewujudkan direktori node_modules yang mengandungi semua fail pakej. Jika anda menggunakan Git, satu -satunya perkara yang anda mahu lakukan dengan ini ialah menambahnya ke fail .gitignore anda.

Di LH.JS, anda akan mahu memadam konsol ujian.log () dan mengimport modul Lighthouse supaya anda boleh menggunakannya dalam kod anda. Seperti So:

 const lighthouse = memerlukan ('rumah api');
Salin selepas log masuk

Di bawahnya, anda juga perlu mengimport modul yang dipanggil Chrome-Launcher, yang merupakan salah satu kebergantungan Lighthouse dan membolehkan Node untuk melancarkan Chrome dengan sendirinya supaya audit dapat dijalankan.

 const lighthouse = memerlukan ('rumah api');
const chromelauncher = memerlukan ('chrome-launcher');
Salin selepas log masuk

Sekarang kita mempunyai akses kepada kedua -dua modul ini, mari buat skrip mudah yang hanya membuka Chrome, menjalankan audit rumah api, dan kemudian mencetak laporan ke konsol.

Buat fungsi baru yang menerima URL sebagai parameter. Kerana kita akan menjalankan ini menggunakan Node.js, kita dapat menggunakan sintaks ES6 dengan selamat kerana kita tidak perlu bimbang tentang pengguna Internet Explorer yang menjengkelkan.

 const launchChrome = (url) => {

}
Salin selepas log masuk

Dalam fungsi ini, perkara pertama yang perlu kita lakukan ialah membuka Chrome menggunakan modul Chrome-Launcher yang kami import dan menghantarnya ke apa-apa hujah yang dilalui melalui parameter URL.

Kita boleh melakukan ini menggunakan kaedah pelancaran () dan pilihan permulaannya.

 const launchChrome = url => {
  chromelauncher.launch ({
    permulaan: url
  });
};
Salin selepas log masuk

Memanggil fungsi di bawah dan lulus URL pilihan pilihan anda di Chrome dibuka pada URL apabila skrip nod dijalankan.

 LaunchChrome ('https://www.lukeharrison.dev');
Salin selepas log masuk

Fungsi pelancaran sebenarnya mengembalikan janji, yang membolehkan kita mengakses objek yang mengandungi beberapa kaedah dan sifat berguna.

Sebagai contoh, menggunakan kod di bawah, kita boleh membuka Chrome, mencetak objek ke konsol, dan kemudian menutup Chrome tiga saat kemudian menggunakan kaedah Kill ()nya.

 const launchChrome = url => {
  Chromelauncher
    .launch ({
      permulaan: url
    })
    .THEN (CHROME => {
      Console.log (Chrome);
      setTimeout (() => chrome.kill (), 3000);
    });
};

LaunchChrome ("https://www.lukeharrison.dev");
Salin selepas log masuk

Sekarang kita telah mendapat Chrome, mari kita beralih ke rumah api.

Running Lighthouse Programmatically

Mula -mula, mari kita menamakan semula fungsi LaunchChrome () kami kepada sesuatu yang lebih mencerminkan fungsi akhir: LaunchChromeanDrunLighthouse (). Dengan bahagian yang sukar, kami kini boleh menggunakan modul rumah api yang kami import sebelum ini dalam tutorial.

Dalam fungsi pelancar Chrome, yang hanya dilaksanakan sebaik sahaja penyemak imbas dibuka, kami akan lulus mercusuar hujah URL fungsi dan mencetuskan audit laman web ini.

 const launchChromeanDrunLighthouse = url => {
  Chromelauncher
    .launch ({
      permulaan: url
    })
    .THEN (CHROME => {
      const opts = {
        Port: Chrome.port
      };
      Mercusuar (URL, OPTS);
    });
};

LaunchChromeAndrunLighthouse ("https://www.lukeharrison.dev");
Salin selepas log masuk

Untuk menghubungkan contoh rumah api ke tetingkap pelayar Chrome kami, kami perlu lulus pelabuhannya bersama -sama dengan URL.

Sekiranya anda menjalankan skrip ini sekarang, anda akan memukul kesilapan dalam konsol:

 (Node: 47714) UnhandledPromiseReckectionWarning: Ralat: Anda mungkin mempunyai banyak tab terbuka kepada asal yang sama.
Salin selepas log masuk

Untuk membetulkannya, kami hanya perlu mengeluarkan pilihan permulaan dari pelancar Chrome dan biarkan rumah api mengendalikan navigasi URL dari sini.

 const launchChromeanDrunLighthouse = url => {
  chromelauncher.launch (). Kemudian (chrome => {
    const opts = {
      Port: Chrome.port
    };
    Mercusuar (URL, OPTS);
  });
};
Salin selepas log masuk

Sekiranya anda melaksanakan kod ini, anda akan melihat bahawa sesuatu yang pasti kelihatan berlaku. Kami hanya tidak mendapat sebarang maklum balas di konsol untuk mengesahkan audit rumah api yang pasti berjalan, dan tidak ada contoh Chrome yang ditutup dengan sendirinya seperti sebelumnya.

Syukurlah, fungsi rumah api () mengembalikan janji yang membolehkan kita mengakses hasil audit.

Mari membunuh Chrome dan kemudian mencetak hasil tersebut ke terminal dalam format JSON melalui harta laporan objek hasil.

 const launchChromeanDrunLighthouse = url => {
  chromelauncher.launch (). Kemudian (chrome => {
    const opts = {
      Port: Chrome.port
    };
    Mercusuar (URL, OPTS) .that (hasil => {
      chrome.kill ();
      console.log (results.report);
    });
  });
};
Salin selepas log masuk

Walaupun konsol bukan cara terbaik untuk memaparkan hasil ini, jika anda menyalinnya ke papan klip anda dan lawati penonton laporan Lighthouse, menampal di sini akan menunjukkan laporan dalam semua kemuliaannya.

Pada ketika ini, penting untuk merapikan kod itu sedikit untuk membuat fungsi LaunchChromeanDrunLighthouse () mengembalikan laporan setelah selesai melaksanakannya. Ini membolehkan kita memproses laporan kemudian tanpa mengakibatkan piramid JavaScript.

 const lighthouse = memerlukan ("rumah api");
const chromelauncher = memerlukan ("chrome-launcher");

const launchChromeanDrunLighthouse = url => {
  kembali chromelauncher.launch (). Kemudian (chrome => {
    const opts = {
      Port: Chrome.port
    };
    Return Lighthouse (URL, Opts) .then (hasil => {
      kembali chrome.kill (). Kemudian (() => results.report);
    });
  });
};

LaunchChromeanDrunLighthouse ("https://www.lukeharrison.dev") .then (hasil => {
  console.log (hasil);
});
Salin selepas log masuk

Satu perkara yang mungkin anda perhatikan ialah alat kami hanya dapat mengaudit laman web tunggal pada masa ini. Mari kita ubah ini supaya anda boleh lulus URL sebagai hujah melalui baris arahan.

Untuk mengambil kesakitan daripada bekerja dengan argumen baris arahan, kami akan mengendalikan mereka dengan pakej yang dipanggil Yargs.

 Pemasangan NPM-Save-Dev Yargs
Salin selepas log masuk

Kemudian mengimportnya di bahagian atas skrip anda bersama -sama dengan Pelancar Chrome dan Lighthouse. Kami hanya memerlukan fungsi ARGV di sini.

 const lighthouse = memerlukan ('rumah api');
const chromelauncher = memerlukan ('chrome-launcher');
const argv = memerlukan ('yargs'). argv;
Salin selepas log masuk

Ini bermakna jika anda lulus hujah baris arahan di terminal seperti itu:

 Node lh.js --url https://www.google.co.uk
Salin selepas log masuk

... Anda boleh mengakses hujah dalam skrip seperti itu:

 const url = argv.url // https://www.google.co.uk
Salin selepas log masuk

Mari edit skrip kami untuk lulus hujah URL baris arahan ke parameter URL fungsi. Adalah penting untuk menambah sedikit jaring keselamatan melalui pernyataan dan mesej ralat jika tiada hujah yang diluluskan.

 jika (argv.url) {
  LaunchChromeanDrunLighthouse (argv.url) .then (hasil => {
    console.log (hasil);
  });
} else {
  Buang "Anda belum lulus URL ke Lighthouse";
}
Salin selepas log masuk

Tada! Kami mempunyai alat yang melancarkan Chrome dan menjalankan audit rumah api secara programatik sebelum mencetak laporan ke terminal dalam format JSON.

Menyelamatkan laporan rumah api

Mempunyai laporan yang dicetak ke konsol tidak begitu berguna kerana anda tidak dapat membaca kandungannya dengan mudah, ataupun mereka tidak diselamatkan untuk kegunaan masa depan. Dalam bahagian tutorial ini, kami akan mengubah tingkah laku ini supaya setiap laporan disimpan ke dalam fail JSON sendiri.

Untuk menghentikan laporan dari laman web yang berbeza yang bercampur -campur, kami akan mengaturnya seperti itu:

  • Lukeharrison.dev
    • 2020-01-31T18: 18: 12.648z.json
    • 2020-01-31T19: 10: 24.110z.json
  • cnn.com
    • 2020-01-14T22: 15: 10.396z.json
  • LH.JS

Kami akan menamakan laporan dengan timestamp yang menunjukkan apabila tarikh/masa laporan dijana. Ini bermakna tiada dua nama fail laporan akan sama, dan ia akan membantu kita dengan mudah membezakan antara laporan.

Terdapat satu isu dengan Windows yang memerlukan perhatian kita: kolon (:) adalah watak haram untuk nama fail. Untuk mengurangkan isu ini, kami akan menggantikan mana -mana Colon dengan garis bawah (_), jadi nama fail biasa akan kelihatan seperti:

  • 2020-01-31T18_18_12.648Z.JSON

Mewujudkan direktori

Pertama, kita perlu memanipulasi hujah URL baris arahan supaya kita boleh menggunakannya untuk nama direktori.

Ini melibatkan lebih daripada sekadar mengeluarkan www, kerana ia perlu mengambil kira audit yang dijalankan di laman web yang tidak duduk di akar (misalnya: www.foo.com/bar), kerana slash adalah aksara tidak sah untuk nama direktori.

Untuk URL ini, kami akan menggantikan watak -watak yang tidak sah dengan garis bawah sekali lagi. Dengan cara itu, jika anda menjalankan audit di https://www.foo.com/bar, nama direktori yang dihasilkan yang mengandungi laporan itu akan menjadi foo.com_bar.

Untuk membuat berurusan dengan URL lebih mudah, kami akan menggunakan modul Node.js asli yang dipanggil URL. Ini boleh diimport seperti mana -mana pakej lain dan tanpa perlu menambahkannya ke ThePackage.json dan tariknya melalui npm.

 const lighthouse = memerlukan ('rumah api');
const chromelauncher = memerlukan ('chrome-launcher');
const argv = memerlukan ('yargs'). argv;
const url = memerlukan ('url');
Salin selepas log masuk

Seterusnya, mari kita gunakan untuk meneliti objek URL baru.

 jika (argv.url) {
  const urlobj = url baru (argv.url);

  LaunchChromeanDrunLighthouse (argv.url) .then (hasil => {
    console.log (hasil);
  });
}
Salin selepas log masuk

Jika anda mencetak URLOBJ ke konsol, anda akan melihat banyak data URL berguna yang boleh kami gunakan.

 $ node lh.js --url https://www.foo.com/bar
Url {
  href: 'https://www.foo.com/bar',
  Asal: 'https://www.foo.com',
  Protokol: 'https:',
  Nama pengguna: '',
  Kata Laluan: '',
  Hos: 'www.foo.com',
  HostName: 'www.foo.com',
  pelabuhan: '',
  Nama Path: '/Bar',
  Cari: '',
  SearchParams: urlSearchParams {},
  Hash: ''
}
Salin selepas log masuk

Buat pembolehubah baru yang dipanggil DirName, dan gunakan kaedah penggantian rentetan () pada harta tuan rumah URL kami untuk menghilangkan WWW sebagai tambahan kepada Protokol HTTPS:

 const urlobj = url baru (argv.url);
biarkan dirname = urlobj.host.replace ('www.', '');
Salin selepas log masuk

Kami telah menggunakan biarkan di sini, yang tidak seperti Const boleh ditugaskan semula, kerana kami perlu mengemas kini rujukan jika URL mempunyai nama laluan, untuk menggantikan slashes dengan garis bawah. Ini boleh dilakukan dengan corak ekspresi biasa, dan kelihatan seperti ini:

 const urlobj = url baru (argv.url);
biarkan dirname = urlobj.host.replace ("www.", "");
jika (urlobj.pathname! == "/") {
  dirname = dirname urlobj.pathname.replace (/\ // g, "_");
}
Salin selepas log masuk

Sekarang kita boleh membuat direktori itu sendiri. Ini boleh dilakukan melalui penggunaan modul Node.js asli yang dipanggil FS (pendek untuk "sistem fail").

 const lighthouse = memerlukan ('rumah api');
const chromelauncher = memerlukan ('chrome-launcher');
const argv = memerlukan ('yargs'). argv;
const url = memerlukan ('url');
const fs = memerlukan ('fs');
Salin selepas log masuk

Kita boleh menggunakan kaedah mkdir () untuk membuat direktori, tetapi terlebih dahulu perlu menggunakan kaedah existsSync () untuk memeriksa sama ada direktori sudah wujud, kerana node.js sebaliknya akan membuang kesilapan:

 const urlobj = url baru (argv.url);
biarkan dirname = urlobj.host.replace ("www.", "");
jika (urlobj.pathname! == "/") {
  dirname = dirname urlobj.pathname.replace (/\ // g, "_");
}
jika (! fs.existssync (dirname)) {
  fs.mkdirsync (dirName);
}
Salin selepas log masuk

Menguji skrip pada titik harus menghasilkan direktori baru yang dibuat. Lulus https://www.bbc.co.uk/news sebagai hujah URL akan menghasilkan direktori bernama bbc.co.uk_news.

Menyimpan laporan

Dalam fungsi itu untuk launchChromeanDrunLighthouse (), kami mahu menggantikan konsol.log sedia ada dengan logik untuk menulis laporan ke cakera. Ini boleh dilakukan menggunakan kaedah WriteFile () modul FS.

 LaunchChromeanDrunLighthouse (argv.url) .then (hasil => {
  Fs.WriteFile ("Report.Json", Hasil, Err => {
    jika (err) membuang err;
  });
});
Salin selepas log masuk

Parameter pertama mewakili nama fail, yang kedua ialah kandungan fail dan yang ketiga adalah panggilan balik yang mengandungi objek ralat sekiranya sesuatu yang salah semasa proses menulis. Ini akan membuat fail baru yang dipanggil Laporan.json yang mengandungi laporan Lighthouse yang kembali.

Kami masih perlu menghantarnya ke direktori yang betul, dengan timestamp sebagai nama failnya. Bekasnya mudah - kami lulus pemboleh ubah dirname yang kami buat sebelum ini, seperti demikian:

 LaunchChromeanDrunLighthouse (argv.url) .then (hasil => {
  fs.writeFile (`$ {dirname}/laporan.json`, hasil, err => {
    jika (err) membuang err;
  });
});
Salin selepas log masuk

Yang terakhir walaupun memerlukan kita entah bagaimana mengambil cap waktu apabila laporan itu dihasilkan. Syukurlah, laporan itu sendiri menangkap ini sebagai titik data, dan disimpan sebagai harta benda.

Kami hanya perlu ingat untuk menukar mana -mana kolon (:) untuk garis bawah (_) supaya ia bermain dengan baik dengan sistem fail Windows.

 LaunchChromeanDrunLighthouse (argv.url) .then (hasil => {
  Fs.WriteFile (
    `$ {dirname}/$ {hasil [" fetchtime "]. ganti (/:/g," _ ")}. json`,
    hasil,
    err => {
      jika (err) membuang err;
    }
  );
});
Salin selepas log masuk

Sekiranya anda menjalankan ini sekarang, bukannya nama fail timestamped.json, sebaliknya anda mungkin akan melihat ralat yang serupa dengan:

 UndandpromisereJectionWarning: TypeError: Tidak Dapat Membaca Harta 'Menggantikan' Tidak Ditentukan
Salin selepas log masuk

Ini berlaku kerana Lighthouse sedang mengembalikan laporan dalam format JSON, dan bukannya objek yang boleh digunakan oleh JavaScript.

Syukurlah, bukannya menghuraikan JSON sendiri, kita hanya boleh meminta Lighthouse untuk mengembalikan laporan itu sebagai objek JavaScript biasa.

Ini memerlukan mengedit garis di bawah dari:

 kembali chrome.kill (). Kemudian (() => results.report);
Salin selepas log masuk

... ke:

 kembali chrome.kill (). Kemudian (() => results.lhr);
Salin selepas log masuk

Sekarang, jika anda menyusun semula skrip, fail akan dinamakan dengan betul. Walau bagaimanapun, apabila dibuka, ia hanya kandungan malangnya ...

 [Objek Objek]
Salin selepas log masuk

Ini kerana kita kini mendapat masalah yang bertentangan seperti dahulu. Kami cuba menjadikan objek JavaScript tanpa merentasi objek JSON terlebih dahulu.

Penyelesaiannya mudah. Untuk mengelakkan daripada membazirkan sumber dalam parsing atau merentasi objek besar ini, kita boleh mengembalikan kedua -dua jenis dari rumah api:

 Return Lighthouse (URL, Opts) .then (hasil => {
  kembali chrome.kill (). Kemudian (() => {
    kembali {
      JS: results.lhr,
      JSON: Results.Report
    };
  });
});
Salin selepas log masuk

Kemudian kita dapat mengubah suai contoh WriteFile untuk ini:

 Fs.WriteFile (
  `$ {dirname}/$ {results.js [" fetchtime "]. ganti (/:/g," _ ")}. json`,
  hasil.json,
  err => {
    jika (err) membuang err;
  }
);
Salin selepas log masuk

Disusun! Setelah selesai audit rumah api, alat kami kini harus menyimpan laporan ke fail dengan nama fail yang unik dalam direktori yang dinamakan sempena URL laman web.

Ini bermakna laporan kini lebih teratur dan tidak akan mengatasi satu sama lain tidak kira berapa banyak laporan yang disimpan.

Membandingkan laporan rumah api

Semasa pembangunan sehari -hari, apabila saya memberi tumpuan kepada peningkatan prestasi, keupayaan untuk membandingkan dengan cepat membandingkan laporan secara langsung di konsol dan melihat jika saya menuju ke arah yang betul boleh menjadi sangat berguna. Dengan ini, keperluan ini membandingkan fungsi ini harus:

  1. Sekiranya laporan terdahulu sudah ada untuk laman web yang sama apabila audit rumah api selesai, secara automatik melakukan perbandingan terhadapnya dan menunjukkan sebarang perubahan kepada metrik prestasi utama.
  2. Saya juga harus dapat membandingkan metrik prestasi utama dari mana -mana dua laporan, dari mana -mana dua laman web, tanpa perlu menghasilkan laporan rumah api baru yang saya tidak perlukan.

Apakah bahagian laporan yang perlu dibandingkan? Ini adalah metrik prestasi utama yang dikumpulkan sebagai sebahagian daripada laporan rumah api. Mereka memberikan gambaran tentang prestasi dan prestasi laman web yang objektif dan dirasakan.

Di samping itu, Lighthouse juga mengumpul metrik lain yang tidak disenaraikan di bahagian laporan ini tetapi masih dalam format yang sesuai untuk dimasukkan ke dalam perbandingan. Ini adalah:

  • Masa ke Byte Pertama - Masa ke Byte Pertama mengenal pasti masa di mana pelayan anda menghantar respons.
  • Jumlah masa menyekat - Jumlah tempoh masa antara FCP dan masa ke interaktif, apabila panjang tugas melebihi 50ms, dinyatakan dalam milisaat.
  • Anggaran latency input - anggaran latency input adalah anggaran berapa lama aplikasi anda diperlukan untuk bertindak balas terhadap input pengguna, dalam milisaat, semasa tetingkap 5S tersibuk beban halaman. Sekiranya latensi anda lebih tinggi daripada 50ms, pengguna mungkin melihat aplikasi anda sebagai laggy.

Bagaimanakah perbandingan metrik akan dikeluarkan ke konsol? Kami akan membuat perbandingan berasaskan peratusan yang mudah menggunakan metrik lama dan baru untuk melihat bagaimana mereka telah berubah dari laporan ke laporan.

Untuk membolehkan pengimbasan cepat, kami juga akan menjadi metrik individu kod warna bergantung kepada jika mereka lebih cepat, lebih perlahan atau tidak berubah.

Kami akan mensasarkan output ini:

Bandingkan laporan baru terhadap laporan sebelumnya

Mari kita mulakan dengan mewujudkan fungsi baru yang dipanggil CompareReports () tepat di bawah fungsi LaunchChromeanDrunLighthouse () kami, yang akan mengandungi semua logik perbandingan. Kami akan memberikan dua parameter -dari dan untuk menerima kedua -dua laporan yang digunakan untuk perbandingan.

Buat masa ini, sebagai pemegang tempat, kami hanya akan mencetak beberapa data dari setiap laporan ke konsol untuk mengesahkan bahawa ia menerima mereka dengan betul.

 const compareReports = (dari, ke) => {
  console.log (dari ["finalUrl"] "dari [" fetchtime "]);
  console.log (kepada ["finalUrl"] "ke [" fetchtime "]);
};
Salin selepas log masuk

Oleh kerana perbandingan ini akan bermula selepas penciptaan laporan baru, logik untuk melaksanakan fungsi ini harus duduk dalam fungsi itu untuk launchChromeanDrunLighthouse ().

Jika, sebagai contoh, anda mempunyai 30 laporan yang duduk di dalam direktori, kita perlu menentukan yang mana yang paling terkini dan menetapkannya sebagai laporan terdahulu yang baru akan dibandingkan. Syukurlah, kami sudah memutuskan untuk menggunakan cap waktu sebagai nama fail untuk laporan, jadi ini memberi kita sesuatu untuk bekerja.

Mula -mula, kita perlu mengumpul sebarang laporan yang ada. Untuk menjadikan proses ini mudah, kami akan memasang kebergantungan baru yang dipanggil Glob, yang membolehkan corak yang sesuai apabila mencari fail. Ini penting kerana kita tidak dapat meramalkan berapa banyak laporan yang akan wujud atau apa yang akan dipanggil.

Pasangnya seperti kebergantungan lain:

 NPM Install Glob--save-dev
Salin selepas log masuk

Kemudian mengimportnya di bahagian atas fail dengan cara yang sama seperti biasa:

 const lighthouse = memerlukan ('rumah api');
const chromelauncher = memerlukan ('chrome-launcher');
const argv = memerlukan ('yargs'). argv;
const url = memerlukan ('url');
const fs = memerlukan ('fs');
const glob = memerlukan ('glob');
Salin selepas log masuk

Kami akan menggunakan Glob untuk mengumpul semua laporan dalam direktori, yang sudah kita ketahui nama melalui pembolehubah Dirname. Adalah penting untuk menetapkan pilihan penyegerakannya kerana kita tidak mahu pelaksanaan JavaScript diteruskan sehingga kita tahu berapa banyak laporan lain.

 LaunchChromeanDrunLighthouse (argv.url) .then (hasil => {
  const prevReports = glob (`$ {dirname}/*. json`, {
    Sync: Benar
  });

  // et al

});
Salin selepas log masuk

Proses ini mengembalikan pelbagai laluan. Jadi jika direktori laporan kelihatan seperti ini:

  • Lukeharrison.dev
    • 2020-01-31T10_18_12.648Z.JSON
    • 2020-01-31T10_18_24.110Z.JSON

... maka array yang dihasilkan akan kelihatan seperti ini:

 [
 'Lukeharrison.dev/2020-01-31t10_18_12.648z.json',
 'Lukeharrison.dev/2020-01-31T10_18_24.110z.json'
]
Salin selepas log masuk

Kerana kita hanya boleh melakukan perbandingan jika laporan sebelumnya wujud, mari kita gunakan array ini sebagai bersyarat untuk logik perbandingan:

 const prevReports = glob (`$ {dirname}/*. json`, {
  Sync: Benar
});

jika (prevReports.length) {
}
Salin selepas log masuk

Kami mempunyai senarai laluan fail laporan dan kami perlu membandingkan nama fail timestamped mereka untuk menentukan mana yang paling terkini.

Ini bermakna kita perlu mengumpulkan senarai semua nama fail, memotong data yang tidak relevan seperti nama direktori, dan berhati -hati untuk menggantikan garis bawah (_) kembali dengan COLONS (:) Untuk menjadikannya kembali ke tarikh yang sah lagi. Cara paling mudah untuk melakukan ini adalah menggunakan Path, modul asli node.js lain.

 const Path = memerlukan ('jalan');
Salin selepas log masuk

Melewati jalan sebagai hujah kepada kaedah parse, seperti itu:

 path.parse ('lukeharrison.dev/2020-01-31t10_18_24.110z.json');
Salin selepas log masuk

Mengembalikan objek berguna ini:

 {
  akar: '',
  Dir: 'Lukeharrison.dev',
  Pangkalan: '2020-01-31T10_18_24.110Z.JSON',
  ext: '.json',
  Nama: '2020-01-31T10_18_24.110z'
}
Salin selepas log masuk

Oleh itu, untuk mendapatkan senarai semua nama fail timestamp, kita boleh melakukan ini:

 jika (prevReports.length) {
  tarikh = [];
  untuk (laporan yang berlaku) {
    tarikh.push (
      Tarikh Baru (path.parse (prevReports [laporan]). name.replace (/_/g, ":"))
    );
  }
}
Salin selepas log masuk

Yang mana lagi jika direktori kami kelihatan seperti:

  • Lukeharrison.dev
    • 2020-01-31T10_18_12.648Z.JSON
    • 2020-01-31T10_18_24.110Z.JSON

Akan menghasilkan:

 [
 '2020-01-31T10: 18: 12.648z',
 '2020-01-31T10: 18: 24.110z'
]
Salin selepas log masuk

Perkara yang berguna mengenai tarikh adalah bahawa mereka sememangnya setanding secara lalai:

 const alpha = tarikh baru ('2020-01-31');
const bravo = tarikh baru ('2020-02-15');

Console.log (Alpha> Bravo); // palsu
Console.log (Bravo> Alpha); // benar
Salin selepas log masuk

Oleh itu, dengan menggunakan fungsi mengurangkan, kita dapat mengurangkan pelbagai tarikh sehingga hanya jenazah yang paling baru:

 tarikh = [];
untuk (laporan yang berlaku) {
  dates.push (tarikh baru (path.parse (prevReports [laporan]). name.replace (/_/g, ":")));
}
const max = dates.reduce (function (a, b) {
  kembali math.max (a, b);
});
Salin selepas log masuk

Sekiranya anda mencetak kandungan Max ke konsol, ia akan membuang timestamp Unix, jadi sekarang, kami hanya perlu menambah baris lain untuk menukar tarikh terbaru kami kembali ke format ISO yang betul:

 const max = dates.reduce (function (a, b) {
 kembali math.max (a, b);
});
const baru -baru ini = tarikh baru (max) .toisoString ();
Salin selepas log masuk

Dengan mengandaikan ini adalah senarai laporan:

  • 2020-01-31T23_24_41.786Z.JSON
  • 2020-01-31T23_25_36.827Z.JSON
  • 2020-01-31T23_37_56.856Z.JSON
  • 2020-01-31T23_39_20.459Z.JSON
  • 2020-01-31T23_56_50.959Z.JSON

Nilai terkini ialah 2020-01-31T23: 56: 50.959Z.

Sekarang kita tahu laporan yang paling baru -baru ini, kita perlu mengekstrak kandungannya. Buat pembolehubah baru yang dipanggil terkiniReportContents di bawah pembolehubah baru -baru ini dan tentukan fungsi kosong.

Seperti yang kita ketahui fungsi ini sentiasa perlu dilaksanakan, bukannya memanggilnya secara manual, masuk akal untuk menjadikannya IFFE (dengan segera menggunakan ekspresi fungsi), yang akan dijalankan dengan sendirinya apabila parser JavaScript mencapai itu. Ini ditandakan dengan kurungan tambahan:

 const baru -baru iniReportContents = (() => {

}) ();
Salin selepas log masuk

Dalam fungsi ini, kita boleh mengembalikan kandungan laporan terbaru menggunakan kaedah ReadFileSync () modul FS asli. Kerana ini akan berada dalam format JSON, penting untuk menghuraikannya menjadi objek JavaScript biasa.

 const baru -baru iniReportContents = (() => {
  output const = fs.readFileSync (
    dirname "/" terkiniReport.replace (/:/g, "_") ".json",
    "UTF8",
    (err, hasil) => {
      Keputusan pulangan;
    }
  );
  kembali json.parse (output);
}) ();
Salin selepas log masuk

Dan kemudian, ia adalah perkara yang memanggil fungsi CompareReports () dan lulus kedua -dua laporan semasa dan laporan terbaru sebagai hujah.

 CompareReports (terkiniReportContents, results.js);
Salin selepas log masuk

Pada masa ini ini hanya mencetak beberapa butiran ke konsol supaya kami dapat menguji data laporan akan datang melalui OK:

 https://www.lukeharrison.dev/ 2020-02-01T00: 25: 06.918z
https://www.lukeharrison.dev/ 2020-02-01T00: 25: 42.169z
Salin selepas log masuk

Sekiranya anda mendapat sebarang kesilapan pada ketika ini, cuba padamkan sebarang fail atau laporan laporan.JSON tanpa kandungan yang sah dari sebelumnya dalam tutorial.

Bandingkan dua laporan

Keperluan utama yang tersisa adalah keupayaan untuk membandingkan mana -mana dua laporan dari mana -mana dua laman web. Cara paling mudah untuk melaksanakan ini adalah untuk membolehkan pengguna lulus laluan fail laporan penuh sebagai argumen baris arahan yang kemudiannya kami hantar ke fungsi CompareReports ().

Dalam baris arahan, ini akan kelihatan seperti:

 Node LH.JS-dari Lukeharrison.dev/2020-02-01T00:25:06.918z-untuk cnn.com/2019-12-16t15:12:07.169z
Salin selepas log masuk

Mencapai ini memerlukan penyuntingan pernyataan bersyarat IF bersyarat yang memeriksa kehadiran argumen baris perintah URL. Kami akan menambah cek tambahan untuk melihat sama ada pengguna baru lulus dari dan ke jalan, jika tidak, periksa URL seperti dahulu. Dengan cara ini, kami akan menghalang audit rumah api baru.

 jika (argv.from && argv.to) {

} else if (argv.url) {
 // et al
}
Salin selepas log masuk

Mari kita mengeluarkan kandungan fail JSON ini, menghirupnya ke dalam objek JavaScript, dan kemudian lulus mereka bersama -sama ke fungsi CompareReports ().

Kami telah menghuraikan JSON sebelum mengambil laporan terbaru. Kita hanya boleh mengekstrapolasi fungsi ini ke dalam fungsi pembantu sendiri dan menggunakannya di kedua -dua lokasi.

Menggunakan fungsi baru -baru ini () sebagai asas, buat fungsi baru yang dipanggil getContents () yang menerima laluan fail sebagai hujah. Pastikan ini hanya fungsi biasa, bukannya IFFE, kerana kita tidak mahu ia melaksanakan sebaik sahaja parser JavaScript menjumpainya.

 const getContents = Pathstr => {
  const output = fs.readFileSync (Pathstr, "UTF8", (ERR, Results) => {
    Keputusan pulangan;
  });
  kembali json.parse (output);
};

const compareReports = (dari, ke) => {
  console.log (dari ["finalUrl"] "dari [" fetchtime "]);
  console.log (kepada ["finalUrl"] "ke [" fetchtime "]);
};
Salin selepas log masuk

Kemudian kemas kini fungsi baru -baru ini () untuk menggunakan fungsi pembantu ekstrapolasi ini sebaliknya:

 const baru -baru iniReportContents = getContents (dirname '/' terkiniReport.replace (/:/g, '_') '.json');
Salin selepas log masuk

Kembali ke dalam bersyarat baru kita, kita perlu lulus kandungan laporan perbandingan kepada fungsi CompareReports ().

 jika (argv.from && argv.to) {
  CompareReports (
    getContents (argv.from ".json"),
    getContents (argv.to ".json")
  );
}
Salin selepas log masuk

Seperti sebelum ini, ini harus mencetak beberapa maklumat asas mengenai laporan di konsol untuk memberitahu kami bahawa semuanya berfungsi dengan baik.

 Node LH.JS-dari Lukeharrison.dev/2020-01-31T23_24_41.786z-untuk lukeharrison.dev/2020-02-01t11_16_25.221z
Salin selepas log masuk

Akan membawa kepada:

 https://www.lukeharrison.dev/ 2020-01-31T23_24_41.786Z
https://www.lukeharrison.dev/ 2020-02-01T11_16_25.221Z
Salin selepas log masuk

Logik perbandingan

Bahagian pembangunan ini melibatkan logik perbandingan bangunan untuk membandingkan kedua -dua laporan yang diterima oleh fungsi CompareReports ().

Di dalam objek yang pulangan rumah api, terdapat harta yang dipanggil audit yang mengandungi metrik prestasi, peluang, dan maklumat yang lain. Terdapat banyak maklumat di sini, yang kebanyakannya kita tidak berminat untuk tujuan alat ini.

Berikut adalah entri untuk Cat Content First, salah satu daripada sembilan metrik prestasi yang kami ingin bandingkan:

 "Kotak pertama": {
  "Id": "Contentful-Paintful",
  "Tajuk": "Cat Pertama Pertama",
  "Keterangan": "Cat Contentful First menandakan masa di mana teks atau imej pertama dicat. [Ketahui lebih lanjut] (https://web.dev/first-contentful-paint).",
  "Skor": 1,
  "scoredisplaymode": "angka",
  "Numericvalue": 1081.661,
  "DisplayValue": "1.1 s"
}
Salin selepas log masuk

Buat array yang menyenaraikan kunci sembilan metrik prestasi ini. Kita boleh menggunakannya untuk menapis objek audit:

 const compareReports = (dari, ke) => {
  const metricfilter = [
    "Kotak pertama",
    "pertama kali tahu",
    "Indeks kelajuan",
    "Anggaran-input-latency",
    "Jumlah penghalang masa",
    "Max-potensi-fid",
    "Time-to-First-byte",
    "CPU-idle pertama",
    "Interaktif"
  ];
};
Salin selepas log masuk

Kemudian kami akan gelung melalui salah satu objek audit laporan dan kemudian merujuk namanya terhadap senarai penapis kami. (Tidak kira objek audit, kerana kedua -duanya mempunyai struktur kandungan yang sama.)

Jika ada di sana, maka cemerlang, kami mahu menggunakannya.

 const metricfilter = [
  "Kotak pertama",
  "pertama kali tahu",
  "Indeks kelajuan",
  "Anggaran-input-latency",
  "Jumlah penghalang masa",
  "Max-potensi-fid",
  "Time-to-First-byte",
  "CPU-idle pertama",
  "Interaktif"
];

untuk (biarkan auditobj dari ["audit"]) {
  jika (metricFilter.includes (auditobj)) {
    Console.log (AuditObj);
  }
}
Salin selepas log masuk

Console.log () ini akan mencetak kunci di bawah ke konsol:

 Catah pertama
Cik pertama kali
indeks kelajuan
anggaran-input-latency
Jumlah menyekat masa
Max-potensi-fid
Time-to-First-byte
First-cpu-idle
interaktif
Salin selepas log masuk

Yang bermaksud kita akan menggunakan dari ['audit'] [auditobj] .numericvalue dan ['audit'] [auditobj]. Numericvalue masing -masing dalam gelung ini untuk mengakses metrik itu sendiri.

Jika kita mencetaknya ke konsol dengan kunci, ia akan menghasilkan output seperti ini:

 Contentful pertama 1081.661 890.774
Paintful pertama 1081.661 954.774
Speed-Index 15576.70313351777 1098.622294504341
Anggaran input-latency 12.8 12.8
Jumlah penghalang-masa 59 31.5
Max-potensi-fid 153 102
Time-to-First-Byte 16.859999999999985 16.09600000000000004
First-CPU-IDLE 1704.8490000000002 1918.774
Interaktif 2266.2835 2374.3615
Salin selepas log masuk

Kami mempunyai semua data yang kami perlukan sekarang. Kami hanya perlu mengira perbezaan peratusan antara kedua-dua nilai ini dan kemudian log ke konsol menggunakan format berkod warna yang digariskan sebelum ini.

Adakah anda tahu bagaimana untuk mengira perubahan peratusan antara dua nilai? Saya tidak. Syukurlah, enjin carian monolith kegemaran semua orang datang untuk menyelamatkan.

Formula adalah:

 ((Dari - hingga) / dari) x 100
Salin selepas log masuk

Oleh itu, katakan kita mempunyai indeks kelajuan 5.7s untuk laporan pertama (dari), dan kemudian nilai 2.1s untuk yang kedua (kepada). Pengiraannya akan:

 5.7 - 2.1 = 3.6
3.6 / 5.7 = 0.63157895
0.63157895 * 100 = 63.157895
Salin selepas log masuk

Rounding to two decimal places would yield a decrease in the speed index of 63.16%.

Let's put this into a helper function inside the compareReports() function, below the metricFilter array.

 const calcPercentageDiff = (from, to) => {
  const per = ((to - from) / from) * 100;
  return Math.round(per * 100) / 100;
};
Salin selepas log masuk

Back in our auditObj conditional, we can begin to put together the final report comparison output.

First off, use the helper function to generate the percentage difference for each metric.

 for (let auditObj in from["audits"]) {
  if (metricFilter.includes(auditObj)) {
    const percentageDiff = calcPercentageDiff(
      from["audits"][auditObj].numericValue,
      to["audits"][auditObj].numericValue
    );
  }
}
Salin selepas log masuk

Next, we need to output values in this format to the console:

This requires adding color to the console output. In Node.js, this can be done by passing a color code as an argument to the console.log() function like so:

 console.log('\x1b[36m', 'hello') // Would print 'hello' in cyan
Salin selepas log masuk

You can get a full reference of color codes in this Stackoverflow question. We need green and red, so that's \x1b[32m and \x1b[31m respectively. For metrics where the value remains unchanged, we'll just use white. This would be \x1b[37m.

Depending on if the percentage increase is a positive or negative number, the following things need to happen:

  • Log color needs to change (Green for negative, red for positive, white for unchanged)
  • Log text contents change.
    • '[Name] is X% slower for positive numbers
    • '[Name] is X% faster' for negative numbers
    • '[Name] is unchanged' for numbers with no percentage difference.
  • If the number is negative, we want to remove the minus/negative symbol, as otherwise, you'd have a sentence like 'Speed Index is -92.95% faster' which doesn't make sense.

There are many ways this could be done. Here, we'll use theMath.sign() function, which returns 1 if its argument is positive, 0 if well… 0, and -1 if the number is negative. That'll do.

 for (let auditObj in from["audits"]) {
  if (metricFilter.includes(auditObj)) {
    const percentageDiff = calcPercentageDiff(
      from["audits"][auditObj].numericValue,
      to["audits"][auditObj].numericValue
    );

    let logColor = "\x1b[37m";
    const log = (() => {
      if (Math.sign(percentageDiff) === 1) {
        logColor = "\x1b[31m";
        return `${percentageDiff "%"} slower`;
      } else if (Math.sign(percentageDiff) === 0) {
        return "unchanged";
      } else {
        logColor = "\x1b[32m";
        return `${percentageDiff "%"} faster`;
      }
    })();
    console.log(logColor, `${from["audits"][auditObj].title} is ${log}`);
  }
}
Salin selepas log masuk

So, there we have it.

You can create new Lighthouse reports, and if a previous one exists, a comparison is made.

And you can also compare any two reports from any two sites.

Complete source code

Here's the completed source code for the tool, which you can also view in a Gist via the link below.

 const lighthouse = require("lighthouse");
const chromeLauncher = require("chrome-launcher");
const argv = require("yargs").argv;
const url = require("url");
const fs = require("fs");
const glob = require("glob");
const path = require("path");

const launchChromeAndRunLighthouse = url => {
  return chromeLauncher.launch().then(chrome => {
    const opts = {
      port: chrome.port
    };
    return lighthouse(url, opts).then(results => {
      return chrome.kill().then(() => {
        return {
          js: results.lhr,
          json: results.report
        };
      });
    });
  });
};

const getContents = pathStr => {
  const output = fs.readFileSync(pathStr, "utf8", (err, results) => {
    return results;
  });
  return JSON.parse(output);
};

const compareReports = (from, to) => {
  const metricFilter = [
    "first-contentful-paint",
    "first-meaningful-paint",
    "speed-index",
    "estimated-input-latency",
    "total-blocking-time",
    "max-potential-fid",
    "time-to-first-byte",
    "first-cpu-idle",
    "interactive"
  ];

  const calcPercentageDiff = (from, to) => {
    const per = ((to - from) / from) * 100;
    return Math.round(per * 100) / 100;
  };

  for (let auditObj in from["audits"]) {
    if (metricFilter.includes(auditObj)) {
      const percentageDiff = calcPercentageDiff(
        from["audits"][auditObj].numericValue,
        to["audits"][auditObj].numericValue
      );

      let logColor = "\x1b[37m";
      const log = (() => {
        if (Math.sign(percentageDiff) === 1) {
          logColor = "\x1b[31m";
          return `${percentageDiff.toString().replace("-", "") "%"} slower`;
        } else if (Math.sign(percentageDiff) === 0) {
          return "unchanged";
        } else {
          logColor = "\x1b[32m";
          return `${percentageDiff.toString().replace("-", "") "%"} faster`;
        }
      })();
      console.log(logColor, `${from["audits"][auditObj].title} is ${log}`);
    }
  }
};

if (argv.from && argv.to) {
  compareReports(
    getContents(argv.from ".json"),
    getContents(argv.to ".json")
  );
} else if (argv.url) {
  const urlObj = new URL(argv.url);
  let dirName = urlObj.host.replace("www.", "");
  if (urlObj.pathname !== "/") {
    dirName = dirName urlObj.pathname.replace(/\//g, "_");
  }

  if (!fs.existsSync(dirName)) {
    fs.mkdirSync(dirName);
  }

  launchChromeAndRunLighthouse(argv.url).then(results => {
    const prevReports = glob(`${dirName}/*.json`, {
      sync: true
    });

    if (prevReports.length) {
      dates = [];
      for (report in prevReports) {
        dates.push(
          new Date(path.parse(prevReports[report]).name.replace(/_/g, ":"))
        );
      }
      const max = dates.reduce(function(a, b) {
        return Math.max(a, b);
      });
      const recentReport = new Date(max).toISOString();

      const recentReportContents = getContents(
        dirName "/" recentReport.replace(/:/g, "_") ".json"
      );

      compareReports(recentReportContents, results.js);
    }

    fs.writeFile(
      `${dirName}/${results.js["fetchTime"].replace(/:/g, "_")}.json`,
      results.json,
      err => {
        if (err) throw err;
      }
    );
  });
} else {
  throw "You haven't passed a URL to Lighthouse";
}
Salin selepas log masuk

View Gist

Next steps

With the completion of this basic Google Lighthouse tool, there's plenty of ways to develop it further. Contohnya:

  • Some kind of simple online dashboard that allows non-technical users to run Lighthouse audits and view metrics develop over time. Getting stakeholders behind web performance can be challenging, so something tangible they can interest with themselves could pique their interest.
  • Build support for performance budgets, so if a report is generated and performance metrics are slower than they should be, then the tool outputs useful advice on how to improve them (or calls you names).

Nasib baik!

Atas ialah kandungan terperinci Bina alat Node.js untuk merakam dan membandingkan laporan Google Lighthouse. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Vue 3 Vue 3 Apr 02, 2025 pm 06:32 PM

Ia ' s! Tahniah kepada pasukan Vue untuk menyelesaikannya, saya tahu ia adalah usaha besar dan lama datang. Semua dokumen baru juga.

Bolehkah anda mendapatkan nilai harta CSS yang sah dari penyemak imbas? Bolehkah anda mendapatkan nilai harta CSS yang sah dari penyemak imbas? Apr 02, 2025 pm 06:17 PM

Saya mempunyai seseorang yang menulis dengan soalan yang sangat legit ini. Lea hanya blog tentang bagaimana anda boleh mendapatkan sifat CSS yang sah dari penyemak imbas. That ' s seperti ini.

Sedikit di CI/CD Sedikit di CI/CD Apr 02, 2025 pm 06:21 PM

Saya '

Kad yang disusun dengan kedudukan melekit dan sasaran sass Kad yang disusun dengan kedudukan melekit dan sasaran sass Apr 03, 2025 am 10:30 AM

Pada hari yang lain, saya melihat sedikit ini sangat indah dari laman web Corey Ginnivan di mana koleksi kad timbunan di atas satu sama lain semasa anda menatal.

Menggunakan Markdown dan Penyetempatan di Editor Blok WordPress Menggunakan Markdown dan Penyetempatan di Editor Blok WordPress Apr 02, 2025 am 04:27 AM

Jika kita perlu menunjukkan dokumentasi kepada pengguna secara langsung dalam editor WordPress, apakah cara terbaik untuk melakukannya?

Membandingkan penyemak imbas untuk reka bentuk responsif Membandingkan penyemak imbas untuk reka bentuk responsif Apr 02, 2025 pm 06:25 PM

Terdapat beberapa aplikasi desktop ini di mana matlamat menunjukkan laman web anda pada dimensi yang berbeza pada masa yang sama. Oleh itu, anda boleh menulis

Cara menggunakan grid CSS untuk tajuk dan kaki melekit Cara menggunakan grid CSS untuk tajuk dan kaki melekit Apr 02, 2025 pm 06:29 PM

CSS Grid adalah koleksi sifat yang direka untuk menjadikan susun atur lebih mudah daripada yang pernah berlaku. Seperti apa -apa, ada sedikit keluk pembelajaran, tetapi grid adalah

Fon Font Google Fon Fon Font Google Fon Apr 09, 2025 am 10:42 AM

Saya melihat Font Google melancarkan reka bentuk baru (tweet). Berbanding dengan reka bentuk besar yang terakhir, ini terasa lebih berulang. Saya hampir tidak dapat memberitahu perbezaannya

See all articles