Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Milo

Rolling Framework Anda sendiri

by
Read Time:13 minsLanguages:
This post is part of a series called Rolling Your Own Framework.
Rolling Your Own Framework: A Practical Example

Indonesian (Bahasa Indonesia) translation by Dendi Deden (you can also view the original English article)

Membangun sebuah framework dari awal bukanlah sesuatu yang kita secara khusus mengatur untuk dilakukan. apa kamu sudah menjadi gila, benar? Dengan kebanyakan framework JavaScript luar sana, apa motivasi yang mungkin bisa kami memiliki untuk membuat versi kita sendiri?

Awalnya, kami sedang mencari suatu framework untuk membangun sistem manajemen konten baru untuk situs web The Daily Mail. Tujuan utama adalah untuk membuat mengedit proses jauh lebih interaktif dengan semua elemen artikel (gambar, komprehensif, kotak panggilan keluar, dan sebagainya) yang draggable, modular, dan self-managing.

Semua framework yang kita bisa meletakkan tangan kita pada yang kita rancang untuk UI lebih atau kurang statis yang didefinisikan oleh pengembang. Kami perlu untuk membuat artikel dengan teks yang dapat diedit dan dinamis merender elemen UI.

Backbone terlalu pada low level. Itu lebih sedikit menyediakan struktur dasar objek dan pesan. Kita harus membangun banyak abstraksi diatas pondasi Backobne, jadi kami memutuskan bahwa kami akan membangun fondasi ini oleh kita sendiri.

AngularJS menjadi framework pilihan kamu untuk membangun untuk ukuran kecil dan menengah aplikasi browser yang relatif statis UIs. Sayangnya, AngularJS adalah sangat banyak black box-itu tidak mengekspos API apa pun nyaman untuk memperluas dan memanipulasi objek yang Anda buat dengan itu- directive, controller, service. Juga, sementara AngularJS menyediakan reaktif koneksi antara view dan lingkup ekspresi, tidak memungkinkan mendefinisikan reaktif koneksi antara model-model, sehingga aplikasi berukuran sedang menjadi sangat mirip dengan aplikasi jQuery dengan spaghetti event listner dan callback, dengan satu-satunya perbedaan bahwa daripada event listener, aplikasi angular memiliki watcher dan bukan memanipulasi DOM Anda memanipulasi scope.

Apa yang selalu kita inginkan adalah framework yang akan memungkinkan;

  • Mengembangkan aplikasi dengan cara deklaratif dengan reaktif binding model ke view.
  • Menciptakan reaktif data binding antara model-model yang berbeda dalam aplikasi untuk mengelola data propagasi di deklaratif bukan dalam imperative style.
  • Memasukkan validator dan penerjemah di binding ini, jadi kami bisa mengikat view model data daripada view model seperti di AngularJS.
  • Kontrol yang lebih tepat komponen yang terhubung ke DOM elemen.
  • Fleksibilitas view manajemen memungkinkan Anda untuk kedua secara otomatis memanipulasi DOM perubahan dan untuk merender ulang beberapa bagian yang menggunakan engine template dalam kasus di mana render lebih efisien daripada DOM manipulasi.
  • Kemampuan untuk secara dinamis membuat UIs.
  • Mampu menghubungkan ke mekanisme di balik reaktivitas data dan justru kontrol Lihat pembaruan dan data flow.
  • Mampu untuk memperluas fungsi komponen yang disediakan oleh fraemework dan untuk menciptakan komponen baru.

Kami tidak dapat menemukan apa yang kami butuhkan di solusi yang ada, jadi kita mulai mengembangkan Milo secara paralel dengan aplikasi yang menggunakan itu.

Mengapa Milo?

Milo dipilih sebagai nama karena Milo Minderbinder, mencatut perang dari Catch 22 oleh Joseph Heller. Karena mulai dari mengelola operasi yang berantakan, ia memperluas mereka menjadi sebuah perusahaan perdagangan menguntungkan yang menghubungkan semua orang dengan segala sesuatu, dan dalam Milo dan orang lain "memiliki saham".

Milo framework memiliki modul binder, yang mengikat DOM elemen untuk komponen (melalui atribut ml-bind khusus), dan minder modul yang memungkinkan membangun live reaktif koneksi antara sumber data yang berbeda (Model dan Data segi komponen adalah seperti sumber data).

Kebetulan, Milo dapat dibaca sebagai sebuah akronim MaIL Online, dan tanpa lingkungan kerja yang unik di Mail Online, kita akan pernah mampu membangunnya.

Mengelola Views

Pengikat

View di Milo dikelola oleh komponen, yang pada dasarnya adalah contoh dari class JavaScript, bertanggung jawab untuk mengelola sebuah elemen DOM. Banyak fraemework menggunakan komponen sebagai konsep untuk mengelola elemen UI, tetapi yang paling jelas yang datang ke pikiran adalah Ext JS. Kami telah bekerja secara ekstensif dengan Ext JS (aplikasi warisan kita mengganti dibangun dengan itu), dan ingin menghindari apa yang kita dianggap menjadi dua kelemahan dari pendekatan.

Yang pertama adalah bahwa Ext JS tidak membuatnya mudah bagi Anda untuk mengelola markup Anda. Satu-satunya cara untuk membangun UI adalah untuk mengumpulkan hierarki yang bersarang komponen konfigurasi. Ini mengarah pada sia-sia kompleks render markup yang diberikan dan mengambil kendali dari tangan pengembang. Kami membutuhkan sebuah metode untuk menciptakan komponen inline, di kita sendiri, kerajinan tangan HTML markup. Ini adalah di mana pengikat masuk.

Pengikat scan markup kami mencari atribut ml-bind sehingga dapat instantiate komponen dan mengikat mereka ke elemen. Atribut memegang informasi tentang komponen; ini dapat memasukan komponen class, aspek, dan harus menyertakan nama komponen.

Kita akan berbicara tentang aspek dalam satu menit, tetapi untuk sekarang mari kita lihat bagaimana kita dapat mengambil nilai atribut ini dan ekstrak konfigurasi dari itu menggunakan regular expression.

Dengan informasi itu, semua yang perlu kita lakukan adalah iterate melalui semua atribut ml-bind, ekstrak nilai-nilai ini dan membuat instance untuk mengelola setiap elemen.

Jadi dengan hanya sedikit regex dan beberapa DOM traversal, Anda dapat membuat sendiri framework mini dengan sintaks kustom sesuai dengan logika bisnis tertentu dan konteks Anda. Dalam kode yang sangat sedikit, kami memiliki setup arsitektur yang memungkinkan modular, mandiri mengelola komponen, yang dapat digunakan namun Anda suka. Kita dapat membuat nyaman dan deklaratif sintaks instantiating dan konfigurasi komponen di HTML kita, tapi tidak seperti angular, kita dapat mengatur komponen ini bagaimanapun kita suka.

Responsibility-Driven Design

Hal kedua yang kita tidak suka tentang Ext JS adalah bahwa ia memiliki hirarki class sangat curam dan kaku, yang akan membuatnya sulit untuk organie class komponen kami. Kami mencoba untuk menulis daftar semua perilaku yang mungkin memiliki komponen yang diberikan dalam artikel. Sebagai contoh, komponen bisa diedit, itu bisa mendengarkan event, mungkin menjadi target drop atau draggable itu sendiri. Ini adalah hanya beberapa dari perilaku yang diperlukan. Daftar awal kami menulis memiliki sekitar 15 jenis fungsi yang dapat diminta dari setiap komponen tertentu.

Mencoba untuk mengatur perilaku ini menjadi semacam struktur hirarkis akan  tidak hanya sakit kepala besar, tetapi juga sangat membatasi harus kita pernah ingin mengubah fungsionalitas dari setiap class komponen tertentu (sesuatu yang kami akhirnya melakukan banyak). Kami memutuskan untuk mengimplementasikan pola desain object-oriented yang lebih fleksibel.

Kami telah membaca desain Responsibility-Driven, yang bertentangan dengan model lebih umum mendefinisikan perilaku class bersama-sama dengan data yang berlaku, lebih berkaitan dengan tindakan objek bertanggungjawab. Ini cocok dengan kita serta kita sedang berhadapan dengan model data yang kompleks dan tak terduga, dan pendekatan ini akan memungkinkan kita untuk meninggalkan implementasi rincian ini nanti.

Hal penting yang kita ambil dari RDD adalah konsep peran. Peran adalah satu set tanggung jawab terkait. Dalam kasus proyek kami, kami mengidentifikasi peran seperti mengedit, menyeret, drop zone, dipilih, atau event di antara banyak lainnya. Tapi bagaimana Anda mewakili peran ini dalam kode? Untuk itu, kami meminjam dari decorator pattern.

decoratpo pattern memungkinkan perilaku yang akan ditambahkan ke objek individu, baik secara statis atau dinamis, tanpa mempengaruhi perilaku lain objek dari class yang sama. Sekarang sementara manipulasi run-time class perilaku tidak telah sangat diperlukan dalam proyek ini, kami sangat tertarik pada jenis enkapsulasi ide ini yang sediakan. Milo's implementasi adalah semacam hibrida yang melibatkan objek yang disebut facets, yang melekat sebagai properti contoh komponen. facet mendapatkan referensi untuk komponen, it's 'pemilik' dan objek konfigurasi, yang memungkinkan kita untuk menyesuaikan facet untuk setiap komponen class.

Anda dapat menganggap facet sebagai maju, dikonfigurasi yang mendapatkan namespace mereka sendiri pada objek pemilik mereka dan bahkan mereka sendiri init metode, yang perlu ditimpa dengan subclass facet.

Jadi kita dapat subclass class Facet ini sederhana dan membuat facet spesifik untuk setiap jenis perilaku yang kita inginkan. Milo datang prebuilt dengan berbagai aspek, seperti facet DOM, yang menyediakan koleksi DOM utilitas yang beroperasi pada komponen pemilik elemen, dan List dan Item facets, yang bekerja sama untuk membuat daftar mengulangi komponen.

facet ini kemudian dibawa bersama-sama dengan apa yang kita disebut FacetedObject, yang merupakan class abstrak yang mewarisi semua komponen. FacetedObject memiliki class metode disebut createFacetedClass itu hanya subclass itu sendiri, dan melekat semua facets ke facets properti di class. Dengan begitu, saat FacetedObject mendapat instantiated, memiliki akses ke semua class facet, dan bisa iterate mereka untuk bootstrap komponen.

Di Milo, kami disarikan sedikit lebih jauh dengan membuat class Component dasar dengan pencocokan createComponentClass metode class, tetapi prinsip dasar adalah sama. Dengan perilaku kunci dikelola oleh facet yang dikonfigurasi, kita dapat membuat banyak class komponen berbeda dalam gaya deklaratif tanpa harus menulis kode kustom terlalu banyak. Berikut adalah contoh menggunakan beberapa segi out-of-the-box yang datang dengan Milo.

Di sini kami telah menciptakan class komponen yang disebut Panel, yang memiliki akses ke metode utilitas DOM, akan secara otomatis ditetapkan class CSS init, dapat mendengarkan event DOM dan akan setup sebuah pengendali klik init, itu dapat diseret di sekitar, dan juga bertindak sebagai target drop. facet terakhir yang ada, container memastikan bahwa komponen ini set up lingkup itu sendiri, dan dapat, pada dasarnya, memiliki child komponen.

Scope

Kami telah membahas sejenak untuk melihat apakah semua komponen yang melekat pada dokumen harus membentuk struktur datar atau harus bentuk pohon mereka sendiri, dimana child hanya dapat diakses dari parent mereka.

Kami pasti membutuhkan scope untuk beberapa situasi, tetapi itu bisa ditangani pada tingkat implementasi, bukan pada tingkat framework. Sebagai contoh, kita memiliki gambar kelompok yang berisi gambar. Itu akan menjadi mudah bagi kelompok-kelompok ini untuk melacak gambar anak-anak mereka tanpa perlu scope yang generik.

Kami akhirnya memutuskan untuk membuat sebuah scope tree komponen dalam dokumen. Memiliki scope membuat banyak hal lebih mudah dan memungkinkan kita untuk memiliki lebih generik penamaan komponen, tapi mereka jelas harus dikelola. Jika Anda menghancurkan komponen, Anda harus menghapusnya dari parent sceope. Jika Anda memindahkan komponen, itu harus dihapus dari satu dan ditambahkan ke yang lain.

Scope adalah hash khusus, atau map objek, dengan masing-masing child yang terkandung dalam scope sebagai properti dari objek. scope, di Milo, ditemukan pada facet kontainer, yang itu sendiri memiliki fungsi sangat sedikit. Objek scope, namun memiliki berbagai metode untuk memanipulasi dan iterasi itu sendiri, tetapi untuk menghindari konflik namespace, semua metode tersebut ditandai dengan tanda garis bawah pada awal.

Messaging-Synchronous vs Asynchronous

Kami ingin memiliki sambungan yang longgar antara komponen, jadi kami memutuskan untuk memiliki fungsi yang melekat pada semua komponen dan facet pesan.

implementasi pertama message adalah koleksi metode yang telah mengelola array subsriber. Metode dan array dicampur langsung ke dalam obyek yang mengimplememtasilkan messaging.

Versi sederhana dari implementasi messenger pertama terlihat seperti ini:

Objek yang digunakan ini mix-in dapat memiliki pesan yang yang diemit di atasnya (oleh obyek itu sendiri atau kode lain) dengan postMessage metode dan subscription ke kode ini dapat diaktifkan dan dinonaktifkan dengan metode yang memiliki nama yang sama.

Saat ini, messenger secara substansial telah berevolusi untuk memungkinkan:

  • Melampirkan sumber-sumber eksternal pesan (DOM pesan, jendela pesan, perubahan data, lain messenger dll) – misalnya Events facet menggunakannya untuk mengekspos event DOM melalui Milo messenger. Fungsi ini dilaksanakan melalui sebuah class terpisah MessageSource dan sub kelasnya.
  • Mendefinisikan api pesan kustom yang menerjemahkan pesan dan data eksternal pesan untuk pesan internal. Misalnya Data facet menggunakannya untuk menerjemahkan perubahan dan peristiwa DOM input data mengubah event (Lihat model di bawah ini). Fungsi ini dilaksanakan melalui sebuah class terpisah MessengerAPI dan sub kelasnya.
  • Pattern subscription (menggunakan regular expression). Misalnya model (lihat bawah) secara internal menggunakan pattern subscription untuk memungkinkan model dalam perubahan subscription.
  • Mendefinisikan konteks apapun (value dari ini di subsriber) sebagai bagian dari subscription dengan sintaks ini:
  • Membuat subscription yang hanya dikirim sekali dengan once metode
  • memasukan callback sebagai parameter ketiga di postMessage (kita dianggap sebagai variabel jumlah argumen postMessage, tapi kami ingin API pesan yang lebih konsisten daripada kita akan memiliki dengan argumen-argumen variabel)
  • dll.

Kesalahan utama desain kami dibuat saat mengembangkan messenger adalah bahwa semua pesan dikirim serentak. Sejak JavaScript single-threaded,lon sequence  pesan dengan operasi kompleks yang dilaksanakan akan cukup mudah mengunci UI. Mengubah Milo untuk membuat pengiriman pesan aasynchronousinkron adalah mudah (semua pelanggan dipanggil blok eksekusi sendiri menggunakan setTimeout (subscriber, 0), mengubah seluruh framework dan aplikasi ini lebih sulit-sementara pesan kebanyakan dapat Dikirim asynchronously, ada banyak yang masih harus dikirim serentak (banyak event DOM yang memiliki data di dalamnya atau tempat-tempat yang mana preventDefault dipanggil). Secara default, pesan yang sekarang dikirim asynchronously, dan ada cara untuk membuat mereka sinkron baik ketika pesan dikirim:

atau subscription dibuat:

Keputusan desain lain yang kami buat adalah cara kita terkena metode Messenger pada objek yang menggunakan mereka. Awalnya, metode hanya dicampur ke dalam obyek, tapi kami tidak suka bahwa semua metode yang terkena dan kita tidak punya standalone messenger. Jadi messenger kembali diimplementasikan sebagai ckass terpisah berdasarkan abstrak class Mixin.

Mixin class memungkinkan memaparkan metode class pada objek host sedemikian rupa bahwa ketika metode dipanggil, konteks masih akan Mixin daripada objek host.

Itu terbukti menjadi mekanisme sangat nyaman – kita dapat memiliki kontrol penuh di mana metode yang terkena dan mengubah nama-nama seperti yang diperlukan. Itu juga memungkinkan kita untuk memiliki dua messenger pada satu objek, yang digunakan untuk model.

Secara umum, Milo messenger ternyata menjadi sangat solid perangkat lunak yang dapat digunakan sendiri, dalam browser dan Node.js. Itu telah mengeras oleh penggunaan dalam sistem manajemen konten kami produksi yang memiliki puluhan ribu baris kode.

Waktu berikutnya

Dalam artikel berikutnya, kita akan melihat mungkin paling berguna dan kompleks bagian dari Milo. Model Milo tidak hanya memperbolehkan akses yang aman, deep ke properti, tetapi juga event subscription perubahan pada setiap tingkat.

Kita juga akan mengeksplorasi pelaksanaan minder, dan bagaimana kita menggunakan konektor objek untuk melakukan One-Way satu atau dua mengikat sumber data.

Perhatikan bahwa artikel ini ditulis oleh Jason Green dan Evgeny Poberezkin.

Advertisement
Did you find this post useful?
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.