Sejak sekitar tahun 2009, MVC secara beransur-ansur bersinar dalam bidang bahagian hadapan, dan akhirnya membawa letupan besar dengan pelancaran React Native pada 2015: AngularJS, EmberJS, Backbone, ReactJS, RiotJS, VueJS... .. Satu siri nama telah muncul dan berubah dengan cara yang mencolok Sesetengah daripada mereka telah beransur-ansur hilang dari pandangan semua orang, ada yang masih berkembang pesat, dan ada yang telah mengambil peranan mereka sendiri dalam persekitaran ekologi tertentu. Tetapi tidak kira apa pun, MVC telah dan akan terus mempengaruhi cara pemikiran dan kaedah kerja jurutera hadapan.
Banyak contoh menerangkan MVC bermula daripada konsep rangka kerja tertentu, seperti koleksi Backbone atau model dalam AngularJS Ini sememangnya pendekatan yang baik. Tetapi sebab rangka kerja ialah rangka kerja, bukan perpustakaan kelas (jQuery) atau set alat (Underscore), adalah kerana terdapat banyak konsep reka bentuk yang sangat baik dan amalan terbaik di sebalik intipati reka bentuk ini saling melengkapi antara satu sama lain, dan adalah amat diperlukan, bukan mudah untuk melihat intipati corak reka bentuk tertentu melalui rangka kerja yang kompleks dalam tempoh yang singkat.
Inilah asal usul esei ini - kod prototaip yang dicipta untuk membantu semua orang memahami konsep hendaklah semudah mungkin, cukup mudah untuk semua orang memahami konsep itu.
1. Asas MVC ialah corak pemerhati, yang merupakan kunci untuk mencapai penyegerakan antara model dan paparan
Untuk kesederhanaan, setiap contoh model mengandungi hanya satu nilai primitif.
function Model(value) { this._value = typeof value === 'undefined' ? '' : value; this._listeners = []; } Model.prototype.set = function (value) { var self = this; self._value = value; // model中的值改变时,应通知注册过的回调函数 // 按照Javascript事件处理的一般机制,我们异步地调用回调函数 // 如果觉得setTimeout影响性能,也可以采用requestAnimationFrame setTimeout(function () { self._listeners.forEach(function (listener) { listener.call(self, value); }); }); }; Model.prototype.watch = function (listener) { // 注册监听的回调函数 this._listeners.push(listener); };
// html代码: <div id="div1"></div> // 逻辑代码: (function () { var model = new Model(); var div1 = document.getElementById('div1'); model.watch(function (value) { div1.innerHTML = value; }); model.set('hello, this is a div'); })();
Dengan bantuan corak pemerhati, kami telah menyedari bahawa apabila kaedah set model dipanggil untuk menukar nilainya, templat juga dikemas kini secara serentak, tetapi pelaksanaan ini sangat janggal kerana kami perlu memantau perubahan secara manual daripada nilai model (melalui kaedah jam tangan ) dan lulus dalam fungsi panggil balik Adakah terdapat cara untuk menjadikannya lebih mudah untuk mengikat pandangan (satu atau lebih nod dom) kepada model?
2. Laksanakan kaedah bind dan ikat model dan lihat
Model.prototype.bind = function (node) { // 将watch的逻辑和通用的回调函数放到这里 this.watch(function (value) { node.innerHTML = value; }); };
// html代码: <div id="div1"></div> <div id="div2"></div> // 逻辑代码: (function () { var model = new Model(); model.bind(document.getElementById('div1')); model.bind(document.getElementById('div2')); model.set('this is a div'); })();
Melalui enkapsulasi ringkas, pengikatan antara paparan dan model telah terbentuk Walaupun berbilang paparan perlu diikat, ia mudah dilaksanakan. Ambil perhatian bahawa bind ialah kaedah asli pada prototaip kelas Fungsi, tetapi ia tidak berkait rapat dengan MVC. Pengarang sangat menyukai perkataan bind, jadi saya hanya menutup kaedah asli di sini . Lebih dekat dengan rumah, walaupun kerumitan pengikatan telah dikurangkan, langkah ini masih memerlukan kami menyelesaikannya secara manual Adakah mungkin untuk memisahkan logik pengikatan sepenuhnya daripada kod perniagaan?
3. Laksanakan pengawal untuk memisahkan pengikatan daripada kod logik
Rakan-rakan yang berhati-hati mungkin menyedari bahawa walaupun kita bercakap tentang MVC, hanya kelas Model yang muncul dalam artikel di atas Adalah difahami bahawa kelas View tidak muncul lagi, HTML adalah View yang sudah siap , artikel ini juga menyebutnya dari awal hingga akhir Hanya menggunakan HTML sebagai View, kelas View tidak muncul dalam kod javascript), maka mengapa kelas Pengawal tidak kelihatan? Jangan risau, sebenarnya, apa yang dipanggil "kod logik" ialah segmen kod dengan tahap gandingan yang tinggi antara logik rangka kerja (mari kita panggil mainan prototaip artikel ini sebagai rangka kerja) dan logik perniagaan Mari kita pecahkannya sekarang.
Jika anda ingin meninggalkan logik mengikat kepada rangka kerja, anda perlu memberitahu rangka kerja bagaimana untuk melengkapkan pengikatan. Memandangkan sukar untuk melengkapkan anotasi dalam JS, kita boleh melakukan lapisan penanda ini dalam paparan - menggunakan atribut tag html ialah cara yang mudah dan berkesan.
function Controller(callback) { var models = {}; // 找到所有有bind属性的元素 var views = document.querySelectorAll('[bind]'); // 将views处理为普通数组 views = Array.prototype.slice.call(views, 0); views.forEach(function (view) { var modelName = view.getAttribute('bind'); // 取出或新建该元素所绑定的model models[modelName] = models[modelName] || new Model(); // 完成该元素和指定model的绑定 models[modelName].bind(view); }); // 调用controller的具体逻辑,将models传入,方便业务处理 callback.call(this, models); }
// html: <div id="div1" bind="model1"></div> <div id="div2" bind="model1"></div> // 逻辑代码: new Controller(function (models) { var model1 = models.model1; model1.set('this is a div'); });
Adakah semudah itu? Semudah itu. Intipati MVC adalah untuk melengkapkan logik perniagaan dalam pengawal dan mengubah suai model Pada masa yang sama, perubahan dalam model menyebabkan kemas kini automatik pandangan ini ditunjukkan dalam kod di atas dan menyokong berbilang paparan dan berbilang model. Walaupun ia tidak mencukupi untuk projek pengeluaran, saya harap ia akan sedikit sebanyak membantu pembelajaran MVC semua orang.
Kod "rangka kerja" selepas mengisih dan mengalih keluar ulasan:
function Model(value) { this._value = typeof value === 'undefined' ? '' : value; this._listeners = []; } Model.prototype.set = function (value) { var self = this; self._value = value; setTimeout(function () { self._listeners.forEach(function (listener) { listener.call(self, value); }); }); }; Model.prototype.watch = function (listener) { this._listeners.push(listener); }; Model.prototype.bind = function (node) { this.watch(function (value) { node.innerHTML = value; }); }; function Controller(callback) { var models = {}; var views = Array.prototype.slice.call(document.querySelectorAll('[bind]'), 0); views.forEach(function (view) { var modelName = view.getAttribute('bind'); models[modelName] = models[modelName] || new Model(); models[modelName].bind(view); }); callback.call(this, models); }
Posskrip:
Dalam proses pembelajaran fluks dan redux, walaupun penulis telah menguasai cara menggunakan alatan, saya hanya tahu tetapi tidak tahu mengapa saya sentiasa menekankan "Flux eschews MVC memihak kepada aliran data satu arah". dalam dokumentasi ReactJS rasmi, saya tidak begitu faham, saya sentiasa merasakan aliran data sehala dan MVC tidak bercanggah adalah satu tanpa dia (eschew, avoid). Akhirnya, saya berazam untuk kembali kepada definisi MVC dan mengkaji semula Walaupun saya menyalin dan menampal secara sambil lewa dalam kerja harian saya, kita masih perlu bersungguh-sungguh dan mengunyah kata-kata itu sekali-sekala, bukan? Kaedah ini benar-benar membantu saya memahami ayat ini Di sini saya boleh berkongsi pendapat saya dengan anda: Sebab mengapa saya merasakan aliran data sehala dalam MVC dan fluks adalah serupa mungkin kerana tiada perbezaan yang jelas antara MVC dan corak pemerhati. . Disebabkan oleh hubungan - MVC adalah berdasarkan corak pemerhati, dan begitu juga fluks, jadi sumber persamaan ini adalah corak pemerhati, bukan MVC dan fluks itu sendiri. Pemahaman ini juga disahkan dalam buku corak reka bentuk asal kuartet: "Contoh corak Observer yang pertama dan mungkin paling terkenal muncul dalam Smalltalk Model/View/Controller (MVC), rangka kerja antara muka pengguna dalam persekitaran Smalltalk [KP88 ]. Kelas Model MVC memainkan peranan Subjek, manakala View ialah kelas asas untuk pemerhati.
Jika pembaca berminat untuk terus mengembangkan mainan prototaip sedemikian, anda boleh merujuk arahan berikut:
Rangka kerja yang lengkap perlu melalui banyak pemurnian dan pengubahsuaian Ini hanyalah langkah pertama. Saya harap semua orang akan terus bekerja keras.