Mac OS上各种开发环境的配置(未完成)

Mac OS下各环境的配置。

很多开发人员喜欢 Mac OS,因为它基于FreeBSD,有原生的命令行工具,配置各种开发环境都很方便。我个人偏爱Windows,我觉得各种可视化用起来更爽。

这篇文章介绍如何在Mac OS 10.10 Yosemite上配置各种开发环境,范围以前端所需为主。

Xcode

无论你是否准备开发iOS或者Mac OS上的应用,Xcode最新版都是必须的。因为里面包含了系统必须的命令行工具和编译工具,没有它们支持,我们就无法安装后面那些东西。

好在Xcode是免费的,虽然体积巨大(而且早年不支持断点续传,我第一遍Xcode装了半个月),但只要你有恒心有毅力,智商正常,就都能装上。

方法:

  1. 打开App Store
  2. 搜索Xcode
  3. 安装
  4. 安装完成后,在命令行里运行xcode-select -p,如果显示/Applications/Xcode.app/Contents/Developer则表明Xcode 命令行工具已经安装成功,否则的话,执行xcode-select --install安装

homebrew

工欲善其事,必先利其器。在*nix环境下装东西,一个好的包管理工具是必须的。我使用的是Homebrew,它的安装非常简单:

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

因为Mac OS默认包含并启动了Ruby环境,所以此时系统就开始执行homebrew的安装。关于brew的用法,可以看它的官网,或者运行brew help阅读帮助。

nodejs

有了homebrew,一切都好办了。

brew install nodejs
node -v

不过我更喜欢标准版,在nodejs的官网下载dmg安装包,直接安装即可。

ruby

Mac OS默认包含ruby,不过我喜欢最新版,所以还是拜托给homebrew好了。

brew install ruby

homebrew会把包安装在/usr/local/Cellar目录下,然后通过软链接链接到/usr/local。有些系统默认包含的组件已经注册在/usr/local或者/usr/bin,创建链接可能会失败。不用担心,homebrew提供了详细的帮助信息,仔细阅读,按照提示一步一步做,就OK了。

Python & httpie

python我用的很少,耳闻3和2差别巨大,也就不敢追新。所以我用系统自带的Python,只是安装了 httpie 这个工具来调试远程数据。安装一样是通过homebrew:

brew install httpie
http httpie.org

apache httpd

Apache httpd是Mac OS的默认组件,直接运行sudo apachectl start即可开启apache服务器,然后访问 http://localhost/ 就能看到默认页面了。

系统默认的文档地址为 /Library/WebServer/Documents,把静态的页面放进去,就能正常浏览,用来测试还是蛮方便的。不过我觉得不算太实用。因为一来埋得很深,通过Finder无法直接到达;二来作为公司配的开发机,一般来说我不建议在公共目录里做东西。

不过配置起来也不复杂。打开 /etc/apache2/extra/httpd-vhost.conf,里面应该有两个没有真正起作用的范例配置。基本上按照那里即可。

先放出去,回头再补充好了……

漏装php55w-mbstring导致中文邮件乱码

yum安装新版本php需要手动安装各种依赖。

朋友的WordPress发中文邮件总是乱码,喊我帮忙看看。很奇怪,后台、文章里的中文都能正常显示,看起来一切正常;我在我电脑上搭了一套,同样的代码,发邮件也没问题。

后来打开phpmailer的debug模式,发现什么都对,就是中文内容都是问号。

继续往上找到发邮件的函数,运气不错插件留了filter,遂修改模板,add_filter强制转换内容。

转换前先检查,不是UTF-8再转:

    if (!mb_detect_encoding($content)) {
      $content = iconv('ASCII', 'UTF-8', $content);
    }
    return $content;

结果代码传上去报错,说没有mb_detect_encoding,然后想起来yum安装php时确实默认不包含很多扩展,于是手动安装yum install php55w-mbstring。然后重启apache,发邮件测试,正常了。

我又想是不是缺少多字节文本模块(Multibyte String)导致原先无法发送中文呢?去掉filter,仍然正常,确实如此。

总结

yum安装新版php需要增加源,而新版的源默认不包含很多常用的库,使用的时候最好都装上。WordPress的翻译机制面对多字节文本时,编码不对不会报错,也需要小心。

使用Backbone的正确姿势

2012年来到点乐之后,我开始大规模开发Web单页应用。期间Backbone一直是我的主力框架,不过直到最近我才明白使用Backbone的正确姿势是什么。希望本文能让后来者少走些弯路。

2012年来到点乐之后,我开始投身Web应用开发。当时我选择Backbone作为主力框架,刚开始做难免会带着各种旧习惯,只有一边使用一边摸索。最近我终于搞明白使用Backbone的正确姿势,记叙于此,希望能让后来者少走些弯路。

使用框架时,我的原则是:既然使用了这个框架,就应该按照这个框架的思路解决问题,一定要把它的功能都用上,要按照它的方式组织代码,这样才对得起学习使用的成本。所以下面的“正确姿势”,自然也是奔着全面使用Backbone的内建功能、尽量符合Backbone的设计思路,这样的目的总结的。

本文假定读者熟悉JavaScript,对Backbone有一定程度的了解。

View

我比较同意《JavaScript设计模式》中的观点,Backbone的设计很难讲是更接近MVP还是更接近MVC,而是在Web前端这个大环境下,基于其技术特点,吸取各种设计模式的优点做出最合适的的实现。Web应用中,视图的实现自然应该交给HTML和CSS负责,而在数据变化时更新视图,以及响应用户操作的事情,就交给Backbone.View

Backbone.View提供非常直观的events帮助我们注册事件,免受使用jQuery的.on.on.on之苦。事件被委托给$el,不仅节省资源,并且做列表类应用需要操作多个子节点时,无需再绑定事件。

不过请注意,这里的事件委托仍然有赖冒泡机制,所以诸如loaderror等不冒泡的事件无法委托给$el处理(submit在早期IE下也不行,但是jQuery会代发冒泡版),只能手工侦听具体节点。另外,Backbone会把处理函数代理给View的实例,所以函数中的this指向是实例,而不再是触发事件的DOM元素。

列表的前世今生

接着,来点代码,看看最常用的UI范式——列表。Backbone中,搭配使用Backbone.CollectionBackbone.View可以方便的实现列表类组件,不过因为Backbone本身的限制很少,实现方法很多。早期我习惯于通过reset方法和reset事件来操作列表,后来慢慢体会到,接下来的做法更合适。

HTML部分:

    // 这里假设我们要做一个todo列表
    <ul>
      <script type="text/x-handlebars-template">
      <li id="{{id}}">
        <input type="checkbox" name="todo" value="{{id}}">
        {{title}} <time datetime="{{create_time}}">{{create_time}}</time>
      </li>
      </script>
    </ul>

JavaScript部分(使用Handlebars作为模板引擎):

    var ListView = Backbone.View.extend({
      fragment: '',
      events: {

      },
      initialize: function () {
        this.template = Handlebars.compile(this.$('script').remove().html().replace(/r|n|s{2,}/g, '');

        this.collection.on('add', this.collection_addHandler, this);
        this.collection.on('remove', this.collection_removeHandler, this);
        this.collection.on('sync', this.collection_syncHandler, this);
        this.collection.fetch();
      },
      collection_addHandler: function (model) {
        this.fragment += this.template(model.toJSON());
      },
      collection_removeHandler: function (model) {
        this.$('#' + model.id).remove();
      },
      collection_syncHandler: function () {
        if (this.fragment) {
          this.$el.append(this.fragment);
          this.fragment = '';
        }
      }
    });

之所以建议这么做,因为Backbone.Collection有三个特点:

  1. model的事件会由collection向外转播(相当于冒泡)
  2. 取得数据后,collection会逐个创建model,每次都会广播add事件
  3. .fetch().set()时,新数据中不曾出现的对象(以id为标识)会被移除

列表中还有一些技巧,将在后文呈现。

钦差大臣options

早先取数据时为了向服务器传递变量,或者使用特定的jQuery参数,我经常覆写.sync()方法,甚至直接使用$.ajax()。后来发现,各函数的options(除去少数几个return之外)都会传递到下一个函数,直至最后;期间每一步的参数都会合并进来向后传递。

所以我们只需要在第一次调用函数时,将需要的值放在options里即可 。比如,要保存model里的数据,API服务器和当前服务器不在同域,就可以这样:

// 关于xhrFields,可以参考jQuery文档:http://api.jquery.com/jQuery.ajax/
model.save(null, {
  xhrFields: {
    withCredentials: true
  }
});

其实,options是个很巧妙的设计。Backbone作为框架,必须给其它库和业务逻辑留出足够的空间,使用options,随便其他开发者传什么值,最后都能传回业务逻辑中。另外,当参数个数比较多的时候,使用options也有助于阅读代码。

options进阶

基于“从头传到尾”这个性质,我们还可以发明一些特殊用法。比如上一节的例子,我希望给每个元素增加一个删除按钮,点击后移除元素。重点是:以渐隐动画来表现移除动作。在Backbone中,.destroy()方法会通知服务器删除对象(.remove()方法只是从当前集合中移除model,无法满足需要),并且触发destroy事件,我们可以在这里插入动画;但立刻就又会触发remove事件,所以只是在collection_destroyHandler的时候fadeOut是不够的,还要防止collection_removeHandler在动画结束前直接移除dom。

这个时候,我们就可以利用optionsdestroy()支持参数{wait: true},可以等待服务器返回成功后才移除model。于是我们就能确保对象已经从服务器上清除后,再以视图体现;同时,只要检查options里是否包含这个属性,就可以知道当前触发collection_removeHandler的是.destroy()还是.remove(),再决定是否立刻移除节点就很容易了。(其实随便传个什么标记都可以,这里使用{wait: true}可以获得更好的体验。)

    // 一致的代码我就不写了
    events: {
      'click .delele-button': 'deleteButton_clickHandler'
    },
    initialize: function () {
      // 一致的代码
      // ....
      // 不再重写
      this.collection.on('destroy', this.collection_destroyHandler, this);
    },
    collection_destroyHandler: function (model) {
      this.$('#' + model.id).fadeOut(function () { $(this).remove(); });
    },
    collection_removeHandler: function (model, collection, options) {
      if (!options.wait) { // 没有wait
        this.$('#' + model.id).remove();
      }
    },
    deleteButton_clickHandler: function (event) {
      var id = $(event.target).closest('li').attr('id');
      this.collection.get(id).destroy({wait: true}); // 服务器返回确认才真正移除
    }

利用options能达成的效果还有很多。比如,有些时候我们想往model里放一些特殊用途的数据,只在渲染时候用,不保存到服务器上。通过研究源码我们发现,同样调用.toJSON().save()会传入含有各种参数的options,而手动调用则不会。于是我们又能根据options里的属性返回不同的数据,满足不同的需要。

这些实现本文不再一一详述,大家请自行琢磨,欢迎留言探讨。

和服务器步调一致—— sync & fetch

有些习惯延续自之前的项目,比如请求远程数据。早期我总想用$.ajax从服务器端把数据取来,然后再resetcollection或者model。直到最近开发新项目:tiger-prawn + lemon-grass(也即点乐后台V5),我开始开发RESTful的后端,配合专为RESTful设计的Backbone,贯彻“同步”思路,于是我终于醒悟,发出文章开头那句感慨——我终于明白使用Backbone的正确姿势了。

“同步”是Backbone非常重要的设计思路,也是用户体验里非常重要的一环。我们经常要面对多端的环境,尤其是开发企业级应用,多人协作办公,必须保证数据在每个终端看起来一致。我一开始很难理解为什么.fetch()回来数据后,除非指定{remove: false},否则新数据中不再存在的对象会被移出collection。这个疑问后来在开发集体协作的todo列表时得到解答。

还拿前文的todo列表做例子。想象列表面向一个工作组,组内成员均从列表当中接受工作,完成后勾上checkbox表示结案;别的地方还会有需求源源不断的塞入这个列表中。这个时候,数据同步就显得尤为重要。某甲勾掉一条任务,需要从其他人的列表中勾掉同一条任务。由于我们数据交互的主要手段仍然是Ajax,服务器无法向浏览器发出指令,只能由浏览器通过返回值进行操作,所以,将“同步”作为强制性要求,本地collection根据返回值的变化,该修改的修改,该移除的移除,就是最佳选择了。

trigger: false未必都有用

最后再说个小问题。有时候我希望改变路径,但不要刷新页面,比如创建文章/article/create/,.save()后得到文章id,跳转到/article/id。因为仍然处于编辑状态,所以不需要刷新页面。一开始我以为router.navigate(‘#/article/id’, {trigger: false});,加个参数{trigger: false}`就可以防止路由生效,后来发现不行。

出于种种原因,比如服务器没做重定向,我一直没用pushState。此种状态下,Backbone使用setInterval检查地址栏变化,所以只要地址栏有修改,就会触发相应的路由。

相关项目

下面是我做过并且在维护的一些使用了Backbone的项目。这些项目未必都做到了以上几点,所以仅供参考。

团队培训项目团队培训项目二期,其实从这两个项目中就能看出我思路的变化。

游戏宝典 手机应用,虽然项目停摆了……找时间更新移植到phonegap上。

总结

Backbone是一个轻量级,入侵程度很低的框架。可以很方便的结合各种其他库来使用,对使用者的要求也很少,易于学习。不过如果能以正确的姿势操作,又能达到事半功倍的效果。这篇文章写得很费劲,有些东西总是感觉话到嘴边写不出来,反复修改多次对最后两段仍不满意。哎,以后再说吧,先发了。

HTML5跨域开发

HTML5中提供了跨域加载数据的方法,让我们得以从JSONP或者Flash中介等各种绕行方案中解脱出来,更加顺畅地与服务器交流。另一方面,因为PHP是最好的语言……所以在它与Node.js之间,我选择前者作为后端语言开发内容服务。这篇文章记录使用jQuery+PHP开发跨域应用时的小心得。

HTML5中提供了跨域加载数据的方法,让我们得以从JSONP或者Flash中介等各种绕行方案中解脱出来,更加顺畅地与服务器交流。另一方面,因为PHP是最好的语言……所以在它与Node.js之间,我选择前者作为后端语言开发内容服务。

这篇文章记录使用jQuery+PHP开发跨域应用时的小心得。

身份验证

做身份验证,最简单的办法就是使用PHP的SESSION保存用户信息,于是就要用到Cookie。默认情况下,跨域Ajax请求发起时候不包含Cookie,需要我们主动将XHRwithCredentials属性设为true才行。

jQuery会把XHR封装成jqXHR,并且不暴露真正的XHR(说实话这点有点难以理解,尤其是在做上传进度条的时候)。然后它提供一个给真正XHR赋值的接口xhrField,所以写成代码就是这样事儿的:

$.ajax(url, {
  xhrField: {
    withCredentials: true
  }
}

各种HTTP头

如果不需要验证用户身份,直接在HTTP头中输出Access-Control-Allow-Origin: *即可。

我的产品需要验证,那么首先,HTTP头中必须有Access-Control-Allow-Credentials: true;此时对域的限制也严格许多,不再允许像前面那样使用*放开给任何来源,必须指明哪个具体域可以接受。

关于Access-Control-Allow-Origin的值,规范中的说明是“域名列表或null”,然则接下来的“注意”有点诡异:“实际生产中,‘列表或null’要求更严格。你可以认为它实际只允许单一域名或null,而非空格分隔的域名列表。”——既然如此你干脆写个“域名或null”不就完了……

总之对于我们而言,返回的HTTP头中还要包含Access-Control-Allow-Origin: http://域名,指定允许作为来源的协议、域名、端口,并且只能有一个(组)。因为通常来说我们开发环境和生产环境不一样,所以这里的域名最好不要写在服务器配置里;使用PHP,通过$_SERVER['HTTP_ORIGIN']取出访问来源,与白名单比对,通过后再输出相应的头,更加合适。

调试

我选择JSON作为前后端交流的格式。为了方便浏览器解析(也是HTML5的要求),我还返回了Content-type: application/json头。

使用PHP少不了使用Xdebug。出现错误时,Xdebug会返回完整的栈,有利排查。但是为了方便阅读,Xdebug还会给返回信息套上<table>结构,这时Chrome的Network工具就会把它解析成奇怪的格式,所以Content-type一定要最后和数据一起返回。

与之相反的是前文说到的Access-Control-Allow-OriginAccess-Control-Allow-Credentials,这二位必须放在最前面。不然如果出现500错,响应头不包含这两个跨域标记,Chrome就会理所当然地不显示返回内容,也就无法看到错误描述,根本无法排查。

参考资料

  1. Using CORS
  2. Cross-Origin Resource Sharing
  3. HTTP access control (CORS)
  4. jQuery.ajax()

2014新年岘港-会安游记

自己开团并不如想象般顺利,不管是人迹罕至的岘港海鲜大排档、虽有无敌海景却地处偏僻的别墅、超售的长寿酒店……简直是老天爷故意为难第一次带队出游的我们俩。不过总归是难得的体验,兴许更难有下次了。今年剩下的旅行,期待中。

去年的游记,埋了一年多,挖出来掸掸灰,发布……

继续阅读“2014新年岘港-会安游记”

2015年计划

一年一度的计划和总结时间,这次是2015年度。

又到了一年一度的总结与计划时间,那就开始吧。

首先回顾下去年

去年我的生活再次发生巨变:我离开了生活八年的北京,南下广州。这次搬家是综合国家政策和北京现状,反复考虑之后决定的,开始没有把握得到老板的支持,我跟老婆都做好了失业的精神准备。其实来个Gap Year也不错嘛。

幸运的是,老板给予我很大支持。我们按照Plan A,国庆节搬到广州,直接在广州分公司就职。时间过得真快,一眨眼已经3个多月了。广州生活很好,因为房价的关系,生活成本比北京低很多;或者说,同样的支出,生活标准会高不少。

工作方面不算太顺利。

先是年初上的手机app项目,产品设计很炫酷,但是安卓系统实在不给力,反复折腾了很久,虽然收获很多,但是最终大家还是裁定先搁置比较好。

同期的后台,我试图改进之前的架构,创造出一套强健的企业级后台框架。但是有些问题当时没想透彻,导致最终结果并不理想,加上移动端不给力,后来一起放弃了。不过,在新的后台产品中,我得以吸取教训,做出了比较理想的产品。

至于Nervenet,当前版本0.1.9。从版本号可以看出,初始两大功能完成了一半:依赖注入和实例构建暂时没问题,包管理和加载还没放在生产环境中应用过。经过学习AMD和CMD,我觉得之前的包管理设计过于想当然,准备重构成CMD规范的。

接着就是新年计划了

  1. 减肥,目标105KG
  2. 篮球恢复到ZOL时水平
  3. 完成Nervenet,用CMD重构包管理模块
  4. 完成新的后台框架,建立健全开发模式和文档
  5. 将游戏泡泡客户端移植到Phonegap+Crosswalk
  6. 完成几个系列文章
    1. 企业级后台开发
    2. Hybrid应用开发
    3. ActionScript插件模式
  7. 旅游
    1. 带老丈人丈母娘去台湾
    2. 带我爸妈去台湾
    3. 甲米
  8. 看10本以上的技术书籍
    1. 《JavaScript框架开发》
    2. 《JavaScript设计模式》
  9. 看5本小说
    1. 龙枪传奇123
    2. 古董局中局23
  10. 每周至少写一篇博客
  11. 建立点乐广州研发团队
  12. 肉大师复活
  13. 去香港背一台iMac 27 Retina回来

山维空间2014

今年除了原计划的系列文章外,还准备把一些以前写的,现在看来有问题的文章更新一下。

(图为花莲七星潭,与文章无关。明天去台湾呀,咿呀咿呀哟~~)

开博客以来,虽然也挂着广告,但我其实并不是很操心访问量——我当然希望有人来看,但是因为懒,不想太花时间……而且你看电视上网站一上线就会有人访问……同样的缘故,我一直只向Google提交sitemap,所以百度开始并未收录这个博客。今年国庆后,不知为何百度突然开始引流,访问量蹭蹭涨起来。但是广告收入几乎一点没变……

WordPress每逢年底都会给一份总结报告,其中“TOP文章排行榜”引起了我的注意。访问量最高的文章里,有些写作时间较早,可能当时我的认识理解还不到位,也可能技术本身发生了不小的变化,总之某些内容已经不适用了。所以今年除去早就计划好的长篇连载之外,我准备把这些文章也翻出来更新一下。

以下就是今年的TOP10文章:

导出Table数据并保存为Excel

这篇文章还算比较新,问题不大。关于多表格的操作可以补进去。

禁用鼠标滚轮事件

说实话我很不明白为啥这篇文章的访问量这么高,可能是页游开发的同行吧。看内容的话,随着浏览器升级似乎也该更新了,不过考虑到现在Flash的市场状况,往后放吧。

HTML5的File API应用

这篇文章写于肉大师第一版时,很早以前。正如我在更新中所说,当时我弄混了FileFileSystem。其实这个题目很大,还是写成系列文章罢。

悲催的Android Webview——记新版广告墙开发Android Hybrid App四大坑原来早期Android的WebView真的很奇葩

关于Android Hybrid应用的几篇文章,内容没太大问题,多半是表述方面的,不很影响阅读或者后来的实施。长篇连载时会好好说说。

Backbone.Collection.fetch小优化一则

使用Backbone两年之后,我终于明白这才是正确姿势……回头写一篇Backbone最佳实践吧。

本地部署weinre帮助移动开发

工具应用,没什么问题。

重写Backbone.js的加载动作

刚开始使用Backbone那会儿写的,问题比较大,得改一改。

Phonegap 2.6在Android上的Icon设置

其实问题不在phonegap,而是小米手机的图标刷新很慢。

总结

一些排名靠后的文章也有问题,我就不一一列举了。整体说来,可以分成三部分:Backbone最佳实践、Hybrid应用开发、HTML5文件API。慢慢写吧。

不要怕花钱

就我个人的生活经验来看,不要怕花钱,学会花钱,今天花掉的钱明天翻倍来找你。

前些天微博上有篇《一个老程序员给年轻程序员的13个发自内心的忠告》,我觉得写得很好,大部分我都同意,唯独第二点:

不要轻易换笔记本电脑,不要跟潮流,不要买过多的电子产品,不要过于频繁的更换手机。

我不同意。就我个人的生活经验来看,刚好相反:不要怕花钱,学会花钱,今天花掉的钱明天翻倍来找你

讲两个我自己的小故事。

绘图板

我上大学那年,2002年,电商刚刚冒头,淘宝还没出现,卖场仍然是主流。我和我爸都是电脑爱好者,几乎每周到要到我们那儿的电脑城去逛一圈。某天,忘记是不是要帮亲戚买写字板,我们偶然逛到一家卖绘图板的铺面,他们家主营Wacom各款绘图板,当时我特别想买一块。我小时候画画有点天赋,参加过比赛拿过小奖,给班上办板报什么的很多,大的成就倒真没有。

我看中的那块绘图板叫非凡630,512级压感,绘图区域7吋,当时售价1050,确实不是个小数目。而且我考上的专业是应用化学,可想而知几乎没有用到绘图板的机会。但是我爸还是给我买了。

后来,我使用这块绘图板的时间加起来可能不超过40个小时,不用说WOW或者文明了,我估计我花在大话西游(电影)的时间都比它长(这是另一个故事……)。似乎买这块板子是完全浪费了,是这样么?

那几年,Flash如日中天,优秀作品和突出人物不断涌现,有一点绘画才能的我自然也蠢蠢欲动。工欲善其事,必先利其器。盗版软件分分钟装好,鼠标键盘绘图板齐备,开搞!一边搞还要一边学啊,熟悉我国教育方面优良传统的人都知道,学校里自然没开这方面的专业课程,不怕,有图书馆。于是,改变我一生的两本书登场了:《闪客实战》《Flash XML实用开发技术》(感谢Google,我都忘记这本书具体名字了)。这两本书成功地将我从“做动画”带到了“做开发”的路上。2006年毕业后,我凭借Flash开发技能成功以应用化学专业应届生的身份踏入互联网行业,直至今日。

如果当年没有买绘图板,我应该不会去玩Flash,也不会去学ActionScript,于是不会被201录用成为前端工程师。冥冥之中,我爸斥巨资给我买的这块我几乎没用过的绘图板成了我事业当中一块重要的垫脚石。

博客空间

也是几年前,博客大热,我既爱现,又好为人师,自然要写。当时公司有博客,也要求大家都用自家产品,我就在上面写。有几篇文章的访问量和评论数都相当高(访问量高的那几篇是关于red5的,评论数高的自然是自黑的),可惜现在因为种种原因找不到了。后来又由于种种原因,我要离开这家我效力多年的公司,思前想后我决定自己买空间搭一个博客,于是便有了现在这个网站。

这个网站是最土的那种,空间,没有root,也没有什么数据库、存储之类的服务,当时我根本不懂这些,就觉得国外空间挺便宜,域名也好用,就买了。后来发现在上面做开发不好搞,就又买了BurstNet的虚拟机。然后就开始关注各种服务器提供商的消息,一旦有什么10刀/半年的活动就赶紧买。买来当然要玩啊,不会就问呗,好歹认识个运维。可是没啥好玩的啊……那就放着,想起来啥再说,隔几个月上去更新一下系统。

就这么着,这两年买来没用或者没大用的服务器少说也有三五千块吧。不过在不断折腾中,成长也是明显的:我学会使用yum、apt-get,也能找到合适的包;我能在各种环境下配置ruby、node、php;我能配apache也能配nginx,还能使用nginx反向代理搭Ghost博客;我能在服务器端调试,快速锁定问题。作为一名应用化学专业,以前端工程师身份入职,早些年只用过Windows的人来说,我觉得我进步很大。而且,关键是,无论处于什么环境下,我都能独立完成工作,搭建一个网站,堪称“全站工程师”。

这两则故事告诉我们

我们生活在一个消费型社会里,太多东西必须花钱才能体会。早年我没有买iPhone,就不觉得iOS应用有什么价值,错过了个人开发者的黄金时代,现在后悔也没用。如今谁都知道iOS应用可以赚钱,大量人力资本入驻,已经没有个人存在的空间了。

再举个例子,百度和Google。普通人上网有百度,很正常,不怪他们,实在是GFW让大家没法选择。但是很多开发人员也只用百度,搜索结果各种烂,下个乌龟git都能捆绑一堆垃圾软件,浪费大量的时间,还不一定能找到答案。时间是最宝贵的,知识也是宝贵的,能够快速获得知识,这种服务自然是值得消费的。

所以我总结,在你的消费能力允许时,不要太多的考虑买这个东西能不能用够本,比如vpn,大家大多数时间都在墙内活动,这没错;但在需要的时候,能够无阻碍的翻出去,才是最重要的。

当然,怎么花钱算合适,是个度的问题,真有人一个月挣800非要买肾6+,那确实太超过了。咱们还是循序渐进,现在一个月能挣5k,就买个一年100块的VPN,保证快速获取知识;一个月能挣15k,电子设备想买就买,大不了将来送人嘛。另外,也别买了真就扔那儿不管了,多少还是得用一用,投入的学一学,不然只能是浪费。

后记

那块非凡630被我送给做设计的亲戚了。我在201二进宫的时候参加年会又中了一块板,原封捐给设计部了。

我买过新网的机器,烂的要死,没怎么用。后来注册参加过各种免费试用,都以过期而告终。现在除了这个空间我还有2台虚拟机,SAE上也充了云豆,不过也一直没怎么用。

图里的kindle,已经被我刷成砖,闲置好几年了。