1. Préface
Dans le sujet précédent, j'ai rapidement introduit les points de connaissances liés à KnockoutJ et j'ai écrit quelques exemples simples. J'espère que grâce à ces exemples, vous pouvez rapidement commencer avec KnockoutJS. Afin de permettre à tout le monde de voir clairement l'application de knockoutjs dans les projets réels, ce sujet présentera comment utiliser WebAPI + bootstrap + knockoutjs + asp.net MVC pour créer un programme Web à une seule page. Ce modèle est également utilisé dans les projets réels de la plupart des entreprises.
2. Avantages Spa (Page unique)
Avant d'introduire la mise en œuvre spécifique, je pense qu'il est nécessaire d'introduire en détail SPA. Spa, l'abréviation de l'application Web d'une seule page, est une application Web qui charge une seule page HTML et met à jour la page dynamiquement lorsque l'utilisateur interagit avec l'application. Le navigateur chargera les HTML, CSS et JavaScript requis au début. Toutes les opérations sont terminées sur cette page et sont contrôlées par JavaScript.
Les avantages des programmes d'une seule page sont:
Une meilleure expérience utilisateur permet aux utilisateurs de vivre la vitesse et la douceur des applications natives dans l'application Web.
Séparez les préoccupations avant et arrière, le frontal est responsable de l'affichage de l'interface et le back-end est responsable du stockage et de l'informatique des données, chacun effectue ses propres tâches et ne mélangera pas la logique des extrémités avant et arrière ensemble.
Pour réduire la pression sur le serveur, le serveur n'a besoin que de générer des données, quelle que soit la logique d'affichage et la logique de page, et augmenter le débit du serveur. La syntaxe frontale écrite en rasoir dans MVC nécessite que le serveur termine la synthèse de la page puis la publie.
Le même ensemble de programmes back-end peut être utilisé directement sur plusieurs clients tels que l'interface Web, les téléphones mobiles, les tablettes, etc. sans modification.
Bien sûr, en plus des avantages énumérés ci-dessus, les programmes à une page ont également leurs lacunes:
Pas propice au référencement. S'il s'agit d'un système de gestion, cela ne l'affectera pas.
Le temps de chargement initial est relativement augmenté. Parce que toutes les ressources JS et CSS seront chargées à la première fois, rendant la page suivante lisse. Pour cela, vous pouvez utiliser Bundle dans ASP.NET MVC pour lier des fichiers. Pour une utilisation détaillée de Bundle, veuillez vous référer aux articles: http://www.vevb.com/article/84329.htm, http://www.vevb.com/article/84329.htm et http://www.vevb.com/article/82174.htm.
La navigation n'est pas disponible. Si vous devez naviguer, vous devez avancer et vous retirer par vous-même. Pour cela, vous pouvez réaliser les fonctions avant et arrière par vous-même pour le compenser. En fait, c'est ce que font les pages Web mobiles maintenant, et maintenant ils doivent encore être navigués ci-dessus. Cela peut également être fait pour certains systèmes de gestion des backend d'entreprise.
Coûts de développement élevés pour les développeurs. Ce n'est pas un problème. Les programmeurs doivent continuer à apprendre à charger. Heureusement, certains cadres frontaux sont très faciles à utiliser.
3. Utilisez ASP.NET MVC + WebAPI + Bootstrap + Knockoutjs pour implémenter Spa
Les avantages et les inconvénients du SPA ont été introduits en détail plus tôt. Ensuite, utilisons ASP.NET MVC + WebAPI + BS + KO pour implémenter un programme d'une seule page, afin de vivre la douceur du SPA et de comparer les effets des pages fabriquées par le rasoir ASP.NET MVC + d'origine.
1. Utilisez VS2013 pour créer un projet d'application Web ASP.NET, consultez la bibliothèque MVC et WebAPI. Voir la figure ci-dessous pour plus de détails:
2. Créez des entrepôts et des modèles correspondants. Voici un système de gestion des tâches simple. Le modèle spécifique et le code d'entreposage sont les suivants:
Implémentation de la classe d'entité de tâche:
Public Enum Taskstate {active = 1, terminé = 2} /// <summary> /// tâche entité //// </ résumé> tâche de classe publique {public int id {get; ensemble; } Nom de chaîne publique {get; ensemble; } public String Description {get; ensemble; } public DateTime CreationTime {get; ensemble; } public DateTime Finishtime {get; ensemble; } propriétaire de la chaîne publique {get; ensemble; } public Taskstate State {get; ensemble; } public task () {CreationTime = DateTime.Parse (DateTime.Now.TolongDateString ()); State = taskstate.active; }}Implémentation de la classe d'entreposage des tâches:
/// <summary> /// Ici, l'entrepôt utilise des données d'échantillons comme démonstration. Le projet réel doit être chargé dynamiquement à partir de la base de données /// </summary> classe publique TaskRepository {#Region STATIC STATIQUE PRIVATE STATIQUE LAZY <TaskRepository> _TaskRepository = new Lazy <TaskRepository> (() => new 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", Description = "Spa (application Web de page unique), L'avantage de Spa est une petite quantité de bande passante et une expérience fluide", propriétaire = "apprentissage dur", finitiontime = DateTime.Parse (DateTime.Now.Adddays (1) .ToString (CultureInfo.InvariantCulture))}, New Task {id = 2, name = "Learning KnockoutJs", Description = "KnockoutJs est une bibliothèque de classe MVVM = qui prend en charge la binding à deux voies", propriétaire = "tommy li", finaltimetime = DateTime.Parse (DateTime.Now.Adddays (2) .ToString (CultureInfo.InvariantCulture))}, New Task {id = 3, name = "Learn AngularJS", Description = "AngularJS est un cadre MVVM qui intègre MVVM et MVC avec un.", Propriétaire = "li zhi", finirtime = DateTime.Parse (DateTime.Now.Adddays (3) .ToString (CultureInfo.InvariantCulture))}, New Task {id = 4, Name = "Learn ASP.NET MVC SITE WEB", Description = "Glimpse est un outil de test de performance sous .net, qui prend en charge ASP.NET, ASP.NET MVC, EF, etc. Projet, et peut sortir le temps d'exécution de chaque lien de l'exécution de code ", propriétaire =" Tonny Li ", FinishTime = DateTime.Parse (DateTime.Now.Adddays (4) .ToString (CultureInfo.invariantCulture))},}; #EndRegion #Region Méthodes publiques publiques Ienumerable <Task> getall () {return _tasks; } tâche publique get (int id) {return _tasks.find (p => p.id == id); } tâche publique add (élément de tâche) {if (item == null) {throw new argumentNullexception ("item"); } item.id = _tasks.Count + 1; _tasks.add (item); return item; } public void dissover (int id) {_tasks.removeall (p => p.id == id); } public bool update (élément de tâche) {if (item == null) {throw new argumentNullexception ("item"); } var taskItem = get (item.id); if (taskItem == null) {return false; } _tasks.remove (taskItem); _tasks.add (item); Retour Vrai; } #endregion}3. Ajoutez des bibliothèques bootstrap et knockoutjs via Nuget.
4. Implémentez les services de données back-end. Ici, le service backend est implémenté à l'aide d'ASP.NET WebAPI. Le code d'implémentation spécifique est le suivant:
/// <summary> /// task webAPI, fournit des services de données /// </summary> tasksController de classe publique: APIController {private readonly taskRepository _TaskRepository = taskRepository.current; public ienumerable <task> getall () {return _taskRepository.getall (). OrderBy (a => a.id); } tâche publique get (int id) {var item = _taskRepository.get (id); if (item == null) {throw new httpResponseException (httpstaturuscode.notfound); } return item; } [Route ("API / TASKS / GetByState")] public ienumerable <Task> getByState (String State) {iEnumerable <Task> Results = new List <Task> (); switch (state.tolower ()) {case "": case "all": résultats = _taskRepository.getall (); casser; cas "actif": résultats = _taskRepository.getall (). Where (t => t.State == TaskState.active); casser; cas "terminé": résultats = _taskRepository.getall (). Where (t => t.state == taskstate.completed); casser; } résultats = results.orderBy (t => t.id); Résultats de retour; } [Httppost] tâche publique create (élément de tâche) {return _taskRepository.add (item); } [Httpput] public void put (élément de tâche) {if (! _TaskRepository.update (item)) {lancez new httpResponseException (httpStaturScode.NotFound); }} public void delete (int id) {_taskRepository.Remove (id); }}5. Utilisez ASP.NET MVC Bundle pour emballer les ressources. Le code d'implémentation BundleConfig correspondant est le suivant:
/// <summary> /// Il suffit d'ajouter des fichiers CSS et JS manquants. Parce que certains fichiers CSS et JS ont été ajoutés lors de la création du modèle /// </summary> classe publique BundleConfig {// Pour plus d'informations sur le regroupement, visitez http://go.microsoft.com/fwlink/?linkid=301862 public static regrinsbundles (bundlecollection bundles) {bundles.add (newdles (news.adddles (newdles. ScriptBundle ("~ / bundles / jQuery"). Include ("~ / scripts / jQuery- {version} .js")); bundles.add (new ScriptBundle ("~ / bundles / jQueryVal"). include ("~ / scripts / jQuery.validate *")); // Utilisez la version de développement de Modernizr pour se développer avec et apprendre. Ensuite, lorsque vous êtes // prêt pour la production, utilisez l'outil de construction sur http://modernizr.com pour choisir uniquement les tests dont vous avez besoin. bundles.add (new scriptBundle ("~ / bundles / modernizr"). include ("~ / scripts / modernizr- *")); bundles.add (new ScriptBundle ("~ / bundles / bootstrap"). include ("~ / scripts / bootstrap.js", "~ / scripts / bootstrap-datepicker.min.js")); bundles.add (new StyleBundle ("~ / content / css"). include ("~ / contenu / bootstrap.css", "~ / contenu / bootstrap-datepicker3.min.css", "~ / contenu / site.css")); bundles.add (new scriptBundle ("~ / bundles / knockout"). include ("~ / scripts / knockout- {version} .js", "~ / scripts / knockout.validation.min.js", "~ / scripts / knockout.mapping-latest.js")); bundles.add (new scriptBundle ("~ / bundles / app"). include ("~ / scripts / app / app.js")); }}6. Parce que nous devons faire apparaître le type d'énumération comme une chaîne sur la page. L'énumération est convertie en un type numérique lorsqu'elle est sérialisée par défaut. Par conséquent, nous devons apporter les modifications suivantes à la classe WebapiConFig:
Classe statique publique WebapiConfig {public static void registre (httpconfiguration config) {// web api configuration and Services // web api routing config.MaphTTPattTributeroutes (); config.routes.maphttproute (name: "defaultapi", routeTemplate: "api / {contrôleur} / {id}", par défaut: new {id = rateparameter.optional}); // Faire une sérialisation Utiliser la propriété de sérialisation du style de cas de camel config.formatters.jsonformatter.serializersettings.contractreSolver = new CamelcaspropertyNamesContractResolver (); // sérialiser la chaîne config.formatters.jsonformatter.serializeSettings.converters.add (new StringNenumConverter ()); }}Remarque: Si la sérialisation minuscule de Camel n'est pas utilisée ci-dessus, vous devez également effectuer des ajustements lors de la liaison des données sur la page. Par exemple, lors de l'attribut de nom de liaison, utilisez directement la capitalisation du nom. Si vous utilisez la méthode du nom, il invite à aucune erreur de définition dans cet attribut. Puisque JS utilise le style minuscule de camel pour nommer des variables. Par conséquent, il est recommandé d'utiliser le style minuscule de chameau pour la sérialisation. À l'heure actuelle, vous ne pouvez utiliser que la forme de "nom" pour se lier. Cela est plus conforme aux spécifications du code JS.
7. Modifiez le contenu de fichier de mise en page et d'index correspondant.
Le code spécifique du fichier de mise en page est le suivant:
<! Doctype html> <html> <éadf> <meta charset = "utf-8" /> <meta name = "Viewport" contenu = "width = device-width, initial-scale = 1.0"> <Title> Learninghard Spa Application </ title> @ styles.render ("~ / CONT / CSS") @ Scripts.Render ("" ~ / Bundles / Modernizr ") </ CSS") <body> <div> <div> <div> <p>Simple Task Management System</p> </div> <div> <ul> <li><a href="/">Home</a></li> </ul> </div> </div> </div> <div id="main"> @RenderBody() <hr /> <footer> <p>© @DateTime.Now.Year - Learninghard SPA Application </p> </footer> </ div> @ scripts.render ("~ / bundles / jQuery") @ scripts.render ("~ / bundles / bootstrap") @ scripts.render ("~ / bundles / knockout") @ scripts.render ("~ / bundles / knockout") @ scripts.Render ("~ / bundles / app") </ body> </ html> Le code de page d'index est le suivant: @ {Viewbag.title = "index"; Layout = "~ / vues / partagé / _layout.cshtml";} <div id = "list" data-bind = "if: cancreate"> <h2> numéro </h2> <div> <s table> <th> <th> <th> les personnes en charge </th> Time </th> <th> statu </ th> <th> </ th> </tr> </t> <tbody data-bind = "foreach: tasks"> <tr> <td data-bind = "text: id"> </ td> <td> <a data-bind = "text: name, click: handLereatEOrUpdate"> </a> </td> <td data-bind = ": Description "> </ td> <td data-bind =" text: propriétaire "> </td> <td data-bind =" text: Creationtime "> </td> <td data-bind =" text: finationtime "> </td> <td data-bind =" texte: state "> </td> <td> <a data-bind =" click: supprimer " href = "JavaScript: void (0)"> supprimer </a> </td> </tbody> </ table> </div> <div> <a href = "javascript: void (0)" data-bind = "click: function (data, event) {settaskList ('all')}"> all </a> | <a href = "javascript: void (0)" data-bind = "click: function (data, event) {setTaskList ('active')}"> actif </a> | <a href = "javascript: void (0)" data-bind = "click: function (data, event) {setTaskList ('terminé')}"> terminé </a> </div> <v> <a href = "javascrip 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 = "Value: Description" Rows = "3" id = "TaskDesc" name = "TaskDesc" PlaceHolder = "Description"> </ TextArea> </ Div> </ Div> <div> <étiquette pour = "TaskOwner"> Personne en charge * </ Label <div> <label for="taskFinish">Estimated completion time*</label> <div> <input id="taskFinish" data-bind="value: finishTime" name="taskFinish"> </div> </div> <div> <label for="taskOwner">Status*</label> <div> <select id="taskState" data-bind="value: state"> <option>Active</option> <option> terminé </ Option> </ SELECT> </ div> </ div> <div> <div> <Button Data-Bind = "Click: handlesaveclick"> Enregistrer </ Button> <Button Data-Bind = "Click: HandleBackClick"> Back </ Button> </div> </div> </div> </div>8. Créez la logique de script frontal correspondante. Utilisez le code JS pour demander des données et créer des objets ViewModel correspondants pour la liaison frontale. Le code d'implémentation JS spécifique est le suivant:
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 = fonction (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); };}; fonction getAllTasks () {SendaJaxRequest ("Get", fonction (data) {taskListViewModel.tasks.removeall (); for (var i = 0; i <data.length; i ++) {taskListViewModel.tasks.push (data [i]);}}, 'GetbyState', {state '', ':' });} fonction setTaskList (état) {sendajaxRequest ("get", function (data) {taskListViewModel.tasks.removeall (); for (var i = 0; i <data.length; i ++) {taskListViewModel.tasks.push (data [i]);}}, 'GetByState', {State ': State}); retire (item) {sendajaxRequest ("Delete", function () {getAllTasks ();}, item.id);} var task = new TaskModel (); fonction handlecreateorupDate (item) {task.fromjs (item); initDatePicker (); TaskListViewModel.Cancreate (false); $ ('# Create'). CSS ('Visibility', 'Visible');} Fonction HandleBackClick () {TaskListViewModel.Cancreate (true); $ ('# Create'). CSS ('Visibility', 'Hidden');} fonction hathelesAVeclick (item) {if (item.id == Undefined) {SendaJaxRequest ("Post", fonction (newitem) {// newitem est l'objet retourné. item.description, finition: item.finishtime, propriétaire: item.owner, état: item.state}); } else {sendajaxRequest ("put", function () {getAllTasks ();}, null, {id: item.id, name: item.name, description: item.description, finition: item.finishtime, propriétaire: item.owner, état: item.state}); } TaskListViewModel.Cancreate (true); $ ('# Create'). CSS ('Visibility', 'Hidden');} fonction sendajaxRequest (httpMethod, rappel, url, reqdata) {$ .ajax ("/ api / task initDatePicker = function () {$ ('# Create .DatePicker'). DatePicker ({autoclose: true});}; $ ('. Nav'). On ('click', 'li', function () {$ ('. Nav li.active'). reposoveclass ('active'); $ (this) .AddClass ('active');}); $ (Document). {getAllTasks ();À ce stade, notre programme à une seule page a été développé. Ensuite, exécutons-le pour voir son effet.
D'après le diagramme de démonstration de résultats en cours d'exécution ci-dessus, nous pouvons voir qu'une fois la page chargée, toutes les opérations semblent fonctionner sur une page, et il a l'impression que la page du navigateur tourne. Par rapport aux pages développées à l'aide du rasoir ASP.NET MVC + auparavant, ressentez-vous la douceur du spa? Auparavant, la page développée à l'aide du rasoir ASP.NET MVC +, il vous suffit de demander une page et vous pouvez sentir le rafraîchissement de la page entière, ce qui rend l'expérience utilisateur très mauvaise.
4. Comparaison avec le modèle de développement du rasoir
Je crois que tout le monde a vu les avantages du SPA des résultats. Ensuite, je pense qu'il est nécessaire de le comparer avec la façon traditionnelle d'implémenter les pages Web. Il existe deux principales différences par rapport à la méthode de développement du rasoir:
1. Lorsque la page est rendue, les données sont traitées du côté du navigateur. Pas sur le serveur. Allouer la pression de rendu au côté navigateur de chaque utilisateur, réduisant ainsi la pression sur le serveur de sites Web. S'il s'agissait de syntaxe de rasoir, l'instruction de liaison de la page frontale doit être la suivante:
@Model ienumerable <KnockoutJSspa.Models.task> @Foreach (var item dans le modèle) {<tr> <td> @ item.name </td> <td> @ item.description </td> </tr>}Ceux-ci sont rendus par le moteur de rasoir du côté du serveur. C'est aussi la raison pour laquelle les pages développées à l'aide de rasoir verront les pages circuler. Parce que chaque fois que vous changez de page, vous devez demander le rendu du serveur et, après l'affichage du serveur, renvoyez le HTML au client pour afficher.
2. Les données liées sont dynamiques. Cela signifie que les modifications du modèle de données seront immédiatement reflétées sur la page. Cet effet est attribué au mécanisme de liaison bidirectionnel mis en œuvre par knockoutjs.
L'utilisation de cette méthode est également simple pour le développement de programmes. L'API Web est uniquement responsable de la fourniture de données, et la page frontale réduit également de nombreuses opérations DOM. Parce que les opérations DOM sont compliquées et sujettes aux erreurs. Cela signifie également réduire les bogues implicites dans le programme. De plus, un service back-end peut être utilisé par les téléphones mobiles, les navigateurs Web et plusieurs plates-formes pour éviter le développement répété.
5. Résumé
À ce stade, l'introduction de cet article sera introduite. Cet article présente principalement l'utilisation de KnockoutJS pour terminer un programme de spa. En fait, dans les travaux réels, le modèle de création de programmes à une page est davantage basé sur AngularJS. Ensuite, il y a beaucoup de knockoutjs, mais knockoutjs n'est qu'un framework MVVM, et son mécanisme de routage doit être utilisé avec d'autres bibliothèques de classe, telles que le mécanisme de routage dans ASP.NET MVC ici, vous pouvez également utiliser le cadre de routage frontal Director.js. Par rapport à KnockoutJS, AngularJS est un cadre MVVM + MVC. Ainsi, dans le sujet suivant, nous présenterons comment utiliser AngularJS pour créer un programme de page (SPA).
Téléchargez tous les codes source de cet article: SpawithKnockoutjs
Si vous souhaitez toujours étudier en profondeur, vous pouvez cliquer ici pour étudier et vous attacher 3 sujets passionnants:
Tutoriel d'apprentissage bootstrap
Tutoriel pratique de bootstrap
Tutoriel d'utilisation du plug-in bootstrap
Ce qui précède concerne cet article, j'espère qu'il sera utile à l'apprentissage de tout le monde.