系統架構——前后端分離


前后端分離大概是指是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.");

  }
  ...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM