2016年1月17日星期日

阮一峰的网络日志

阮一峰的网络日志


更多的人死于心碎

Posted: 16 Jan 2016 07:38 PM PST

(说明:本文原载2016年第二期《财新周刊》)

2015年12月28日中午,Debian操作系统的创始人Ian Murdock,在推特上发布了一条简短的消息。

"我将在今晚自杀,请不要打扰,我要抓紧时间说一些事情,不让它们跟我一起走。"

没过多久,他就删除了这条消息,并且关闭了推特账户。大家猜测纷纷,不知道是真是假。第二天,他的死讯被确认。

对于大多数人来说,Debian是一个陌生的名字,但是在程序员之中,它非常有名,国际空间站就使用它。这是一种计算机操作系统,类似于Windows,但是一般只用于服务器。所以,普通用户接触不到,只有企业级用户和专业程序员才会使用。

你可以想象一下,有一样东西你每天使用,突然某天清晨传来消息,它的发明人非正常死亡,你会是什么感受,内心会不会震动?这就是整个IT业界听到这个消息的反应,尤其是死者只有42岁,实在太年轻了。

1973年,Ian Murdock生于西德,后来随父母移民美国。他从小就对计算机感兴趣,9岁开始编程。1993年8月,在普度大学读大二时,他接触到了芬兰大学生林奈斯的Linux项目,顿时产生了兴趣。Linux是操作系统的内核,就好比汽车的发动机,虽然重要,但是本身并不足以使用,必须配合各种零件,装配成一辆汽车以后才能行驶。Ian Murdock想做的就是,将Linux装配成一辆全功能的汽车。

经过几个月的努力,他在1993年底发布了Debian的最初版本。这个名字是女友的名字Deb和他自己名字的结合。当时的计算机社区,急需一个新的操作系统,很多人看到以后,就对这个项目产生了兴趣,纷纷试用和加入。

跟Windows不一样的是,Debian完全免费,可以任意使用。因为Ian Murdock相信免费软件可以帮助到更多需要的人,所以采用了一种叫做GPL的软件授权协议。根据这种协议,只要满足一个条件,你怎么使用都可以,那就是一旦你修改了软件,就必须把你的修改,也同样免费地公开出来。这个条件防止了有人将他人的免费成果占为己有。

随着加入者越来越多,以及GPL协议的保障,Debian项目的影响力越变越大,逐渐成为两种主流的基于Linux的操作系统之一。后来,又得到自由软件基金会的赞助,在全世界获得广泛采用。

1996年,Debian发布0.93版以后,Ian Murdock就不再参与开发了,把领导权交给了其他人。他成立了一家创业公司,担任CTO。2007年,他加入SUN公司担任副总裁,直到2011年。然后,又在不同的硅谷公司任职,直到去世为止。

由于家人要求保护隐私,他的死因没有公布,外界不得而知。现在只知道,他在早先的推特上曾经提到过,与警察发生了冲突,决心抗争到底。旧金山警方则说,有人报警他醉酒后企图闯入民宅,警方赶到制止时,他与警察发生暴力冲突,导致被拘留。警方没有公布,他到底是不是自杀。

Ian Murdock的性格内向,采访和报道都很少。1996年,他与大学时代的女友Debra Lynn(就是Debian这个名字的另一半)结婚,但是在2007年离婚,没有子女。

因为Ian Murdock的死,很多人提到了另一个计算机天才Phil Katz。他是ZIP压缩算法的发明人,2000年4月14日,他被发现孤独一人死在一家小旅馆的桌子旁边,怀里抱着一个空的酒瓶,死因是长期酗酒导致的胰脏严重内出血。此时,他与家人已经长期断绝关系,与自己创立的公司的员工也很久没联系了。他死时只有37岁。

奥地利小说家茨威格的最后一篇小说《象棋的故事》,描述过一个令人心碎的故事。一个普通人关进纳粹监狱,身边只有一本象棋棋谱。漫长的关押和惊恐绝望之中,他只能靠背棋谱,转移一些注意力。由于长期地自己跟自己下棋,他几乎陷入精神分裂和疯狂。等到后来放出监狱,他的棋艺竟然可以战胜世界冠军,但是他对象棋乃至人生,已经毫无兴趣了。茨威格用这篇小说比喻自己的人生,写完后五个月,他就自杀了。

Ian Murdock以及许多计算机天才,就像《象棋的故事》的这位主人公。他们精通计算机,但是个人生活失去了平衡。除了计算机以外,没有人可以对话,生活也失去了意义,最终走上了绝路。

我们这个计算机时代,让人可以不依赖社交,只靠机器就能满足生活的基本需求。生活因此变得方便,但也不可避免地变得更孤独。索尔·贝娄有一部长篇小说,名字叫做《更多的人死于心碎》,这大概就是Ian Murdock真正的死因。

(完)

文档信息

2016年1月15日星期五

阮一峰的网络日志

阮一峰的网络日志


Flux 架构入门教程

Posted: 14 Jan 2016 06:03 PM PST

过去一年中,前端技术大发展,最耀眼的明星就是React

React 本身只涉及UI层,如果搭建大型应用,必须搭配一个前端框架。也就是说,你至少要学两样东西,才能基本满足需要:React + 前端框架。

Facebook官方使用的是 Flux 框架。本文就介绍如何在 React 的基础上,使用 Flux 组织代码和安排内部逻辑,使得你的应用更易于开发和维护。

阅读本文之前,我假设你已经掌握了 React 。如果还没有,可以先看我写的《React入门教程》。与以前一样,本文的目标是使用最简单的语言、最好懂的例子,让你一看就会。

一、Flux 是什么?

简单说,Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC 架构是同一类东西,但是更加简单和清晰

Flux存在多种实现(至少15种),本文采用的是Facebook官方实现

二、安装 Demo

为了便于讲解,我写了一个Demo

请先安装一下。

  $ git clone https://github.com/ruanyf/extremely-simple-flux-demo.git  $ cd extremely-simple-flux-demo && npm install  $ npm start  

然后,访问 http://127.0.0.1:8080 。

你会看到一个按钮。这就是我们的Demo。

三、基本概念

讲解代码之前,你需要知道一些 Flux 的基本概念。

首先,Flux将一个应用分成四个部分。

  • View: 视图层
  • Action(动作):视图层发出的消息(比如mouseClick)
  • Dispatcher(派发器):用来接收Actions、执行回调函数
  • Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

Flux 的最大特点,就是数据的"单向流动"。

  1. 用户访问 View
  2. View 发出用户的 Action
  3. Dispatcher 收到 Action,要求 Store 进行相应的更新
  4. Store 更新后,发出一个"change"事件
  5. View 收到"change"事件后,更新页面

上面过程中,数据总是"单向流动",任何相邻的部分都不会发生数据的"双向流动"。这保证了流程的清晰。

读到这里,你可能感到一头雾水,OK,这是正常的。接下来,我会详细讲解每一步。

四、View(第一部分)

请打开 Demo 的首页index.jsx ,你会看到只加载了一个组件。

  // index.jsx  var React = require('react');  var ReactDOM = require('react-dom');  var MyButtonController = require('./components/MyButtonController');    ReactDOM.render(    <MyButtonController/>,    document.querySelector('#example')  );  

上面代码中,你可能注意到了,组件的名字不是 MyButton,而是 MyButtonController。这是为什么?

这里,我采用的是 React 的 controller view 模式。"controller view"组件只用来保存状态,然后将其转发给子组件。MyButtonController源码很简单。

  // components/MyButtonController.jsx  var React = require('react');  var ButtonActions = require('../actions/ButtonActions');  var MyButton = require('./MyButton');    var MyButtonController = React.createClass({    createNewItem: function (event) {      ButtonActions.addNewItem('new item');    },      render: function() {      return <MyButton        onClick={this.createNewItem}      />;    }  });    module.exports = MyButtonController;  

上面代码中,MyButtonController将参数传给子组件MyButton。后者的源码甚至更简单。

  // components/MyButton.jsx  var React = require('react');    var MyButton = function(props) {    return <div>      <button onClick={props.onClick}>New Item</button>    </div>;  };    module.exports = MyButton;  

上面代码中,你可以看到MyButton是一个纯组件(即不含有任何状态),从而方便了测试和复用。这就是"controll view"模式的最大优点。

MyButton只有一个逻辑,就是一旦用户点击,就调用this.createNewItem 方法,向Dispatcher发出一个Action。

  // components/MyButtonController.jsx      // ...    createNewItem: function (event) {      ButtonActions.addNewItem('new item');    }  

上面代码中,调用createNewItem方法,会触发名为addNewItem的Action。

五、Action

每个Action都是一个对象,包含一个actionType属性(说明动作的类型)和一些其他属性(用来传递数据)。

在这个Demo里面,ButtonActions 对象用于存放所有的Action。

  // actions/ButtonActions.js  var AppDispatcher = require('../dispatcher/AppDispatcher');    var ButtonActions = {    addNewItem: function (text) {      AppDispatcher.dispatch({        actionType: 'ADD_NEW_ITEM',        text: text      });    },  };  

上面代码中,ButtonActions.addNewItem方法使用AppDispatcher,把动作ADD_NEW_ITEM派发到Store。

六、Dispatcher

Dispatcher 的作用是将 Action 派发到 Store、。你可以把它看作一个路由器,负责在 View 和 Store 之间,建立 Action 的正确传递路线。注意,Dispatcher 只能有一个,而且是全局的。

Facebook官方的 Dispatcher 实现输出一个类,你要写一个AppDispatcher.js,生成 Dispatcher 实例。

  // dispatcher/AppDispatcher.js  var Dispatcher = require('flux').Dispatcher;  module.exports = new Dispatcher();  

AppDispatcher.register()方法用来登记各种Action的回调函数。

  // dispatcher/AppDispatcher.js  var ListStore = require('../stores/ListStore');    AppDispatcher.register(function (action) {    switch(action.actionType) {      case 'ADD_NEW_ITEM':        ListStore.addNewItemHandler(action.text);        ListStore.emitChange();        break;      default:        // no op    }  })  

上面代码中,Dispatcher收到ADD_NEW_ITEM动作,就会执行回调函数,对ListStore进行操作。

记住,Dispatcher 只用来派发 Action,不应该有其他逻辑。

七、Store

Store 保存整个应用的状态。它的角色有点像 MVC 架构之中的Model 。

在我们的 Demo 中,有一个ListStore,所有数据都存放在那里。

  // stores/ListStore.js  var ListStore = {    items: [],      getAll: function() {      return this.items;    },      addNewItemHandler: function (text) {      this.items.push(text);    },      emitChange: function () {      this.emit('change');    }  };    module.exports = ListStore;  

上面代码中,ListStore.items用来保存条目,ListStore.getAll()用来读取所有条目,ListStore.emitChange()用来发出一个"change"事件。

由于 Store 需要在变动后向 View 发送"change"事件,因此它必须实现事件接口。

  // stores/ListStore.js  var EventEmitter = require('events').EventEmitter;  var assign = require('object-assign');    var ListStore = assign({}, EventEmitter.prototype, {    items: [],      getAll: function () {      return this.items;    },      addNewItemHandler: function (text) {      this.items.push(text);    },      emitChange: function () {      this.emit('change');    },      addChangeListener: function(callback) {      this.on('change', callback);    },      removeChangeListener: function(callback) {      this.removeListener('change', callback);    }  });  

上面代码中,ListStore继承了EventEmitter.prototype,因此就能使用ListStore.on()ListStore.emit(),来监听和触发事件了。

Store 更新后(this.addNewItemHandler())发出事件(this.emitChange()),表明状态已经改变。 View 监听到这个事件,就可以查询新的状态,更新页面了。

八、View (第二部分)

现在,我们再回过头来修改 View ,让它监听 Store 的 change 事件。

  // components/MyButtonController.jsx  var React = require('react');  var ListStore = require('../stores/ListStore');  var ButtonActions = require('../actions/ButtonActions');  var MyButton = require('./MyButton');    var MyButtonController = React.createClass({    getInitialState: function () {      return {        items: ListStore.getAll()      };    },      componentDidMount: function() {      ListStore.addChangeListener(this._onChange);    },      componentWillUnmount: function() {      ListStore.removeChangeListener(this._onChange);    },      _onChange: function () {      this.setState({        items: ListStore.getAll()      });    },      createNewItem: function (event) {      ButtonActions.addNewItem('new item');    },      render: function() {      return <MyButton        items={this.state.items}        onClick={this.createNewItem}      />;    }  });  

上面代码中,你可以看到当MyButtonController 发现 Store 发出 change 事件,就会调用 this._onChange 更新组件状态,从而触发重新渲染。

  // components/MyButton.jsx  var React = require('react');    var MyButton = function(props) {    var items = props.items;    var itemHtml = items.map(function (listItem, i) {      return <li key={i}>{listItem}</li>;    });      return <div>      <ul>{itemHtml}</ul>      <button onClick={props.onClick}>New Item</button>    </div>;  };    module.exports = MyButton;  

九、致谢

本文受到了Andrew Ray 的文章《Flux For Stupid People》的启发。

(完)

文档信息

2016年1月8日星期五

阮一峰的网络日志

阮一峰的网络日志


世界第一个机器人

Posted: 08 Jan 2016 04:33 AM PST

上个月,"机器人之父"恩格尔伯格去世。

《财新周刊》的编辑让我写他的介绍。我查了网上资料,发现那些早期机器人的照片非常有意思。

下面就是我写的人物生平,配上这些有趣的照片。

1.

12月1日,被誉为"机器人之父"的约瑟夫·恩格尔伯格(Joseph Engelberger),在美国因病去世,享年90岁。他发明制造了人类第一台机器人。

恩格尔伯格1925年生于纽约,先后获得哥伦比亚大学物理学士和电子工程硕士学位。

2.

1950年,恩格尔伯格读到了阿西莫夫的小说集《我,机器人》(I, Robot),爱不释手,产生了制造机器人的念头。

3.

1956年的一场酒会,他偶遇发明家德沃尔(George C. Devol)。后者提到,他刚刚申请了一个专利,叫做"可编程的用于移动物体的设备"(Programmed Article Transfer)。恩格尔伯格脱口而出,"这不是阿西莫夫笔下的机器人吗!"两人一拍即合,决定合作创立一家生产机器人的公司。

4.

新公司取名Unimation,意为"自动化单位",1958年正式运作。当年就拿出了第一个产品:一个可以自动完成搬运的机械手臂。虽然,这个产品庞大而笨重,只能完成很简单的任务,但它是人类历史上第一个机器人。

5.

恩格尔伯格和德沃尔从一开始就很清楚,机器人的研发成本很高,只有大公司才买得起。他们把研发方向定在为重型制造业服务,让机器人承担对人类有危险的工作。美国最大的通用汽车公司,是他们首先想到的目标客户。

6.

恩格尔伯格游说通用汽车,希望他们买一台机器人。由于以前从来没有这种东西,很多人对这个产品将信将疑。1961年,通用汽车好不容易答应,在离纽约最近的新泽西工厂,装一台试试看。第一台Unimation机器人的成本是6万美元,售价却只有2万5千美元,因为不这样就卖不出去。

7.

工业机器人在生产线上的作用非常明显,动作精准、永不疲倦、不怕高温和污染。比如,焊接工作处于高温环境,会产生有害气体,工人一不小心就会中毒,机器人就没有这个问题。

通用汽车开始订购更多的机器人,安装在全美各地的工厂,承担的工作扩展到焊接、油漆、粘合和装配。这帮助它取得了自动化生产的革命性突破,巩固和扩大了行业领先地位。其他汽车公司纷纷跟进,将机器人用于自家的流水线。美国机器人协会后来评价,恩格尔伯格的这个发明"彻底改变了现代工业和汽车制造的流程"。

8.

上个世纪60年代,现代工业革命达到了顶峰,阿波罗计划把人类送上了月球,整个美国都对新技术、新设备充满了兴趣。1966年,最热门的晚间谈话节目把Unimation机器人请上了电视,让它对着300万全国观众,发高尔夫球、倒啤酒、挥舞指挥棒,甚至拉手风琴。从此,恩格尔伯格一举成名。

9.

1969年,日本人将恩格尔伯格请到东京演讲,指导日本汽车厂商研发机器人。

10.

川崎重工引进了Unimation机器手臂。这件事对日本汽车工业有深远影响,日本后来超过美国成了"机器人王国"。恩格尔伯格在这个过程中扮演了重要角色,他在日本的知名度甚至比在美国还要大。

11.

随着年龄和荣誉的增长,恩格尔伯格在技术上变得保守。他不赞成在机器人内部,使用电机替代液压,也不认为制造有腿的机器人是必要的,使用轮子更符合实际。这导致他在1983年将Unimation公司以1.07亿美元的价格,卖给了西屋公司,退出了工业机器人行业。

那时,恩格尔伯格可谓功成名就。第一个Unimation机器人,在运作了10万个小时之后,已经光荣退役,送进了博物馆。全世界制作业使用的机器人,超过了300万个。他本人也当选了美国工程院院士。

12.

他并没有就此止步,1984年又创建了TRC公司,研发服务机器人。1988年,他又推出了世界第一个服务业机器人HelpMate。

13.

这个机器人用来在医院走廊穿行,为病人送饭、送药、送邮件,并记录病人的情况。

14.

恩格尔伯格的目标是为机器人装上各种传感器,使其能够直接为人类服务,首先是帮助老年人和残疾人。

15.

他觉得,很多老人进入养老院时,心智还十分健康,只是行动不便,不能很好地照顾自己。

16.

机器人能够承担重复性家务,比如取物、清洁、做饭等等,用来护理老年人,最合适不过了。

17.

接受彭博商业周刊采访时,恩格尔伯格说道,"家用机器人比工业机器人有更广阔的市场前景"。

18.

他认为,机器人未来一定无处不在。他更想作为"家用机器人之父"被人们铭记。

(完)

文档信息

2016年1月7日星期四

阮一峰的网络日志

阮一峰的网络日志


Commit message 和 Change log 编写指南

Posted: 06 Jan 2016 05:23 AM PST

Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交。

  $ git commit -m "hello world"  

上面代码的-m参数,就是用来指定 commit mesage 的。

如果一行不够,可以只执行git commit,就会跳出文本编译器,让你写多行。

  $ git commit  

基本上,你写什么都行(这里这里这里)。

但是,一般来说,commit message 应该清晰明了,说明本次提交的目的。

目前,社区有多种 Commit message 的写法规范。本文介绍Angular 规范(见上图),这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。

一、Commit message 的作用

格式化的Commit message,有几个好处。

(1)提供更多的历史信息,方便快速浏览。

比如,下面的命令显示上次发布后的变动,每个commit占据一行。你只看行首,就知道某次 commit 的目的。

  $ git log <last tag> HEAD --pretty=format:%s  

(2)可以过滤某些commit(比如文档改动),便于快速查找信息。

比如,下面的命令仅仅显示本次发布新增加的功能。

  $ git log <last release> HEAD --grep feature  

(3)可以直接从commit生成Change log。

Change Log 是发布新版本时,用来说明与上一个版本差异的文档,详见后文。

二、Commit message 的格式

每次提交,Commit message 都包括三个部分:Header,Body 和 Footer。

  <type>(<scope>): <subject>  // 空一行  <body>  // 空一行  <footer>  

其中,Header 是必需的,Body 和 Footer 可以省略。

不管是哪一个部分,任何一行都不得超过72个字符(或100个字符)。这是为了避免自动换行影响美观。

2.1 Header

Header部分只有一行,包括三个字段:type(必需)、scope(可选)和subject(必需)。

(1)type

type用于说明 commit 的类别,只允许使用下面7个标识。

  • feat:新功能(feature)
  • fix:修补bug
  • docs:文档(documentation)
  • style: 格式(不影响代码运行的变动)
  • refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  • test:增加测试
  • chore:构建过程或辅助工具的变动

如果typefeatfix,则该 commit 将肯定出现在 Change log 之中。其他情况(docschorestylerefactortest)由你决定,要不要放入 Change log,建议是不要。

(2)scope

scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。

(3)subject

subject是 commit 目的的简短描述,不超过50个字符。

  • 以动词开头,使用第一人称现在时,比如change,而不是changedchanges
  • 第一个字母小写
  • 结尾不加句号(.

2.2 Body

Body 部分是对本次 commit 的详细描述,可以分成多行。下面是一个范例。

  More detailed explanatory text, if necessary.  Wrap it to   about 72 characters or so.     Further paragraphs come after blank lines.    - Bullet points are okay, too  - Use a hanging indent  

有两个注意点。

(1)使用第一人称现在时,比如使用change而不是changedchanges

(2)应该说明代码变动的动机,以及与以前行为的对比。

2.3 Footer

Footer 部分只用于两种情况。

(1)不兼容变动

如果当前代码与上一个版本不兼容,则 Footer 部分以BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。

  BREAKING CHANGE: isolate scope bindings definition has changed.        To migrate the code follow the example below:        Before:        scope: {        myAttr: 'attribute',      }        After:        scope: {        myAttr: '@',      }        The removed `inject` wasn't generaly useful for directives so there should be no code using it.  

(2)关闭 Issue

如果当前 commit 针对某个issue,那么可以在 Footer 部分关闭这个 issue 。

  Closes #234  

也可以一次关闭多个 issue 。

  Closes #123, #245, #992  

2.4 Revert

还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以revert:开头,后面跟着被撤销 Commit 的 Header。

  revert: feat(pencil): add 'graphiteWidth' option    This reverts commit 667ecc1654a317a13331b17617d973392f415f02.  

Body部分的格式是固定的,必须写成This reverts commit &lt;hash>.,其中的hash是被撤销 commit 的 SHA 标识符。

如果当前 commit 与被撤销的 commit,在同一个发布(release)里面,那么它们都不会出现在 Change log 里面。如果两者在不同的发布,那么当前 commit,会出现在 Change log 的Reverts小标题下面。

三、Commitizen

Commitizen是一个撰写合格 Commit message 的工具。

安装命令如下。

  $ npm install -g commitizen  

然后,在项目目录里,运行下面的命令,使其支持 Angular 的 Commit message 格式。

  $ commitizen init cz-conventional-changelog --save --save-exact  

以后,凡是用到git commit命令,一律改为使用git cz。这时,就会出现选项,用来生成符合格式的 Commit message。

四、validate-commit-msg

validate-commit-msg 用于检查 Node 项目的 Commit message 是否符合格式。

它的安装是手动的。首先,拷贝下面这个JS文件,放入你的代码库。文件名可以取为validate-commit-msg.js

接着,把这个脚本加入 Git 的 hook。下面是在package.json里面使用 ghooks,把这个脚本加为commit-msg时运行。

    "config": {      "ghooks": {        "commit-msg": "./validate-commit-msg.js"      }    }  

然后,每次git commit的时候,这个脚本就会自动检查 Commit message 是否合格。如果不合格,就会报错。

  $ git add -A   $ git commit -m "edit markdown"   INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>" ! was: edit markdown  

五、生成 Change log

如果你的所有 Commit 都符合 Angular 格式,那么发布新版本时, Change log 就可以用脚本自动生成(例1例2例3)。

生成的文档包括以下三个部分。

  • New features
  • Bug fixes
  • Breaking changes.

每个部分都会罗列相关的 commit ,并且有指向这些 commit 的链接。当然,生成的文档允许手动修改,所以发布前,你还可以添加其他内容。

conventional-changelog 就是生成 Change log 的工具,运行下面的命令即可。

  $ npm install -g conventional-changelog  $ cd my-project  $ conventional-changelog -p angular -i CHANGELOG.md -w  

上面命令不会覆盖以前的 Change log,只会在CHANGELOG.md的头部加上自从上次发布以来的变动。

如果你想生成所有发布的 Change log,要改为运行下面的命令。

  $ conventional-changelog -p angular -i CHANGELOG.md -w -r 0  

为了方便使用,可以将其写入package.jsonscripts字段。

  {    "scripts": {      "changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0"    }  }  

以后,直接运行下面的命令即可。

  $ npm run changelog  

(完)

文档信息

2016年1月3日星期日

阮一峰的网络日志

阮一峰的网络日志


网站的肥胖症危机

Posted: 03 Jan 2016 02:58 AM PST

最近,有一篇文章正在疯传。

它是上个月,Maciej Ceglowski在澳大利亚的一次演讲,名为《网站的肥胖症危机》(文本视频),反思了互联网开发的现状。

该文非常值得一读,Hacker News排行榜高居榜首,得到了1000多人的推荐。

下面就是我的中文节译版。

===============================

网站的肥胖症危机(节译版)

作者:Maciej Ceglowski

译者:阮一峰

原文网址:The Website Obesity Crisis

1.

大多数网站的主要内容是文本,更准确地说,是简短的文本。

文本本身并不大,但是展示它们的网页,正变得越来越大。Twitter展示单条评论(140个字符)的页面,超过900KB。Medium的一篇文章大约400个词,页面大小是1.2MB。

如果这种趋势持续下去,2020年,网页的体积平均将超过5MB,比一本俄罗斯长篇小说还大。比如,陀思妥耶夫斯基的《罪与罚》,文本压缩后不到800KB。

TechTimes.com有一篇报道,介绍Google正在为大网页做标记。但是,这篇报道的网页,体积为18MB,外加一个3MB的视频。

2015年5月,Facebook引入了"Instant Articles",帮助用户快速浏览新闻。但是,介绍这个功能的页面,体积为6.8MB,外加一个41MB的视频。你想了解这个功能的细节,唯一的方法就是去看这个视频。

2.

网页真的有必要这么大吗?明明200KB就足够,为什么要做成2MB?

因为我们要往里面塞很多不需要的东西:广告、高清图片、视频、用户追踪系统、社交媒体的代码......你不塞,公司就可能解雇你。

如今的时代,你跟雇主说,想做一张体积只有几百KB的网页,就好像跟SUV车主谈论省油的经济型轿车。

有人会说,这是免费内容的代价。但是,我想问,谁会从海量的互联网广告获利?广告主,还是消费者?真正获得暴利的是网络服务提供商和互联网广告公司,其他人都付出了巨大的成本。

3.

我们都忘了健康的网页,应该是什么样子。

  • 值得阅读的文本,配上结构良好的标签。
  • 适度的图片和视觉设计。
  • 一层CSS
  • 少量的JavaScript,只在必需时使用

但是,2015年真实的网页,却是下面这样。

  • 一层HTML
  • 一大堆垃圾
  • 顶部还有一层监控代码

4.

宽带和光纤上网并不解决问题,实际上还鼓励了人们往网页上添加更多的东西。

为了平衡网页体积,工程师想出了很多方法:首屏快速渲染、压缩文件、异步加载、批量HTTP请求、管道发送等等......

网站开发越来越依赖代码精简、压缩、缓存、服务器配置这些中间步骤,这使得找出错误越来越困难,成本越来越高。

5.

复杂性让聪明人上瘾。

即使我们知道复杂不是好事,但难以抵抗。复杂的东西总是显得很酷,让人情不自禁想继续干下去。

大多数网站都过度复杂了。

我们做的每件事,都使得创造网站或编辑网页变得困难。把一篇文章放上网,正在变得需要一个专家团队才能完成。

新手越来越难通过源码学习。我们抽走了人们学习互联网的梯子。

6.

其实只需要两步,就可以大大缩小网页体积,提高性能。

第一步,确保最重要的内容,首先下载和渲染;

第二步,就此结束。

你不需要那些多余的垃圾,对最简主义保持信心就行。

7.

让我们保持互联网是一个超链接构成的媒体,不要把它变成另一种东西。

(完)

文档信息