1. Prefacio
En el tema anterior, introduje rápidamente los puntos de conocimiento relacionados con Knockoutjs y escribí algunos ejemplos simples. Espero que a través de estos ejemplos, pueda comenzar rápidamente con Knockoutjs. Para permitir que todos vean claramente la aplicación de KnockoutJs en proyectos reales, este tema presentará cómo usar WebAPI+Bootstrap+KnockoutJS+ASP.NET MVC para crear un programa web de una sola página. Este modelo también se utiliza en los proyectos reales de la mayoría de las empresas ahora.
2. SPA (Página única) Beneficios
Antes de introducir la implementación específica, creo que es necesario introducir SPA en detalle. SPA, la abreviatura de la aplicación web de una sola página, es una aplicación web que carga una sola página HTML y actualiza la página dinámicamente cuando el usuario interactúa con la aplicación. El navegador cargará el HTML, CSS y JavaScript requeridos al principio. Todas las operaciones se completan en esta página y están controladas por JavaScript.
Los beneficios de los programas de una sola página son:
Una mejor experiencia de usuario permite a los usuarios experimentar la velocidad y la suavidad de las aplicaciones nativas en la aplicación web.
Separe las preocupaciones delanteras y traseras, la parte delantera es responsable de la pantalla de la interfaz, y el back -end es responsable del almacenamiento de datos y la computación, cada uno realiza sus propias tareas y no mezclará la lógica de los extremos delanteros y traseros.
Para reducir la presión en el servidor, el servidor solo necesita generar datos, independientemente de la lógica de visualización y la lógica de la página, y aumentar el rendimiento del servidor. El front-end escrito en la sintaxis de Razor en MVC requiere que el servidor complete la síntesis de la página y luego la emita.
El mismo conjunto de programas de fondo se puede usar directamente en múltiples clientes, como interfaz web, teléfonos móviles, tabletas, etc. sin modificación.
Por supuesto, además de las ventajas enumeradas anteriormente, los programas de una sola página también tienen sus deficiencias:
No propicio para el SEO. Si este es un sistema de gestión, no lo afectará.
El tiempo de carga inicial aumenta relativamente. Porque todos los recursos JS y CSS se cargarán por primera vez, haciendo que la página posterior sea sin problemas. Para esto, puede usar Bundle en ASP.NET MVC para vincular archivos. Para un uso detallado del paquete, consulte los artículos: http://www.vevb.com/article/84329.htm, http://www.vevb.com/article/84329.htm y http://www.vevb.com/article/82174.htm.
La navegación no está disponible. Si tiene que navegar, debe avanzar y retirarlo usted mismo. Para esto, puede realizar las funciones de avance y espalda usted mismo para compensarlo. De hecho, esto es lo que hacen las páginas web móviles ahora, y ahora aún necesitan ser la navegación anterior. Esto también se puede hacer para algunos sistemas de gestión de backend empresarial.
Altos costos de desarrollo para los desarrolladores. Esto no es un problema. Los programadores deben seguir aprendiendo a cobrar. Afortunadamente, algunos marcos frontales son muy fáciles de usar.
3. Use ASP.NET MVC+WebAPI+Bootstrap+KnockoutJS para implementar SPA
Las ventajas y desventajas de SPA se introdujeron en detalle anteriormente. A continuación, usemos ASP.NET MVC+WebAPI+BS+KO para implementar un programa de una sola página, para experimentar la suavidad del SPA y comparar los efectos de las páginas hechas por la Razor ASP.NET MVC+original.
1. Use VS2013 para crear un proyecto de aplicación web ASP.NET, consulte la biblioteca MVC y WebAPI. Vea la siguiente figura para más detalles:
2. Cree almacenes y modelos correspondientes. Aquí hay un sistema de gestión de tareas simple. El modelo específico y el código de almacenamiento son los siguientes:
Implementación de la clase de entidad de tareas:
public enum taskState {activo = 1, completado = 2} //// <summary> /// Entidad de tareas ///// </summary> public class Task {public int id {get; colocar; } nombre de cadena pública {get; colocar; } Descripción de cadena pública {get; colocar; } public dateTime CreationTime {get; colocar; } public dateTime FinalTime {get; colocar; } propietario de cadena pública {get; colocar; } Public TaskState State {get; colocar; } task public () {CreationTime = DateTime.Parse (DateTime.Now.TolongDateString ()); State = taskState.active; }}Implementación de la clase de almacenamiento de tareas:
/// <summary> /// aquí el almacén utiliza datos de muestra como demostración. El proyecto real debe cargarse dinámicamente desde la base de datos /// </summary> public class TaskRepository {#region Static Falled private Static Lazy <taskRepository> _TaskRepository = new Lazy <taskRepository> (() => new TaskRepository ()); public static taskRepository Current {get {return _TaskRepository.Value; }} #EnDregion #region Fields Private Readonly List <Kark> _Tasks = New List <Kark> () {New Task {id = 1, name = "Crear un programa SPA", Descripción = "SpA (aplicación web de una sola página), la ventaja de SPA es una pequeña cantidad de altura de banda y una experiencia suave", "Aprendizaje de aprendizaje duro", Tiempo de finalización = Datetime.parse (datetime.now.adddays (1) .ToString (culturaInfo.invariantCulture))}, nueva tarea {id = 2, name = "Learning Knockoutjs", Descripción = "KnockoutJS es una biblioteca de clase MVVM que admite la unión de dos vías", propietario = "Tommy Li", Finishtime = Datetime.parse (datetime.now.adddays (2) .ToString (culturinfo.invariantCulture))}, nueva tarea {id = 3, name = "Learn AngularJS", Descripción = "AngularJS es un marco MVVVM que integra MVVM y MVC con uno.", Propietaria = "Li Zhi", Tiempo de finalización = Datetime.parse (dateTime.Now.adddays (3) .ToString (culturinfo.invariantCulture))}, nueva tarea {id = 4, name = "Learn asp.net MVC Sitio web", descripción = "Glimpse es una herramienta de prueba de rendimiento en .NET, que admite ASP.NET, ASP.NET MVC, EF, etc. La ventaja es que la ventaja es la ventaja de Modify. proyecto, y puede generar el tiempo de ejecución de cada enlace de la ejecución del código ", propietario =" tonny li ", finktime = dateTime.parse (datetime.now.adddays (4) .ToString (culturinfo.invariantCulture))},}; #EnDregion #region Métodos públicos public IEnumerable <ark> getAll () {return _Tasks; } Tarea pública get (int id) {return _tasks.find (p => p.id == id); } public tarea add (elemento de tarea) {if (item == NULL) {tire nuevo argumentoNulLException ("item"); } item.id = _tasks.count + 1; _tasks.Add (ítem); artículo de devolución; } public void remove (int id) {_tasks.removeall (p => p.id == id); } Update public bool (elemento de tarea) {if (item == null) {lanzar nueva argumentoNulLException ("item"); } var taskItem = get (item.id); if (taskItem == null) {return false; } _tasks.remove (taskItem); _tasks.Add (ítem); devolver verdadero; } #Endregion}3. Agregue las bibliotecas Bootstrap y Knockoutjs a través de Nuget.
4. Implementar servicios de datos de fondo. Aquí el servicio de backend se implementa utilizando ASP.NET WebAPI. El código de implementación específico es el siguiente:
/// <summary> /// tareas webapi, proporcionar servicios de datos /// </summary> public class TasksController: apicOntroller {private readonly taskRepository _TaskRepository = taskRepository.Current; public IEnumerable <ark> getAll () {return _TaskRepository.getall (). Orderby (a => a.id); } Public Task get (int id) {var item = _TaskRepository.get (id); if (item == null) {tire httpresponsexception (httpstatuscode.notfound); } elemento de retorno; } [Route ("API/Tasks/GetByState")] public IEnumerable <ark> getByState (string state) {ienumerable <kark> resultados = new List <Kark> (); switch (state.tolower ()) {case "": case "all": resultados = _taskRepository.getAll (); romper; caso "activo": resultados = _TaskRepository.getAll (). Where (t => t.state == tareasstate.active); romper; Caso "completado": resultados = _TaskRepository.getAll (). Donde (t => t.state == tareaState.completed); romper; } resultados = results.orderBy (t => t.id); resultados de devolución; } [Httppost] Tarea pública create (elemento de tarea) {return _TaskRepository.Add (item); } [Httpput] public void put (item de tareas) {if (! _TaskRepository.Update (item)) {tire httpResponseException (httpstatuscode.notfound); }} public void Delete (int id) {_TaskRepository.Remove (id); }}5. Use el paquete ASP.NET MVC para empaquetar los recursos. El código de implementación de BundLeconFig correspondiente es el siguiente:
/// <summary> /// Simplemente agregue algunos archivos CSS y JS faltantes. Because some CSS and JS files have been added when creating the template /// </summary> public class BundleConfig { // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new ScriptBundle ("~/bundles/jQuery"). Include ("~/scripts/jQuery- {versión} .js")); Bundles.Add (nuevo scriptbundle ("~/bundles/jqueryval"). include ("~/scripts/jQuery.validate*")); // Use la versión de desarrollo de Modernizr para desarrollar y aprender. Luego, cuando esté // listo para la producción, use la herramienta de compilación en http://modernizr.com para elegir solo las pruebas que necesita. bundles.add (nuevo scriptbundle ("~/bundles/modernizr"). incluir ("~/scripts/modernizr-*")); bundles.add (nuevo scriptbundle ("~/bundles/bootstrap"). include ("~/scripts/bootstrap.js", "~/scripts/bootstrap-datepicker.min.js")); bundles.add (nuevo styleBundle ("~/content/css"). include ("~/content/bootstrap.css", "~/content/bootstrap-datepicker3.min.css", "~/content/sitio.css")); Bundles.Add (nuevo scriptbundle ("~/bundles/knockout"). include ("~/scripts/knockout- {versión} .js", "~/scripts/knockout.validation.min.js", "~/scripts/knockout.mapping-latest.js"); bundles.add (nuevo scriptbundle ("~/bundles/app"). include ("~/scripts/app/app.js")); }}6. Porque necesitamos hacer que el tipo enum aparezca como una cadena en la página. La enumeración se convierte en un tipo numérico cuando se está serializado de forma predeterminada. Por lo tanto, necesitamos realizar los siguientes cambios en la clase Webapiconfig:
Public Static Class Webapiconfig {registro de voides estáticos públicos (httpconfiguration config) {// Configuración y servicios de API web // enrutamiento de API web Config.MaphttPatTributeroutes (); config.routes.maphttProute (nombre: "defaultApi", rutetEmplate: "API/{Controller}/{id}", defaults: new {id = Routeparameter.optional}); // Hacer que la serialización use la propiedad de serialización de estilo Camel Config.Formatters.jsonFormatter.SerializerSettings.Contractresolver = new CamelCasProperTynamesContractresolver (); // Serialice la cadena config.Formatters.jsonFormatter.SerializerSettings.Converters.add (new StringEnumConverter ()); }}Nota: Si la serialización en minúsculas de camello no se usa anteriormente, también debe hacer ajustes al vincular los datos en la página. Por ejemplo, cuando el atributo de nombre vinculante, use la capitalización de nombre directamente. Si usa el método de nombre, solicitará que no hay un error de definición en este atributo. Dado que JS usa estilo minúsculas de camello para nombrar variables. Por lo tanto, se recomienda que use estilo minúsculas de camello para la serialización. En este momento, solo puede usar la forma de "nombre" para vincular. Esto está más en línea con las especificaciones del código JS.
7. Modifique el archivo de diseño correspondiente y el contenido del archivo de índice.
El código específico del archivo de diseño es el siguiente:
<! Doctype html> <html> <fead> <meta charset = "utf-8"/> <meta name = "viewport" content = "width = dispositivo-width, inicial-escale = 1.0"> <title> Aplicación de SPA de aprendizaje </title> @styles.render ("~/contenido/css") @scripts.render ("~/bundles/bunds/lizrr") <Body> <VIV> <VIV> <VIV> <P> Sistema simple de gestión de tareas </p> </div> <div> <ul> <li> <a href = "/"> HOME </a> </li> </ul> </div> </div> </div> <ivi = "Main"> @renderbody () <hr/> <foTer> <P> © @ @ @ickeetime.now.now.neo Aplicación </p> </tafeer> </div> @scripts.render ("~/bundles/jQuery") @scripts.render ("~/bundles/bootstrap") @scripts.render ("~/bundles/knockout") @scripts.render ("~/bundles/knockout") @scripts.render ("~/bundles/app") </body </htmll. Sigue: @{Viewbag.title = "index"; Layout = "~/Views/Shared/_Layout.cshtml";} <divi id = "list" data-bind = "if: cancreate"> <h2> tareas </h2> <div> <table> <thead> <tr> <th> number </th> <th> name </th> <th> DETRESION Tiempo </th> <th> statu </th> <th> </th> </tr> </t> <tbody data-bind = "foreach: tareas"> <tr> <td data-bind = "text: id"> </td> <td> <a data-bind = "text: nombre, haga clic: handleCreaReorUpdate"> </a> </td> <td data bind: "text:" TEXT: HANDLECREATEUPDATE "> </A> </aTD> <TD <TD data Bind:" Descripción "> </td> <td data-bind =" text: propietario "> </td> <td data-bind =" text: creationtime "> </td> <td data-bind =" text: finktime "> </td> <td data-bind =" text: state "> </td> <td> <a data-bind =" haga clic: eliminar "" href = "javascript: void (0)"> eliminar </a> </td> </tbody> </bable> </div> <div> <a href = "javascript: void (0)" data-spind = "Click: function (data, event) {setTaskList ('todos')}"> all </a> | | <a href = "javascript: void (0)" data-bind = "haga clic: función (data, event) {setTaskList ('activo')}"> Active </a> | <a href = "javascript: void (0)" data-bind = "click: function (data, event) {settaskList ('completado')}"> completado </a> </div> <div> <a href = "javascript: void (0)" bind de datos = "click: manualeReApDate"> Agregar Task </a> </Div> <DIV> <Div Id = " Hidden "> <h2> Agregar tarea </h2> <br/> <div> <div> <etiqueta for =" taskname "> name*</label> <div> <input type =" text "data-bind =" value: name "id =" taskName "name =" taskName "PlaceHolder =" name "> </div> </div> <iv> <lelebel para =" TaskDesc "> Descripción </Etiquet data-bind = "Value: Descripción" Rows = "3" id = "TaskDesc" name = "TaskDesc" PlaceHolder = "Descripción"> </extArea> </div> </div> <div> <lel etiqueta for = "taskOwner"> Persona en el cargo*<//div> <divi ID = "TaskOwner" Nombre = "TaskOwner" Bind = "Value: Propietario" PRODECTOR "LUGAR" PLOGAR "LATATER =" LATATER "ELATERIOR" ELATERIOR "HISTA" ALTOTERE. <Viv> <etiqueta for = "taskFinish"> Tiempo de finalización estimado*</selabel> <div> <input id = "taskFinish" data-bind = "valor: finktime" name = "taskfinish"> </div> </div> <viv> <label: "TaskOwner"> status*</etiqueta> <div> <diMid = "Taskstate" Data-bind = "Value: State: State"> <Option> Option> status*</etiqueta> <div> <div Id = "Taskstate" Data-bind = "Value: State: State" <Option> Opción> opción> option> option> <ppection> <Div> <Sect Id = "Taskstate" Data-bind = "Value: State: State <Option" <Opción> Completado </opción> </select> </div> </div> <div> <div> <button data-bind = "haga clic: HandlesaveClick"> Guardar </botón> <botón data-bind = "Haga clic en: HandlebackClick"> Back </boton> </div> </div> </div> </div> </div> </div> </div>8. Cree la lógica de secuencia de comandos frontal correspondiente. Use el código JS para solicitar los datos y crear objetos ViewModel correspondientes para el enlace frontal. El código de implementación JS específico es el siguiente:
var tareas listViewModel = {tareas: ko.observableArray (), cantCreate: ko.observable (true)}; var tareasmodel = 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.wner (data.wner); this.state (data.state); };};function getAllTasks() { sendAjaxRequest("GET", function (data) { taskListViewModel.tasks.removeAll(); for (var i = 0; i < data.length; i++) { taskListViewModel.tasks.push(data[i]); } }, 'GetByState', { 'state': 'all' });} función setkaskList (state) {sendAraJaxRequest ("get", function (data) {taskListViewModel.tasks.removeall (); for (var i = 0; i <data.length; i ++) {tareaslistviewModel.Tasks.push (data [i]);}}, 'getbystate', state ', {»{» {»{» {', {', {»{» {', {», {» {', {», {', {», {»{', {», {», {», {»{», {», {», {»{', {», {», {», {», {», {», {» {', {{ });} function remove (item) {sendAraJaxRequest ("delete", function () {getAllTasks ();}, item.id);} var tarea = new TaskModel (); function handLecreateRupdate (item) {task.fromjs (item); initDatePicker (); TaskListViewModel.Cancreate (falso); $ ('#create'). CSS ('Visibilidad', 'Visible');} function handlebackClick () {taskListViewModel.Cancreate (true); $ ('#create'). css ('visibilidad', 'hidden');} function handlesaveClick (item) {if (item.id == Undefined) {sendajaxRequest ("post", function (newItem) {// NewItem es el objeto devuelto. TaskListViewModel.Tasks.push (newitem);}, name, name, istem.em.tname, Item. Descripción: Elemento. Descripción, finalización: elemento. Finishtime, propietario: item.wner, estado: item.state}); } else {sendAraJaxRequest ("put", function () {getAllTasks ();}, null, {id: item.id, name: item.name, descripción: item.description, finktime: item.finishtime, propietario: item.owner, state: item.state}); } TaskListViewModel.Cancreate (true); $ ('#create'). CSS ('Visibility', 'Hidden');} Función SendAJAxRequest (httpmethod, callback, url, reqdata) {$ .AJAX ("/api/tareas" + (url? "/" + url: ""), {type: httpmethod, exitoso: callback, data: data: reqdates}) initDatePicker = function () {$ ('#create .DatePicker'). DatePicker ({AutoClose: true});}; $ ('. Nav'). on ('click', 'li', function () {$ ('. Nav Li.Active'). RemoveClass ('activo'); $ (this) .AddClass ('activo');}); $ (documento) .Ready (function () {getAllTasks (); // use knockoutJs para atar ko.applybindings (tareaListViewModel, $ ('#list'). get (0)); ko.appybybindings (tarea, $ ('#create'). get (0));});});});});En este punto, se ha desarrollado nuestro programa de una sola página. A continuación, lo ejecutemos para ver su efecto.
Desde el diagrama de demostración de resultados en ejecución anterior, podemos ver que una vez que se carga la página, todas las operaciones parecen funcionar en una página, y parece que la página del navegador está dando vueltas. En comparación con las páginas desarrolladas usando ASP.NET MVC + Razor antes, ¿sientes la suavidad del spa? Anteriormente, la página se desarrolló usando ASP.NET MVC + Razor, solo necesita solicitar una página y puede sentir la actualización de toda la página, lo que hace que la experiencia del usuario sea muy mala.
4. Comparación con el modelo de desarrollo de afeitar
Creo que todos han visto las ventajas del spa de los resultados. A continuación, creo que es necesario compararlo con la forma tradicional de implementar páginas web. Hay dos diferencias principales con respecto al método de desarrollo de afeitar:
1. Cuando se representa la página, los datos se procesan en el lado del navegador. No en el servidor. Asignar presión de representación al lado del navegador de cada usuario, reduciendo así la presión en el servidor del sitio web. Si fuera sintaxis de afeitar, la declaración de enlace de la página frontal debería ser la siguiente:
@Model IEnumerable <knockoutjsspa.models.task> @foreach (elemento var en modelo) {<tr> <td>@item.name </td> <td>@item.description </td> </tr>}El motor de afeitar en el lado del servidor representa estos en el lado del servidor. Esta es también la razón por la cual las páginas desarrolladas usando Razor verán las páginas circular. Debido a que cada vez que cambia una página, debe solicitar al servidor que renderice, y después de que el servidor se repite, devuelva el HTML al cliente para su visualización.
2. Los datos unidos son dinámicos. Esto significa que los cambios en el modelo de datos se reflejarán en la página de inmediato. Este efecto se atribuye al mecanismo de unión bidireccional implementado por KnockoutJS.
El uso de este método también es simple para el desarrollo del programa. La API web solo es responsable de proporcionar datos, y la página de front-end también reduce muchas operaciones DOM. Porque las operaciones DOM son complicadas y propensas a errores. Esto también significa reducir los errores implícitos en el programa. Además, los teléfonos móviles, los navegadores web y múltiples plataformas pueden utilizar un servicio de fondo para evitar el desarrollo repetido.
5. Resumen
En este punto, se introducirá la introducción de este artículo. Este artículo presenta principalmente el uso de knockoutjs para completar un programa de spa. De hecho, en el trabajo real, el modelo de creación de programas de una sola página se basa más en AngularJS. Luego hay muchos knockoutjs, pero KnockoutJS es solo un marco MVVM, y su mecanismo de enrutamiento debe usarse con algunas otras bibliotecas de clase, como el mecanismo de enrutamiento en ASP.NET MVC aquí, también puede usar el marco de enrutamiento delantero Director.js. En comparación con KnockoutJS, AngularJS es un marco MVVM+MVC. Entonces, en el siguiente tema, presentaremos cómo usar AngularJS para crear un programa de una sola página (SPA).
Descargue todos los códigos de origen en este artículo: Spawithknockoutjs
Si aún desea estudiar en profundidad, puede hacer clic aquí para estudiar y adjuntar 3 temas emocionantes a usted:
Tutorial de aprendizaje de bootstrap
Tutorial práctico de bootstrap
Tutorial de uso de complemento de bootstrap
Lo anterior se trata de este artículo, espero que sea útil para el aprendizaje de todos.