計劃構建個人博客系統,技術棧鎖定為 ASP.NET MVC5 + EF6 (Code First) + ZUI + jQuery 插件生態。ZUI 作為基於 Bootstrap 深度定制的國產前端框架,上手較快且適配國內環境。但在表格組件選型上陷入糾結:ZUI 僅提供基礎表格樣式,分頁、排序等交互需自行對接 JS 插件。經調研,主流選擇集中在 jqGrid 與 jQuery DataTables,兩者各有優劣,本文記錄雙方在服務端分頁集成中的關鍵配置與踩坑經驗。
| 前端參數 | 含義 | 後端對應 |
|---|---|---|
| rows | 每頁記錄數 | pageSize |
| page | 當前頁碼(從 1 開始) | pageIndex |
| sidx | 排序列 | sortField |
| sord | 排序方向 | sortOrder |
// JsonResult 返回結構
return Json(new {
total = totalPages, // 總頁數
page = pageIndex, // 當前頁碼
records = totalRecords, // 總記錄數
rows = dataList // 當前頁數據集合
}, JsonRequestBehavior.AllowGet);
var query = dbContext.Set<T>().Where(whereLambda);
// 總記錄數
int totalRecords = query.Count();
// 總頁數
int totalPages = (int)Math.Ceiling((double)totalRecords / pageSize);
// 分頁查詢:跳過 + 獲取
var data = query.OrderBy(sortField + " " + sortOrder)
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToList();
$("#grid").jqGrid({
url: '/JQGrid/GetData',
datatype: 'json', // 注意:MVC 返回純 JSON,非 jsonp
mtype: 'GET',
colModel: [ /* 列定義 */ ],
rowNum: 10,
pager: '#pager',
viewrecords: true
});
避坑提示:官方 Demo 多用 PHP + jsonp,MVC 必須設置 datatype: 'json',否則會因跨域回調失敗導致數據不顯示。
| DataTables 參數 | 含義 | jqGrid 對應 |
|---|---|---|
| draw | 繪制計數器(防異步亂序) | 無 |
| start | 起始記錄索引(從 0 開始) | page (從 1 開始) |
| length | 每頁記錄數 | rows |
| order[i][column] | 排序列索引 | sidx |
| order[i][dir] | 排序方向 | sord |
| search[value] | 全局搜索值 | _search + searchString |
return Json(new {
draw = draw, // 必須原樣返回
recordsTotal = totalRecords, // 總記錄數(無過濾)
recordsFiltered = filtered, // 過濾後總數
data = dataList // 數據數組
}, JsonRequestBehavior.AllowGet);
DataTables 首次請求傳 start=0,若直接套用 jqGrid 的 Skip((page-1)*size) 邏輯:
// 錯誤寫法 int page = start / length + 1; // start=0 時 page=1 var data = query.Skip((page - 1) * length).Take(length); // Skip(0) 正常 // 但若誤以為 start 是頁碼: var data = query.Skip((start - 1) * length).Take(length); // start=0 時 Skip(-10) 拋異常!
正確處理:
// start 是記錄偏移量,直接用於 Skip int skip = start; // 無需 -1 var data = query.OrderBy(...).Skip(skip).Take(length).ToList();
[HttpPost]
public ActionResult GetData(DataTablesParam param) {
var query = db.Set<Entity>().AsQueryable();
// 總記錄數
int totalRecords = query.Count();
// 搜索過濾
if (!string.IsNullOrEmpty(param.search.value)) {
query = query.Where(x => x.Name.Contains(param.search.value)
|| x.Code.Contains(param.search.value));
}
int filteredRecords = query.Count();
// 排序
var sortCol = param.columns[param.order[0].column].data;
var sortDir = param.order[0].dir; // asc/desc
query = query.OrderBy(sortCol + " " + sortDir);
// 分頁
var data = query.Skip(param.start).Take(param.length).ToList();
return Json(new {
draw = param.draw,
recordsTotal = totalRecords,
recordsFiltered = filteredRecords,
data = data
});
}
// DataTables 參數模型綁定類
public class DataTablesParam {
public int draw { get; set; }
public int start { get; set; }
public int length { get; set; }
public List<ColumnDef> columns { get; set; }
public List<OrderDef> order { get; set; }
public SearchDef search { get; set; }
}
public class ColumnDef { public string data { get; set; } }
public class OrderDef { public int column { get; set; } public string dir { get; set; } }
public class SearchDef { public string value { get; set; } }
| 維度 | jqGrid | jQuery DataTables |
|---|---|---|
| 學習曲線 | 中等,配置項多 | 較低,API 更直觀 |
| 服務端模式參數 | page/rows (頁碼制) | start/length (偏移量制) |
| 異步請求序列保證 | 無內建機制 | draw 計數器自動處理 |
| 內建功能豐富度 | 極高(子表格、分組、凍結列等) | 高(需插件擴展部分高級功能) |
| 社區活躍度 | 成熟穩定,更新較慢 | 非常活躍,生態豐富 |
| 樣式適配 | 需額外引入 Bootstrap 主題 | 原生支持 Bootstrap/Foundation |
服務端分頁的核心在於參數映射正確與返回格式標準。jqGrid 的頁碼制與 DataTables 的偏移量制是最大認知差異點,搞清 start 從 0 開始且直接等於 Skip 參數,即可避開 90% 的坑。無論選擇哪款插件,建議封裝通用的 PagedResult<T> 返回模型與 DataTablesParam 綁定模型,提升復用性與可維護性。