1. Pendahuluan
Dalam topik sebelumnya, saya dengan cepat memperkenalkan poin -poin pengetahuan yang terkait dengan KnockoutJ dan menulis beberapa contoh sederhana. Saya berharap bahwa melalui contoh -contoh ini, Anda dapat dengan cepat memulai dengan KnockoutJs. Untuk membiarkan semua orang dengan jelas melihat aplikasi KnockoutJ dalam proyek aktual, topik ini akan memperkenalkan cara menggunakan webapi+bootstrap+knockoutjs+asp.net mvc untuk membuat program web satu halaman. Model ini juga digunakan dalam sebagian besar proyek aktual perusahaan sekarang.
2. Manfaat Spa (Single Page)
Sebelum memperkenalkan implementasi spesifik, saya merasa perlu untuk memperkenalkan SPA secara rinci. SPA, singkatan dari aplikasi web satu halaman, adalah aplikasi web yang memuat satu halaman HTML dan memperbarui halaman secara dinamis ketika pengguna berinteraksi dengan aplikasi. Browser akan memuat HTML, CSS, dan JavaScript yang diperlukan di awal. Semua operasi diselesaikan pada halaman ini dan dikendalikan oleh JavaScript.
Manfaat program satu halaman adalah:
Pengalaman pengguna yang lebih baik memungkinkan pengguna untuk mengalami kecepatan dan kehalusan aplikasi asli di aplikasi web.
Pisahkan kekhawatiran ujung depan dan belakang, ujung depan bertanggung jawab untuk tampilan antarmuka, dan ujung belakang bertanggung jawab untuk penyimpanan dan komputasi data, masing -masing melakukan tugasnya sendiri dan tidak akan mencampur logika ujung depan dan belakang bersama -sama.
Untuk mengurangi tekanan pada server, server hanya perlu menghasilkan data, terlepas dari logika tampilan dan logika halaman, dan meningkatkan throughput server. Front-end yang ditulis dalam sintaksis pisau cukur di MVC mengharuskan server untuk menyelesaikan sintesis halaman dan kemudian menghasilkannya.
Set program back-end yang sama dapat digunakan langsung pada beberapa klien seperti antarmuka web, ponsel, tablet, dll. Tanpa modifikasi.
Tentu saja, selain keuntungan yang tercantum di atas, program satu halaman juga memiliki kekurangannya:
Tidak kondusif untuk SEO. Jika ini adalah sistem manajemen, itu tidak akan mempengaruhi itu.
Waktu pemuatan awal relatif meningkat. Karena semua sumber daya JS dan CSS akan dimuat pada pertama kalinya, membuat halaman berikutnya lancar. Untuk ini, Anda dapat menggunakan bundel di ASP.NET MVC untuk mengikat file. Untuk penggunaan bundel secara terperinci, silakan merujuk ke artikel: http://www.vevb.com/article/84329.htm, http://www.vevb.com/article/84329.htm dan http://www.vevb.com/article/82174.htp://www.vevb.com/article/82174.htp://www.vevb.com.
Navigasi tidak tersedia. Jika Anda harus menavigasi, Anda harus maju dan mundur sendiri. Untuk ini, Anda dapat mewujudkan fungsi ke depan dan belakang sendiri untuk menebusnya. Bahkan, inilah yang dilakukan halaman web seluler sekarang, dan sekarang mereka masih perlu navigasi di atas. Ini juga dapat dilakukan untuk beberapa sistem manajemen backend perusahaan.
Biaya pengembangan yang tinggi untuk pengembang. Ini bukan masalah. Pemrogram perlu terus belajar untuk mengisi daya. Untungnya, beberapa kerangka kerja front-end sangat mudah digunakan.
3. Gunakan ASP.NET MVC+WEBAPI+Bootstrap+KnockoutJs untuk mengimplementasikan SPA
Keuntungan dan kerugian SPA diperkenalkan secara rinci sebelumnya. Selanjutnya, mari kita gunakan ASP.NET MVC+WEBAPI+BS+KO untuk mengimplementasikan program satu halaman, sehingga mengalami kehalusan spa dan membandingkan efek dari halaman yang dibuat oleh Asp.net MVC+Razor asli.
1. Gunakan VS2013 untuk membuat proyek aplikasi Web ASP.NET, periksa perpustakaan MVC dan WebAPI. Lihat gambar di bawah ini untuk detailnya:
2. Buat gudang dan model yang sesuai. Berikut ini adalah sistem manajemen tugas yang sederhana. Model spesifik dan kode pergudangan adalah sebagai berikut:
Implementasi kelas entitas tugas:
Public Enum TaskState {Active = 1, SELESAI = 2} /// <summary> /// Entitas Tugas //// </summary> Tugas kelas publik {public int id {get; mengatur; } Nama string publik {get; mengatur; } deskripsi string publik {get; mengatur; } public DateTime CreationTime {get; mengatur; } public DateTime finishtime {get; mengatur; } pemilik string publik {get; mengatur; } state public taskstate {get; mengatur; } public Task () {CreationTime = DateTime.parse (Datetime.now.toLongDateString ()); State = taskstate.active; }}Implementasi Kelas Pergudangan Tugas:
/// <summary> /// Di sini gudang menggunakan data sampel sebagai demonstrasi. Proyek nyata perlu dimuat secara dinamis dari database /// </summary> kelas publik TaskRepository {#Region Static Static Static Static Lazy <KeckRepository> _taskRepository = new Lazy <AckSaskRepository> (() => TaskRepository () baru ()); Public Static TaskRepository Current {get {return _taskRepository.Value; }} #endregion #region bidang Private Readonly List <Keck> _tasks = Daftar baru <Keck> () {Tugas baru {id = 1, name = "Buat program spa", deskripsi = "spa (aplikasi web single), finish daku, finish, spa adalah sejumlah kecil bandwidth dan pengalaman lancar", pemilik = "finishtime", finish time = Datetime.parse (DateTime.now.Adddays (1) .toString (cultureInfo.invariantCulture)))}, tugas baru {id = 2, name = "Learning Knockoutjs", description = "Knockoutjs adalah perpustakaan kelas MVVM yang mendukung pengikat dua arah", pemilik = "TOMMY Li. Datetime.parse (datetime.now.adddays (2) .toString (cultureInfo.invariantCulture)))}, tugas baru {id = 3, name = "pelajari angularjs", description = "AngularJS adalah kerangka MVVM yang mengintegrasikan MVVM dan MVC dengan satu." Datetime.parse (datetime.now.adddays (3) .toString (cultureInfo.invariantCulture))}, tugas baru {id = 4, name = "pelajari situs web asp.net mvc tidak ada. dari proyek asli, dan dapat menghasilkan waktu eksekusi dari setiap tautan eksekusi kode ", pemilik =" tonny li ", finishtime = datetime.parse (datetime.now.adddays (4) .tostring (cultureinfo.invariantculture))},}; #endregion #Region Metode publik IEnumerable PUBLIK <Keck> getAll () {return _tasks; } tugas publik get (int id) {return _tasks.find (p => p.id == id); } Tugas Publik Tambah (item tugas) {if (item == null) {lempar argumentNullexception baru ("item"); } item.id = _tasks.count + 1; _tasks.add (item); item pengembalian; } public void Remove (int id) {_tasks.removeall (p => p.id == id); } pembaruan bool publik (item tugas) {if (item == null) {throw new ArgumentNullexception ("item"); } var taskitem = get (item.id); if (taskitem == null) {return false; } _tasks.remove (taskitem); _tasks.add (item); Kembali Benar; } #endregion}3. Tambahkan pustaka Bootstrap dan KnockoutJs melalui Nuget.
4. Menerapkan layanan data back-end. Di sini layanan backend diimplementasikan menggunakan asp.net webapi. Kode implementasi spesifik adalah sebagai berikut:
/// <summary> /// tugas webapi, berikan layanan data /// </summary> kelas publik TasksController: Apicontroller {private readonly TaskRepository _taskrepository = TaskRepository.current; public iEnumerable <ceck> getAll () {return _taskRepository.getAll (). orderby (a => a.id); } Tugas publik get (int id) {var item = _taskrepository.get (id); if (item == null) {lempar httpresponseException baru (httpstatuscode.notfound); } return item; } [Rute ("API/Tugas/GetByState")] public iEnumerable <Keck> getByState (state string) {iEnumerable <Uckation> hasil = Daftar baru <Keck> (); switch (state.tolower ()) {case "": case "all": hasil = _taskrepository.getall (); merusak; kasus "aktif": hasil = _taskrepository.getall (). Di mana (t => t.state == TaskState.active); merusak; case "selesai": result = _taskrepository.getall (). Where (t => t.state == TaskState.Competed); merusak; } result = results.orderby (t => t.id); hasil pengembalian; } [Httppost] Tugas publik Buat (item tugas) {return _taskrepository.add (item); } [Httpput] public void put (item tugas) {if (! _Taskrepository.update (item)) {lempar httpresponseException baru (httpstatuscode.notfound); }} public void delete (int id) {_taskrepository.remove (id); }}5. Gunakan bundel ASP.NET MVC untuk mengemas sumber daya. Kode Implementasi BundleConfig yang sesuai adalah sebagai berikut:
/// <summary> /// Cukup tambahkan beberapa file CSS dan JS yang hilang. Karena beberapa file CSS dan JS telah ditambahkan saat membuat template /// </summary> kelas publik BundleConfig {// untuk informasi lebih lanjut tentang bundling, kunjungi http://go.microsoft.com/fwlink/?linkid=301862 statis public static registerbundles (Bundlecollection) bundle public static void Registerbundles (Bundlecollechultion ScriptBundle ("~/bundles/jQuery"). Sertakan ("~/skrip/jQuery- {Versi} .js")); bundles.add (new ScriptBundle ("~/bundles/jQueryVal"). Sertakan ("~/skrip/jQuery.validate*")); // Gunakan versi pengembangan Modernizr untuk dikembangkan dan pelajari. Kemudian, saat Anda // siap untuk diproduksi, gunakan alat build di http://modernizr.com untuk hanya memilih tes yang Anda butuhkan. bundles.add (new scriptBundle ("~/bundles/modernizr"). Sertakan ("~/skrip/modernizr-*")); bundles.add (new scriptBundle ("~/bundles/bootstrap"). Sertakan ("~/skrip/bootstrap.js", "~/script/bootstrap-DatePicker.min.js")); bundles.add (styleBundle baru ("~/content/css"). Sertakan ("~/content/bootstrap.css", "~/content/bootstrap--pickicker3.min.css", "~/content/site.css"))); bundles.add (new scriptBundle ("~/bundles/knockout"). Sertakan ("~/skrip/knockout- {Versi} .js", "~/skrip/knockout.validation.min.js", "~/skrip/knockout.mapping-latest.js")); bundles.add (new scriptBundle ("~/bundles/app"). Sertakan ("~/skrip/app/app.js")); }}6. Karena kita perlu membuat tipe enum muncul sebagai string di halaman. Pencacahan dikonversi ke tipe numerik saat serial secara default. Oleh karena itu, kita perlu membuat perubahan berikut pada kelas webapiconfig:
kelas public static WebApiconfig {public static void register (httpConfiguration config) {// Web API Configuration and Services // Web API Routing config.maphttpattributeroutes (); config.routes.maphttproute (nama: "DefaultApi", routeTemplate: "API/{controller}/{id}", default: baru {id = routeParameter.optional}); // Make serialisasi menggunakan Camel Case Style Serialization Property Config.Formatters.jsonformatter.serializersettings.contractresolver = CamelCasePropertynamescontracTolver () baru; // Serialize string config.formatters.jsonformatter.serializersettings.converters.add (stringEnumConverter baru ()); }}Catatan: Jika serialisasi huruf kecil unta tidak digunakan di atas, Anda juga harus melakukan penyesuaian saat mengikat data pada halaman. Misalnya, saat mengikat atribut nama, gunakan kapitalisasi nama secara langsung. Jika Anda menggunakan metode nama, itu akan meminta bahwa tidak ada kesalahan definisi dalam atribut ini. Karena JS menggunakan gaya huruf kecil unta untuk menyebutkan variabel. Oleh karena itu, Anda disarankan untuk menggunakan gaya huruf kecil unta untuk serialisasi. Pada saat ini, Anda hanya dapat menggunakan bentuk "nama" untuk mengikat. Ini lebih sesuai dengan spesifikasi kode JS.
7. Ubah file tata letak dan konten file indeks yang sesuai.
Kode spesifik file tata letak adalah sebagai berikut:
<! Doctype html> <html> <head> <meta charset = "utf-8"/> <meta name = "viewport" content = "width = device-width, skala awal = 1.0"> <itement> <crips> </title> @styles.render ("~/konten/css") @scrips.rem </title> @render ("~/content/css") @scripts.render </title> @render ("~/content/css") @scripts. <dv> <div> <p> Sistem manajemen tugas sederhana </p> </div> <v> <ul> <li> <a href = "/"> home </a> </li> </ul> </div> </div> </div> <v id = "Main"> @renderbody () <hr/<footer> </div> <p> @ @@renderbody () <hr/<footer> <p> <p> © @@renderbody (hr/<footer> <p> <p> © @@renderbody (hr/<footer> <p> <p> @ @@renderbody (hr/<footer> <p> <p> @ @@renderbody (hr/<footer> <p> <p> @ @didear. </div> @scripts.render ("~/bundles/jQuery") @scripts.render ("~/bundles/bootstrap") @scripts.render ("~/bundel/knockout") @scripts.render (" @ @ @ @{ @{ @{ @{ @{ @{ @ @{ @ @{ @ @{~ @ @{ @ @{ Viewbag.title = "index"; Tata letak = "~/views/shared/_layout.cshtml";} <div id = "list" data-bind = "if: cantcreate"> <h2> tugas </h2> <viv> <able> <Tead> <t/thr> </th> </th> </th> </th> </ther </th> </ther </th> name </th> name </th> name </threct </thrip </thrip </th </th> name </th> name </thripse </thripse </thripse </thrips </thr> <th> Waktu Penyelesaian </th> <th> statu </th> <th> </th> </tr> </t> <tbody data-bind = "foreach: tugas"> <tr> <td-bind = "Text: ID"> </td> <td> </td-bind = "TEXT: NAME, KLIK: HANDLECRECERORTORORorororor" TD "</td </td </Tex description"></td> <td data-bind="text: owner"></td> <td data-bind="text: creationTime"></td> <td data-bind="text: finishTime"></td> <td data-bind="text: state"></td> <td><a data-bind="click:remove" href = "JavaScript: void (0)"> hapus </a> </td> </tbody> </able> </div> <v div> <a href = "javascript: void (0)" data-bind = "klik: function, event) {settasklist ('all')}"> semua </a> | <a href = "javascript: void (0)" data-bind = "klik: function (data, event) {setasklist ('active')}"> aktif </a> | <a href = "javascript: void (0)" data-bind = "klik: function (data, peristiwa) {setasklist ('selesai')}"> selesai </a> </div> <ver> <a href = "javascript: void (0)" data-bind = "klik: handlecreateororpdate" = batal (0) "data-bind =" klik: handlecreateororor "ucccript: void (0)" DATA-BIND = "Klik: HandleCreateorororor"> "Data-Bind =" Klik: HandleRecreateoror "> hidden"> <h2>Add task</h2> <br/> <div> <div> <label for="taskName">Name*</label> <div> <input type="text" data-bind="value: name" id="taskName" name="taskName" placeholder="name"> </div> </div> <div> <label for="taskDesc">Description</label> <div> <textarea Data-bind = "Nilai: Deskripsi" Baris = "3" id = "TaskDesc" Nama = "TaskDesc" Placeholder = "Deskripsi"> </textarea> </div> </div> <ver> <label untuk = "TaskOwner"> Divan> </Label> <Div> <Input ID = "TaskOwnerer" Name = "Data-Bind" Data-Bind = " <Div> <label for = "TaskFinish"> Perkiraan waktu penyelesaian*</label> <ver> <input id = "TaskFinish" data-bind = "value: finishtime" name = "TaskFinish"> </div> </Div> <Div> <label untuk = "TaskOwner"> Status*</Label> <Div> <Div> <Div> <label untuk = "TaskOwner" Status*</Label> <Div> <Div> <Div> <label for = "TaskOwner"> Status*</Label> <Div> <Div> <Div> <label for = "TaskOwner"> </Label> <Div> <Div> <Div> <label for = "TaskOwner" status*</label> <Div> <Div> <Div> DATA TERKESUS <pection> Selesai </tipe> </dectt> </div> </div> <div> <div> <tombol data-bind = "Klik: HandlesAveClick"> Simpan </button> <tombol data-bind = "Klik: candlebackClick"> Back </buton> </Div> </div> </Div> </div>8. Buat logika skrip front-end yang sesuai. Gunakan kode JS untuk meminta data dan membuat objek ViewModel yang sesuai untuk pengikatan front-end. Kode implementasi JS spesifik adalah sebagai berikut:
var TaskListViewModel = {Tugas: ko.observableArray (), canCreate: ko.observable (true)}; var TaskModel = function () {this.id = 0; this.name = ko.observable (); this.description = ko.observable (); this.finishtime = ko.observable (); this.owner = ko.observable (); this.state = ko.observable (); this.fromjs = function (data) {this.id = data.id; this.name (data.name); this.description (data.description); this.finishtime (data.finishtime); this.Owner (data.Owner); this.state (data.state); };}; function getAllTasks () {sendaJaxRequest ("get", function (data) {TaskListViewModel.tasks.removeall (); untuk (var i = 0; i <data.length; i ++) {TaskListViewModel.tasks. });} function setKaskList (state) {sendaJaxRequest ("get", function (data) {TaskListViewModel.tasks.removeall (); untuk (var i = 0; i <data '{i]) {TaskListViewModel.tasks.push (data') {i]) {TaskListViewModel.tasks.push (data '), });} fungsi hapus (item) {sendajaxRequest ("delete", function () {getAllTasks ();}, item.id);} var Task = new TaskModel (); function handleCreateRupDate (item) {Task.fromjs (item); initDatePicker (); TaskListViewModel.cancreate (false); $ ('#create'). css ('visibilitas', 'visible');} function candackClick () {TaskListViewModel.cancreate (true); $ ('#create'). css ('visibilitas', 'tersembunyi');} function handlesveClick (item) {if (item.id == tidak terdefinisi) {sendajaxRequest ("post", function (newItem) {// newItem adalah objek yang dikembalikan. TaskListViewMiew. Deskripsi: Item.Description, finishtime: item.finishtime, pemilik: item.Owner, state: item.state}); } else {sendaJaxRequest ("put", function () {getalltasks ();}, null, {id: item.id, name: item.name, deskripsi: item.description, finishtime: item.finishtime, pemilik: item.Owner, state: item.state}); } TaskListViewModel.cancreate (true); $ ('#create'). css ('visibilitas', 'tersembunyi');} function sendajaxRequest (httpmethod, callback, url, reqdata) {$ .Ajax ("/API/Tugas" + (Url? "/" + url: "), {type: httpm" + uRl? "/" + url: "), {type: httpm" + uRl? initDatePicker = function () {$ ('#create .datePicker'). DatePicker ({autoclose: true});}; $ ('. nav'). on ('klik', 'li', function () {$ ('. () {getAllTasks ();Pada titik ini, program satu halaman kami telah dikembangkan. Selanjutnya, mari kita jalankan untuk melihat efeknya.
Dari diagram demonstrasi hasil yang berjalan di atas, kita dapat melihat bahwa setelah halaman dimuat, semua operasi tampaknya beroperasi pada satu halaman, dan rasanya seperti halaman browser berputar. Dibandingkan dengan halaman yang dikembangkan menggunakan ASP.NET MVC + Razor sebelumnya, apakah Anda merasakan kehalusan spa? Sebelumnya, halaman yang dikembangkan menggunakan Asp.net MVC + Razor, Anda hanya perlu meminta satu halaman dan Anda dapat merasakan penyegaran seluruh halaman, yang membuat pengalaman pengguna sangat buruk.
4. Perbandingan dengan model pengembangan pisau cukur
Saya percaya semua orang telah melihat keuntungan spa dari hasilnya. Selanjutnya, saya pikir perlu membandingkannya dengan cara tradisional mengimplementasikan halaman web. Ada dua perbedaan utama dari metode pengembangan pisau cukur:
1. Saat halaman diterjemahkan, data diproses di sisi browser. Tidak di server. Alokasikan tekanan render ke sisi browser dari setiap pengguna, sehingga mengurangi tekanan pada server situs web. Jika itu sintaksis pisau cukur, pernyataan pengikatan halaman front-end harus sebagai berikut:
@Model IEnumerable <knockoutjsspa.models.task> @foreach (item var dalam model) {<tr> <td>@item.name </td> <td>@item.description </td> </tr>}Ini diterjemahkan oleh mesin pisau cukur di sisi server. Ini juga alasan mengapa halaman yang dikembangkan menggunakan Razor akan melihat halaman berputar. Karena setiap kali Anda beralih halaman, Anda perlu meminta server untuk merender, dan setelah server render, mengembalikan HTML ke klien untuk ditampilkan.
2. Data terikat adalah dinamis. Ini berarti bahwa perubahan dalam model data akan segera tercermin pada halaman. Efek ini disebabkan oleh mekanisme pengikatan dua arah yang diimplementasikan oleh KnockoutJS.
Menggunakan metode ini juga sederhana untuk pengembangan program. API Web hanya bertanggung jawab untuk menyediakan data, dan halaman front-end juga mengurangi banyak operasi DOM. Karena operasi DOM rumit dan rentan kesalahan. Ini juga berarti mengurangi bug implisit dalam program ini. Selain itu, layanan back-end dapat digunakan oleh ponsel, browser web, dan beberapa platform untuk menghindari pengembangan berulang.
5. Ringkasan
Pada titik ini, pengenalan artikel ini akan diperkenalkan. Artikel ini terutama memperkenalkan penggunaan knockoutJs untuk menyelesaikan program spa. Bahkan, dalam pekerjaan aktual, model membuat program halaman tunggal lebih didasarkan pada AngularJS. Lalu ada banyak knockoutjs, tetapi KnockoutJs hanyalah kerangka kerja MVVM, dan mekanisme peruteannya perlu digunakan dengan beberapa perpustakaan kelas lainnya, seperti mekanisme perutean di ASP.NET MVC di sini, Anda juga dapat menggunakan kerangka routing front-end director.js. Dibandingkan dengan knockoutJs, AngularJS adalah kerangka kerja MVVM+MVC. Jadi dalam topik berikutnya, kami akan memperkenalkan cara menggunakan AngularJS untuk membuat satu halaman halaman (SPA).
Unduh semua kode sumber dalam artikel ini: spawithknockoutjs
Jika Anda masih ingin belajar secara mendalam, Anda dapat mengklik di sini untuk mempelajari dan melampirkan 3 topik menarik kepada Anda:
Tutorial Pembelajaran Bootstrap
Tutorial Praktis Bootstrap
Tutorial Penggunaan Plug-In Bootstrap
Di atas adalah semua tentang artikel ini, saya harap ini akan membantu untuk pembelajaran semua orang.