系统架构——前后端分离

前后端分离大概是指是HTML和服务器代码的分离,因为浏览器中解释执行的HTML+JS+CSS代码混合着<%%>包含的内容确实是不友好,于是就有人发明了代码分离的技术,比如asp.net的基于事件的codebehind,struts的mvc方式。再后来,为了更彻底地分离,前端直接做成了独立的程序包或应用,比如基于浏览器的angular、react之类的。这种前后端分离的好处是显而易见的,即增加了代码的可读性,增加了可维护性,同时也容易分配开发任务,前后端可以并行开发,互不干扰。

在实际开发中,新的项目,只要是正式的项目,肯定是尽量代码分离的,况且现在主流的程序框架都是MVC结构的,你不想分离都难,唯一纠结的地方就是选择服务端渲染还是客户端渲染的问题。服务端渲染和客户端渲染的比较大的差别大概有两点,一个是响应速度上,服务端渲染每次返回的一个完整的HTML代码或是部分HTML代码,而客户端渲染则所有界面逻辑都在客户端,变更界面不用每次都向服务器请求。由于客户端渲染每次操作只请求纯数据的部分,这样无论响应速度还是网络流量都会比服务端渲染好点。当然服务端渲染也有服务端渲染的好处,就是生成页面连同数据一同发过来的,不用复杂的Dom操作,这样有利于开发的效率,毕竟服务端的调试要比客户调试来得方便。

我们可以用代码的方式来比较一下这两者的不同。假设我们要开发一个网店系统,其中有一功能就是商品的管理,商品有一个分类属性,商品和分类是从属关系,一般设计成多对一的关系。比如以下关于分类和商品实体的定义代码:

    /// <summary>
    /// 分类实体
    /// </summary>
    [Table("CategoryInfo")]
    public class CategoryInfo
    {
        public Guid Id { get; set; }
        public string Title { get; set; }

        public override string ToString()
        {
            return Title;
        }
    }
    
    
    /// <summary>
    /// 商品实体
    /// </summary>
    [Table("CommodityInfo")]
    public class CommodityInfo
    {
        public Guid Id { get; set; }

        public string Title { get; set; }

        public float Price { get; set; }

        public Guid CategoryInfoId { get; set; }

        public CategoryInfo Category { get; set; }

        public override string ToString()
        {
            return Title;
        }
    }

然后,我们在DbContext里边加上这两行:

        public DbSet<CategoryInfo> CategoryInfos { get; set; }

        public DbSet<CommodityInfo> CommodityInfos { get; set; }

因为我们只做简单的CRUD操作,所以这个示例中免去了业务层代码逻辑,我们将直接在Controller中进行Linq查询,无论是MVC项目,还是WEB API项目,查询逻辑是一样的。

//MVC中的Action
        public IActionResult Index()
        {
            return View(dbContext.CommodityInfos.ToList());
        }

//API中的查询
        [HttpGet()]
        public CommodityInfo[] All()
        {
            return dbContext.CommodityInfos.ToArray();
        }

接下来就有区别了,MVC中我们只要定义一个View视图。

@model List<RelationData.CommodityInfo>
@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<table class="table">
    <tr><th>分类</th><th>商品名称</th></tr>
    @foreach (var p in Model)
    {
        <tr><td>@p.Category</td><td>@p</td></tr>
    }
</table>

而客户端渲染要先建一个Angular(当然也可以是其它的),我们可以在VS中创建,也可以命令行创建。为了方便,我们用DevExpress。
HTML部分:

<dx-data-grid id="gridContainer"
    [dataSource]="dataSource"
    [remoteOperations]="false"
    [allowColumnReordering]="true"
    [rowAlternationEnabled]="true"
    [showBorders]="true"
    (onContentReady)="contentReady($event)">

    <dxo-paging [pageSize]="10"></dxo-paging>
    <dxo-pager
        [showPageSizeSelector]="true"
        [allowedPageSizes]="[10, 25, 50, 100]"
    ></dxo-pager>
    <dxo-search-panel
        [visible]="true"
        [highlightCaseSensitive]="true"
    ></dxo-search-panel>
    <dxo-group-panel
        [visible]="true"
    ></dxo-group-panel>
    <dxo-grouping
        [autoExpandAll]="false"
    ></dxo-grouping>

    <dxi-column dataField="Category.Title" caption="分类"></dxi-column>
    <dxi-column caption="Title" dataField="商品名称"></dxi-column>

</dx-data-grid>

组件部分:

import { NgModule, Component, Pipe, PipeTransform, enableProdMode } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { DxDataGridModule,
         DxBulletModule,
         DxTemplateModule } from 'devextreme-angular';
import DataSource from 'devextreme/data/data_source';
import { Service } from './app.service';

@Component({
  selector: 'app-counter-component',
  templateUrl: './commodity.component.html'
})
export class CommodityComponent {
    
    dataSource: DataSource;

    constructor(service: Service) {
        this.dataSource = service.getDataSource();
    }
}

数据服务:

import { Injectable } from '@angular/core';
import 'devextreme/data/odata/store';
import DataSource from 'devextreme/data/data_source';

@Injectable()
export class Service {
    getDataSource() {
        return new DataSource({
            store: {
                url: 'https://localhost/api/commodity/all'
            }
        });
    }
}

相较而言,两者似乎差不多,区别是服务器端渲染是内存传递对象,而客户端渲染是通过json来传递对象。另外就是客户端增加了一个技能栈,当然你也可以不用Angular框架,而是直接用ajax的方式直接操作Dom树。

前后端分离,我们还可以再扩展一下。现在的客户端框架,其实就是将原来的瘦端进行了富化,现在有了丰富的第三方UI组件,原来需要桌面程序才能实现的东西,现在通过纯网页也可以实现了,因而前端并不限于HTML,不包括桌面应用,手机APP、小程序等不直接处理业务逻辑的各类终端。

Xamarin中获取Json数据:

  var uri = new Uri (string.Format ("http://localhost/api/all", string.Empty));

  ...
  var json = JsonConvert.SerializeObject (item);
  var content = new StringContent (json, Encoding.UTF8, "application/json");

  HttpResponseMessage response = null;
  if (isNewItem)
  {
    response = await _client.PostAsync (uri, content);
  }
  ...

  if (response.IsSuccessStatusCode)
  {
    Debug.WriteLine (@"\tTodoItem successfully saved.");

  }
  ...
posted @ 2020-04-28 22:16  八爻老骥  阅读(1904)  评论(0编辑  收藏  举报