本文的要點不在移動端調試上,移動端調試無非就是調試頁面和調試工具之間存在分離,消除這種分離並創建連結就能解決移動端的調試問題。重點闡述的是網站建設所見即所得的調試模式下會遇到的阻礙。
當我們打開網頁,發現一個模塊沒有正確地渲染或者空白時,如果控制檯有報錯,會直接根據報錯定位到源碼位置開始 debug;如果控制檯沒有報錯,則會根據模塊名或者模塊特徵的一個值,通過全局搜索找到這個模塊的位置,然後在調試工具中斷點,單步調試,找到問題所在,此時我們可能會這樣做:
情形一:
小A同學打開控制檯,發現斷點調試不好寫代碼,於是將壓縮的源碼複製一份保存到本地,格式化,然後將線上資源通過代理工具代理到本地文件。
情形二:
小B同學早早的爲自己配了一份本地開發環境,於是他遇到問題之後,直接去源碼中定位錯誤位置,由於使用的是預處理語言,所以需要先打包編譯之後再在本地預覽效果。
情形三:
小C同學的調試方式是小A和小B的綜合版本,將線上的資源代理到本地 build 目錄文件,在 src 目錄下修改之後編譯打包到 build,然後預覽。
☞ 代理調試的煩惱
而對於比較複雜的線上環境,代理也會遇到很多障礙,比如:
線上資源 combo
出現錯誤的腳本地址爲http://example.com/path/??a.js,b.js,c.js ,它對應着
a.js
,b.js
,c.js
三個腳本文件,如果我們使用 Fiddler/Charles 這樣的經典代理工具調試代碼,就必須給這些工具編寫插件,或者在替換配置裏頭加一堆判斷或者正則,成本高,門檻高。
線上代碼壓縮
打包壓縮,這是上線之前的必經流程。由於我們在打包的環節中並沒有考慮爲代碼添加 sourceMap,而線上之前對應 index-min.js
的
index.js
也因爲安全方面的原因給幹掉了,這給我們調試代碼造成了極大的不便利。
代碼依賴較多,拉取代碼問題
很多時候,我們的頁面依賴了多個 asserts 資源,而這些資源各自分佈在多個倉庫之中,甚至分佈在不同的發佈平臺上,爲了能夠在源碼上清晰的調試代碼,我們不得不將所有的資源下載到本地,期間一旦存在下載代碼的權限問題,整個調試進度就慢下來,這是十分不能忍受的事情。比如某系統構建的頁面,頁面上的模塊都是以倉庫爲維度區分的,一個頁面可能對應了5-50個倉庫,下載代碼實爲麻煩。
最可怕的調試是,本地沒有對應的測試環境、代理工具又不滿足我們的需求,然後就只能,編輯代碼->打包壓縮->提交代碼->查看效果->編輯代碼->... ,如果你的項目開發是這種模式,請停下來,思考調試優化方案,正所謂磨刀不誤砍柴工。
☞ 開啓懶人調試模式
當看到線上出現問題(可能是其他維修網同學負責頁面的問題),腦中浮出這樣的場景:
1
2
3
4
5
6
7
8
|
複製代碼
我:"嘿,線上有問題啦!我要調試代碼!"
電腦:"好的,主人。請問是哪個頁面?"(彈出浮層)
我:浮層中輸入URL。
電腦:"請問是哪個地方出問題了?"
我:(指着電腦)"模塊A和模塊B。"
電腦:正在下載A、B資源...正在將上線A、B映射到本地...自動打開A、B對應文件夾
我:編輯代碼,然後實時預覽效果。
|
在這裏我們需要解決這樣幾個問題
- 將頁面對應的所有倉庫/資源羅列在用戶面前
- 下載資源的權限提示和權限處理
- 線上資源解 combo,然後映射到本地
當然調試之後,可以還有一個操作:
1
2
|
我:"哈,已經修復了,幫我提交代碼~"
電腦:正在diff代碼...收到確認提交信號,提交到預發環境...收到已經預覽信號...正在發佈代碼...收到線上迴歸信號...流程結束
|
除了 debug 代碼,我們需要做的就只是用眼睛看效果是否 ok,整個流程優化下來,體驗是很讚的!
☞ 解決代理遇到的問題
上面我們提到了三個問題,平時開發遇到最頭疼的一個是 combo ,曾經我們頁面上的代碼加一個?_xxx 參數就能夠直接開始調試模式,那是因爲程序的入口只有一個,而且所有腳本的依賴也打包到一個叫做 deps.js 文件中,加上調試參數之後,可以將原來 combo 加載的文件: http://example.com/path/??a-min.js,b-min.js,c-min.js ,按照非 combo 的方式加載:
1
2
3
|
http://example.com/path/a.js
http://example.com/path/b.js
http://example.com/path/c.js
|
上面的代碼可以輕鬆地代理到本地,但是有的系統生成的代碼並沒有 deps.js 文件,它是將腳本直接輸出到頁面上:
1
|
<script
src="http://example.com/path/??a-min.js,b-min.js,c-min.js"></script>
|
☞ 解決 combo 問題
此時通過 Fiddler/Charles 工具比較難滿足需求,對於這個問題有兩個處理方案:
1). 瀏覽器請求全部代理到本地的一個服務
首先寫一個本地服務:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var
http =
require('http');
// npm i http-proxy --save
var
httpProxy =
require('http-proxy');
var proxy
= httpProxy.createProxyServer({});
var server
= http.createServer(function(req,
res)
{
console.log(req.url);
if(req.url.indexOf("??")
> -1){
// combo資源讓 3400 端口的服務處理
proxy.web(req,
res,
{ target:
'http://127.0.0.1:3400'
});
}
else {
// 直接返回
proxy.web(req,
res,
{ target:
req.url
});
}
}).listen(3399,
function(){
console.log("在端口 3399 監聽瀏覽器請求");
});
|
代碼的意思是,利用 http-proxy 這個 npm 包,代理瀏覽器的請求,瀏覽器上使用 switchSharp 設置本地代理爲
http://127.0.0.1:3399 ,當請求過來,先判斷 url,如果 url 中包含了
??
則將其作爲 combo 資源處理,代理給本地的另一個服務
http://127.0.0.1:3400 ,這個服務收到請求後會將 combo 內容分解成多個,全部請求完之後再吐出來。
2). 使用本地服務請求 html 代碼,替換 html 代碼內容
使用強制手段(源碼替換)將代碼解 combo,比如源碼頁面爲:
1
2
3
|
<!--
html code
-->
<script src="http://example.com/path/??a-min.js,b-min.js,c-min.js"></script>
<!--
html code
-->
|
使用本地服務請求這個url,然後轉換成:
1
2
3
4
5
|
<!--
html code
-->
<script src="http://example.com/path/a.js"></script>
<script
src="http://example.com/path/b.js"></script>
<script src="http://example.com/path/c.js"></script>
<!--
html code
-->
|
實現這個操作的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
var
http =
require('http');
// npm i request --save;
var
request =
require('request');
http.createServer(function(req,
res){
var
path =
req.url.slice(req.url.indexOf("path=")
+ 5);
console.log(path);
if(!path)
{
res.write("path is empty");
res.end();
return;
}
request(path,
function (error,
response,
body)
{
if
(!error
&& response.statusCode
== 200)
{
console.log(body);
// 代碼替換
body
= body.replace('<script
src="http://example.com/path/??a-min.js,b-min.js,c-min.js"></script>',
'<script
src="http://example.com/path/a.js"></script>\
<script src="http://example.com/path/b.js"></script>\
<script
src="http://example.com/path/c.js"></script>'
);
res.write(body);
res.end();
}
});
}).listen(3399,
function(){
console.log("listening on port 3399");
});
|
比如請求http://127.0.0.1:3399/?path=http://www.taobao.com ,即可拿到淘寶首頁的源碼,然後對拿到的代碼做替換。
☞ 解決代碼壓縮問題
對於這個問題,建議在線上放兩份源碼,一份是壓縮源碼,一份是未壓縮源碼,當頁面 url
存在 debug
參數的時候,返回未壓縮版本,正常返回壓縮版本。當然,也可以採用上述方式處理問題。
不過,更合理的方式應該是 sourceMap
,前端沒有祕密,壓縮代碼只是增加了 hacker 的攻擊成本,並不妨礙有能力的 hacker 借系統漏洞入侵。所以可以爲源碼提供一份
sourceMap 文件。
1
2
3
4
5
6
7
8
9
10
|
var
gulp =
require('gulp');
var sourcemaps
= require('gulp-sourcemaps');
gulp.task('javascript',
function()
{
gulp.src('src/**/*.js')
.pipe(sourcemaps.init())
//.pipe(xx())
.pipe(sourcemaps.write())
.pipe(gulp.dest('dist'));
});
|
關於 sourceMap 的 gulp 插件配置,詳情可以戳這裏。不僅僅是 JavaScript,CSS 也有 source maps,這個信息可以在 Chrome 控制檯的設置選項中看到:
☞ 代碼的拉取
如果一個項目只有你知道如何修改,那這個項目的技術設計就有點糟糕了,爲了讓衆人都能處理你項目中的問題,一定要需要一個簡潔的模式爲開發者快速搭建測試環境,文檔是一方面,如果有個一鍵操作的命令,那就更棒了!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# 啓動腳本
start:
createFile getMod
getPage
# 創建目錄
createFile:
@[
-d
module ]
|| mkdir
module
@[
-d
page ]
|| mkdir
page
# 拉取模塊倉庫,這裏有幾十個,比較費時,請耐心等待...
getMod:
cd
module;
\
for
i in
$(MODS);
do \
[
-d
$(MODPATH)$$i
] ||
git clone
$(MODPATH)$$i;
\
git co
-b
master;\
git
co -b
$(MODSV);
done
# 拉取頁面倉庫,tbindex
getPage:
cd page;
\
@[
-d
tbindex ]
|| git
clone $(PAGEPATH)$PAGE;
|
上面是一個 MakeFile
的部分代碼,作用是創建開發目錄,拉取分支信息,然後開始服務器,打開瀏覽器,使用 IDE 打開目錄,萬事就緒,只等主人敲代碼。
整個流程就一兩分鐘,完成開發之前所有的準備工作。這個腳本不僅僅是給自己使用,如果其他人也需要參與開發,一個命令就能讓參與者進入開發模式,加上文檔說明,省卻了很多溝通成本。
☞ 在線調試實踐(一個系統的調試工具)
輸入需要調試的頁面URL(如 http://www.taobao.com):
插件會分析 DOM,遍歷拿到頁面所有被引用到的倉庫:
選擇需要調試的模塊(顆粒度細分到了html/js/css),點擊調試按鈕,可以看到調試頁面的資源都會引用本地下載的文件。
☞ 小結
優化流程、優化架構是我們矢志不渝堅持的方向,本文主要闡述,編輯代碼到調試線上效果的過程,提出瞭解決 combo 和代碼壓縮等問題的方案和建議。希望可以給不擅長代理調試的同學一點啓示。文章來源:李靖(@Barret李靖)。