1. 程式人生 > >改造 Combo Select支持服務器端模糊搜索

改造 Combo Select支持服務器端模糊搜索

hid setting 基本 界面 lec 隱藏 != hub serve

項目中使用了 combo select,為缺省的select增加模糊搜索的功能,一直運行得很好。

1 碰到的問題

但最近碰到一個大數據量的select:初始化加載的數據項有2000多個。我們采用的是ajax讀取所有的option json,並由js在瀏覽器中遍歷並最終生成完整的html。當數據量變大的時候,ajax讀取數據和瀏覽器處理數據都會有比較明顯的損耗,頁面初始化時需要較長時間,降低了用戶友好度。

2 備選解決方案

大家簡單分析了一下這個問題,想到了三種可能的解決方案。

2.1 修改數據結構

目前的同級數據有2000多條,數據從邏輯上可以拆分為兩級結構。這樣,將數據拆分為兩級結構後,使用兩個聯動Select,能大大減少每個select加載的option數量。

2.2 使用redis緩存數據

因為采用分布式部署,這些數據實際上經過了多次服務器之間的傳輸。數據量大,每一級傳輸耗時增加,導致最終的耗時難於接受。

可以在api server端利用nosql對數據進行緩存,能在一定程度上降低耗時。

2.3 修改combo select插件

從前端入手,select只顯示少量數據,當用戶輸入關鍵字進行搜索時,實時從服務器加載。這種方式增加了調用次數,但可以大大降低數據量,縮短頁面加載的耗時。

三種方案,都能在一定程度上解決問題。我們決定先從combo select插件嘗試,如果達不到效果,再考慮redis緩存或修改數據結構的方案。

3 Combo Select代碼分析

網址 https://github.com/PebbleRoad/combo-select ,感謝提供如此優秀的一個插件。

3.1 基本用法

首先在頁面中構建一個select,並初始化option數據,然後調用腳本

$("#selectId").comboSelect();

其他更復雜的功能,請自行前往官網學習。

3.2 html結構

技術分享圖片

Combo Select在執行時,在原 select 外層套了一個 <div class=”combo-select”>,然後在select後面添加了三個element。

div.combo-arrow,是下拉箭頭

ul.combo-dropdown是用來顯示的下拉列表

input.combo-input 是用來輸入模糊搜索內容的輸入框

並通過修改原 select 的屬性,隱藏掉。

3.3 js數據模型

combo select初始化時,經過一系列代碼,最終構造幾個屬性:

$container : 生成一個新的div,將原來的select和新生成的ul等都放在其中。

$el : 初始的select element

$options : 所有的option 數據

$dropdown : 生成的 ul.combo-dropdown 對象

$items : 所有的options轉成 li 格式後的數據。

下圖是數據模型和html元素之間的對應關系。

技術分享圖片

3.4 插件初始化

在js插件的代碼function Plugin ( element, options )會完成插件的初始化,根據select當前的數據,完成html元素的調整,以及js數據模型的初始化。初始化流程如下

技術分享圖片

3.5 模糊查詢的邏輯

當用戶在input中輸入文字的時候,會觸發 keydown和keyup事件,在keyup事件中,對 $items中的數據依次進行匹配,設置 visible屬性,實現部分數據的展示。

技術分享圖片

在這個過程中,原始的select($el)及其所有的options($options)沒有變化,下拉列表的變化,主要是將ul.li($items)設置為可見或不可見。

4 修改為Server端實時查詢方案

整個修改方案,分別從Server API、js組件、前端調用三方面解決。

4.1 Server API 修改

Server端需要提供根據名稱進行模糊搜索的接口。不贅述,需要註意的是返回數據要設置最大條數。避免根據查詢條件返回了大量的數據,就失去了解決的優勢。

限制最大條數後,需要跟產品介紹清楚這個實現邏輯,如果用戶輸入的關鍵字區分度不大時,可能無法查到真正需要的數據;此時需要用戶輸入更具有區分度的關鍵字。

4.2 ComboSelect組件修改

4.2.1 修改方案

修改keyup事件時的邏輯:原來是分別設置ul.li是否可見,修改為重新加載select的所有options,並根據options重建$items,並設置為所有ul.li都是可見的。

技術分享圖片

4.2.2 為組件新增幾個參數

entity: ‘entity‘,

itemName: ‘itemName‘,

curItemField: ‘curItemCode‘,

curItemValue: ‘‘,

curItemName: ‘‘,

url: ‘‘,

limit: 7

  • entity: 當前處理的數據類型,這是為了適應不同api返回的json定義的差異。更好的辦法是要求所有數據類型使用相同的屬性名;變通的方案就是增加這個entity,在js上做差異化處理。這樣就減少了改造的通用性。
  • itemName: 調用api時需要的用戶輸入值的參數名
  • curItemField:在html中,item的input名稱
  • curItemValue: 當前已選中數據的value
  • curItemName: 當前已選中數據的title
  • limit: 服務器api模糊搜索返回值的分頁大小

4.2.3 修改 _filter() 方法實現服務器端模糊查詢

修改了原組件的這個方法,判斷是否設置了服務器端刷新的url。如果沒設置,沿用原來的邏輯;如果設置了,根據用戶輸入進行模糊查詢,並重新生成瀏覽器中被隱藏的select的所有options,並更新到$dropdown中。

if(self.settings.url != ‘‘){

// 準備調用api需要的json數據

var self = this;

var ajaxData = {

"paging": true,

"offset": 0,

"limit": self.settings.limit

};

if(self.settings.itemName != ‘‘ && needle != ‘‘){

ajaxData[self.settings.itemName] = needle;

}

if(self.settings.curItemField != ‘‘ && self.settings.curItemValue != ‘‘){

ajaxData[self.settings.curItemField] = self.settings.curItemValue;

}

// 從服務器查詢數據

$.ajax({

url : self.settings.url,

type : ‘post‘,

data: ajaxData,

success : function(data) {

var obj = $.parseJSON(data);

// 先刪掉select原來的數據,並遍歷查詢結果生成option添加到select中

var dropdownHtml = ‘‘, k = 0, p = ‘‘;

self.$el.empty();

self.$el.append("<option value=‘‘>請選擇</option>");

var confirmedValue;

self.$dropdown.html("<li class=‘option-item‘ data-index=‘0‘ data-value=‘‘>請選擇</li>");

for (var i = 0; i < obj.length; i++) {

var itemCode;

var itemName;

var itemExtraCode;

if(self.settings.entity == ‘entity‘){

itemCode = obj[i].entityCode;

itemName = obj[i].entityName;

itemExtraCode = obj[i].entityShortName;

}else{

itemCode = obj[i].itemCode;

itemName = obj[i].itemName;

itemExtraCode = itemCode;

}

// 生成select option

var oneOption = $("<option></option>");

$(oneOption).val(itemCode);

$(oneOption).html(itemName);

if(itemCode == self.settings.curItemValue || itemExtraCode == self.settings.curItemValue || itemName == self.settings.curItemName){

$(oneOption).attr("selected", "selected");

self.settings.curItemValue = itemCode;

confirmedValue = itemCode;

}

self.$el.append(oneOption);

if(confirmedValue != undefined && confirmedValue != ‘‘){

self.$el.val(confirmedValue);

}

// 生成$dropdown 中的li

var oneItem = $("<li></li>");

$(oneItem).attr("class",this.disabled? self.settings.disabledClass : "option-item");

$(oneItem).attr("data-index", (i+1));

$(oneItem).attr("data-value", itemCode);

$(oneItem).html(itemName);

self.$dropdown.append(oneItem);

}

// 為$items 重新賦值

self.$items = self.$dropdown.children();

// 觸發後續的open方法

self.$container.trigger(‘comboselect:open‘)

}

});

}

4.2.4 修改 init() 實現首次加載

代碼類似_filter()。應該要獨立出一個方法來在兩個方法中調用,沒做。

init: function () {

var self = this;

if(self.settings.url != ‘‘){

//動態刷新代碼

... ...

}else{

// 組件的原始邏輯

self._construct();

self._events();

}

},

4.3 前端調用

4.3.1 html代碼中添加參數

使用 comboselect- 前綴,如

<select class="list-filedV" id="entityCode" name="entityCode"

comboselect-entity="entity" onchange="getBranch(‘‘)">

</select>

<input type=‘hidden‘ name=‘entityName‘ id=‘entityName‘>

4.3.2 js生成沒有缺省值的combobox

在js代碼中完成初始化,代碼

//獲取數據

function getEntityData(){

$("#entityCode").comboSelect({

"itemName": "entityName",

"url": contextPath+"/new/dictionary/searchEntityData.ajax",

"limit": 7

});

}

4.3.3 js生成有缺省值的combobox

在編輯界面比較常見

//獲取數據_修改

function getEntityDataUp(curEntityCode, curEntityName){

$("#entityCode").comboSelect({

"itemName": "entityName",

"curItemField": "includeEntityCode",

"curItemValue": curEntityCode,

"curItemName": curEntityName,

"url": contextPath+"/new/dictionary/searchEntityData.ajax",

"limit": 7

});

}

5 降低調用服務器頻度

註意看_keyup 的代碼,每次按鍵(不包括該函數忽略的特殊字符),每次都會刷新數據。如果是在瀏覽器內部進行數據過濾,問題還不明顯。但每次模糊查詢都通過服務器查詢,就會帶來大量的api訪問。

5.1 修改方案

在_keyup()中,調用_delayFilter(),由它觸發前面修改後的 _filter()方法。

技術分享圖片

5.2 代碼 _delayFilter()

this.filterTimer = 0;

_delayFilter: function(search){

if(this.filterTimer > 0){

clearTimeout(this.filterTimer);

this.filterTimer = 0;

}

var self = this;

this.filterTimer = setTimeout(function(){

self._filter(search);

}, 500);

},

改造 Combo Select支持服務器端模糊搜索