在线本地调试大观
线上页面有 bug 了,或者需要修改或增加某项功能时,传统的开发/调试方法如下:
- 在本地搭建一套完整的开发环境,前后端代码甚至数据库等全在本机。想修改啥就修改啥,修改验证好了发布上线即可。好处是全盘控制非常强大,坏处是环境越复杂越麻烦。当本地与线上存在差异时,有些 bug 甚至无法重现。对于云时代的前端开发攻城师来说,这种方式太笨重了。
- 本地仅保存前端代码,修改后,提交到测试环境验证。这种方式无需搭建本地环境,但调试起来麻烦,遇到棘手的 bug 时,很可能需要反复 修改代码 -> 提交代码 -> 验证功能 -> 再次修改。虽然可以通过脚本部分实现自动化,但还是不够方便。
- 将线上页面保存到本地,直接修改页面中的 css/js 引用。这招非常简明快捷,在某些场合下的确是上上之选。但坏处也很明显,页面稍微复杂一点,就有可能失灵。
为了过上快乐幸福的生活,爱折腾的攻城师们想出了一个好点子:在线本地调试。
名词解释
在线本地调试是指:
- 在线是指环境。环境是线上的,包括数据请求、cookie 等等,不是高仿,是货真价实的正品。
- 本地是指前端代码在本地,包括 js/css 等静态资源。
- 调试是指修改本地代码 + 刷新页面即可看到效果。
基本原理
很多事情,没有做不到,只有想不到。一旦开始追求美好的调试生活,立刻就能发现原理非常简单:
只要能将需要调试的代码映射到本地即可。
对前端来说,需要调试的代码一般是 js/css 文件。
html 代码有时也需要修改,可以搭建本地环境或用 Fiddler 等工具来实现,这篇博客里就不多说了。
我们将关注点聚焦到 js/css 文件的本地映射。
方式一:客户端工具
在 Windows 系统下,可以用大名鼎鼎的 Fiddler 来实现:AutoResponder Reference . 阿里巴巴中文站 UED 博客有图文教程:使用 Fiddler 提高前端工作效率
在 Mac 系统下,也有类似的工具:Charles .
客户端工具的好处是灵活,想映射什么就映射什么,想映射到哪就映射到哪。
坏处是要安装软件。在云时代,安装客户端软件显得太 out 啦。更恼人的是,Fiddler 依赖 Microsoft .NET Framework. Charles 更不用说了,是收费的。
方式二:服务端代理
比如在淘宝,可以先让所有攻城师都修改 hosts:
10.1.2.3 a.tbcdn.cn
使得 js/css 文件都通过内部代理服务器来访问。既然是内部服务器,就可以进一步通过 samba 等服务 mount 到本地。
好处是可定制性高,可以实现本地、测试、预发和线上等环境的自由切换等等功能。
坏处是对内网环境有依赖,在外网使用时,需要开启 VPN 软件。其次,要从服务器端代理回本机目录时,要通过 HFS 等客户端软件来实现,有点小麻烦。还有一个小缺陷是,当同时使用的人数过多时,服务器的性能和稳定性很重要。一旦出故障,攻城师就只能看美女了。
无论如何,淘宝聪明的攻城师们已实现了一个内部方案:UCool. 非常赞非常好用,内部人士不容错过。
方式三:利用虚拟主机与文件重写
作为攻城师,工作电脑上开一个 web 服务是家常便饭。假设我们使用的是老牌的 apache. 通过 Virtual Host 和 Rewrite Module 就可以实现本地映射。下面以淘宝为例来说明。
淘宝的 js/css 文件存放在 cdn 上,有两个域名:a.tbcdn.cn
和 assets.taobaocdn.com
. 绝大部分线上页面使用的是 a.tbcdn.cn
. 因此我们可以绑定 a.tbcdn.cn
到本地,同时保持 assets.taobaocdn.com
依旧指向线上:
127.0.0.1 a.tbcdn.cn
接着修改 apache 的配置文件:
<VirtualHost *:80> DocumentRoot "/Users/lifesinger/Sites/svn/assets/" ServerName a.tbcdn.cn RewriteEngine On # # handle combo url "/??path/to/a.js,path/to/b.js" # RewriteCond %{QUERY_STRING} ^\?.*\.(?:js|css)(?:,|$) [NC] RewriteRule ^/.*$ /combo.php [QSA,L,NS,NC] # # redirect to online version when the requested file does not exist in local file system. # RewriteCond /Users/lifesinger/Sites/svn/assets/%{REQUEST_FILENAME} !-F RewriteRule ^/(.+)$ http://assets.taobaocdn.com/$1 [QSA,P,L,NC] </VirtualHost>
combo.php
的内容如下:
<?php $LOCAL_ROOT = "/Users/lifesinger/Sites/svn/assets"; $REAL_CDN = "http://assets.taobaocdn.com"; $parts = explode("??", $_SERVER["REQUEST_URI"]); $base = $parts[0]; $files = explode(",", $parts[1]); if (strpos($files[0], ".js")) { header("Content-type: application/x-javascript"); } else { header("Content-type: text/css"); } foreach($files as $file) { $url = $base.$file; $root = $REAL_CDN; if(file_exists($LOCAL_ROOT.$url)) { echo "/* fetched from local file system */\n"; $root = $LOCAL_ROOT; } echo file_get_contents($root.$url); }
这样,要调试某个文件时,仅需要在本地 assets
目录下,按照线上的目录结构,放上需要修改的文件即可。本地没有的文件,会依旧访问线上。
这种方式很灵活强大,稍微修改修改,对绝大部分项目都适用。缺点是稍有门槛,要自己配置。对于爱折腾的攻城师来说,强烈推荐这种方式。
方式四:基于 seajs 的本地映射
如果一个页面是用 seajs 组织的,意味着所有用 seajs 加载的 js/css, 都可以映射到任意路径。我们以 markzhi.com
为例来说明。
正常访问 markzhi.com 时,加载的 js 文件都是线上的:
要将线上文件映射到本地,首先需要在访问地址上加上标识 http://markzhi.com/?seajs-debug. 注意右下角的悬浮层:
在输入框里填写上映射配置文件:http://localhost/seajs-map.js
seajs-map.js
的内容为:
define(function() { var rules = []; // the map rules for markzhi.com rules.push([ 'http://markzhi.com/assets/', 'http://localhost/~lifesinger/markzhi/portal/src/main/webapp/assets/' ]); seajs.config({'map': rules}); });
rules 还可以是正则,比如:
// replace compressed version to debug version rules.push([ /^(.*)\.js$/i, '$1-debug\.js' ]);
点击 SeaJS Debug Console 悬浮层的 Refresh 按钮,就可以看到除了 seajs 自身,其他文件到映射到本地了:
成功映射后,可以点击 Hide 按钮隐藏掉悬浮层,点击 Exit 按钮是退出 debug 模式,还原到正常状态。
注意:在 debug 状态时,页面的 title 会自动添加 [debug] 前缀以供辨识。
除了通过 ?seajs-debug
的方式来开启调试控制层,还可以通过 bookmarklet 的方式来开启:映射插件。
这种方式的好处是:方便快捷,对调试页面所在机器无要求,开发机器上也只需要有 www 服务即可。比如对于 iPad 来说,可以用 ?seajs-debug
打开配置层,让配置文件指向开发机器。这样,就可以在开发机器上写代码,刷新 iPad 上的页面立刻就可以看到修改后的效果。这种便捷,是其他几种方式很难做到的。
坏处是:要调试的代码必须是通过 seajs 加载的,这是前提条件,必不可少。除此之外,暂时想不到有什么不妥之处。
小结
我最常用的方式是:
- 基于 seajs 的本地映射。如果要调试的文件是用 seajs 加载的,这种开发调试方式非常便捷。
- 利用虚拟主机和文件重写。当要调试的文件不是用 seajs 加载的,通过这种方式,可以一次配置,永久受用。
调试方式就如编辑器一样,没有最好,只有最合适。偶尔,notepad 会比 IDE 还方便,保存到本地的调试方式也有可能比其他任何方式都快捷。清晰地知道各种方式的利弊,合理选择就好。