Mengembangkan kerangka kerja Anda sendiri dan pertumbuhan profesional seorang programmer JS

Pernahkah Anda bertanya-tanya bagaimana kerangka kerja bekerja? Penulis bahan, terjemahan yang kami terbitkan hari ini, mengatakan bahwa ketika ia, bertahun-tahun yang lalu, setelah mempelajari jQuery , menemukan Angular . Kemudian Vue.js muncul, dan berurusan dengan kerangka kerja ini, ia terinspirasi untuk menulis sistem pengikatan data dua arah sendiri. Eksperimen semacam itu berkontribusi pada pengembangan profesional programmer. Artikel ini ditujukan bagi mereka yang ingin memperluas pengetahuan mereka sendiri di bidang teknologi yang menjadi dasar kerangka kerja JS modern. Secara khusus, ini akan fokus pada bagaimana menulis inti dari kerangka kerja Anda sendiri yang mendukung atribut khusus elemen HTML, reaktivitas dan pengikatan data dua arah.

gambar

Tentang sistem reaktivitas


Akan lebih baik jika, pada awalnya, kami mencari tahu bagaimana sistem reaktivitas kerangka kerja modern. Sebenarnya, semua yang ada di sini cukup sederhana. Misalnya, Vue.js, ketika mendeklarasikan komponen baru, proksi setiap properti (getter dan setter) menggunakan pola desain " proxy ".

Akibatnya, kerangka kerja akan dapat mendeteksi setiap fakta bahwa nilai berubah, dieksekusi baik dari kode dan dari antarmuka pengguna.

Pola proksi


Pola proksi didasarkan pada mekanisme akses yang berlebihan ke suatu objek. Ini mirip dengan cara orang bekerja dengan rekening bank mereka.

Misalnya, tidak ada yang bisa langsung bekerja dengan akun mereka, mengubah saldo sesuai dengan kebutuhan mereka. Untuk melakukan tindakan apa pun dengan akun tersebut, Anda harus menghubungi seseorang yang memiliki izin untuk bekerja dengannya, yaitu ke bank. Bank bertindak sebagai proksi antara pemegang akun dan akun itu sendiri.

var account = {    balance: 5000 } var bank = new Proxy(account, {    get: function (target, prop) {        return 9000000;    } }); console.log(account.balance); // 5,000 ( ) console.log(bank.balance);    // 9,000,000 (   ) console.log(bank.currency);   // 9,000,000 (  ) 

Dalam contoh ini, ketika menggunakan objek bank untuk mengakses saldo akun yang diwakili oleh objek account , fungsi pengambil menjadi kelebihan beban, yang mengarah pada fakta bahwa sebagai hasil dari permintaan seperti itu, nilai 9.000.000 selalu dikembalikan, bukan nilai properti riil, bahkan jika Properti ini tidak ada.

Dan ini adalah contoh dari overloading fungsi setter.

 var bank = new Proxy(account, {   set: function (target, prop, value) {       //      0       return Reflect.set(target, prop, 0);   } }); account.balance = 5800; console.log(account.balance); // 5,800 bank.balance = 5400; console.log(account.balance); // 0 (  ) 

Di sini, dengan membebani fungsi yang set , Anda dapat mengontrol perilakunya. Misalnya, Anda dapat mengubah nilai yang ingin Anda tulis ke properti, menulis data ke properti lain, atau bahkan tidak melakukan apa-apa.

Contoh Sistem Reaktivitas


Sekarang kami telah menemukan pola proksi, kami akan mulai mengembangkan kerangka kerja JS kami sendiri.

Agar tidak menyulitkannya, kami akan menggunakan sintaks yang sangat mirip dengan yang digunakan di Angular.js. Akibatnya, deklarasi pengontrol dan pengikatan elemen templat ke properti pengontrol akan terlihat sederhana dan jelas.

Ini kode templatnya.

 <div ng-controller="InputController">   <!-- "Hello World!" -->   <input ng-bind="message"/>     <input ng-bind="message"/> </div> <script type="javascript"> function InputController () {     this.message = 'Hello World!'; } angular.controller('InputController', InputController); </script> 

Pertama, Anda perlu mendeklarasikan pengontrol dengan properti. Selanjutnya, gunakan pengontrol ini di templat, dan akhirnya, gunakan atribut ng-bind untuk membuat data dua arah yang mengikat untuk nilai elemen.

Parsing template dan instantiating controller


Agar kita memiliki properti tertentu yang datanya dapat dilampirkan, kita membutuhkan tempat (pengontrol) tempat properti ini dapat dideklarasikan. Dengan demikian, perlu untuk menggambarkan controller dan memasukkannya ke dalam framework.

Dalam proses bekerja dengan pengontrol, kerangka kerja akan mencari elemen yang memiliki atribut ng-controller . Jika apa yang Anda temukan cocok dengan salah satu pengontrol yang dideklarasikan, kerangka kerja akan membuat instance baru dari pengontrol ini. Mesin pengontrol ini bertanggung jawab hanya untuk fragmen template ini.

 var controllers = {}; var addController = function (name, constructor) {   //     controllers[name] = {       factory: constructor,       instances: []   };     // ,     var element = document.querySelector('[ng-controller=' + name + ']');   if (!element){      return; //  ,      }     //         var ctrl = new controllers[name].factory;   controllers[name].instances.push(ctrl);     //   ..... }; addController('InputController', InputController); 

Di bawah ini adalah deklarasi dari controllers variabel "buatan sendiri". Perhatikan bahwa objek controllers berisi semua pengontrol yang dideklarasikan dalam framework dengan memanggil addController .

 var controllers = {   InputController: {       factory: function InputController(){           this.message = "Hello World!";       },       instances: [           {message: "Hello World"}       ]   } }; 

Setiap pengontrol memiliki fungsi factory , ini dilakukan sehingga, jika perlu, Anda dapat membuat turunan pengontrol baru. Selain itu, framework menyimpan, di properti instances , semua instance controller dengan tipe yang sama yang digunakan dalam templat.

Cari item yang terlibat dalam pengikatan data


Saat ini, kami memiliki instance dari controller dan sebuah fragmen templat menggunakan controller ini. Langkah kami selanjutnya adalah mencari elemen dengan data yang Anda butuhkan untuk mengikat properti controller.

 var bindings = {}; //  : element   DOM,   Array.prototype.slice.call(element.querySelectorAll('[ng-bind]'))   .map(function (element) {       var boundValue = element.getAttribute('ng-bind');       if(!bindings[boundValue]) {           bindings[boundValue] = {               boundValue: boundValue,               elements: []           }       }       bindings[boundValue].elements.push(element);   }); 

Organisasi penyimpanan semua binding objek menggunakan tabel hash ditampilkan di sini. Variabel yang dipertimbangkan berisi semua properti untuk mengikat, dengan nilai saat ini, dan semua elemen DOM terikat ke properti tertentu.

Inilah yang terlihat seperti versi variabel bindings :

     var bindings = {       message: {           //   :           // controllers.InputController.instances[0].message           boundValue: 'Hello World',           // HTML- (   ng-bind="message")           elements: [               Object { ... },               Object { ... }           ]       }   }; 

Ikatan bilateral sifat pengontrol


Setelah kerangka kerja menyelesaikan persiapan awal, saatnya tiba untuk satu hal yang menarik: pengikatan data dua arah. Arti dari ini adalah sebagai berikut. Pertama, sifat-sifat pengontrol harus terikat ke elemen DOM, yang akan memungkinkan mereka untuk diperbarui ketika nilai properti berubah dari kode, dan kedua, elemen DOM juga harus terikat ke properti pengontrol. Karena ini, ketika pengguna bertindak pada elemen-elemen tersebut, ini mengarah pada perubahan dalam sifat-sifat controller. Dan jika beberapa elemen HTML dilampirkan ke properti, ini juga mengarah pada fakta bahwa status mereka juga diperbarui.

Mendeteksi perubahan yang dilakukan dari kode menggunakan proxy


Seperti disebutkan di atas, Vue.js membungkus komponen dalam proxy untuk mendeteksi perubahan properti. Kami akan melakukan hal yang sama dengan mem-proksi hanya setter untuk properti controller yang terlibat dalam pengikatan data:

 //  : ctrl -    var proxy = new Proxy(ctrl, {   set: function (target, prop, value) {       var bind = bindings[prop];       if(bind) {           //    DOM,               bind.elements.forEach(function (element) {               element.value = value;               element.setAttribute('value', value);           });       }       return Reflect.set(target, prop, value);   } }); 

Akibatnya, ternyata ketika nilai ditulis ke properti terikat, proksi akan mendeteksi semua elemen yang terikat pada properti ini dan memberikannya nilai baru.

Dalam contoh ini, kami hanya mendukung pengikatan elemen input , karena hanya atribut value yang ditetapkan di sini.

Respons terhadap peristiwa elemen


Sekarang kita hanya perlu memberikan respons sistem untuk tindakan pengguna. Untuk melakukan ini, perhatikan fakta bahwa elemen DOM meningkatkan peristiwa ketika mereka mendeteksi perubahan dalam nilainya.

Kami mengatur mendengarkan acara ini dan merekam di properti yang terkait dengan elemen data baru yang diperoleh dari acara. Semua elemen lain yang terikat pada properti yang sama akan, berkat proxy, diperbarui secara otomatis.

 Object.keys(bindings).forEach(function (boundValue) {   var bind = bindings[boundValue];     //          bind.elements.forEach(function (element) {     element.addEventListener('input', function (event) {       proxy[bind.boundValue] = event.target.value; //  ,          });   }) }); 

Ringkasan


Sebagai hasilnya, mengumpulkan semua yang kami diskusikan di sini, Anda akan mendapatkan sistem Anda sendiri yang mengimplementasikan mekanisme dasar kerangka kerja JS modern. Berikut adalah contoh kerja dari implementasi sistem semacam itu. Kami berharap materi ini akan membantu semua orang untuk lebih memahami cara kerja alat pengembangan web modern.

Pembaca yang budiman! Jika Anda secara profesional menggunakan kerangka kerja JS modern, tolong beri tahu kami tentang bagaimana Anda memulai studi mereka dan tentang apa yang membantu Anda memahami mereka.

Source: https://habr.com/ru/post/id416021/


All Articles