1. Prefácio
No tópico anterior, introduzi rapidamente os pontos de conhecimento relacionados aos knockoutjs e escrevi alguns exemplos simples. Espero que, através desses exemplos, você possa começar rapidamente com os knockoutjs. Para permitir que todos vejam claramente a aplicação de knockoutjs em projetos reais, este tópico introduzirá como usar o webapi+bootstrap+knockoutjs+asp.net MVC para criar um programa da Web de página única. Este modelo também é usado nos projetos reais da maioria das empresas agora.
2. Spa (página única) benefícios
Antes de introduzir a implementação específica, acho que é necessário introduzir o spa em detalhes. O Spa, a abreviação do aplicativo da Web de página única, é um aplicativo da Web que carrega uma única página HTML e atualiza a página dinamicamente quando o usuário interage com o aplicativo. O navegador carregará o HTML, CSS e JavaScript necessários no início. Todas as operações são concluídas nesta página e são controladas pelo JavaScript.
Os benefícios dos programas de página única são:
Uma melhor experiência do usuário permite que os usuários experimentem a velocidade e a suavidade dos aplicativos nativos no aplicativo da web.
Separe as preocupações frontal e traseiro, o front end é responsável pela exibição da interface e o back -end é responsável pelo armazenamento e computação de dados, cada um executa suas próprias tarefas e não misturará a lógica das extremidades dianteiras e traseiras.
Para reduzir a pressão no servidor, o servidor precisa apenas gerar dados, independentemente da lógica de exibição e da lógica da página e aumentar a taxa de transferência do servidor. O front-end escrito na Sintaxe Razor no MVC exige que o servidor preencha a síntese da página e a produza.
O mesmo conjunto de programas de back-end pode ser usado diretamente em vários clientes, como interface da Web, telefones celulares, tablets etc. sem modificação.
Obviamente, além das vantagens listadas acima, os programas de página única também têm suas deficiências:
Não é propício ao SEO. Se este for um sistema de gerenciamento, não o afetará.
O tempo de carregamento inicial é relativamente aumentado. Porque todos os recursos JS e CSS serão carregados pela primeira vez, tornando a página subsequente suave. Para isso, você pode usar o Bundle no ASP.NET MVC para vincular arquivos. Para uso detalhado do pacote, consulte os artigos: http://www.vevb.com/article/84329.htm, http://www.vevb.com/article/84329.htm e http:/ww.vevb.com/article/8174.htm.
Navegação não está disponível. Se você precisar navegar, deve avançar e recuar sozinho. Para isso, você pode perceber as funções avançadas e traseiras para compensar isso. De fato, é isso que as páginas da Web móveis fazem agora e agora elas ainda precisam estar navegando acima. Isso também pode ser feito para alguns sistemas de gerenciamento de back -end da empresa.
Altos custos de desenvolvimento para desenvolvedores. Isso não é um problema. Os programadores precisam continuar aprendendo a cobrar. Felizmente, algumas estruturas front-end são muito fáceis de usar.
3. Use ASP.NET MVC+Webapi+Bootstrap+KnockoutJs para implementar o SPA
As vantagens e desvantagens do SPA foram introduzidas em detalhes anteriormente. Em seguida, vamos usar asp.net mvc+webapi+bs+ko para implementar um programa de página única, para experimentar a suavidade do spa e comparar os efeitos das páginas feitas pelo ASP.NET original MVC+Razor.
1. Use o VS2013 para criar um projeto de aplicativo da Web ASP.NET, verifique a biblioteca MVC e Webapi. Veja a figura abaixo para obter detalhes:
2. Crie armazéns e modelos correspondentes. Aqui está um sistema de gerenciamento de tarefas simples. O modelo específico e o código de armazenamento são os seguintes:
Implementação de classe de entidade de tarefas:
public Enum TaskState {Active = 1, concluído = 2} /// <summary> /// entidade de tarefa //// </summary> public class Task {public int id {get; definir; } nome da string pública {get; definir; } public string description {get; definir; } public DateTime CreationTime {get; definir; } public DateTime FinationTime {get; definir; } public string proprietário {get; definir; } Public TaskState State {get; definir; } public tarefa () {CreationTime = DateTime.Parse (DateTime.now.tolongDateString ()); Estado = taskstate.active; }}Tarefa de implementação de classe de armazenamento:
/// <summary> /// Aqui o armazém usa dados de amostra como demonstração. O projeto real precisa ser carregado dinamicamente no banco de dados /// </summary> public class classe TaskRepository {#Region estático privado arquivado estático privado Lazy <ketRepository> _taskrepositor public static TaskRepository Current {get {return _TaskRepository.Value; }} #ENDRegion #Region Campos List Private ReadOnly <Task> _Tasks = new List <Task> () {nova tarefa {id = 1, name = "Crie um programa de spa", Descrição = "Spa (Aplicativo da Web de uma única página), a vantagem do SPA é uma pequena quantidade de largura de banda e uma experiência suave", "," aprendizagem ",", dura ", Finalmente", Finalize é uma pequena quantidade de largura de banda e uma experiência suave ",", "aprendizado DateTime.Parse (DateTime.now.adddays (1) .toString (culturaInfo.inVariantCulture))}, nova tarefa {id = 2, name = "Aprendendo knockoutjs", description = "Knockoutjs é uma MVVM Class Library que suporta bidirecionais", proprietário = "" DateTime.Parse (DateTime.now.adddays (2) .tostring (culturaInfo.invariantculture))}, nova tarefa {id = 3, name = "Learn Angularjs", description = "Angularjs é uma estrutura MVVM que integra MVV e MVC com um." DateTime.Parse (DateTime.now.adddays (3) .tostring (culturaInfo.inVariantculture))}, nova tarefa {id = 4, name = "Learn asp.net MVC Site", como o que não é um teste de desempenho, como o que não é o site de que não é o que não é um teste, como o site de que não é o que não é um teste de desempenho, como o que não é um teste de desempenho, que não é um teste, que não é um que precisa de uma ferramenta de performance. código do projeto original e pode gerar o tempo de execução de cada link da execução do código ", proprietário =" tonny li ", finalTime = dateTime.parse (dateTime.now.adddays (4) .tostring (cultivinfo.inVariantCulture))},}; #endregion #region público métodos public ienumerable <kky> getAll () {return _Tasks; } tarefa pública get (int id) {return _tasks.find (p => p.id == id); } public tarefa add (item de tarefa) {if (item == null) {tiro novo argumentnullexception ("item"); } item.id = _tasks.count + 1; _Tasks.add (item); item de retorno; } public void remover (int id) {_tasks.removeall (p => p.id == id); } public bool update (item de tarefa) {if (item == null) {lança novo argumentnulLexception ("item"); } var taskItem = get (item.id); if (taskItem == null) {return false; } _tasks.remove (taskItem); _Tasks.add (item); retornar true; } #endregion}3. Adicione as bibliotecas de bootstrap e knockoutjs através do NUGET.
4. Implementar serviços de dados de back-end. Aqui, o serviço de back -end é implementado usando o ASP.NET webapi. O código de implementação específico é o seguinte:
/// <summary> /// webapi de tarefas, forneça serviços de dados /// </summary> public classe TaskScontroller: apicontroller {private readonly taskrepository _taskrepository = taskrepository.current; public ienumerable <kky> getAll () {return _TaskRepository.getall (). OrderBy (a => a.id); } tarefa pública get (int id) {var item = _taskrepository.get (id); if (item == null) {lança nova httprosseException (httpstatuscode.notfound); } retornar item; } [Route ("API/Tasks/getByState")] public ienumerable <kky> getByState (estado da string) {ienumerable <kky> Results = new List <Task> (); switch (state.tolower ()) {case "": case "all": resultados = _taskrepository.getall (); quebrar; caso "ativo": resulta = _taskrepository.getall (). where (t => t.state == taskState.active); quebrar; caso "concluído": resultados = _TaskRepository.getall (). Onde (t => t.state == taskState.completed); quebrar; } resultados = Results.OrderBy (t => t.id); RETORNO DE RECURSOS; } [Httppost] Tarefa pública Criar (item de tarefa) {return _taskrepository.add (item); } [Httpput] public void put (item de tarefa) {if (! _TaskRepository.UpDate (item)) {THROW NOVA HTTPRESPOnseException (httpstatuscode.notfound); }} public void delete (int id) {_taskrepository.remove (id); }}5. Use o pacote ASP.NET MVC para empacotar os recursos. O código de implementação do BundleConfig correspondente é o seguinte:
/// <summary> /// Basta adicionar alguns arquivos CSS e JS ausentes. Como alguns arquivos CSS e JS foram adicionados ao criar o modelo /// </summary> classe pública BundleConfig {// Para obter mais informações sobre o Bundling, visite http://go.microsoft.com/fwlink/?linkid=301862 estático public static Registerbundles (Bundleclollection Bundles) bundles) bundles) (bundlink/?linkid=301862 BUNDBUNDLES (Bundllectlection) Scriptbundle ("~/Bundles/jQuery"). pacote.add (novo scriptbundle ("~/bundles/jQueryVal"). // Use a versão de desenvolvimento do Modernizr para se desenvolver e aprender. Então, quando estiver pronto para a produção, use a ferramenta Build em http://modernizr.com para escolher apenas os testes necessários. pacote.add (novo scriptbundle ("~/bundles/modernizr"). Incluir ("~/scripts/modernizr-*")); pacote.add (novo scriptbundle ("~/pacote/bootstrap"). Bundles.add (new StyleBundle ("~/content/css"). Bundles.add (novo scriptbundle ("~/pacote/knockout"). pacote.add (novo scriptbundle ("~/pacote/app"). incluir ("~/scripts/app/app.js")); }}6. Porque precisamos fazer o tipo de enume aparecer como uma string na página. A enumeração é convertida em um tipo numérico quando serializada por padrão. Portanto, precisamos fazer as seguintes alterações na classe WebApiconfig:
classe estática public static webapiconfig {public static void Register (httpconfiguration config) {// Configuração da API e serviços da Web // Web API Routing Config.maphttpattributeroutes (); config.routes.MaphttProute (Nome: "DefaultApi", RouteTemplate: "API/{Controller}/{Id}", Padrões: new {id = routeParameter.optional}); // Faz de serialização Use a propriedade de serialização do estilo de case camel config.formatters.jsonformatter.serializersettings.contractresolver = new CamelCasePropertyNamesContRacTresolver (); // serialize o string config.formatters.jsonformatter.serializersettings.converters.add (new stringenumConverter ()); }}NOTA: Se a serialização de minúsculas do camelo não for usada acima, você também deverá fazer ajustes ao vincular dados na página. Por exemplo, ao atributo de nome de ligação, use diretamente a capitalização de nomes. Se você usar o método de nome, ele solicitará que não haja erro de definição neste atributo. Como o JS usa o estilo minúsculo de camelo para nomear variáveis. Portanto, recomenda -se que você use o estilo de baixa camelo para serializar. Neste momento, você só pode usar a forma de "nome" para vincular. Isso está mais alinhado com as especificações do código JS.
7. Modifique o arquivo de layout correspondente e o conteúdo do arquivo de índice.
O código específico do arquivo de layout é o seguinte:
<! Doctype html> <html> <head> <meta charset = "utf-8"/> <meta name = "viewport" content = "width = width de dispositivo, scale inicial = 1.0"> <title> script spa spa </title> @styles ("~///css") @scripts.rMaths.spats script @script </title> @styles "("///css ") @script @styles ("///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 APLICATIVO </P> </SOWER> </div> @scripts.render ("~/Bundles/jQuery") @scripts.render ("~/bundles/bootstrap") @scripts.render ("~/bundles/knockout") @scripts.rnder ("~/Bundles/knockout.) </body> </html> O código da página de índice é o seguinte: @{viewbag.title = "index"; Layout = "~/views/shared/_layout.cshtml";} <div id = "list" data-bind = "if: canCreate"> <h2> tarefas </h2> <div> <tath> <hth> <hhead> <tr> <the> número </</thy <h> <hthe> <hth> thy, thhead> <tr> <the> número </</the </ <th> Tempo de compleção </th> <th> statu </th> <the> </th> </tr> </t> <tdody data-bind = "foreach: tarefas"> <tr> <td data-bind = "text: idlecrateguddate" td> <td> <a data-bind = "text: name, clique: handlCrateReRateNtate" Descrição "> </td> <td data-bind =" text: proprietário "> </td> <td data-bind =" text: criação time "> </td> <td data-bind =" text: time time "> </td> <td data-bind =" text: "> </td> <td> <a-bind =" clique ":" State "> </td> <Td> <a-bind = =" clique: "State"> </td> <td> <a-bind = "" href = "JavaScript: void (0)"> remover </a> </td> </tbody> </tabela> </div> <div> <a href = "javaScript: void (0)" Data-bind = "Click: function (Event) {Settasklist ('All')}"> all </aa> <a href = "javascript: void (0)" data-bind = "click: function (dados, evento) {settasklist ('ativo')}"> ativo </a> | <a href = "javascript: void (0)" data-bind = "clique: function (dados, event) {settaskList ('completed')}"> concluído </a> </div> <div> <a href = "javascript: void (0)" data-bind = "click: handlecReateDate"> "> style = "Visibilidade: Hidden"> <H2> Adicionar tarefa </h2> <br/> <div> <div> <gravador para = "taskname"> name*</elated> <div> <input type = "text" data-bind = "name: name" id = "taskname" name = "taskName" Praze = "name"> <//> <//" <texttarea data-bind = "value: descrição" linhas = "3" id = "taskdesc" name = "taskdesc" placeholder = "description"> </sexttarea> </div> </div> <div> <gravador para = "Taskner "wner"> na carga "</bel> <div> <= input ID =" Tasker "Nome =" ">" </div> <div> <gravador para = "taskfinish"> tempo de conclusão estimado*</celt> <div> <input id = "taskfinish" data-bind = "value: time time" name = "taskfinish"> </div> </div> <fiv> <div> <div> <bel para = "TaskOwner"> status*</celt> <select> <leclect> <select> <pution> concluído </pption> </leclect> </div> </div> <div> <div> <button data-bind = "clique: handleSaveclick"> salvar </botão> <button data-bind = "clique: lankingclick"> back </butão> </div> </div> </div> </div> </div>8. Crie a lógica de script front-end correspondente. Use o código JS para solicitar dados e criar objetos ViewModel correspondentes para ligação front-end. O código específico de implementação do JS é o seguinte:
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); };}; function getAlltasks () {SendajaxRequest ("get", function (data) {taskListViewModel.tasks.removeall (); para (var i = 0; i <data.length; i ++) {taskListViewModel.Tasks.push (Data.length; });} função SettaskList (estado) {SendajaxRequest ("get", function (data) {taskListViewModel.tasks.removeall (); para (var i = 0; i <data.length; i ++) {taskListViewModel.Tasks.push (Data. [i]; });} função remover (item) {sendajaxRequest ("delete", function () {getAlltasks ();}, item.id);} var task = new TaskModel (); function handleCreateRupdate (item) {Task.Fromjs (item); initdatepicker (); TaskListViewModel.cancreate (false); $ ('#create'). css ('visibilidade', 'visível');} function handlebackClick () {taskListViewModel.cancreate (true); $ ('#create'). CSS ('visibilidade', 'hidden');} função manipulaveclick (item) {if (item.id == indefinido) {sendajaxRequest ("post", function (newItem) {// newItem é o objeto retornado. item.Description, FinationTime: Item.FinishTime, Proprietário: Item.owner, Estado: Item.State}); } else {sendajaxRequest ("put", function () {getAlltasks ();}, null, {id: item.id, nome: item.name, descrição: item.description, finalTime: item.finishtime, proprietário: item.owner, estado: item.state}); } taskListViewModel.Cancreate (true); $ ('#create'). CSS ('visibilidade', 'Hidden');} função sendajaxRequest (httpmethod, retorno de chamada, url, reqdata) {$ .ajax ("/api/tasks" + (url? initdatepicker = function () {$ ('#create .datepicker'). datepicker ({autoclose: true});}; $ ('. nav'). on ('click', 'li', function () {$ ('. {getAlltasks ();Neste ponto, nosso programa de página única foi desenvolvida. Em seguida, vamos executá -lo para ver seu efeito.
A partir do diagrama de demonstração de resultados em execução acima, podemos ver que, uma vez carregado, todas as operações parecem operar em uma página e parece que a página do navegador está circulando. Comparado com as páginas desenvolvidas usando o ASP.NET MVC + Razor antes, você sente a suavidade do spa? Anteriormente, a página desenvolvida usando o ASP.NET MVC + RAZOR, você só precisa solicitar uma página e pode sentir a atualização de toda a página, o que torna o usuário muito ruim.
4. Comparação com o modelo de desenvolvimento de barbear
Acredito que todos viram as vantagens do spa dos resultados. Em seguida, acho que é necessário compará -lo com a maneira tradicional de implementar páginas da web. Existem duas diferenças principais do método de desenvolvimento de barbear:
1. Quando a página é renderizada, os dados são processados no lado do navegador. Não no servidor. Alocar pressão de renderização para o lado do navegador de cada usuário, reduzindo assim a pressão no servidor do site. Se fosse a Sintaxe Razor, a declaração de encadernação da página front-end deve ser a seguinte:
@Model ienumerable <nockoutJssSpa.models.task> @foreach (var item no modelo) {<tr> <td>@item.name </td> <td>@item.description </td> </tr>}Estes são renderizados pelo motor de barbear no lado do servidor. Essa também é a razão pela qual as páginas desenvolvidas usando Razor verão as páginas circulando. Como toda vez que você muda uma página, você precisa solicitar que o servidor renderize e, depois que o servidor renderiza, retorne o HTML ao cliente para exibição.
2. Os dados vinculados são dinâmicos. Isso significa que as alterações no modelo de dados serão refletidas na página imediatamente. Esse efeito é atribuído ao mecanismo de ligação bidirecional implementado pelos knockoutjs.
O uso deste método também é simples para o desenvolvimento do programa. A API da Web é responsável apenas por fornecer dados e a página front-end também reduz muitas operações DOM. Porque as operações DOM são complicadas e propensas a erros. Isso também significa reduzir bugs implícitos no programa. Além disso, um serviço de back-end pode ser usado por telefones celulares, navegadores da Web e várias plataformas para evitar o desenvolvimento repetido.
5. Resumo
Neste ponto, a introdução deste artigo será introduzida. Este artigo apresenta principalmente o uso de knockoutjs para concluir um programa de spa. De fato, no trabalho real, o modelo de criação de programas de página única é mais baseada em AngularJS. Depois, existem muitos knockoutjs, mas os knockoutjs são apenas uma estrutura MVVM, e seu mecanismo de roteamento precisa ser usado com outras bibliotecas de classes, como o mecanismo de roteamento no ASP.NET MVC aqui, você também pode usar a estrutura de roteamento do diretor.js. Comparado com os knockoutjs, o AngularJS é uma estrutura MVVM+MVC. Portanto, no próximo tópico, apresentaremos como usar o AngularJS para criar um programa de uma única página (SPA).
Faça o download de todos os códigos de origem neste artigo: spawithknockoutjs
Se você ainda deseja estudar em profundidade, pode clicar aqui para estudar e anexar 3 tópicos interessantes a você:
Tutorial de aprendizado de bootstrap
Tutorial prático de bootstrap
Tutorial de uso de plug-in bootstrap
O exposto acima é tudo sobre este artigo, espero que seja útil para o aprendizado de todos.