Pertanyaan Bagaimana cara kerja pengikatan data di AngularJS?


Bagaimana cara kerja data mengikat di AngularJS kerangka?

Saya belum menemukan detail teknis tentang situs mereka. Ini lebih atau kurang jelas bagaimana cara kerjanya ketika data disebarkan dari tampilan ke model. Tapi bagaimana cara AngularJS melacak perubahan properti model tanpa setter dan getter?

Saya menemukan bahwa ada Pengamat JavaScript yang dapat melakukan pekerjaan ini. Tetapi mereka tidak didukung di Internet Explorer 6 dan Internet Explorer 7. Jadi bagaimana AngularJS tahu bahwa saya berubah misalnya berikut dan mencerminkan perubahan ini pada tampilan?

myobject.myproperty="new value";

1803
2018-03-13 10:16


asal


Jawaban:


AngularJS mengingat nilai dan membandingkannya dengan nilai sebelumnya. Ini adalah pemeriksaan kotor dasar. Jika ada perubahan nilai, maka ia mengaktifkan acara perubahan.

Itu $apply() metode, yang Anda sebut ketika Anda bertransisi dari dunia non-AngularJS ke dunia AngularJS, panggilan $digest(). Intisari hanyalah pengecekan kotor lama. Ini berfungsi di semua browser dan benar-benar dapat diprediksi.

Untuk membedakan pengecekan-kotor (AngularJS) vs mengubah pendengar (KnockoutJS dan Backbone.js): Meskipun pengecekan yang kotor mungkin tampak sederhana, dan bahkan tidak efisien (saya akan membahasnya nanti), ternyata semantiknya benar sepanjang waktu, sementara pendengar perubahan memiliki banyak kasus sudut aneh dan membutuhkan hal-hal seperti pelacakan ketergantungan untuk membuat lebih semantik yang benar. KnockoutJS ketergantungan pelacakan adalah fitur pintar untuk masalah yang tidak memiliki AngularJS.

Masalah dengan pendengar perubahan:

  • Sintaksnya mengerikan, karena browser tidak mendukungnya secara asli. Ya, ada proksi, tetapi mereka tidak semantis benar dalam semua kasus, dan tentu saja tidak ada proksi pada peramban lama. Intinya adalah bahwa pemeriksaan kotor memungkinkan Anda melakukannya POJO, sedangkan KnockoutJS dan Backbone.js memaksa Anda untuk mewarisi dari kelas mereka, dan mengakses data Anda melalui pengakses.
  • Ubah perpaduan. Misalkan Anda memiliki berbagai item. Katakanlah Anda ingin menambahkan item ke dalam array, karena Anda ingin menambah, setiap kali Anda menambahkan Anda menembakan acara pada perubahan, yang render UI. Ini sangat buruk untuk kinerja. Yang Anda inginkan adalah memperbarui UI hanya sekali, di bagian akhir. Peristiwa perubahan terlalu halus.
  • Ubah pendengar segera tembak pada penyetel, yang merupakan masalah, karena pendengar perubahan dapat lebih lanjut mengubah data, yang memicu lebih banyak peristiwa perubahan. Ini buruk karena di tumpukan Anda, Anda mungkin memiliki beberapa peristiwa perubahan yang terjadi sekaligus. Misalkan Anda memiliki dua larik yang perlu disinkronkan untuk alasan apa pun. Anda hanya dapat menambahkan satu atau yang lain, tetapi setiap kali Anda menambahkan Anda mengaktifkan acara perubahan, yang kini memiliki pandangan dunia yang tidak konsisten. Ini adalah masalah yang sangat mirip dengan penguncian benang, yang JavaScript hindari karena setiap panggilan balik dijalankan secara eksklusif dan selesai. Ubah peristiwa istirahat ini karena setter dapat memiliki konsekuensi yang jauh jangkauannya yang tidak dimaksudkan dan tidak jelas, yang menciptakan masalah utas seluruhnya. Ternyata apa yang ingin Anda lakukan adalah untuk menunda pelaksanaan pendengar, dan menjamin, bahwa hanya satu pendengar yang berjalan pada suatu waktu, maka setiap kode bebas untuk mengubah data, dan ia tahu bahwa tidak ada kode lain yang berjalan saat ia melakukannya .

Bagaimana dengan kinerja?

Jadi kelihatannya kita lambat, karena pengecekan yang kotor tidak efisien. Di sinilah kita perlu melihat bilangan real daripada hanya memiliki argumen teoritis, tetapi pertama mari kita mendefinisikan beberapa kendala.

Manusia adalah:

  • Lambat - Sesuatu yang lebih cepat dari 50 ms tidak terlihat oleh manusia dan dengan demikian dapat dianggap sebagai "instan".

  • Terbatas - Anda tidak bisa benar-benar menunjukkan lebih dari 2000 informasi kepada manusia di satu halaman. Apa pun yang lebih dari itu benar-benar UI yang buruk, dan manusia juga tidak bisa memproses ini.

Jadi pertanyaan sebenarnya adalah ini: Berapa banyak perbandingan yang dapat Anda lakukan pada browser dalam 50 ms? Ini adalah pertanyaan yang sulit untuk dijawab karena banyak faktor ikut bermain, tetapi di sini adalah kasus uji coba: http://jsperf.com/angularjs-digest/6 yang menciptakan 10.000 pengamat. Pada browser modern ini hanya membutuhkan kurang dari 6 ms. Di Internet Explorer 8 dibutuhkan sekitar 40 ms. Seperti yang Anda lihat, ini bukan masalah bahkan pada browser lambat hari ini. Ada peringatan: Perbandingan harus sederhana agar sesuai dengan batas waktu ... Sayangnya itu terlalu mudah untuk menambahkan perbandingan lambat ke AngularJS, sehingga mudah untuk membangun aplikasi yang lambat ketika Anda tidak tahu apa yang Anda sedang melakukan. Tetapi kami berharap memiliki jawaban dengan menyediakan modul instrumentasi, yang akan menunjukkan kepada Anda perbandingan yang lambat.

Ternyata video game dan GPU menggunakan pendekatan pemeriksaan kotor, khususnya karena konsisten. Selama mereka mendapatkan lebih dari tingkat refresh monitor (biasanya 50-60 Hz, atau setiap 16,6-20 ms), kinerja apa pun melebihi itu adalah pemborosan, jadi Anda lebih baik menggambar lebih banyak barang, daripada mendapatkan FPS lebih tinggi.


2661
2018-03-13 23:47



Misko sudah memberikan uraian yang sangat baik tentang cara kerja bindings data, tetapi saya ingin menambahkan pandangan saya tentang masalah kinerja dengan pengikatan data.

Seperti yang dikatakan Misko, sekitar 2000 binding adalah tempat Anda mulai melihat masalah, tetapi Anda seharusnya tidak memiliki lebih dari 2000 informasi di halaman. Ini mungkin benar, tetapi tidak setiap pengikatan data terlihat oleh pengguna. Setelah Anda mulai membuat semacam widget atau data grid dengan dua arah yang mengikat Anda bisa dengan mudah memukul 2.000 binding, tanpa ux buruk.

Pertimbangkan, misalnya, kotak kombo tempat Anda dapat mengetik teks untuk memfilter opsi yang tersedia. Kontrol semacam ini bisa memiliki ~ 150 item dan masih sangat bermanfaat. Jika memiliki beberapa fitur tambahan (misalnya kelas khusus pada opsi yang saat ini dipilih), Anda mulai mendapatkan 3-5 bindings per opsi. Taruh tiga dari widget ini pada halaman (misalnya satu untuk memilih suatu negara, yang lain untuk memilih kota di negara tersebut, dan yang ketiga untuk memilih hotel) dan Anda berada di antara 1000 dan 2.000 binding sudah.

Atau pertimbangkan data-grid dalam aplikasi web perusahaan. 50 baris per halaman tidak masuk akal, masing-masing dapat memiliki 10-20 kolom. Jika Anda membangun ini dengan ng-mengulangi, dan / atau memiliki informasi di beberapa sel yang menggunakan beberapa bindings, Anda bisa mendekati 2.000 binding dengan grid ini saja.

Saya menemukan ini menjadi sebuah besar sekali masalah saat bekerja dengan AngularJS, dan satu-satunya solusi yang dapat saya temukan sejauh ini adalah membangun widget tanpa menggunakan pengikatan dua arah, alih-alih menggunakan ngOnce, mendelegitarkan pengamat dan trik serupa, atau membuat arahan yang membangun DOM dengan jQuery dan Manipulasi DOM. Saya merasa ini mengalahkan tujuan menggunakan Sudut di tempat pertama.

Saya ingin mendengar saran tentang cara lain untuk menangani hal ini, tetapi mungkin saya harus menulis pertanyaan saya sendiri. Saya ingin memasukkan ini dalam komentar, tapi ternyata terlalu lama untuk itu ...

TL; DR 
Pengikatan data dapat menyebabkan masalah kinerja pada halaman yang kompleks.


308
2017-08-22 13:28



Dengan memeriksa kotor $scope obyek

Angular mempertahankan yang sederhana array pengamat di $scope objek. Jika Anda memeriksa $scope Anda akan menemukan bahwa itu mengandung array bernama $$watchers.

Setiap pengamat adalah seorang object yang berisi antara lain

  1. Ekspresi yang diawasi oleh pengintai. Ini mungkin saja sebuah attribute nama, atau sesuatu yang lebih rumit.
  2. Nilai terakhir dari ekspresi. Ini dapat diperiksa terhadap nilai saat ini dari ekspresi yang dihitung. Jika nilai berbeda, pengamat akan memicu fungsi dan menandai $scope sebagai kotor.
  3. Fungsi yang akan dijalankan jika pengamat kotor.

Bagaimana pengamat didefinisikan

Ada banyak cara berbeda dalam mendefinisikan seorang pengamat di AngularJS.

  • Anda bisa secara eksplisit $watch sebuah attribute di $scope.

    $scope.$watch('person.username', validateUnique);
    
  • Anda dapat menempatkan {{}} interpolasi dalam template Anda (seorang pengamat akan dibuat untuk Anda pada saat ini $scope).

    <p>username: {{person.username}}</p>
    
  • Anda dapat meminta arahan seperti ng-model untuk menentukan pengamat untuk Anda.

    <input ng-model="person.username" />
    

Itu $digest siklus memeriksa semua pengamat terhadap nilai terakhir mereka

Ketika kita berinteraksi dengan AngularJS melalui saluran normal (ng-model, ng-repeat, dll) siklus digest akan dipicu oleh arahan.

Siklus digest adalah a traversal kedalaman-pertama $scope dan semua anak-anaknya. Untuk setiap $scope  object, kami mengulanginya $$watchers  array dan mengevaluasi semua ekspresi. Jika nilai ekspresi baru berbeda dari nilai terakhir yang diketahui, fungsi pengamat disebut. Fungsi ini mungkin mengkompilasi ulang bagian dari DOM, menghitung ulang nilai pada $scope, memicu sebuah AJAX  request, apa pun yang perlu Anda lakukan.

Setiap lingkup dilalui dan setiap ekspresi jam dievaluasi dan diperiksa terhadap nilai terakhir.

Jika seorang pemantau dipicu, maka $scope kotor

Jika seorang pemantau dipicu, aplikasi tahu sesuatu telah berubah, dan $scope ditandai sebagai kotor.

Fungsi watcher dapat mengubah atribut lainnya $scope atau pada orang tua $scope. Jika satu $watcher fungsi telah dipicu, kami tidak dapat menjamin yang lain $scopeMasih bersih, jadi kita jalankan seluruh siklus cerna lagi.

Ini karena AngularJS memiliki pengikatan dua arah, sehingga data dapat dikirimkan kembali ke atas $scopepohon. Kami dapat mengubah nilai pada yang lebih tinggi $scope yang telah dicerna. Mungkin kita mengubah nilai pada $rootScope.

Jika itu $digest kotor, kita jalankan seluruhnya $digest siklus lagi

Kami terus mengulang melalui $digest siklus sampai siklus intisari muncul bersih (semua $watch ekspresi memiliki nilai yang sama seperti pada siklus sebelumnya), atau kita mencapai batas intisari. Secara default, batas ini ditetapkan pada 10.

Jika kita mencapai batas intaian, AngularJS akan meningkatkan kesalahan di konsol:

10 $digest() iterations reached. Aborting!

Intisinya sulit pada mesin tetapi mudah pada pengembang

Seperti yang Anda lihat, setiap kali sesuatu berubah dalam aplikasi AngularJS, AngularJS akan memeriksa setiap pengamat di $scope hierarki untuk melihat bagaimana cara merespons. Untuk pengembang, ini adalah keuntungan produktivitas yang sangat besar, karena Anda sekarang perlu menulis hampir tanpa kode pengkabelan, AngularJS hanya akan melihat jika nilai telah berubah, dan membuat sisa aplikasi konsisten dengan perubahan.

Dari perspektif mesin meskipun ini sangat tidak efisien dan akan memperlambat aplikasi kami jika kami membuat terlalu banyak pengamat. Misko telah mengutip angka sekitar 4000 pengamat sebelum aplikasi Anda akan terasa lambat pada browser yang lebih lama.

Batas ini mudah dijangkau jika Anda ng-repeat lebih dari satu besar JSON  array sebagai contoh. Anda dapat mengurangi ini menggunakan fitur seperti pengikatan satu kali untuk mengkompilasi template tanpa membuat pengamat.

Bagaimana cara menghindari membuat terlalu banyak pengamat

Setiap kali pengguna Anda berinteraksi dengan aplikasi Anda, setiap pengamat tunggal dalam aplikasi Anda akan dievaluasi setidaknya sekali. Bagian besar dari mengoptimalkan aplikasi AngularJS adalah mengurangi jumlah pengamat di Anda $scope pohon. Salah satu cara mudah untuk melakukan ini adalah dengan satu kali mengikat.

Jika Anda memiliki data yang jarang berubah, Anda dapat mengikatnya hanya sekali menggunakan :: syntax, seperti:

<p>{{::person.username}}</p>

atau

<p ng-bind="::person.username"></p>

Pengikatan hanya akan dipicu ketika templat yang berisi dirender dan data dimuat ke dalamnya $scope.

Ini sangat penting ketika Anda memiliki ng-repeat dengan banyak barang.

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>

142
2018-06-02 12:31



Ini adalah pemahaman dasar saya. Itu mungkin salah!

  1. Item diawasi dengan meneruskan fungsi (mengembalikan apa yang seharusnya disaksikan) ke $watch metode.
  2. Perubahan pada item yang diawasi harus dilakukan dalam satu blok kode dibungkus oleh $apply metode.
  3. Di akhir $apply itu $digest metode dipanggil yang pergi melalui masing-masing jam dan cek untuk melihat apakah mereka berubah sejak itu terakhir kali $digest berlari.
  4. Jika ada perubahan yang ditemukan, maka digest akan dipanggil lagi sampai semua perubahan stabil.

Dalam perkembangan normal, sintaks yang mengikat data dalam HTML memberitahu compiler AngularJS untuk membuat jam tangan untuk Anda dan metode pengendali dijalankan di dalam $apply sudah. Jadi untuk pengembang aplikasi semuanya transparan.


77
2018-03-13 21:01



Saya bertanya-tanya ini sendiri untuk sementara waktu. Tanpa setter bagaimana caranya AngularJS perhatikan perubahan pada $scope obyek? Apakah itu mereka polling?

Apa yang sebenarnya adalah ini: Setiap tempat "normal" Anda memodifikasi model sudah dipanggil dari nyali AngularJS, sehingga panggilan secara otomatis $apply untuk Anda setelah kode Anda berjalan. Misalnya pengontrol Anda memiliki metode yang terhubung ng-click pada beberapa elemen. Karena AngularJS kabel panggilan metode itu bersama-sama untuk Anda, ia memiliki kesempatan untuk melakukan itu $apply di tempat yang tepat. Demikian juga, untuk ekspresi yang muncul tepat di pandangan, yang dieksekusi oleh AngularJS jadi itu melakukan $apply.

Ketika dokumentasi berbicara tentang harus menelepon $apply manual untuk kode di luar AngularJS, ini berbicara tentang kode yang, ketika dijalankan, tidak berasal AngularJS sendiri di tumpukan panggilan.


57
2017-09-03 17:45



Menjelaskan dengan Gambar:

Data-Binding membutuhkan pemetaan

Referensi dalam ruang lingkup tidak persis referensi dalam template. Ketika Anda mengikat dua objek data, Anda membutuhkan yang ketiga yang mendengarkan yang pertama dan memodifikasi yang lain.

enter image description here

Di sini, ketika Anda memodifikasi <input>, Anda menyentuh data-ref3. Dan mecanism data-bind klasik akan berubah data-ref4. Jadi bagaimana yang lain {{data}} ekspresi akan bergerak?

Acara mengarah ke $ digest ()

enter image description here

Angular mempertahankan a oldValue dan newValue dari setiap ikatan. Dan setelah semua Peristiwa bersudut, Yang terkenal $digest() loop akan memeriksa WatchList untuk melihat apakah sesuatu berubah. Ini Peristiwa sudut adalah ng-click, ng-change, $http selesai ... The $digest() akan loop selama ada oldValue berbeda dari newValue.

Pada gambar sebelumnya, akan terlihat bahwa data-ref1 dan data-ref2 telah berubah.

Kesimpulan

Ini sedikit seperti Telur dan Ayam. Anda tidak pernah tahu siapa yang memulai, tetapi semoga itu bekerja sebagian besar waktu seperti yang diharapkan.

Poin lainnya adalah Anda dapat dengan mudah memahami dampak dari pengikatan sederhana pada memori dan CPU. Semoga Desktop cukup gemuk untuk menangani ini. Ponsel tidak begitu kuat.


29
2018-05-20 13:33



Tentunya tidak ada pemeriksaan periodik Scope apakah ada perubahan pada Objek yang melekat padanya. Tidak semua objek yang dilampirkan ke ruang lingkup diawasi. Ruang lingkup secara prototipe mempertahankan a Pengamat $$ . Scope hanya melakukan iterasi melalui ini $$watchers kapan $digest disebut .

Angular menambahkan pengamat ke pengamat $$ untuk masing-masing

  1. {{expression}} - Dalam template Anda (dan di mana saja jika ada ekspresi) atau ketika kita mendefinisikan ng-model.
  2. $ scope. $ watch (‘expression / function’) - Dalam JavaScript Anda, kita bisa melampirkan objek lingkup untuk sudut untuk menonton.

$ jam fungsi mengambil tiga parameter:

  1. Yang pertama adalah fungsi pengamat yang mengembalikan objek atau kita bisa menambahkan ekspresi.

  2. Yang kedua adalah fungsi pendengar yang akan dipanggil ketika ada perubahan dalam objek. Semua hal seperti perubahan DOM akan diimplementasikan dalam fungsi ini.

  3. Yang ketiga adalah parameter opsional yang mengambil dalam boolean. Jika benar, dalam sudut melihat objek & jika Sudut keliru hanya melakukan referensi menonton pada objek.     Pelaksanaan kasar dari jam tangan terlihat seperti ini

Scope.prototype.$watch = function(watchFn, listenerFn) {
   var watcher = {
       watchFn: watchFn,
       listenerFn: listenerFn || function() { },
       last: initWatchVal  // initWatchVal is typically undefined
   };
   this.$$watchers.push(watcher); // pushing the Watcher Object to Watchers  
};

Ada hal yang menarik di Angular yang disebut Digest Cycle. Siklus $ digest dimulai sebagai hasil dari panggilan ke $ scope. $ Digest (). Asumsikan bahwa Anda mengubah model lingkup $ dalam fungsi handler melalui direktif ng-klik. Dalam hal ini AngularJS secara otomatis memicu siklus $ digest dengan memanggil $ digest (). Selain ng-click, ada beberapa arahan / layanan bawaan lain yang memungkinkan Anda mengubah model (mis. Ng-model, $ timeout, dll) dan secara otomatis memicu siklus $ digest. Implementasi kasar dari $ digest terlihat seperti ini.

Scope.prototype.$digest = function() {
      var dirty;
      do {
          dirty = this.$$digestOnce();
      } while (dirty);
}
Scope.prototype.$$digestOnce = function() {
   var self = this;
   var newValue, oldValue, dirty;
   _.forEach(this.$$watchers, function(watcher) {
          newValue = watcher.watchFn(self);
          oldValue = watcher.last;   // It just remembers the last value for dirty checking
          if (newValue !== oldValue) { //Dirty checking of References 
   // For Deep checking the object , code of Value     
   // based checking of Object should be implemented here
             watcher.last = newValue;
             watcher.listenerFn(newValue,
                  (oldValue === initWatchVal ? newValue : oldValue),
                   self);
          dirty = true;
          }
     });
   return dirty;
 };

Jika kami menggunakan JavaScript setTimeout () berfungsi untuk memperbarui model cakupan, Sudut tidak memiliki cara untuk mengetahui apa yang mungkin Anda ubah. Dalam hal ini adalah tanggung jawab kita untuk memanggil $ apply () secara manual, yang memicu siklus $ digest. Demikian pula, jika Anda memiliki arahan yang mengatur pendengar acara DOM dan mengubah beberapa model di dalam fungsi handler, Anda perlu memanggil $ apply () untuk memastikan perubahan diterapkan. Ide besar dari $ apply adalah bahwa kita dapat mengeksekusi beberapa kode yang tidak menyadari Angular, kode itu mungkin masih mengubah hal-hal di ruang lingkup. Jika kita membungkus kode itu dalam $ apply, itu akan mengurus menelepon $ digest (). Implementasi kasar $ apply ().

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};

19
2018-05-22 18:18



AngularJS menangani mekanisme pengikatan data dengan bantuan tiga fungsi kuat: $ jam (),$ digest ()dan $ apply (). Sebagian besar waktu AngularJS akan memanggil $ scope. $ Watch () dan $ scope. $ Digest (), tapi dalam beberapa kasus Anda mungkin harus memanggil fungsi-fungsi ini secara manual untuk memperbarui dengan nilai-nilai baru.

$ jam () : -

Fungsi ini digunakan untuk mengamati perubahan dalam variabel pada lingkup $.   Ini menerima tiga parameter: ekspresi, pendengar dan objek kesetaraan,   di mana listener dan objek kesetaraan adalah parameter opsional.

$ digest () -

Fungsi ini beriterasi melalui semua jam di objek $ scope,   dan anak-anak objek lingkupnya
     (jika ada). Ketika $ digest () mengulangi   di atas jam tangan, ia memeriksa apakah nilai dari ekspresi tersebut   berubah. Jika nilai telah berubah, AngularJS memanggil pendengar dengan   nilai baru dan nilai lama. Fungsi $ digest () dipanggil   setiap kali AngularJS berpikir itu perlu. Misalnya, setelah tombol   klik, atau setelah panggilan AJAX. Anda mungkin memiliki beberapa kasus di mana AngularJS   tidak memanggil fungsi $ digest () untuk Anda. Dalam hal ini Anda harus melakukannya   sebut sendiri.

$ apply () -

Angular melakukan pembaruan otomatis otomatis hanya perubahan model yang   dalam konteks AngularJS. Ketika Anda melakukan perubahan pada model apa pun di luar   konteks Sudut (seperti acara browser DOM, setTimeout, XHR atau ketiga   perpustakaan pesta), maka Anda perlu menginformasikan Sudut dari perubahan oleh   panggil $ apply () secara manual. Ketika $ berlaku () fungsi panggilan selesai   AngularJS memanggil $ digest () secara internal, jadi semua data bindings   diperbarui.


12
2018-05-16 15:05



Kebetulan saya perlu menghubungkan model data seseorang dengan formulir, yang saya lakukan adalah pemetaan langsung data dengan formulir.

Misalnya jika model memiliki sesuatu seperti:

$scope.model.people.name

Masukan kontrol dari formulir:

<input type="text" name="namePeople" model="model.people.name">

Dengan begitu jika Anda memodifikasi nilai pengontrol objek, ini akan tercermin secara otomatis dalam tampilan.

Contoh di mana saya lulus model diperbarui dari data server adalah ketika Anda meminta kode pos dan kode pos berdasarkan beban tertulis daftar koloni dan kota yang terkait dengan tampilan itu, dan secara default menetapkan nilai pertama dengan pengguna. Dan ini saya bekerja dengan sangat baik, apa yang terjadi, apakah itu angularJS terkadang membutuhkan beberapa detik untuk menyegarkan model, untuk melakukan ini Anda dapat meletakkan pemintal saat menampilkan data.


7
2017-09-18 05:57