Skip to content

Instantly share code, notes, and snippets.

@benbai123
Last active October 19, 2016 13:08
Show Gist options
  • Save benbai123/ebb40e9aa6d6a731ffa015a3af5ffeaf to your computer and use it in GitHub Desktop.
Save benbai123/ebb40e9aa6d6a731ffa015a3af5ffeaf to your computer and use it in GitHub Desktop.
給前公司 找產品心得功能的修改建議
gzip 測試使用線上 gzip 網站
可以直接估狗 gzip online 就找得到了
http://www.txtwizard.net/compression
sample JSON 為連到
https://www.urcosme.com/find-product/1
開啟 Chrome Developer Tools 的 console
執行以下 SampleJSON.js 內的 JavaScript *1
即會從當前頁面爬內容生成 JSON string
*1 只在剛連入時可用, 點選選項後因 s_href 變更就無法正確運作
此 Demo 為 Ctrl + Click 僅更新選項狀態, 選單不變動的狀況
目前搜尋多個選項的狀況
https://youtu.be/4HxmtMEwYQc
改成 Ctrl+Click 只在 Client 端更新選項狀態的情形
https://youtu.be/HD58lU7C01A
傳送完整 HTML 與傳送 JSON 的差別
https://youtu.be/V_IzBwBFNtI
連到頁面在 Chrome Console 執行下方 DemoImpl.js 內容
即可測試效果 *1 *2
*1 只有剛連入未點選任何選項前可用
*2 選單不會變動, 更新內容時也仍是生完整內容且非 JSON 格式
Ctrl + Click - 只更新 Client 端選項的選取/未選取狀態, 無傳輸 *1
Alt + Click - 更新選取狀態外也更新選單內容, 僅傳輸選單內容 *2
Click - 更新選取狀態外也更新產品內容, 僅傳輸產品內容
雖然會有多選時可能無結果的問題,
但就是提供多一個操作選擇
希望一定有結果的人也可以不按 Ctrl
以上是主要部份
另一種可能是在多選無結果時, 提供建議結果
簡單點的話是
找 "產品+品牌+效果" 沒結果
-> 找 "產品+品牌" 看有沒有結果 (可先 count 就好)
-> 再沒有就找 "產品" 的結果
回傳 "沒有 OOO 的搜尋結果, 以下是搜尋 XXX 的結果
(OOO XXX 為條件內容)
要再細還可以試著找到符合最多選項的結果
簡單些就先 count 產品+品牌 再一個一個加上其它條件
要效率更好可套用二分搜尋的做法
或者更單純些,
把有結果的部份依由新到舊排序篩前二十筆,
在前二十筆中找有符合搜尋選項的屬性列出
*1 右方動態出現的選項亦可直接由 Client 端同步調整
另外可於此操作時隱藏之前的產品內容,
改為顯示 "以目前選項做查詢" 的按鈕
*2 假若只有特定選項會變動選單, 可取消此操作,
直接在選特定選項時更新選單內容
若有實作 "推薦搜尋" 功能
也可直接列出完整選單只更新產品內容即可
只更新選項選取狀況只需要 Client 端操作, 對伺服器完全沒負擔
更新選項清單, 會有生成及傳送選項清單的負擔,
但只會有一個 request
更新產品內容, 傳輸內容本身比選項清單少,
但是有額外的產品圖片等要讀取,
會多十多個 request (不含廣告)
假設使用者進行五次操作,
其中三次只變更選取狀況, 一次更新了選單, 最後一次更新了產品,
總共會是 1 個選單傳輸量 + 1 個產品傳輸量 + 十多個 request
相比原先 5 個選單傳輸量 + 5 個產品傳輸量 + 十多個 request * 5
就改善了五倍
首先要知道的是, 這會是後端最主要的 loading 之一,
雖然 DB, Redis 也會有 CPU Memory 負載,
但它們很多時候是 "被共用" 的, 尤其是 Memory
但 "各 Request 組字串回應" 基本上各自獨立,
少有能共用的部份
原本的回傳內容 HTML 的部份 及其 gzip 後的狀況為
Original size: 137364 bytes Result size: 12329 bytes
若用 JSON 則是
Original size: 16918 bytes Result size: 8589 bytes
可以看到壓縮前 size 差距約 8 倍
這表示若是即時組成
在 後端 要花 8 倍的 CPU/Memory 去組出內容及進行壓縮
有可能會更多,
因為在 template 插入內容及壓縮通常需要額外的資源
需求資源的成長通常不會是線性的
如果是用 Redis Cache 則是 Cache 8 倍的內容
且如果對它們使用 Cache,
不是很快被洗掉而效果不好 (Cache Size 有限制時)
就是要花很多空間 (Cache Size 無限制時)
而且注意到這是在 JSON 包含所有選單選項及產品的情形
假如選單部份是只有傳 要隱藏的項目
甚至選單是固定的可以完全 Client 端調整狀態
那會少更多
只傳所有產品的情形下是
Original size: 4356 bytes Result size: 1232 bytes
可以看到未壓縮前 size 差 30 倍, 壓縮後差 10 倍
綜合以上, 除操作上更為順暢外,
對伺服器的負擔以及傳輸量等分別可改善數倍到上百倍
如 5 次操作只傳一次 產品 JSON
在傳輸量上可改善 50 倍 (5*產品 JSON 壓縮後差 10 倍)
組裝回傳結果的 CPU/MEM 上可改善 5*30 = 150 倍
另外也可以避免
無法區分使用者點擊只是要選選項還是要查內容
的問題, 讓操作的 GA 資料更有意義
目前選項是帶 s_href
每次點選透過 Server 端去重新生成所有選項的 s_href
若調整成 Client 端可這麼做
不要由 s_href 帶完整 request url
改為帶該選項的查詢項目 及 查詢值 如下
data-key="cat4" data-val="1"
點擊時若為要更新內容的操作再組 url
組 url 的部份見以下 BuildURL.js 內容
var allC = {},
allP = {},
sampleJSON;
$('.group-box-body a').each(function () {
var self = $(this);
var lb = self.find('.custom-checkbox .label').html(),
key = self.attr('s_href').replace('/find-product/1?', '').split('=')[0],
val = self.attr('s_href').replace('/find-product/1?', '').split('=')[1];
if (!allC[key]) allC[key] = {};
allC[key][val] = lb;
});
$('.product-list .item').filter(function () {
return !$(this).closest('.single-selector-pp-real')[0]
}).each(function () {
var prod = {},
self = $(this),
ucph = self.find('.uc-point').html(),
uoc = self.find('.uo-stat'),
pid = self.find('a.img-block').attr('href').replace('/products/','');
ucph = ucph.substring(0, ucph.indexOf('<'));
allP[pid] = {
img: [self.find('a.img-block img').attr('alt'),
self.find('a.img-block img').attr('src')
],
dt: self.find('.date-place').html().replace('上市', ''),
n: self.find('.item-name a').html(),
ucp: ucph.replace('UrCosme指數 ', ''),
rv: uoc.html().substring(0, uoc.html().indexOf('<')).replace('使用心得', '').replace('篇', ''),
p: uoc.find('.price').html().replace('NT$ ', ''),
br: [
self.find('a.item-brand').attr('href').replace('/find-brand/', ''),
self.find('a.item-brand').html()
],
}
});
// 包含左側選單及所有產品
sampleJSON = JSON.stringify({
cb:allC,
p:allP
});
/* 只有所有產品的情形
sampleJSON = JSON.stringify(allP);
*/
var baseURL = window.location.href,
ajaxurl = baseURL,
cond = {},
data = [],
$sel = $('.group-box-body a').filter(function () {
return $(this).find('.custom-checkbox-checked')[0];
});
if ($sel.length) {
ajaxurl += '?';
$sel.each(function () {
var s = $(this);
if (!cond[s.data('key')]) {
cond[s.data('key')] = [];
}
cond[s.data('key')].push(s.data('val'));
});
for (var key in cond) {
data.push(key+'='+cond[key].join(','));
}
ajaxurl += data.join('&');
}
// 到此 ajaxurl 組合完成
console.log(ajaxurl);
function buildOptUrl () {
var baseURL = window.location.href,
ajaxurl = baseURL,
cond = {},
data = [],
$sel = $('.group-box-body a').filter(function () {
return $(this).find('.custom-checkbox-checked')[0];
});
if ($sel.length) {
ajaxurl += '?';
$sel.each(function () {
var s = $(this);
if (!cond[s.data('key')]) {
cond[s.data('key')] = [];
}
cond[s.data('key')].push(s.data('val'));
});
for (var key in cond) {
data.push(key+'='+cond[key].join(','));
}
ajaxurl += data.join('&');
}
return ajaxurl;
}
function newLoading (ajaxurl) {
loading();
$.ajax({
url: ajaxurl,
type: "GET",
data: {
is_ajax: !0
},
success: function(res) {
var $res = $(res);
closeLoading(),
// 換掉產品內容
$(".product-filter-list .product-list").replaceWith($res.filter('.product-list'));
// 換掉分頁
$(".product-filter-list .paging-wrapper").replaceWith($res.filter('.paging-wrapper'));
// 綁定分頁事件
$('.pagination a').click(function(e){
e.preventDefault();
newLoading($(this).attr('href'));
});
// 載入廣告
googletag.pubads().refresh();
$("body,html").animate({
scrollTop: 200
}, 800)
},
error: function() {}
});
}
// 給每個選項加上 data-key 和 data-val
$('.group-box-body a').each(function () {
var self = $(this);
var lb = self.find('.custom-checkbox .label').html(),
key = self.attr('s_href').replace('/find-product/1?', '').split('=')[0],
val = self.attr('s_href').replace('/find-product/1?', '').split('=')[1];
self.attr('data-key', self.attr('s_href').replace('/find-product/1?', '').split('=')[0]);
self.attr('data-val', self.attr('s_href').replace('/find-product/1?', '').split('=')[1]);
});
// 停用原本綁定的方法
$('.group-box-body a').off('click');
// 綁定新方法
$('.group-box-body a').on('click', function (e) {
// 改 checked 的狀態
// 也改 show 的狀態
e.preventDefault();
$(this).find('.custom-checkbox')
.toggleClass('custom-checkbox-checked')
.toggleClass('show');
// 如果沒有按 Ctrl Key 就換內容
if (!e.ctrlKey) {
newLoading(buildOptUrl());
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment