Rumah  >  Artikel  >  hujung hadapan web  >  Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScript

Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScript

WBOY
WBOYke hadapan
2022-04-25 17:05:203119semak imbas

Artikel ini membawakan anda pengetahuan yang berkaitan tentang javascript terutamanya isu berkaitan tentang mekanisme dan prinsip pengendalian JavaScript, termasuk enjin penghuraian dan kandungan lain Mari kita lihat bersama-sama .

Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScript

[Cadangan berkaitan: tutorial video javascript, bahagian hadapan web]

Saya telah menulis js selama lebih daripada dua tahun , saya tidak pernah memahami mekanisme dan prinsip operasinya dengan baik Hari ini saya merekodkan teori-teori sarjana dan ringkasan saya sendiri di bawah:

Apakah itu enjin parsing JavaScript

Ringkasnya, enjin penghuraian JavaScript ialah program yang boleh "membaca" kod JavaScript dan memberikan hasil pelaksanaan kod dengan tepat.

Contohnya, apabila anda menulis sekeping kod seperti var a = 1 1;, apa yang dilakukan oleh enjin JavaScript adalah untuk memahami (menghuraikan) sekeping kod anda dan menukar nilai a kepada 2.

Mereka yang telah mempelajari prinsip penyusunan tahu bahawa untuk bahasa statik ​​​​(seperti Java, C, C), bahasa yang mengendalikan perkara di atas dipanggil pengkompil (Penyusun), dan selaras dengan itu untuk bahasa dinamik seperti JavaScript, ia dipanggil pengkompil. Perbezaan antara kedua-duanya boleh diringkaskan dalam satu ayat: pengkompil menyusun kod sumber ke dalam jenis kod lain (seperti kod mesin, atau kod bait), manakala jurubahasa secara langsung menghuraikan dan mengeluarkan hasil kod yang dijalankan. Contohnya, konsol Firebug ialah penterjemah JavaScript.

Walau bagaimanapun, kini sukar untuk menentukan sama ada enjin JavaScript ialah penterjemah atau pengkompil, kerana, sebagai contoh, V8 (enjin JS Chrome) sebenarnya menggunakan enjin JavaScript untuk meningkatkan prestasi JS yang dijalankan berjalan, JS akan disusun menjadi kod mesin asli (kod mesin asli), dan kemudian kod mesin akan dilaksanakan (ini akan menjadi lebih cepat).

Apakah hubungan antara enjin penghuraian JavaScript dan ECMAScript

Enjin JavaScript ialah atur cara, dan kod JavaScript yang kami tulis juga merupakan satu atur cara program? Ini memerlukan peraturan yang ditetapkan. Contohnya, var a = 1 1; yang disebut sebelum ini, ia bermaksud:

Var di sebelah kiri mewakili pengisytiharan, yang mengisytiharkan pembolehubah a

Yang di sebelah kanan mewakili perbezaan antara 1 dan 1 Lakukan penambahan
Tanda sama di tengah menunjukkan bahawa ini adalah pernyataan tugasan
Titik koma di hujung menunjukkan penghujung pernyataan ini
Ini adalah peraturan dengannya ukuran, dan enjin JavaScript akan kod JavaScript boleh dihuraikan mengikut piawaian ini. Kemudian ECMAScript di sini mentakrifkan peraturan ini. Antaranya, dokumen ECMAScript 262 mentakrifkan satu set lengkap standard untuk bahasa JavaScript. Ini termasuk:

var, if, else, break, continue, dsb. ialah kata kunci JavaScript

abstrak, int, panjang, dsb. ialah perkataan simpanan JavaScript
Cara mengira sebagai nombor dan cara untuk dikira sebagai rentetan dsb.
mentakrifkan operator ( , -, >, mentakrifkan sintaks JavaScript
mentakrifkan algoritma pemprosesan standard untuk ungkapan, pernyataan, dsb., seperti semasa menghadapi == Cara menangani
⋯⋯
Enjin JavaScript standard akan dilaksanakan mengikut set dokumen ini Ambil perhatian bahawa standard ditekankan di sini, kerana terdapat juga pelaksanaan yang tidak mengikut piawaian, seperti Enjin JS IE. Inilah sebabnya JavaScript mempunyai masalah keserasian. Mengenai mengapa enjin JS IE tidak dilaksanakan mengikut piawaian, ia datang kepada perang pelayar saya tidak akan pergi ke butiran di sini.

Jadi, secara ringkasnya, ECMAScript mentakrifkan standard bahasa, dan enjin JavaScript melaksanakannya mengikutnya. Ini adalah hubungan antara kedua-duanya.

Apakah hubungan antara enjin penghuraian JavaScript dan penyemak imbas

Ringkasnya, enjin JavaScript ialah salah satu komponen penyemak imbas. Kerana penyemak imbas perlu melakukan banyak perkara lain, seperti menghuraikan halaman, halaman rendering, pengurusan kuki, rekod sejarah, dll. Oleh kerana ia adalah komponen, enjin JavaScript biasanya dibangunkan oleh pembangun penyemak imbas sendiri. Contohnya: Chakra IE9, TraceMonkey Firefox, V8 Chrome, dsb.

Ia juga boleh dilihat bahawa pelayar yang berbeza menggunakan enjin JavaScript yang berbeza. Jadi, apa yang boleh kita katakan ialah enjin JavaScript yang mana untuk mendapatkan pemahaman yang lebih mendalam.

Mengapa JavaScript berbenang tunggal

Ciri utama bahasa JavaScript ialah ia berbenang tunggal, iaitu, ia hanya boleh melakukan satu perkara pada satu masa. Jadi mengapa JavaScript tidak boleh mempunyai berbilang benang? Ini boleh meningkatkan kecekapan.

Benang tunggal JavaScript adalah berkaitan dengan tujuannya. Sebagai bahasa skrip pelayar, tujuan utama JavaScript adalah untuk berinteraksi dengan pengguna dan memanipulasi DOM. Ini menentukan bahawa ia hanya boleh menjadi satu-benang, jika tidak, ia akan menyebabkan masalah penyegerakan yang sangat kompleks. Sebagai contoh, katakan JavaScript mempunyai dua utas pada masa yang sama Satu utas menambah kandungan pada nod DOM tertentu, dan utas lain memadamkan nod tersebut.

Jadi, untuk mengelakkan kerumitan, JavaScript telah menjadi satu utas sejak ia dilahirkan Ini telah menjadi ciri teras bahasa ini dan tidak akan berubah pada masa hadapan.

Untuk memanfaatkan kuasa pengkomputeran CPU berbilang teras, HTML5 mencadangkan piawaian Pekerja Web, yang membenarkan skrip JavaScript mencipta berbilang rangkaian, tetapi urutan anak dikawal sepenuhnya oleh utas utama dan mesti tidak mengendalikan DOM. Oleh itu, piawaian baharu ini tidak mengubah sifat berbenang tunggal JavaScript.

Bezakan antara proses dan utas

Proses ialah unit terkecil peruntukan sumber CPU dan proses boleh mengandungi berbilang utas. Penyemak imbas adalah berbilang proses, dan setiap tetingkap penyemak imbas yang dibuka adalah satu proses.

Thread ialah unit terkecil penjadualan CPU Ruang memori program dikongsi antara utas dalam proses yang sama.

Proses ini boleh dianggap sebagai gudang, dan benang itu adalah trak yang boleh diangkut pada masa yang sama, tetapi setiap kenderaan hanya boleh melakukan satu perkara sahaja iaitu mengangkut kargo ini.

Inti teras:

Proses ialah unit terkecil peruntukan sumber CPU (unit terkecil yang boleh memiliki sumber dan berjalan secara bebas)

Thread ialah CPU Unit penjadualan terkecil (benang ialah unit yang menjalankan program berdasarkan proses. Terdapat berbilang benang dalam proses)

Proses yang berbeza juga boleh berkomunikasi, tetapi pada kos yang lebih tinggi.
Penyemak imbas adalah pelbagai proses
Selepas memahami perbezaan antara proses dan urutan, mari kita fahami tertentu tentang penyemak imbas: (Mari kita lihat pemahaman yang dipermudahkan dahulu)

Pelayar adalah pelbagai proses Sebabnya mengapa penyemak imbas

proses boleh dijalankan adalah kerana sistem memperuntukkan sumber (cpu, memori) kepada prosesnya

Untuk memahami secara ringkas, setiap kali anda membuka halaman Tab, ia adalah bersamaan dengan mencipta Proses penyemak imbas yang berasingan.

Ambil Chrome sebagai contoh, ia mempunyai berbilang halaman tab dan anda boleh melihat berbilang proses dalam pengurus tugas Chrome (setiap halaman Tab mempunyai proses bebas dan proses utama ), yang juga boleh dilihat dalam Pengurus Tugas Windows.

Nota: Penyemak imbas juga harus mempunyai mekanisme pengoptimumannya sendiri di sini Kadangkala selepas membuka berbilang halaman tab, anda boleh melihat dalam Pengurus Tugas Chrome bahawa beberapa proses telah digabungkan (jadi setiap label Tab sepadan dengan proses A. tidak semestinya mutlak)

Apakah proses yang terkandung dalam penyemak imbas

Setelah mengetahui bahawa penyemak imbas adalah pelbagai proses, mari kita lihat proses yang terkandung di dalamnya: (Untuk memudahkan pemahaman, Hanya proses utama disenaraikan)

(1) Proses penyemak imbas: Terdapat hanya satu proses utama penyemak imbas (bertanggungjawab untuk penyelarasan dan kawalan). Fungsi:

  • bertanggungjawab untuk memaparkan antara muka penyemak imbas dan berinteraksi dengan pengguna. Seperti ke hadapan, ke belakang, dsb.
  • Bertanggungjawab untuk pengurusan setiap halaman, mencipta dan memusnahkan proses lain
  • Lukis Peta Bit dalam memori yang diperolehi oleh proses Renderer ke antara muka pengguna
  • Pengurusan Rangkaian sumber, muat turun, dll.

(2) Proses pemalam pihak ketiga: Setiap jenis pemalam sepadan dengan proses, yang hanya dibuat apabila pemalam digunakan
(3) Proses GPU: sehingga Satu, digunakan untuk lukisan 3D, dsb.
(4) Proses pemaparan penyemak imbas (inti penyemak imbas, proses pemapar, berbilang benang dalaman): Secara lalai, setiap halaman Tab mempunyai satu proses dan tidak menjejaskan satu sama lain. Fungsi utama ialah: pemaparan halaman, pelaksanaan skrip, pemprosesan acara, dsb.

Tingkatkan ingatan: membuka halaman web dalam penyemak imbas adalah bersamaan dengan memulakan proses baharu (proses itu mempunyai berbilang benangnya sendiri)

Sudah tentu, penyemak imbas kadangkala menggabungkan berbilang proses (contohnya, selepas membuka berbilang tab kosong, anda akan mendapati berbilang tab kosong digabungkan menjadi satu proses)

Kelebihan penyemak imbas berbilang proses

Berbanding dengan penyemak imbas satu proses, pelbagai proses mempunyai kelebihan berikut:

  • Mengelakkan ranap satu halaman yang menjejaskan keseluruhan penyemak imbas
  • Menghalang palam pihak ketiga- dalam ranap sistem daripada menjejaskan keseluruhan penyemak imbas
  • Berbilang proses memanfaatkan sepenuhnya kelebihan berbilang teras
  • Adalah mudah untuk menggunakan model kotak pasir untuk mengasingkan proses seperti pemalam dan meningkatkan kestabilan penyemak imbas

Pemahaman yang mudah:

Jika penyemak imbas adalah satu proses, maka jika halaman tab tertentu terhempas, ia akan menjejaskan seluruh penyemak imbas, dan pengalaman itu akan menjadi miskin; adalah satu proses, jika pemalam ranap, ia juga akan menjejaskan keseluruhan penyemak imbas.

Sudah tentu, memori dan penggunaan sumber lain juga akan lebih besar, yang bermaksud ruang ditukar dengan masa. Tidak kira berapa besar memori itu, ia tidak mencukupi untuk Chrome Masalah kebocoran memori telah diperbaiki sedikit sekarang. Ia hanya peningkatan, dan ia juga akan membawa kepada peningkatan penggunaan kuasa.

Inti penyemak imbas (proses pemaparan)

Inilah perkara utama yang kita dapat lihat bahawa terdapat begitu banyak proses yang disebutkan di atas, jadi, untuk operasi bahagian hadapan biasa, yang paling diperlukan ialah apa? Jawapannya ialah proses rendering.

Boleh difahami bahawa pemaparan halaman, pelaksanaan JS dan gelung acara semuanya dilakukan dalam proses ini. Seterusnya, fokus pada menganalisis proses ini

Sila ingat bahawa proses pemaparan penyemak imbas adalah berbilang benang (enjin JS adalah satu benang)

Kemudian mari kita ambil lihatlah benang manakah yang disertakan (senarai beberapa utas pemastautin utama):

1. Benang pemaparan GUI

  • bertanggungjawab untuk memaparkan antara muka penyemak imbas, menghurai HTML, CSS, membina pepohon DOM dan pepohon RenderObject (hanya difahami sebagai pepohon gaya yang dibentuk oleh CSS, salah satu daripada teras Flutter), Susun atur dan lukisan dsb.
  • Apabila antara muka perlu dicat semula (Cat semula) atau aliran semula disebabkan oleh operasi tertentu, utas ini akan dilaksanakan
  • Perhatikan bahawa utas pemaparan GUI dan utas enjin JS adalah saling exclusive , apabila enjin JS sedang dilaksanakan, benang GUI akan digantung (dibekukan), dan kemas kini GUI akan disimpan dalam baris gilir dan akan dilaksanakan serta-merta apabila enjin JS melahu.

2. JS engine thread

  • juga dipanggil kernel JS dan bertanggungjawab untuk memproses program skrip Javascript. (Contohnya, enjin V8)
  • Benang enjin JS bertanggungjawab untuk menghuraikan skrip Javascript dan menjalankan kod.
  • Enjin JS telah menunggu ketibaan tugasan dalam baris gilir tugas, dan kemudian memprosesnya Hanya terdapat satu urutan JS yang menjalankan program JS dalam halaman Tab (proses pemapar) pada bila-bila masa
  • Juga ambil perhatian , benang pemaparan GUI dan utas enjin JS adalah saling eksklusif, jadi jika masa pelaksanaan JS terlalu lama, ia akan menyebabkan pemaparan halaman menjadi tidak koheren, menyebabkan pemaparan dan pemuatan halaman menjadi disekat.

3. Benang pencetus acara

  • kepunyaan penyemak imbas dan bukannya enjin JS dan digunakan untuk mengawal gelung peristiwa (boleh difahami bahawa enjin JS itu sendiri terlalu sibuk dan memerlukan Penyemak imbas membuka utas baharu untuk membantu)
  • Apabila enjin JS mahu melaksanakan blok kod seperti setTimeOut (ia juga boleh datang daripada utas lain dalam kernel penyemak imbas, seperti klik tetikus , permintaan tak segerak Ajax, dsb.), tugasan yang sepadan akan ditambahkan pada urutan acara. Dan akan bertanggungjawab untuk mengisih
  • Apabila acara yang sepadan memenuhi syarat pencetus dan dicetuskan, utas akan menambah acara ke penghujung baris gilir untuk diproses, menunggu pemprosesan oleh enjin JS
  • Perhatikan bahawa disebabkan oleh hubungan satu-benang JS, jadi peristiwa dalam baris gilir yang belum selesai perlu digilir untuk diproses oleh enjin JS (ia akan dilaksanakan apabila enjin JS melahu)
  • Ini dia boleh difahami dengan mudah bahawa ia bertanggungjawab untuk menguruskan sekumpulan acara dan "baris gilir acara", hanya tugas dalam baris gilir acara akan dilaksanakan oleh enjin JS apabila ia melahu, dan apa yang perlu dilakukan adalah bertanggungjawab untuk menambahkan acara pada baris gilir acara apabila ia dicetuskan. Contohnya, klik tetikus.

4. Benang pencetus masa

  • Urutan tempat letak setInterval dan setTimeout legenda
  • Kaunter pemasaan penyemak imbas tidak dikira oleh enjin JavaScript , (kerana enjin JavaScript adalah satu-benang, jika ia dalam keadaan benang yang disekat, ia akan menjejaskan ketepatan masa)
  • Oleh itu, benang yang berasingan digunakan untuk masa dan mencetuskan masa Selepas pemasaan selesai, ia ditambahkan pada baris gilir acara (bersamaan dengan Benang pencetus peristiwa ("apabila acara memenuhi syarat pencetus dan dicetuskan") menunggu enjin JS melahu sebelum pelaksanaan.
  • Perhatikan bahawa W3C menetapkan dalam standard HTML bahawa selang masa di bawah 4ms dalam setTimeout dikira sebagai 4ms.

5. Asynchronous http request thread

  • Selepas XMLHttpRequest disambungkan, permintaan thread baharu dibuka melalui penyemak imbas
  • apabila perubahan status dikesan , jika fungsi panggil balik ditetapkan, utas tak segerak akan menjana peristiwa perubahan keadaan dan meletakkan panggilan balik ini ke dalam baris gilir acara. Kemudian dilaksanakan oleh enjin JavaScript.

Proses komunikasi antara proses Penyemak imbas dan kernel penyemak imbas (proses Renderer)

Melihat perkara ini, pertama sekali, anda harus mempunyai pemahaman tertentu tentang proses dan urutan dalam pelayar, kemudian Seterusnya, mari kita bercakap tentang bagaimana proses Penyemak imbas (proses kawalan) berkomunikasi dengan kernel Selepas memahami perkara ini, kita boleh menyambung bahagian pengetahuan ini bersama-sama untuk mempunyai konsep yang lengkap dari awal hingga akhir.

Jika anda membuka pengurus tugas dan kemudian membuka penyemak imbas, anda boleh melihat: dua proses muncul dalam pengurus tugas (satu ialah proses kawalan utama dan satu lagi proses pemaparan yang membuka halaman Tab) , dan kemudian di bawah premis ini, mari kita lihat keseluruhan proses: (banyak dipermudahkan)

  • Apabila proses Penyemak Imbas menerima permintaan pengguna, ia perlu mendapatkan kandungan halaman terlebih dahulu (seperti memuat turun sumber melalui rangkaian), dan kemudian memindahkan tugasan dihantar ke proses Render (kernel) melalui antara muka RendererHost
  • Antara muka Renderer proses Renderer menerima mesej itu diserahkan kepada urutan pemaparan, dan kemudian mula pemaparan
  1. Urutan pemaparan menerima permintaan, memuatkan halaman web dan memaparkan halaman web, yang mungkin memerlukan proses Penyemak Imbas untuk mendapatkan sumber dan proses GPU untuk membantu membuat
  2. Sudah tentu mungkin terdapat utas JS yang mengendalikan DOM (ini mungkin menyebabkan pengaliran semula dan lukisan semula)
  3. Akhir sekali, proses Render menyerahkan hasilnya kepada proses Penyemak Imbas
  • Proses Penyemak Imbas menerima hasilnya dan melukis hasilnya

Berikut ialah gambar lukisan mudah: (sangat dipermudahkan)
Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScript

Hubungan antara utas dalam kernel penyemak imbas

Pada ketika ini, anda telah pun mengetahui tentang pengendalian penyemak imbas Mari kita fahami konsep keseluruhan Seterusnya, mari kita selesaikan secara ringkas beberapa konsep

Benang pemaparan GUI dan utas enjin JS adalah saling eksklusif

Memandangkan JavaScript boleh memanipulasi DOM, jika anda mengubah suai atribut elemen ini semasa memaparkan antara muka (iaitu, utas JS dan UI thread dijalankan pada masa yang sama), maka thread rendering sebelum dan selepas Data elemen yang diperoleh mungkin tidak konsisten.

Oleh itu, untuk mengelakkan hasil pemaparan yang tidak dijangka, penyemak imbas menetapkan urutan pemaparan GUI dan enjin JS menjadi saling eksklusif Apabila enjin JS dilaksanakan, utas GUI akan digantung dan kemas kini GUI akan disimpan dalam Tunggu dalam baris gilir untuk dilaksanakan dengan segera apabila benang enjin JS melahu.

JS menyekat pemuatan halaman

Daripada perhubungan yang saling eksklusif di atas, dapat disimpulkan bahawa JS akan menyekat halaman jika ia mengambil masa terlalu lama untuk dilaksanakan.

Sebagai contoh, dengan mengandaikan bahawa enjin JS melakukan sejumlah besar pengiraan, walaupun GUI dikemas kini pada masa ini, ia akan disimpan dalam baris gilir dan menunggu pelaksanaan apabila enjin JS melahu. Kemudian, disebabkan jumlah pengiraan yang banyak, enjin JS mungkin akan melahu untuk masa yang lama dan lama, dan secara semula jadi ia akan berasa sangat besar.

Oleh itu, cuba elakkan pelaksanaan JS mengambil masa yang terlalu lama, yang akan menyebabkan pemaparan halaman menjadi tidak konsisten, yang membawa kepada perasaan bahawa pemaparan dan pemuatan halaman disekat.

Untuk menyelesaikan masalah ini, selain meletakkan pengiraan pada bahagian belakang, jika ia tidak dapat dielakkan dan pengiraan besar berkaitan dengan UI, maka idea saya ialah menggunakan setTimeout untuk membahagikan tugas dan memberikan sedikit masa lapang di tengah Masa untuk membiarkan enjin JS mengendalikan UI supaya halaman tidak membeku secara langsung.

Jika anda membuat keputusan terus tentang versi HTML5 minimum yang diperlukan, anda boleh melihat WebWorker di bawah.

WebWorker, multi-threading JS

Seperti yang dinyatakan dalam artikel sebelumnya, enjin JS adalah single-threading, dan jika masa pelaksanaan JS terlalu lama, ia akan menyekat halaman, jadi JS akan benar-benar Adakah ia tidak berkuasa untuk pengiraan intensif CPU?

Jadi, kemudian, Pekerja Web disokong dalam HTML5.

Penjelasan rasmi MDN ialah:

Web Worker menyediakan cara mudah untuk menjalankan skrip dalam urutan latar belakang untuk kandungan web.

Thread boleh melaksanakan tugas tanpa mengganggu antara muka pengguna Pekerja ialah objek yang dibuat menggunakan pembina (Worker()) yang menjalankan fail JavaScript bernama (fail ini mengandungi kod yang akan dijalankan dalam pekerja. benang).

Pekerja berjalan dalam konteks global yang lain, berbeza daripada tetingkap semasa.

Oleh itu, menggunakan pintasan tetingkap untuk mendapatkan skop global semasa (bukan diri sendiri) dalam Pekerja akan mengembalikan ralat

Fahami dengan cara ini:

Apabila mencipta Pekerja, JS Enjin digunakan pada penyemak imbas untuk membuka sub-benang (sub-benang dibuka oleh penyemak imbas, dikawal sepenuhnya oleh utas utama dan tidak boleh mengendalikan DOM)
Benang enjin JS dan pekerja thread berkomunikasi melalui kaedah tertentu (postMessage API, yang perlu dihantar Serialize objek untuk berinteraksi dengan thread untuk data tertentu)
Oleh itu, jika terdapat kerja yang sangat memakan masa, sila buka thread Worker yang berasingan, supaya tidak kira betapa gegarannya bumi, ia tidak akan menjejaskan benang utama enjin JS, dan hanya hasilnya akan dikira Selepas itu, hanya sampaikan hasilnya kepada utas utama, sempurna!

Dan sila ambil perhatian bahawa. Enjin JS adalah satu benang Intipati ini tidak berubah.

Yang lain, penjelasan terperinci Pekerja adalah di luar skop artikel ini, jadi saya tidak akan menerangkan secara terperinci.

WebWorker dan SharedWorker

Sekarang kita berada di sini, mari sebut SharedWorker sekali lagi (untuk mengelakkan kekeliruan kedua-dua konsep ini nanti)

WebWorker hanya Kepunyaan halaman tertentu dan tidak akan dikongsi dengan proses Render (proses kernel penyemak imbas) halaman lain
Jadi Chrome mencipta urutan baharu dalam proses Render (setiap halaman Tab ialah proses Render) untuk menjalankan JavaScript dalam program Worker .
SharedWorker dikongsi oleh semua halaman penyemak imbas dan tidak boleh dilaksanakan dengan cara yang sama seperti Worker kerana ia tidak bergabung dengan proses Render dan boleh dikongsi oleh berbilang proses Render
Oleh itu, penyemak imbas Chrome mencipta pelayar berasingan SharedWorker Proses digunakan untuk menjalankan program JavaScript Hanya terdapat satu proses SharedWorker untuk setiap JavaScript yang sama dalam penyemak imbas, tidak kira berapa kali ia dicipta.
Selepas melihat ini, ia sepatutnya mudah difahami, pada asasnya, ia adalah perbezaan antara proses dan utas. SharedWorker diuruskan oleh proses bebas dan WebWorker hanyalah rangkaian di bawah proses Render.

Proses pemaparan penyemak imbas

Tambahkan proses pemaparan penyemak imbas (versi ringkas)

Untuk memudahkan pemahaman, kerja awal secara langsung ditinggalkan sebagai:

Semak Imbas Penyemak imbas memasukkan URL, proses utama penyemak imbas mengambil alih, membuka utas muat turun, dan kemudian membuat permintaan http (meninggalkan pertanyaan DNS, pengalamatan IP, dll.), kemudian menunggu respons, mendapatkan kandungan, dan kemudian memindahkan kandungan ke proses Renderer melalui antara muka RendererHost
Proses pemaparan penyemak imbas bermula
Selepas kernel pelayar mendapat kandungan, pemaparan boleh dibahagikan secara kasar kepada langkah berikut:

Menghuraikan HTML dan membina pepohon DOM
Menghuraikan CSS dan membina pepohon pemaparan (menghuraikan kod CSS ke dalam struktur data berbentuk pepohon, dan kemudian menggabungkannya dengan DOM untuk bergabung menjadi pepohon pemaparan)
Pepohon pemaparan reka letak (Layout/ reflow), bertanggungjawab untuk setiap Pengiraan saiz dan kedudukan elemen
Lukiskan pokok pemaparan (cat), lukis maklumat piksel halaman
Penyemak imbas akan menghantar maklumat setiap lapisan kepada GPU, dan GPU akan menggabungkan setiap lapisan dan memaparkannya pada skrin.
Semua langkah terperinci telah diabaikan Selepas pemaparan, ia adalah acara pemuatan, dan kemudian ia adalah pemprosesan logik JS anda sendiri.

Memandangkan beberapa langkah terperinci telah ditinggalkan, izinkan saya menyebut beberapa butiran yang mungkin memerlukan perhatian.
Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScript

Jujukan acara pemuatan dan acara DOMContentLoaded

Seperti yang dinyatakan di atas, acara pemuatan akan dicetuskan selepas pemaparan selesai, jadi anda boleh membezakan urutan acara pemuatan dan acara DOMContentLoaded ?

Ia sangat mudah, hanya tahu takrifnya:

Apabila acara DOMContentLoaded dicetuskan, hanya apabila DOM dimuatkan, tidak termasuk helaian gaya, imej, skrip async, dsb.
Apabila acara onload dicetuskan, semua DOM, helaian gaya, skrip dan imej pada halaman telah dimuatkan, iaitu, dipaparkan
Oleh itu, susunannya ialah: DOMContentLoaded -> >Sama ada pemuatan css akan menyekat pemaparan pokok DOM

Di sini kita bercakap tentang situasi di mana css diperkenalkan dalam pengepala

Pertama sekali, kita semua tahu bahawa css dimuat turun secara tidak segerak oleh yang berasingan muat turun benang.

Kemudian mari kita bincangkan tentang fenomena berikut:

Pemuatan CSS tidak akan menyekat penghuraian pokok DOM (DOM dibina seperti biasa semasa pemuatan tak segerak)
  • Tetapi ia akan pemaparan pokok pemaparan blok. (Anda perlu menunggu sehingga css dimuatkan semasa membuat pemaparan, kerana pepohon pemaparan memerlukan maklumat css)
  • Ini juga mungkin merupakan mekanisme pengoptimuman penyemak imbas. Kerana apabila anda memuatkan css, anda boleh mengubah suai gaya nod DOM di bawah Jika pemuatan css tidak menyekat pemaparan pepohon pemaparan, maka selepas css dimuatkan, pepohon pemaparan mungkin perlu dilukis semula atau dialirkan semula. menyebabkan beberapa masalah.

Jadi hanya menghuraikan struktur pokok DOM dahulu, selesaikan kerja yang boleh dilakukan, dan kemudian tunggu css anda dimuatkan, dan kemudian render pokok render mengikut gaya akhir prestasi, pendekatan ini Ia sememangnya akan menjadi lebih baik.

Lapisan biasa dan lapisan komposit

Konsep komposit disebut dalam langkah pemaparan.

Ia boleh difahami secara ringkas seperti ini Lapisan yang diberikan oleh penyemak imbas biasanya merangkumi dua kategori: lapisan biasa dan lapisan komposit

Pertama sekali, aliran dokumen biasa boleh difahami sebagai a lapisan komposit. (Ini dipanggil lapisan komposit lalai. Tidak kira berapa banyak elemen yang ditambahkan padanya, mereka sebenarnya berada dalam lapisan komposit yang sama)

Kedua, susun atur mutlak (yang sama berlaku untuk tetap) , walaupun ia boleh dipisahkan daripada aliran dokumen biasa, ia Masih tergolong dalam lapisan komposit lalai.

Kemudian, anda boleh mengisytiharkan lapisan komposit baharu melalui pecutan perkakasan, yang akan memperuntukkan sumber secara berasingan (sudah tentu ia juga akan dipisahkan daripada aliran dokumen biasa, supaya tidak kira bagaimana lapisan komposit berubah, Ia akan tidak menjejaskan lukis semula aliran semula dalam lapisan komposit lalai)

Ia boleh difahami dengan mudah: dalam GPU, setiap lapisan komposit dilukis secara berasingan, jadi ia tidak menjejaskan satu sama lain, itulah sebabnya beberapa kesan pecutan perkakasan adegan Kelas pertama

Anda boleh melihatnya dalam Chrome DevTools --> Lagi Alat --> ia menjadi Lapisan komposit (pecutan perkakasan)

Tukar elemen ini menjadi lapisan komposit, iaitu teknologi pecutan perkakasan legenda

Kaedah yang paling biasa digunakan: translate3d, translateZ
atribut kelegapan/ Animasi peralihan (lapisan gubahan akan dibuat semasa pelaksanaan animasi, dan elemen akan kembali ke keadaan sebelumnya selepas animasi belum bermula atau tamat)

atribut will-chang (ini lebih jauh), biasanya digunakan bersempena dengan kelegapan dan menterjemah, fungsinya Ia adalah untuk memberitahu penyemak imbas terlebih dahulu bahawa perubahan perlu dibuat, supaya penyemak imbas akan mula melakukan beberapa kerja pengoptimuman (sebaik-baiknya melepaskan ini selepas digunakan)

video, iframe, kanvas, webgl dan elemen lain

yang lain, seperti pemalam denyar sebelumnya
Perbezaan antara pecutan mutlak dan perkakasan

Ia dapat dilihat bahawa walaupun mutlak boleh dipisahkan daripada yang biasa aliran dokumen, ia tidak boleh dipisahkan daripada lapisan komposit lalai. Oleh itu, walaupun maklumat dalam perubahan mutlak, ia tidak akan mengubah pepohon pemaparan dalam aliran dokumen biasa Walau bagaimanapun, apabila penyemak imbas akhirnya melukis, keseluruhan lapisan komposit dilukis, jadi perubahan dalam maklumat secara mutlak masih akan menjejaskan lukisan. daripada keseluruhan lapisan komposit. (Pelayar akan melukis semulanya. Jika terdapat banyak kandungan dalam lapisan komposit, maklumat lukisan yang dibawa oleh mutlak akan berubah terlalu banyak, dan penggunaan sumber akan menjadi sangat serius)

Dan pecutan perkakasan adalah secara langsung dalam lapisan komposit lain ( Mula dari awal), jadi perubahan maklumatnya tidak akan menjejaskan lapisan komposit lalai (sudah tentu, lapisan komposit dalaman pasti akan menjejaskan lapisan kompositnya sendiri), ia hanya mencetuskan komposisi akhir (pandangan keluaran)

Peranan lapisan komposit

Secara amnya, selepas pecutan perkakasan dihidupkan, elemen akan menjadi lapisan komposit, yang boleh bebas daripada aliran dokumen biasa Selepas pengubahsuaian, ia boleh mengelakkan lukisan semula keseluruhan halaman dan tingkatkan prestasi, tetapi cuba Jangan gunakan sebilangan besar lapisan komposit, jika tidak disebabkan penggunaan sumber yang berlebihan, halaman akan menjadi lebih tersekat

Sila gunakan indeks apabila menggunakan pecutan perkakasan

Apabila menggunakan pecutan perkakasan, gunakan indeks sebanyak mungkin untuk menghalang Penyemak imbas mencipta pemaparan lapisan komposit untuk elemen berikutnya secara lalai

Prinsip khusus adalah seperti berikut: Dalam webkit CSS3, jika elemen ini mempunyai pecutan perkakasan ditambah dan tahap indeks agak rendah, maka elemen lain di belakang elemen ini (Jika tahap lebih tinggi daripada elemen ini, atau sama, dan sifat releatif atau mutlak adalah sama), ia akan menjadi pemaparan lapisan komposit secara lalai tidak dikendalikan dengan betul, ia akan sangat mempengaruhi prestasi

Untuk memahaminya secara ringkas, ia sebenarnya boleh Ia dianggap sebagai konsep gubahan tersirat: jika a ialah lapisan komposit dan b berada di atas a, maka b juga akan secara tersirat ditukar menjadi lapisan komposit Ini memerlukan perhatian khusus.

Bercakap tentang mekanisme pengendalian JS daripada EventLoop

Sehingga ke tahap ini, ia sudah pun selepas pemaparan awal halaman penyemak imbas, beberapa analisis mekanisme pengendalian daripada enjin JS.

Perhatikan bahawa kami tidak akan bercakap tentang konsep seperti konteks boleh laku, VO, rantai skop, dsb. (ini boleh disusun menjadi artikel lain di sini terutamanya tentang cara kod JS dilaksanakan bersama dengan Acara). gelung.

Prasyarat untuk membaca bahagian ini ialah anda sudah tahu bahawa enjin JS adalah satu benang, dan beberapa konsep yang dinyatakan di atas akan digunakan di sini:

  • Benang enjin JS
  • Benang pencetus peristiwa
  • Benang pencetus bermasa
    Kemudian fahami konsep:

JS dibahagikan kepada tugas segerak dan tugas tak segerak

  • Tugas Penyegerakan dilaksanakan pada utas utama, membentuk tindanan pelaksanaan
  • Di luar utas utama, utas pencetus peristiwa mengurus baris gilir tugasan Selagi tugas tak segerak mempunyai hasil yang sedang berjalan, acara diletakkan di dalamnya barisan tugas.
  • Setelah semua tugasan segerak dalam tindanan pelaksanaan telah dilaksanakan (enjin JS melahu pada masa ini), sistem akan membaca baris gilir tugas, menambah tugasan tak segerak boleh jalan pada tindanan boleh laku dan memulakan pelaksanaan.

Lihat gambar:
Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScript

Melihat ini, anda seharusnya dapat memahami: Mengapakah peristiwa ditolak oleh setTimeout kadangkala tidak dilaksanakan tepat pada masanya? Oleh kerana utas utama mungkin belum melahu dan sedang melaksanakan kod lain apabila ia ditolak ke dalam senarai acara, jadi secara semula jadi akan ada ralat.

Mekanisme gelung acara ditambah lagi

Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScript

Gambar di atas secara kasar menerangkan:

  • Apabila utas utama sedang berjalan, timbunan pelaksanaan akan dijana apabila kod dalam tindanan memanggil API tertentu, mereka akan menambah pelbagai acara pada baris gilir acara (apabila syarat pencetus dipenuhi, seperti permintaan ajax selesai)
  • Apabila kod dalam tindanan dilaksanakan, ia akan dibaca Acara dalam baris gilir acara, laksanakan panggilan balik tersebut dan seterusnya

Ambil perhatian bahawa anda sentiasa perlu menunggu kod dalam tindanan selesai melaksanakan sebelum membaca peristiwa dalam baris gilir acara

Mari kita bincangkan tentang pemasa secara berasingan

Inti mekanisme gelung acara di atas ialah: Benang enjin JS dan benang pencetus peristiwa

Tetapi ada adalah beberapa butiran tersembunyi dalam acara Contohnya, selepas memanggil setTimeout, Bagaimana untuk menunggu masa tertentu sebelum menambah baris gilir acara?

Adakah ia dikesan oleh enjin JS? Sudah tentu tidak. Ia dikawal oleh benang pemasa (kerana enjin JS itu sendiri terlalu sibuk dan tidak mempunyai masa untuk melakukan apa-apa lagi)

Mengapa kita memerlukan benang pemasa yang berasingan? Oleh kerana enjin JavaScript adalah satu-benang, jika ia berada dalam keadaan benang yang disekat, ia akan menjejaskan ketepatan pemasaan, jadi ia perlu membuka benang yang berasingan untuk pemasaan.

Bilakah benang pemasa akan digunakan? Apabila menggunakan setTimeout atau setInterval, ia memerlukan benang pemasa mengikut masa dan selepas masa selesai, acara tertentu akan ditolak ke dalam baris gilir acara.

setTimeout dan bukannya setInterval

Terdapat perbezaan antara menggunakan setTimeout untuk mensimulasikan pemasaan biasa dan menggunakan setInterval secara langsung.

Oleh kerana setiap kali setTimeout dilaksanakan, ia akan dilaksanakan, dan kemudian ia akan terus setTimeout selepas tempoh pelaksanaan akan terdapat ralat di tengah (ralat berkaitan dengan masa pelaksanaan kod)

Dan setInterval Ia adalah untuk menolak acara dengan tepat setiap kali Walau bagaimanapun, masa pelaksanaan sebenar acara mungkin juga tidak tepat sebelum acara selesai.

Dan setInterval mempunyai beberapa masalah maut:

Kesan kumulatif, jika kod setInterval belum menyelesaikan pelaksanaan sebelum ditambahkan pada baris gilir sekali lagi, ia akan menyebabkan kod pemasa berjalan beberapa kali berturut-turut tanpa selang waktu. Walaupun ia dilaksanakan pada selang waktu biasa, masa pelaksanaan berbilang kod setInterval mungkin lebih pendek daripada jangkaan (kerana pelaksanaan kod mengambil masa tertentu)
Contohnya, penyemak imbas seperti paparan web iOS atau Safari mempunyai ciri yang apabila menatal Jika anda tidak melaksanakan JS, jika anda menggunakan setInterval, anda akan mendapati bahawa ia akan dilaksanakan beberapa kali selepas penatalan Kerana penatalan tidak melaksanakan JS, panggilan balik terkumpul akan menyebabkan bekas menjadi beku dan menyebabkan beberapa ralat yang tidak diketahui. jika masa pelaksanaan panggil balik terlalu lama ( Bahagian ini akan ditambah kemudian. Pengoptimuman terbina dalam SetInterval tidak akan menambah panggilan balik berulang kali)
Dan apabila penyemak imbas diminimumkan dan dipaparkan, setInterval tidak akan melaksanakan program fungsi panggil balik setInterval dalam baris gilir , apabila tetingkap penyemak imbas dibuka semula, semua akan dilaksanakan dalam sekelip mata
Oleh itu, memandangkan begitu banyak masalah, penyelesaian terbaik pada masa ini biasanya dianggap sebagai: gunakan setTimeout untuk. simulasi setInterval, atau gunakan terus requestAnimationFrame

untuk majlis khas : Seperti yang dinyatakan dalam JS Elevation, enjin JS akan mengoptimumkan setInterval Jika terdapat panggilan balik setInterval dalam baris gilir acara semasa, ia tidak akan ditambah berulang kali.

Gelung peristiwa lanjutan: macrotask dan microtask

Perkara di atas telah menyusun mekanisme gelung peristiwa JS, yang mencukupi dalam kes ES5 , tetapi kini ES6 berleluasa, anda masih akan menghadapi beberapa masalah, seperti soalan berikut:

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
    console.log('promise1');
}).then(function() {
    console.log('promise2');
});

console.log('script end');

Hmm, susunan pelaksanaan yang betul adalah seperti ini:

script start
script end
promise1
promise2
setTimeout

Mengapa ? Kerana terdapat konsep baharu dalam Promise: microtask

Atau, selanjutnya, JS dibahagikan kepada dua jenis tugas: macrotask dan microtask Dalam ECMAScript, microtask dipanggil kerja, dan macrotask boleh dipanggil tugas.

Apakah definisi mereka? perbezaannya? Ringkasnya, ia boleh difahami seperti berikut:

1 macrotask (juga dipanggil tugasan makro)

Dapat difahami bahawa kod yang dilaksanakan oleh timbunan pelaksanaan setiap kali adalah makro. tugasan (termasuk setiap kali daripada acara Dapatkan panggilan balik acara daripada baris gilir dan masukkannya ke dalam tindanan pelaksanaan untuk pelaksanaan)

  • Setiap tugasan akan melaksanakan tugas ini dari awal hingga akhir, dan tidak akan melaksanakan tugasan lain
  • Untuk dapat Ini membolehkan tugas dalaman JS dan tugas DOM dilaksanakan dengan teratur, dan halaman akan dipaparkan semula selepas pelaksanaan satu tugas tamat dan sebelum pelaksanaan tugas seterusnya bermula (tugas -> rendering -> tugas -> ...)
2 sebagai tugasan yang dilaksanakan sejurus selepas pelaksanaan tugas semasa

Dalam erti kata lain, selepas tugasan semasa, sebelum tugasan seterusnya dan sebelum memberikan
  • , kelajuan tindak balasnya akan lebih cepat daripada setTimeout (setTimeout ialah tugas), kerana tidak perlu menunggu untuk rendering
  • Dalam erti kata lain, selepas macrotask tertentu dilaksanakan, semua microtasks yang dijana semasa pelaksanaannya akan dilaksanakan (sebelum rendering )
  • 3. Apakah jenis senario yang akan Bagaimana untuk membentuk tugasan makro dan mikro
tugasan makro: blok kod utama, setTimeout, setInterval, dsb. (setiap acara dalam acara baris gilir ialah tugasan makro)
  • tugas mikro: Janji, proses.nextTick Tunggu
  • Tambahan: Dalam persekitaran nod, process.nextTick mempunyai keutamaan yang lebih tinggi daripada Promise, yang boleh difahami dengan mudah sebagai: selepas tugasan makro tamat, bahagian nextTickQueue pada baris gilir microtask akan dilaksanakan terlebih dahulu Kemudian bahagian Promise microtask akan dilaksanakan.

Mari kita fahami berdasarkan urutan:

(1) Acara dalam macrotask diletakkan dalam baris gilir acara, dan baris gilir ini dikekalkan oleh urutan pencetus acara

(2 ) Semua tugasan mikro dalam tugasan mikro ditambahkan pada baris gilir microtask (Barisan Kerja), menunggu pelaksanaan selepas tugasan makro semasa selesai, dan baris gilir ini dikekalkan oleh benang enjin JS (ini disimpulkan daripada pemahaman saya sendiri, kerana ia adalah dilaksanakan dengan lancar di bawah utas utama)

Jadi, untuk meringkaskan mekanisme pengendalian:


Laksanakan tugasan makro (jika tiada pada tindanan, dapatkannya daripada baris gilir acara)
  • Jika microtask ditemui semasa pelaksanaan, tambahkannya pada baris gilir tugas microtask
  • Selepas makrotask dilaksanakan, semua microtask dalam baris gilir microtask semasa akan dilaksanakan serta-merta (dilaksanakan mengikut turutan)
  • Selepas tugas makro semasa dilaksanakan, ia mula menyemak pemaparan, dan kemudian utas GUI mengambil alih pemaparan
  • Selepas pemaparan selesai, utas JS terus mengambil alih dan memulakan tugas makro seterusnya (diperolehi daripada baris gilir acara)
  • Seperti yang ditunjukkan dalam gambar:


Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScriptSelain itu, sila beri perhatian kepada perbezaan antara polyfill Promise dan versi rasmi:

Dalam versi rasmi, ia adalah bentuk microtask standard

Polyfill biasanya disimulasikan melalui setTimeout, jadi ia adalah dalam bentuk macrotask

Ambil perhatian bahawa sesetengah penyemak imbas mempunyai pelaksanaan yang berbeza keputusan (kerana mereka mungkin melaksanakan tugas mikro sebagai tugasan makro), tetapi demi kesederhanaan, beberapa perbezaan tidak akan diterangkan di sini di bawah penyemak imbas standard (tetapi ingat, sesetengah penyemak imbas mungkin tidak standard)

补充:使用MutationObserver实现microtask

MutationObserver可以用来实现microtask (它属于microtask,优先级小于Promise, 一般是Promise不支持时才会这样做)

它是HTML5中的新特性,作用是:监听一个DOM变动, 当DOM对象树发生任何变动时,Mutation Observer会得到通知

像以前的Vue源码中就是利用它来模拟nextTick的, 具体原理是,创建一个TextNode并监听内容变化, 然后要nextTick的时候去改一下这个节点的文本内容, 如下:

var counter = 1
var observer = new MutationObserver(nextTickHandler)
var textNode = document.createTextNode(String(counter))

observer.observe(textNode, {
    characterData: true
})
timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
}

不过,现在的Vue(2.5+)的nextTick实现移除了MutationObserver的方式(据说是兼容性原因), 取而代之的是使用MessageChannel (当然,默认情况仍然是Promise,不支持才兼容的)。

MessageChannel属于宏任务,优先级是:MessageChannel->setTimeout, 所以Vue(2.5+)内部的nextTick与2.4及之前的实现是不一样的,需要注意下。

【相关推荐:javascript视频教程web前端

Atas ialah kandungan terperinci Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:csdn.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam