岁月如歌

用开放的心态,打造专业的人生。

在线本地调试大观

with 21 comments

线上页面有 bug 了,或者需要修改或增加某项功能时,传统的开发/调试方法如下:

  1. 在本地搭建一套完整的开发环境,前后端代码甚至数据库等全在本机。想修改啥就修改啥,修改验证好了发布上线即可。好处是全盘控制非常强大,坏处是环境越复杂越麻烦。当本地与线上存在差异时,有些 bug 甚至无法重现。对于云时代的前端开发攻城师来说,这种方式太笨重了。
  2. 本地仅保存前端代码,修改后,提交到测试环境验证。这种方式无需搭建本地环境,但调试起来麻烦,遇到棘手的 bug 时,很可能需要反复 修改代码 -> 提交代码 -> 验证功能 -> 再次修改。虽然可以通过脚本部分实现自动化,但还是不够方便。
  3. 将线上页面保存到本地,直接修改页面中的 css/js 引用。这招非常简明快捷,在某些场合下的确是上上之选。但坏处也很明显,页面稍微复杂一点,就有可能失灵。

为了过上快乐幸福的生活,爱折腾的攻城师们想出了一个好点子:在线本地调试

名词解释

在线本地调试是指:

  1. 在线是指环境。环境是线上的,包括数据请求、cookie 等等,不是高仿,是货真价实的正品。
  2. 本地是指前端代码在本地,包括 js/css 等静态资源。
  3. 调试是指修改本地代码 + 刷新页面即可看到效果。

基本原理

很多事情,没有做不到,只有想不到。一旦开始追求美好的调试生活,立刻就能发现原理非常简单:

只要能将需要调试的代码映射到本地即可。

对前端来说,需要调试的代码一般是 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.cnassets.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 加载的,这是前提条件,必不可少。除此之外,暂时想不到有什么不妥之处。

小结

我最常用的方式是:

  1. 基于 seajs 的本地映射。如果要调试的文件是用 seajs 加载的,这种开发调试方式非常便捷。
  2. 利用虚拟主机和文件重写。当要调试的文件不是用 seajs 加载的,通过这种方式,可以一次配置,永久受用。

调试方式就如编辑器一样,没有最好,只有最合适。偶尔,notepad 会比 IDE 还方便,保存到本地的调试方式也有可能比其他任何方式都快捷。清晰地知道各种方式的利弊,合理选择就好。

Advertisements

Written by lifesinger

July 24, 2011 at 12:54

Posted in Articles

21 Responses

Subscribe to comments with RSS.

  1. 受益匪浅,确实想让本地同线上环境一样是非常困难的
    喜欢方式三

    richieliu

    July 24, 2011 at 13:01

  2. BTW: ucool由@czy88840616 开发,已经开源~

    https://github.com/ued-tools-team/ucool

    yyfrankyyXu

    July 24, 2011 at 14:14

  3. 更完整的combo.php可以参照这里:
    https://github.com/jayli/combo

    jayli

    July 24, 2011 at 14:25

  4. 三年前已经考虑过这个问题,方法也是一样.

    bkkkdim

    July 27, 2011 at 14:09

  5. […] 实时本地调试线上页面2011年07月27日 | 1 次浏览  关于在线本地调试的方法,玉伯的文章《在线本地调试大观》已经分析叙述非常全面了。这里提供另外一种思路,供前端工程师们参考。我觉得可能是最简单的方式,也容易扩展。 […]

  6. 一般都用方式一,用习惯了,或者没有人告诉我更好的方法

    N

    July 28, 2011 at 15:31

  7. 个人觉得方式四需要起服务还是比较麻烦,要是支持file://这样本地文件读取就更方便了

    TerryLee

    July 28, 2011 at 17:02

  8. 用js本身来做跨平台的加载本地文件进行线上调试很强大.

    will

    August 8, 2011 at 13:32

  9. […] 这样,就能做到一个应用的所有模块的配置都集中放在一起,当某个模块有新版本,需要升级时,修改下配置上版本号即可。还可以加入 map 和 debug 等参数来自由的控制时间戳,可参考:在线本地调试大观 […]

  10. 非常不错的想法,受益匪浅

    MrHack

    September 8, 2011 at 14:09

  11. […] 和 less 插件扩展了支持的模块类型。SeaJS 提供了 map 插件,方便开发调试:在线本地调试大观。对于 order 功能,推荐组合使用 LABjs 来实现,需要 domReady 时,使用 jQuery […]

  12. 第一种用代理的方法一样可以调试iPad上的程序啊,而且可以调试任意页面,不需要改hosts

    ayanamist (@gh05tw01f)

    October 27, 2011 at 21:37

  13. […] 映射插件,可用来做 在线本地调试 […]

  14. […] 作者:岁月如歌 […]

  15. 利用虚拟主机与文件重写,我现在碰到本地服务localhost不能访问的问题,请问玉伯是不是哪个地方配置错了!

    hanshuang

    March 27, 2012 at 20:31

  16. 翻越千山万水,终于找到了这里~~

    珊瑚海

    April 1, 2012 at 18:27

  17. 能看到这些,最想说的就是感谢。 Thanks 岁月如歌。

    Wang

    November 1, 2012 at 14:33

  18. 有收获,mark了

    andy

    November 10, 2012 at 22:20

  19. […] 在线本地调试大观 […]

  20. Wow that was odd. I just wrote an really long comment but after I clicked submit my comment didn’t show up. Grrrr… well I’m not writing
    all that over again. Anyways, just wanted to say great blog!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s