1. مقدمة
في الموضوع السابق ، قدمت بسرعة نقاط المعرفة المتعلقة بـ Knockoutjs وكتبت بعض الأمثلة البسيطة. آمل أنه من خلال هذه الأمثلة ، يمكنك البدء بسرعة مع Knockoutjs. من أجل السماح للجميع بمشاهدة تطبيق KnockOutJs بوضوح في المشاريع الفعلية ، سيقدم هذا الموضوع كيفية استخدام WebAPI+Bootstrap+KnockOutJS+ASP.NET MVC لإنشاء برنامج ويب من صفحة واحدة. يستخدم هذا النموذج أيضًا في مشاريع معظم الشركات الفعلية الآن.
2. فوائد سبا (صفحة واحدة)
قبل تقديم التنفيذ المحدد ، أشعر أنه من الضروري تقديم سبا بالتفصيل. SPA ، اختصار تطبيق ويب صفحة واحدة ، هو تطبيق ويب يقوم بتحميل صفحة HTML واحدة وتحديث الصفحة بشكل ديناميكي عندما يتفاعل المستخدم مع التطبيق. سيقوم المتصفح بتحميل HTML و CSS و JavaScript المطلوب في البداية. يتم الانتهاء من جميع العمليات في هذه الصفحة ويتم التحكم فيها بواسطة JavaScript.
فوائد البرامج ذات الصفحة الواحدة هي:
تتيح تجربة مستخدم أفضل للمستخدمين تجربة سرعة وسلاسة التطبيقات الأصلية في تطبيق الويب.
افصل المخاوف الأمامية والخلفية ، الواجهة الأمامية هي المسؤولة عن عرض الواجهة ، والطرف الخلفي مسؤول عن تخزين البيانات وحسابها ، كل منها يؤدي واجباته الخاصة ولن يخلط منطق الأمامي والخلف معًا.
لتقليل الضغط على الخادم ، يحتاج الخادم فقط إلى إنشاء البيانات ، بغض النظر عن منطق العرض ومنطق الصفحة ، وزيادة إنتاجية الخادم. تتطلب الواجهة الأمامية المكتوبة في بناء جملة الحلاقة في MVC من الخادم إكمال توليف الصفحة ثم إخراجها.
يمكن استخدام نفس مجموعة البرامج الخلفية مباشرة على عملاء متعددين مثل واجهة الويب والهواتف المحمولة والأجهزة اللوحية وما إلى ذلك دون تعديل.
بالطبع ، بالإضافة إلى المزايا المذكورة أعلاه ، فإن البرامج ذات الصفحات الواحدة لها أوجه القصور:
لا يفضي إلى كبار المسئولين الاقتصاديين. إذا كان هذا نظامًا للإدارة ، فلن يؤثر عليه.
يتم زيادة وقت التحميل الأولي نسبيا. لأنه سيتم تحميل جميع موارد JS و CSS في المرة الأولى ، مما يجعل الصفحة التالية سلسة. لهذا ، يمكنك استخدام الحزمة في ASP.NET MVC لربط الملفات. للاستخدام التفصيلي للحزمة ، يرجى الرجوع إلى المقالات: http://www.vevb.com/article/84329.htm ، http://www.vevb.com/article/84329.htm و http://www.vevb.com/article/82174.htm.
الملاحة غير متوفر. إذا كان عليك التنقل ، فيجب عليك التقدم والتراجع بنفسك. لهذا ، يمكنك إدراك وظائف الأمام والخلفية بنفسك لتعويضها. في الواقع ، هذا ما تفعله صفحات الويب المحمولة الآن ، والآن لا يزالون بحاجة إلى التنقل أعلاه. يمكن القيام بذلك أيضًا لبعض أنظمة إدارة الواجهة الخلفية للمؤسسات.
ارتفاع تكاليف التطوير للمطورين. هذه ليست مشكلة. يحتاج المبرمجون إلى مواصلة تعلم الشحن. لحسن الحظ ، فإن بعض الأطر الأمامية سهلة الاستخدام للغاية.
3. استخدم ASP.NET MVC+WebAPI+Bootstrap+Knockoutjs لتنفيذ SPA
تم تقديم مزايا وعيوب SPA بالتفصيل في وقت سابق. بعد ذلك ، دعنا نستخدم ASP.NET MVC+WebAPI+BS+KO لتنفيذ برنامج صفحة واحدة ، وذلك لتجربة نعومة سبا ومقارنة تأثيرات الصفحات التي يصنعها ASP.NET MVC+Razor الأصلي.
1. استخدم VS2013 لإنشاء مشروع تطبيق ويب ASP.NET ، والتحقق من مكتبة MVC و WebAPI. انظر الشكل أدناه للحصول على التفاصيل:
2. إنشاء المستودعات المقابلة والنماذج. هنا نظام بسيط لإدارة المهام. النموذج المحدد ورمز التخزين هو كما يلي:
تنفيذ فئة كيان المهمة:
التعداد العام TaskState {Active = 1 ، Complete = 2} /// <summary> /// task ictity /// </summary> Task Class Public Class {public int id {get ؛ تعيين؛ } اسم السلسلة العامة {get ؛ تعيين؛ } وصف السلسلة العامة {get ؛ تعيين؛ } dateTime CreationTime {get ؛ تعيين؛ } DateTime FinishTime {get ؛ تعيين؛ } مالك السلسلة العامة {get ؛ تعيين؛ } حالة TaskState العامة {get ؛ تعيين؛ } المهمة العامة () {creationTime = dateTime.parse (dateTime.now.tolongDatestring ()) ؛ State = TaskState.Active ؛ }}تنفيذ فئة تخزين المهام:
/// <summary> /// هنا يستخدم المستودع بيانات عينة كإظهار. يجب تحميل المشروع الحقيقي ديناميكيًا من قاعدة البيانات /// </summary> TaskRepository {#region ثابتة ساكنة الثابتة البطيئة <TaskRepository> _taskRepository = New Lazy <TaskRepository> (() => جديد TaskRepository ()) ؛ Public Static TaskRepository Current {get {return _taskrepository.value ؛ }}} #endregion #region fields private readonly list <Task> _tasks = new list <Task> () {new Task {id = 1 ، name = "Create a Spa Program dateTime.parse (dateTime.now.addDays (1) .ToString (CultureInfo.InvariantCulture))} ، مهمة جديدة {id = 2 ، name = "Learning jrokenoutjs" ، description = "straintjs is عبارة dateTime.parse (dateTime.now.addDays (2) .ToString (CultureInfo.InvariantCulture))} ، مهمة جديدة {id = 3 ، name = "Learn AngularJs" ، description = "AngularJS هو إطار MVVM الذي يدمج mvvm و mvc مع واحد. dateTime.Parse (dateTime.now.addDays (3) .ToString (CultureInfo.InvariantCulture))} ، مهمة جديدة {id = 4 ، name = "تعلم ASP.NET MVC موقع" ، الوصف = "لمحة هو أداة اختبار الأداء تحت .NET ، والتي تدعم asp.net ، كما هو مميز ، رمز المشروع الأصلي ، ويمكنه إخراج وقت تنفيذ كل رابط لتنفيذ الكود "، owner =" tonny li "، FinishTime = dateTime.parse (dateTime.Now.Addays (4) .ToString (CultureInfo.invariantCulture))} ،} ؛ #endregion #region الأساليب العامة العامة ienumerable <Task> getAll () {return _tasks ؛ } المهمة العامة GET (int id) {return _tasks.find (p => p.id == id) ؛ } إضافة المهمة العامة (عنصر المهمة) {if (item == null) {رمي new argumentNullexception ("item") ؛ } item.id = _tasks.count + 1 ؛ _tasks.add (عنصر) ؛ عنصر الإرجاع ؛ } public void remove (int id) {_tasks.removeall (p => p.id == id) ؛ } تحديث Bool Public (عنصر المهمة) {if (item == null) {رمي new argumentNullexception ("item") ؛ } var taskItem = get (item.id) ؛ if (taskItem == null) {return false ؛ } _tasks.remove (TaskItem) ؛ _tasks.add (عنصر) ؛ العودة صحيح. } #endregion}3. أضف مكتبات Bootstrap و Knockoutjs عبر Nuget.
4. تنفيذ خدمات البيانات الخلفية. هنا يتم تنفيذ خدمة الواجهة الخلفية باستخدام ASP.NET WebAPI. رمز التنفيذ المحدد كما يلي:
/// <summary> /// Task WebAPI ، توفير خدمات البيانات /// </summary> مهام الفئة العامة: apicontroller {private readOnly TaskRepository _TaskRepository = taskRepository.current ؛ public ienumerable <Task> getAll () {return _taskRepository.getAll (). orderby (a => A.ID) ؛ } المهمة العامة GET (int id) {var item = _taskRepository.get (id) ؛ if (item == null) {رمي httpresponsexception (httpstatuscode.notfound) ؛ } عنصر الإرجاع ؛ } [Route ("API/CASKS/GETBYSTATE")] public ienumerable <Task> getByState (String State) {ienumerable <Task> Results = New List <Task> () ؛ Switch (state.toLower ()) {case ": case" all ": results = _taskRepository.getAll () ؛ استراحة؛ الحالة "نشطة": النتائج = _taskRepository.getAll (). حيث (t => t.state == taskState.Active) ؛ استراحة؛ الحالة "مكتملة": النتائج = _taskRepository.getAll (). حيث (t => t.state == taskState.completed) ؛ استراحة؛ } النتائج = results.orderby (t => t.id) ؛ نتائج العودة } [httppost] المهمة العامة إنشاء (عنصر مهمة) {return _taskRepository.add (item) ؛ } [httpput] public void pum (task item) {if (! _taskrepository.update (item)) {رمي httpresponsexception (httpstatuscode.notfound) ؛ }} public void delete (int id) {_taskrepository.remove (id) ؛ }}5. استخدم حزمة ASP.NET MVC لتعبئة الموارد. رمز تنفيذ BundleConfig المقابل هو كما يلي:
/// <summary> /// فقط أضف بعض ملفات CSS و JS المفقودة. نظرًا لأنه تمت إضافة بعض ملفات CSS و JS عند إنشاء القالب /// </summary> فئة عامة bundleconfig {// لمزيد من المعلومات حول الحزم ، تفضل بزيارة http://go.microsoft.com/fwlink/؟linkid=301862 REGUNTBERDLES (BUNDLECOLLECTION) {bundles. ScriptBundle ("~/bundles/jQuery"). bundles.add (scriptbundle جديد ("~/bundles/jqueryval"). // استخدم نسخة تطوير Modernizr لتتطور معها والتعلم منها. ثم ، عندما تكون جاهزًا للإنتاج ، استخدم أداة الإنشاء على http://modernizr.com لاختيار الاختبارات التي تحتاجها فقط. bundles.add (scriptbundle جديد ("~/bundles/modernizr"). bundles.add (scriptbundle جديد ("~/bundles/bootstrap"). bundles.add (new StyleBundle ("~/content/css"). bundles.add (scriptbundle جديد ("~/bundles/jrokenout"). bundles.add (scriptbundle جديد ("~/bundles/app"). }}6. لأننا بحاجة إلى جعل نوع التعداد يظهر كسلسلة على الصفحة. يتم تحويل التعداد إلى نوع رقمي عند التسلسل افتراضيًا. لذلك ، نحن بحاجة إلى إجراء التغييرات التالية على فئة WebApiconFig:
الفئة الثابتة العامة webapiconfig {سجل الفراغ العام الثابت (تكوين httpConfiguration) {// web API التكوين والخدمات // Web API Routing config.maphttpattributeroutes () ؛ config.routes.maphttproute (الاسم: "DefaultApi" ، RoutetEmplate: "API/{controller}/{id}" ، الافتراضات: new {id = routeparameter.optional}) ؛ // جعل التسلسل التسلسلي استخدام خاصية CASE CASE Serialization Config.Formatters.jsonformatter.Serializersettings.ContractResolver = camelcasepropertynamescontractrosolver () ؛ . }}ملاحظة: إذا لم يتم استخدام تسلسل الجمل الصغير أعلاه ، فيجب عليك أيضًا إجراء تعديلات عند ربط البيانات على الصفحة. على سبيل المثال ، عند سمة الاسم الربط ، استخدم Granshandization Name مباشرة. إذا كنت تستخدم طريقة الاسم ، فسيطلب من عدم وجود خطأ في التعريف في هذه السمة. نظرًا لأن JS يستخدم نمط الجمل الصغير لتسمية المتغيرات. لذلك ، يوصى باستخدام نمط الجمل الصغير للتسلسل. في هذا الوقت ، يمكنك فقط استخدام شكل "الاسم" لربط. هذا أكثر تمشيا مع مواصفات رمز JS.
7. تعديل ملف التصميم المقابل ومحتوى ملف الفهرس.
الكود المحدد لملف التصميم هو كما يلي:
<! doctype html> <html> <head> <meta charset = "utf-8"/> <meta name = "viewport" content = "width = width device ، inial-scale = 1.0"> <title> levershard spa application </title> @styles.render (~/contert/css ") @scripts <Body> <viv> <viv> <viv> <p> نظام إدارة المهام البسيط </p> </viv> <viv> <ul> <li> <a href = "/"> home </a> </li> </ul> </viv> </viv> </div> <div id = "main" التطبيق </p> </tourer> </viv> @scripts.render ("~/bundles/jQuery") @scripts.render ("~/bundles/bootstrap") @scripts.render ("~/bundles/bundles") @scripts.render ("~/bundles/jackout") @scripts.render ( </body> </html> رمز صفحة الفهرس كما يلي: @{viewbag.title = "index" ؛ التصميم = "~/views/shared/_layout.cshtml" ؛} <div id = "list" data-bind = "if: cancreate" <h2> assks </h2> <viv> <flable> <thead> <tr> <tr> الوقت </th> <th> Statu </h> <th> </h> </tr> </t> <tbody data-bind = "foreach: assks"> <tr> <td data-bind = "text: id" الوصف "> </td> <td data-bind =" text: owner "> </td> <td data-bind =" text: createTime "> </td> <td data-bind =" text: finishtime "> </td> <td data-bind =" text: stale "> href = "javaScript: void (0)"> قم بإزالة </a> </td> </tbody> </table> </viv> <viv> <a href = "javaScript: void (0)" data-bind = "click: function (data ، event) {all ')}}"> </a> <a href = "javaScript: void (0)" data-bind = "click: function (data ، event) {SettaskList ('active')}"> Active </a> | <a href = "javaScript: void (0)" data-bind = "click: function (data ، event) {settasklist ('exced')}"> excled </a> </viv> <viv> <a href = "javaScript: void (0)" data-bind = click: handlerecreaterupdate " مخفي "> <h2> أضف مهمة </h2> <br/> <viv> <viv> <label for =" taskName "> name*</billy> <viv> <input type =" text "data-bind =" value: name "id =" taskname "name =" taskname " data-bind = "value: description" rows = "3" id = "taskdesc" name = "taskdesc" placeholder = "description"> </textarea> </viv> </viv> <viv> <label for = "taskowner"> express*</label> <viv> <input id = "taskounder" <div> <label for = "taskfinish"> وقت الانتهاء المقدر*</label> <div> <input id = "taskfinish" data-bind = "value: finishtime name =" taskfinish "> </vistate> <div> <div> <sivel> active for =" taskowner " <Spoint> مكتمل </orpear> </sephar> </viv> </viv> <viv> <viv> <button butto data-bind = "انقر فوق: handlesaveclick"8. قم بإنشاء منطق البرنامج النصي المقابل. استخدم رمز JS لطلب البيانات وإنشاء كائنات ViewModel المقابلة للربط الأمامي. رمز تنفيذ JS المحدد كما يلي:
var taskListViewModel = {tasks: 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) ؛ ؛ }) ؛} الوظيفة setSaskList (state) {sendajaxRequest ("get" ، function (data) {taskListViewModel.tasks.removeall () ؛ for (var i = 0 ؛ i <data.length ؛ i ++) {taskListViewModel.tasks.push (data [i]) ؛ }) ؛} إزالة الوظيفة (العنصر) {sendajaxRequest ("delete" ، function () {getAllTasks () ؛} ، item.id) ؛} var task = new taskModel () ؛ function handlecreatorupDate (item) {task.fromjs (item) ؛ initDatePicker () ؛ TaskListViewModel.CancReate (false) ؛ $ ('#create'). css ('الرؤية' ، 'Visible') ؛} وظيفة wallebackclick () {taskListViewModel.cancreeate (true) ؛ $ ('#create'). css ('الرؤية' ، 'Hidden') ؛} function handlesaveclick (item) {if (item.id == undefined) {sendaaxRequest ("post" ، function (newItem) {// newitem is the combise. item.description ، finishtime: item.FinishTime ، المالك: item.Owner ، الحالة: item.state}) ؛ } else {sendajaxRequest ("put" ، function () {getAlltasks () ؛} ، null ، {id: item.id ، name: item.name ، الوصف: item.description ، finishtime: item.finishtime ، المالك: item.Owner ، الحالة: item.state}) ؛ } TaskListViewModel.CancReate (true) ؛ $ ('#create'). css ('الرؤية' ، 'Hidden') ؛} الوظيفة sendajaxRequest (httpmethod ، callback ، url ، reqdata) {$ .ajax ("/api/assks" + (url؟ " initDatePicker = function () {$ ('#create .DatePicker'). DatePicker ({autoclose: true}) ؛} ؛ $ ('. $ (this) .addClass ('active') ؛}) ؛ $ (وثيقة). ready (function () {getAlltasks () ؛ // استخدم knockoutjs لربط ko.applybindings (TaskListViewModel ، $ ('#list'). get (0)) ؛ ko.applybindings (task ، $ (في هذه المرحلة ، تم تطوير برنامجنا من صفحة واحدة. بعد ذلك ، دعنا نديرها لنرى تأثيرها.
من مخطط العرض التوضيحي للنتيجة أعلاه ، يمكننا أن نرى أنه بمجرد تحميل الصفحة ، يبدو أن جميع العمليات تعمل في صفحة واحدة ، ويبدو أن صفحة المتصفح تدور. بالمقارنة مع الصفحات التي تم تطويرها باستخدام ASP.NET MVC + Razor من قبل ، هل تشعر بنعومة سبا؟ في السابق ، تم تطوير الصفحة التي تم تطويرها باستخدام ASP.NET MVC + Razor ، فأنت بحاجة فقط إلى طلب صفحة واحدة ويمكنك أن تشعر بتحديث الصفحة بأكملها ، مما يجعل تجربة المستخدم سيئة للغاية.
4. مقارنة مع نموذج تطوير الحلاقة
أعتقد أن الجميع قد شاهدوا مزايا المنتجع الصحي من النتائج. بعد ذلك ، أعتقد أنه من الضروري مقارنتها بالطريقة التقليدية لتنفيذ صفحات الويب. هناك اختلافان رئيسيان من طريقة تطوير الحلاقة:
1. عند تقديم الصفحة ، تتم معالجة البيانات على جانب المتصفح. ليس على الخادم. تخصيص الضغط على جانب المتصفح لكل مستخدم ، وبالتالي تقليل الضغط على خادم الموقع. إذا كانت بناء جملة الحلاقة ، فيجب أن تكون عبارة ربط الصفحة الأمامية على النحو التالي:
model ienumerable <jackoutjsspa.models.task> foreach (var item في النموذج) {<tr> <td>@item.name </td> <td>@item.description </td> </td>}يتم تقديمها بواسطة محرك الحلاقة على جانب الخادم. هذا هو السبب أيضًا في أن الصفحات التي تم تطويرها باستخدام Razor ستشاهد الصفحات التي تدور حولها. لأنه في كل مرة تقوم فيها بتبديل صفحة ، تحتاج إلى طلب تقديم الخادم ، وبعد تقديم الخادم ، قم بإرجاع HTML إلى العميل للعرض.
2. البيانات المرتبطة ديناميكية. هذا يعني أن التغييرات في نموذج البيانات سوف تنعكس على الصفحة على الفور. ويعزى هذا التأثير إلى آلية الربط ثنائية الاتجاه التي تنفذها KnockOutJs.
باستخدام هذه الطريقة أمر بسيط أيضًا لتطوير البرنامج. تعد واجهة برمجة تطبيقات الويب مسؤولة فقط عن توفير البيانات ، كما أن الصفحة الأمامية تقلل أيضًا الكثير من عمليات DOM. لأن عمليات DOM معقدة ومعرضة للخطأ. هذا يعني أيضًا تقليل الحشرات الضمنية في البرنامج. علاوة على ذلك ، يمكن استخدام الخدمة الخلفية من قبل الهواتف المحمولة ومتصفحات الويب والمنصات المتعددة لتجنب التطوير المتكرر.
5. ملخص
في هذه المرحلة ، سيتم تقديم مقدمة من هذه المقالة. تقدم هذه المقالة بشكل أساسي استخدام KnockoutJs لإكمال برنامج SPA. في الواقع ، في العمل الفعلي ، يعتمد نموذج إنشاء برامج من صفحة واحدة على AngularJS. بعد ذلك ، هناك العديد من Knockoutjs ، ولكن Knockoutjs هو مجرد إطار MVVM ، ويجب استخدام آلية التوجيه الخاصة به مع بعض المكتبات الفصول الدراسية الأخرى ، مثل آلية التوجيه في ASP.NET MVC هنا ، يمكنك أيضًا استخدام إطار توجيه الواجهة الأمامية Director.JS. بالمقارنة مع KnockOutJs ، AngularJS هو إطار MVVM+MVC. لذلك في الموضوع التالي ، سنقدم كيفية استخدام AngularJS لإنشاء برنامج صفحة واحدة (SPA).
قم بتنزيل جميع رموز المصدر في هذه المقالة: SpawithKnockoutjs
إذا كنت لا تزال ترغب في الدراسة بعمق ، فيمكنك النقر هنا لدراسة وإرفاق 3 مواضيع مثيرة لك:
Bootstrap التعلم البرنامج التعليمي
Bootstrap البرنامج التعليمي العملي
تعليمي استخدام المكونات الإضافية Bootstrap
ما سبق هو كل شيء عن هذا المقال ، آمل أن يكون مفيدًا لتعلم الجميع.