乐者为王

Do one thing, and do it well.

checked/unchecked应该翻译成什么?

翻译有关Java异常的文章时,总是犹豫是否该把checked/unchecked也翻译过来。原因是,不是很清楚该如何优雅传神地翻译这两个单词。

《Java核心技术》将它们翻译成“已检查/未检查”。《Java编程思想》和《Effictive Java中文版》则翻译成“被检查的/不检查的”。至于技术文章的翻译更是花样百出,有“检测/非检测”、“可检测/非检测”、“可查/不可查”、“受查/非受查”、“检查型/非检查型”、“检查/非检查”等。

到底该翻译成什么呢?在回答这个问题前,让我们先确定什么是checked/unchecked异常?

exception-hierarchy

上图是Java中的异常层次结构图。Java语言规范将派生自RuntimeException类和Error类的所有异常称为“unchecked异常”,其它的异常称为“checked异常”。

The unchecked exception classes are the run-time exception classes and the error classes.

The checked exception classes are all exception classes other than the unchecked exception classes. That is, the checked exception classes are Throwable and all its subclasses other than RuntimeException and its subclasses and Error and its subclasses.

并且,在编译时编译器会检查程序是否为所有的“checked异常”提供处理器。

This compile-time checking for the presence of exception handlers is designed to reduce the number of exceptions which are not properly handled.

从上述的描述可以得出,“checked异常”和“unchecked异常”是两种异常类型,且“checked异常”隐含有必须要检查的思想。

紧紧围绕这些描述,细细地思考和比较,个人认为:1. 《Java核心技术》的翻译存在问题,“已检查”和“未检查”说明的是异常的检查状态,没有表达出异常的分类这个概念。2. 《Java编程思想》和《Effictive Java中文版》的翻译则正确地表达了异常的分类,但“被检查”翻译的有点无厘头,如果能改成“要检查”则会更好,缺陷是连接“异常”这个词组后是短语,而非名词,读来费劲,也不上口;如果去掉“的”的话,后者会有歧义,听起来像是命令。3. “检测/非检测”和“检查/非检查”是同个意思。4. “可检测”这个翻译看上去似乎表示异常是可以检查的,和Java语言规范要求的该类异常必须要检查不符。5. “可查/不可查”也是如此。6. “受查/非受查”的翻译则有些莫名其妙的感觉。7. “检查型/非检查型”翻译的很好,既表达了异常的分类,也表达了一种异常是要检查的,另一种异常是不要检查的意义,只是前者还缺少点强制的意味。

分析到这里,结果已经是不言而明。“要检查的/不检查的”和“检查型/非检查型”是两种更好的翻译,都能把Java语言规范对checked/unchecked异常的描述尽量地表述出来。而后者在实际使用中更为简洁适宜。

接下来的事情就是把以前译文中未翻译的checked/unchecked修改成“检查型/非检查型”。在以后的翻译中也继续使用这个翻译结果,除非能找到更好的表述方式。

费曼技巧:最好的学习方法

英文原文:https://www.farnamstreetblog.com/2012/04/learn-anything-faster-with-the-feynman-technique/

费曼技巧有4个简单的步骤,我将在下面解释它们:

  • 选择一个概念
  • 把它教给某个小孩
  • 识别薄弱环节,回到原始材料
  • 回顾和简化(可选)

如果你不学习就会固步自封。那么,学习新主题并识别现有知识的薄弱环节的最好方式是什么?

两种类型的知识

两种类型的知识,我们大多数人关注错误的那种。第一类知识注重知道某事物的名称。第二类注重知道某事物。它们不是一回事。著名的诺贝尔物理学奖获得者理查德·费曼(Richard Feynman)明白知道某事物和知道某事物的名称之间的差异,这是他成功的最重要的原因之一。事实上,他创造了一个学习公式,确保他比其他人更明白某些东西。

这被称为费曼技巧,它将帮助你更快更明白地学到东西。最重要的是,它极其容易实现。

一个人如果说他知道他在想些什么,却表达不出来,通常是他其实并不知道自己在想些什么。——莫提默·艾德勒

费曼技巧

费曼技巧有4个步骤。

步骤1:把它教给某个小孩

拿出一张白纸,在顶部写下你想要学习的主题。写出你对这个主题的了解,好像你正在把它教给某个小孩。不是你聪明的成年朋友,而是一个8岁的小孩,他刚好有足够的词汇和注意力来涵盖基本的概念和关系。

很多人倾向于使用复杂的词汇和行话来掩盖他们不明白的东西。问题是我们仅仅愚弄自己,因为我们不知道我们不明白。另外,使用行话会掩盖周围的人对我们的误解。

当你自始至终都用孩子可以理解的简单的语言写出某个想法时(提示:只用最常见的单词),你迫使自己在更深的层次上去理解这个概念,并简化想法之间的关系和连接。如果你努力,你会清楚地知道自己在哪里还有薄弱环节。这种压力很好——它预示着学习的机会。

步骤2:回顾

在第一步中,你不可避免地会遇到你的知识的薄弱环节,你忘记了某些重要的东西,或者不能解释它,或者只是很难把重要的概念联系起来。

这是宝贵的反馈,因为你已经发现你的知识的边缘。胜任力是知道你能力的极限,你刚刚已经识别出一个!

这是学习开始的地方。现在你知道在哪里会遇到困难,回到原始材料并重新学习,直到你可以用基本的术语去解释它们。

识别你的理解的边界也限制了你可能犯的错误,并增加了在应用知识时成功的机会。

步骤3:整理和简化

现在你有一套手工制作的笔记。检查它们以确保你没有错误地从原始材料中借用任何行话。将它们组织成一个丰满的简单的故事。

把它们大声地朗读出来,如果解释不直白或者听起来很混乱,这表明你在该领域的理解仍需要做些工作。

步骤4(可选):传播

如果你真的想要确保自己的理解没有任何偏差,那就把它告诉别人(理想状态是这个人对该主题知之甚少,或者就找个8岁的小孩)。对你的知识的最终考验是你将其传达给另一个人的能力。

这不仅是学习的一个妙诀,它也是一种不同的思维方式的窗口,允许你将想法分解,然后从头开始重建。(Elon Musk称它为从第一个原则思考)。这会导致对想法和概念的更深入的理解。重要的是,以这种方式解决问题,你可以在别人不知道他们自己在说什么的情况下理解这个问题。

费曼的方法直观地认为智力是一个成长的过程,这与卡罗尔·德韦克(Carol Dweck)的工作非常吻合,卡罗尔·德韦克漂亮地描述了固定型和成长型思维之间的区别

100%代码覆盖率的悲剧

英文原文:http://labs.ig.com/code-coverage-100-percent-tragedy

有趣的是,我对测试的观点正在发生变化。十五年来,我一直在宣扬TDD(测试驱动开发,或者被称为测试先行方法),或至少让开发者写些单元测试。不过,最近我发现自己更经常地说,“你为什么要写测试?”而不是“你应该写测试”。

怎么回事?

在办公室四处走走时,开发者要求我帮助他进行单元测试。看来他在使用Mockito测试以下代码时遇到了麻烦:

initialise-method

我想他是非常惊讶于我的回应:“你不需要测试。”

“但我不得不测啊!”他说。“否则如何知道这段代码是正常的?”

“这段代码很明显。没有条件,没有循环,没有转换,没有任何东西。它们只是一些普通的旧式胶水代码。”

“但没有测试,任何人都可以来修改和破坏这段代码呀!”

“看,如果那个虚构的邪恶/无知的开发者来了,破坏了这些简单的代码,如果相关的单元测试中断,你认为他会做什么?他只会删除它。”

“但是如果非要写测试怎么办?”

“在这种情况下,我将这样写测试:”

initialise-test

“但是你没有使用Mockito啊!”

“那又怎么样呢?Mockito没有帮助你。恰恰相反:它会妨碍你,并且它也不会使测试变得更易读或更简单。”

“但是我们决定使用Mockito进行所有测试!”

我:“……”

后来我碰到他,他自豪地说,他已经设法用Mockito写了测试。我明白让测试代码正常运行的心理满足感,但尽管如此,这种解决方案让我难过。

另一个例子

我加入的某个开发团队,他们对新应用程序的高代码覆盖率以及对BDD(行为驱动设计)的新发现感到兴奋。查看代码,可以发现如下的Cucumber测试:

cucumber-test

如果你以前使用过Cucumber,你就不会震惊于它所需的支持代码的数量:

cucumber-support

cucumber-support2

和所有要测试的代码:

cucumber-code

是的,一个简单的地图查找。我和这个开发者有足够的信任去直言不讳地说,“这是在浪费时间。”

“但我的老板希望我能为所有的类写测试,”他回答。

“代价是什么?”

“费用?”

“无论如何,这些测试与BDD无关。”

“我知道,但是我们决定使用Cucumber进行所有测试”

我:“……”

我明白按照自己意愿改造工具的心理满足感,但尽管如此,这种解决方案让我难过。

悲剧在哪里?

悲剧是两位聪明的开发者(我需要带他们去团队面试)浪费时间写那种测试,测试是毫无意义的,但需要后来的IG开发者维护。

悲剧是不使用正确的工具,因为没有特别好的理由,我们决定坚持不懈地使用错误的工具。

悲剧是一旦某个“良好实践”成为主流,我们似乎就忘记它是怎么来的,它的好处是什么,最主要的是,使用它的代价是什么。

如果我们只是机械地应用它而没有太多的思考,这通常意味着我们最终得到最平庸的结果,失去大部分的好处,但支付所有(甚至更多)的成本。根据我的经验,编写好的单元测试并非易事。

那么100%的代码覆盖率值得追求吗?

是的,每个人都应该实现它……在一个项目中。我认为你必须用极端的手段去了解限制是什么。

我们已经有了一个极端的大量经验:0个单元测试的项目,所以我们知道在这上面工作的痛苦。我们通常缺乏的是在另一个极端的经验:强制100%代码覆盖率和一切都是TDD的项目。单元测试(尤其是测试先行方法)是一个非常好的实践,但我们应该学习哪些测试是有用的,哪些是适得其反的。

要记住没有什么是免费的,没有什么是银弹。使用工具前请停下来想一想。

关于作者

Daniel Lebrero在IG的大数据团队担任技术架构师。拥有超过15年的Java经验和4年的Clojure经验,他现在是函数式编程的大力倡导者。可以在TwitterLinkedIn或者他的个人博客找到他。

学习新编程语言的非传统方式

英文原文:https://hackernoon.com/unconventional-way-of-learning-a-new-programming-language-e4d1f600342c

现在已经有500多种编程语言。因此,开始学习新的编程语言对你来说是很正常的。你可能知道C++和Java,但是你的工作需要Python;或者你精通Python,但是需要用Java编写代码;或者也许你想要学习这种很酷的语言只是为了扩展你的编程技能。

如果你想学习新的编程语言,你会选择哪种方式?

  • 从若干在线教程中学习
  • 或者从若干在线课程(MOOC)中学习

有些人甚至可能认为,学习新语言的最佳方式应该是这样的:

  • 学习这门新的编程语言的语法
  • 再用这门语言构建一些个人项目

有道理!这样可以确保你能够应用学习语言的语法而获得的知识。

我开发过20多个迷你项目,同时学习不同的语言。相信我,当你为个人项目编写代码的时候,不管这些项目是周末项目还是紧急快速补丁,你编写代码都只是为了完成任务。你只会关注——“我的代码是否工作?”你几乎不关心代码的质量。

任何傻瓜都能写出计算机可以理解的代码。好的程序员能写出人类可以理解的代码。——Martin Fowler

那么,你是如何学习你正在尝试学习的新的编程语言的良好实践呢?

向该语言的开源项目贡献代码

惊讶吗?有些人可能在想——“等等,开源是很难的。只有当我们是该语言的专家时,我们才能为开源项目贡献代码,对吗?”答案是不。

让我给你们讲个故事。

去年,我收到Booking.com全职工作的邀请,而且我知道我将使用Perl(这是该公司后端使用的主要语言)工作。2016年6月,当我完成大学学位后,我开始学习Perl,以便为自己在大学毕业后的首份工作做准备。因为我会在7月的第二周入职,所以我差不多有1个月的时间。

我开始阅读Perl的语法,并开始理解这门语言的一些常见模式。现在,我真的想使用Perl构建一些东西,以便我可以应用我的这门语言的知识和实践这门语言的各种概念。当我在寻找使用Perl构建某些东西的想法时,我在GitHub偶遇DuckDuckGo的开源组织。我注意到这个组织的某些开放项目是用Perl写的。我浏览这些项目的Issues发现有很多“新手”问题。我立即开始去解决它们,并提交了几个pull request。到今天为止,我已经是该组织的几个开放项目的主要贡献者之一,也是DuckDuckGo的20个开源社区领袖之一。

故事的寓意——通过向用Perl编写的开源项目贡献代码我学会了Perl。

为什么这种方法奏效呢?

就在我学会Perl的语法之后,我开始向开源项目贡献代码。当这样做的时候,我总是习惯看看现有的模块。我经常留意在Perl中使用的模式。此后,我开始在自己的代码中吸收这些良好的实践,它帮助我学习如何使用Perl编写好的代码。

这并不是偶然。让我给你们讲个另外类似的故事。

最近,当我在Booking.com工作的时候,我挑选了一些任务,包括给用Go语言编写的服务之一添加新功能。以下是我和队友的对话:

我:我真的喜欢这项任务。我想做它。你怎么看?

他:是的,它的确很有意思。但是,它需要Go的知识。你知道Go吗?

我:不知道。

他:你想学习Go吗?

我:是的!

他:😊 那就去吧!

我去了,那也是我学习另外一门编程语言——Go的起点!

我开始阅读Go的语法,并在它的官方网站上发现了一个非常棒的初学者语言教程。它足以让我熟悉该语言的所有基本概念。

再次地,我开始寻找含有“新手”或“易于修复”问题的Go开源项目。我发现了一个Google的项目,它基本上是GitHub的REST API的Go包装器。

在我开始学习Go的2天后,我有了这个项目的第一个PR。下图是我过去1年的贡献图表

contribution-graph

开源是如何帮助的?

现在你可能会疑惑给开源贡献代码如何帮助你学习一门语言的良好实践。它有多个方面。让我们来逐个讨论。

代码质量

大多数良好的开源项目都有严格的编码指导原则,你必须遵守它们才能使你的代码被合并。参与开源将帮助你适应这些指导原则,从而编写优质的代码,即使你只是在学习这门语言。

不仅如此,你还有机会查看其余的代码,学习别人是如何写代码和/或写文档的。

代码审查

给开源贡献代码的最好部分是代码审查。当你推送代码时,你将获得与该项目相关的专家的反馈,因此可以让你有机会提升对语言的理解。

这就像获得了关于如何编写好代码的一次免费的个人指导。

赞赏

下图是我在Go语言上的第一个PR的首个评论

go-appreciation-comment

作为软件开发者,我们的工作真的需要得到赞赏。而开源社区能够确保这些。在我的整个开源贡献经历中,我从来没有收到过甚至一条侮辱或者挫伤的评论。每个人都善于鼓舞和乐于助人。

下图是DuckDuckGo社区中另个人的评论:

duckduckgo-appreciation-comment

所以,下次你想学习一门新语言,只管去学!找个开源项目贡献代码,在学习这门语言和它微妙之处的道路上奋勇前进吧;)

务必让我知道这种非传统方式是否对你有效。另外,如果你认为这种方式对某人有用,请推荐(❤)这篇文章。

如果有任何其它有效的方法也请告诉我。可以在Twitter上关注我@sahildua2305

GitHub对软件职业生涯的影响

英文原文:https://medium.com/@sitapati/the-impact-github-is-having-on-your-software-career-right-now-6ce536ec0b50

在未来的12-24个月里——换句话说,即2018到2019年间——程序员的聘用方式将彻底改变。

2004-2014年间,我任职于Red Hat,世界上最大的开源软件工程公司。2004年7月,在我工作的第一天,我的上司Marty Messer对我说:“你在这里所做的一切工作都是开源的。在将来,你不再需要简历,人们可以直接Google你。”

在那时,它是在Red Hat工作的一个独特之处:我们有机会在开源社区创立自己的个人品牌和声誉。我们通过邮件列表、缺陷追踪器以及提交源代码到Mercurial、Subversion和CVS版本库 来和其他软件工程师进行交流。所有这些都是公开的,并且可以被Google索引。

快进到2017,我们生活的这个世界已经被开源软件所吞噬。

有两个因素可以让你真切地感受到开源时代的到来:

  1. 微软——曾经是闭源私有软件的典型代表和反对开源的圣战士——已经全心全意地拥抱开源软件,成立.NET基金会(Red Hat是其中的一员)和加入Linux基金会。现在.NET已经作为开源项目进行开发。
  2. GitHub已经成为一个奇特的社交网络,它把问题追踪和分布式代码控制捆绑在一起。

对于来自主要是闭源背景的软件开发者来说,刚刚发生了什么还不是很清楚。对他们来说,开源等于“在业余时间免费工作”。

然而,对于我们这些在过去10年里建成一个10亿美元开源软件公司的人来说,为开源工作没有什么免费或业余时间。并且,为开源工作的好处和结果是显而易见的:你的声誉是你的,而且在公司间是可携带的。GitHub是一个社交网络,在那里,你的社会资本,通过你的提交和对你正在工作的任何技术的全球交流的贡献创造的,是你的——不会绑定到你正在临时工作的公司。

聪明人会利用这个优势——他们会向他们日常工作中使用的语言和框架贡献补丁、问题和评论——TypeScript、.NET、Redux。

他们同样会提倡并创造性地安排他们的工作尽可能地以公开的方式完成——即使那只是他们对私有版本库的贡献图。

GitHub是一个很好的均衡器。你可能不能从印度找到一份澳大利亚的工作,但没有什么阻止你在印度利用GitHub与澳大利亚人进行合作。

在过去的十年里,从Red Hat获取一份工作的方式是显而易见的。你只要开始与Red Hat的工程师一起协作开发他们的一些开源项目,然后作出有价值的贡献并且得到他们的认可,你就可以申请一份工作。或者他们会找你。

现在,同样的途径对每个人都开放,不过仅限于技术职位。随着世界被开源所吞噬,同样的求职方式在各个地方开始流行起来。

最近的访谈中,Linux和Git的发明者Linus Torvalds(在GitHub上有4.9万关注者)这样说道:

你提交大量的小补丁,直到项目的维护者信任你,到那时你会成为信任网络的一部分,而不仅仅是个发送补丁的家伙。

你的声誉是你在信任网络中的定位。当你换公司时,它们会减弱并且有所丢失。如果你生活在一个小镇,并且已经在那里很长一段时间,那么小镇里所有的人都了解你。如果你去了其他国家,那么你最终到了一个没人了解你的地方——更糟糕的是,没人知道有谁了解你。

你已经丢失了你的第一度和第二度,甚至可能是第三度连接(译者:不明白什么是“度”的可以搜索六度分隔理论)。除非你已经通过在会议上演讲或者其它一些重要的事情建立品牌,否则你通过与其他人合作以及给企业内部版本库提交代码建立的信任将会不复存在。

但是,如果这些工作一直都在GitHub上完成,它就不会消失。它是可见的。它连接到了一个可见的信任网络。

首先发生的事情之一是弱势群体将开始利用这个优势。学生、新毕业生、移民,他们将利用这个优势搬到澳大利亚。

并且这也将改变整个软件开发的生态环境。以前的特权开发者会突然发现他们的网络被破坏了。开源的原则之一是精英政治——最好的想法胜出,最多的提交胜出,通过测试最多的胜出,最好的实现胜出,等等。

它并不完美(没有什么是完美的)。并且它不会让成为一个好同事的努力废除或打折。在Red Hat,我们解雇过一些摇滚明星工程师,他们只是不能很好地与其他人一起工作——这样的事情不会出现在GitHub,因为大部分开发者都在与其他贡献者互动。

正如有些人用稻草人谬误描述它一样,GitHub不仅仅是代码版本库和原始提交数字的列表。它是一个社交网络。这么说吧:

它不是你的代码在GitHub上的计数——它是其他人在GitHub上谈及你的代码的计数。

那是你的可携带声誉。在未来的12-24个月里,由于一些开发者开发这种声誉而其他开发者不,它将成为一个鲜明的区分因素。就像有电子邮箱和没电子邮箱(现在每个人都有电子邮箱)、有蜂窝电话和没蜂窝电话(现在每个人都有蜂窝电话)。最终绝大多数将会以开源的方式工作,它将再次是区别于其它因素的一个公平竞争的领域。

但现在,开发者的职业生涯空间正在被GitHub破坏。

如何修改Android的hosts文件

由于「你懂的」的原因,某些时候我们需要修改Android的hosts文件。Android的hosts文件路径是/system/etc/hosts,在修改该文件前首先需要Android手机获取root权限。至于如何root你的手机,这里就不加详述,可以自行在网络上查找,很多也很简单。

本文将要阐述的是如何在命令行下通过adb程序访问root过的手机,把hosts拖到电脑上修改,然后再复制回手机来实现修改hosts的方法。

下面就开始具体的步骤:

1
2
C:\tools>adb pull /system/etc/hosts hosts.mod
[100%] /system/etc/hosts

上面的命令是把手机上的hosts文件拖到电脑上,[100%]表明文件已经传输完成,可以修改hosts文件了。在修改完成后就使用以下命令上传到手机:

1
2
C:\tools>adb push hosts.mod /system/etc/hosts
adb: error: failed to copy 'hosts.mod' to '/system/etc/hosts': Read-only file system

从回显的消息可以看到,文件系统是只读的,所以不能直接上传。

试试以下的命令看能不能成功?!

1
2
3
C:\tools>adb root  # 帮助文档说该命令可以让adbd守护进程获得root权限
C:\tools>adb push hosts.mod /system/etc/hosts
adb: error: failed to copy 'hosts.mod' to '/system/etc/hosts': Read-only file system

还是不行,看来要重新挂载/system目录才可以。

1
2
C:\tools>adb remount
remount failed: Operation not permitted

没有权限?这是必须祭起shell大法的节奏啊!

1
2
3
4
5
C:\tools>adb shell
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
shell@maguro:/ $ ls -al /system/etc/hosts
-rw-r--r-- root     root           25 2013-08-14 07:00 hosts

从上面最后一行可以看出hosts这个文件只有它的拥有者能写入,对于其他人来说都是只读的。要想让其他人也能做修改,必须使用以下命令进行提权,再改变hosts文件的属性才行。

1
shell@maguro:/ $ su

如果是第一次执行这个命令,手机会亮起,SuperSU应用会提示你是否同意权限的分配。这里当然是要同意的!接着你就可以看到终端下的提示符从$变成了#,@前的字符也由shell变成了root。然后我们就可以修改hosts文件的权限属性了。

1
2
3
4
root@maguro:/ # chmod +666 /system/etc/hosts
Bad mode
root@maguro:/ # chmod 666 /system/etc/hosts
Unable to chmod /system/etc/hosts: Read-only file system

又是Read-only file system!输入以下命令看看/system目录的文件系统详情呢。

1
2
root@maguro:/ # mount | grep system
/dev/block/platform/omap/omap_hsmmc.0/by-name/system /system ext4 ro,seclabel,relatime...

看到ext4后面的ro了吗?它是read only的缩写,即只读的意思。这说明/system目录是只读的。接下来我们要把它改成可以读写。

1
root@maguro:/ # mount -o rw,remount /system

上面的-o用于指定加载文件系统时的选项。这些选项包括:

1
2
3
remount 重新加载设备。通常用于改变设备的设置状态。
ro 以只读模式加载。
rw 以可读写模式加载。

再次查看,可以看到原来ro的位置已经变成rw了。

1
2
root@maguro:/ # mount | grep system
/dev/block/platform/omap/omap_hsmmc.0/by-name/system /system ext4 rw,seclabel,relatime...

不过到这里我们还不能向手机拷贝hosts文件,因为hosts文件的权限属性还没被改过,如果强行上传的话,会得到如下的错误消息:

1
2
C:\tools>adb push hosts.mod /system/etc/hosts
adb: error: failed to copy 'hosts.mod' to '/system/etc/hosts': Permission denied

运行以下命令:

1
root@maguro:/ # chmod 666 /system/etc/hosts

然后查看hosts文件的属性。

1
2
root@maguro:/ # ls -al /system/etc/hosts
-rw-rw-rw- root     root           25 2013-08-14 07:00 hosts

可以看到所有人都可以读写hosts文件了。

既然一切都准备就绪,那就再来试试上传修改后的hosts文件吧。

1
2
C:\tools>adb push hosts.mod /system/etc/hosts
adb: error: failed to copy 'hosts.mod' to '/system/etc/hosts': Read-only file system

怎么回事,为什么还是拷贝失败呢?不是已经把文件系统改为可读写了吗?

并且在查找原因的过程中还发现一个奇怪的事情。在root模式下/system目录是可读写的,但在shell模式下/system却是只读的。

1
2
3
4
5
root@maguro:/ # mount | grep system
/dev/block/platform/omap/omap_hsmmc.0/by-name/system /system ext4 rw,seclabel,relatime...
root@maguro:/ # exit
shell@maguro:/ # mount | grep system
/dev/block/platform/omap/omap_hsmmc.0/by-name/system /system ext4 ro,seclabel,relatime...

而且在手机上的Terminal Emulator中把/system目录mount成可读写之后,在adb shell的root模式下查看/system的状态仍然显示为只读。

在网上找啊找啊找啊,都快要绝望了,终于找到可能之问题所在。就是这个帖子:mount in shell as user or root with different output。有个回答提到mount namespace这样东西。然后才知道:

A mount namespace is the set of filesystem mounts that are visible to a process.

每个进程的挂载点对其它进程是不可见的。Terminal Emulator中mount后的挂载点属于该进程,而adb shell中shell模式和root模式的挂载点分别属于各自的进程。这就是前面root模式下修改/system目录为可读写后在shell模式下仍显示为只读的原因。

知道问题的原因了,那如何解决呢?

在SuperSu应用的设置中有个mount namespace separation的选项,如下图所示:

mount-namespace-separation

把勾选取消,然后mount的挂载点就是全局性的了,不再为mount它们的进程所独有。不过要记住的是,只有在重启手机后该修改才有效。

下面是取消mount namespace separation后在上传的结果:

1
2
C:\tools>adb push hosts.mod /system/etc/hosts
[100%] /system/etc/hosts

可以看到[100%]的回显,说明文件已经上传完成。

查看hosts文件的属性:

1
2
root@maguro:/ # ls -al /system/etc/hosts
-rw-rw-rw- root     root       137679 2017-02-16 00:20 hosts

文件的大小已经由25变成137679,说明文件已经替换完成。

下面就是恢复手机到原先的状态:

1
2
3
4
root@maguro:/ # chmod 644 /system/etc/hosts
root@maguro:/ # ls -al /system/etc/hosts
-rw-r--r-- root     root       137679 2017-02-16 00:20 hosts
root@maguro:/ # mount -o ro,remount /system

至此,修改hosts文件的工作就算大功告成。

共享代码的风险

英文原文:https://www.innoq.com/en/blog/the-perils-of-shared-code/

通往地狱的道路往往是由良好的意愿铺就。在各种软件项目中,我看到人们走在这样的道路上,他们在微服务之间借助库共享代码。在几乎每个组织支持微服务架构的项目中,各个团队和开发者都期望以某些核心库为基础构建他们的微服务。显然,即使可能带来的问题已经被知道很长时间了,很多人仍然不知道它们。在这篇博文中,我想研究为什么使用这样的库可能起初听起来有吸引力,为什么可能会出现问题,以及如何能够减轻这些问题。

共享代码的目的

通过库来共享代码有两个主要目的:共享领域逻辑和共享基础设施层中的抽象。

  1. 共享的领域模型:领域模型的特定部分在两个或多个有界上下文之间是共同的,因此,作为三番五次实现它的替换,你消除了重复的需要和引入该领域逻辑的不一致实现的可能性。通常,人们想要像那样共享的领域模型的部分是核心领域或一个或多个通用子领域。在领域驱动设计的行话中,这也被称为共享内核。通常,你可以在这里找到像会话和身份验证逻辑这样的概念,但不限于此。一套相关的方法是规范数据模型。

  2. 基础设施层抽象:你想避免一次又一次地实现基础设施层的有用抽象,因此你把它们放进一个库里。通常,这些库在数据库访问、消息传递和序列化等方面提供一套统一的方法。

两者的动机是相同的——避免重复,也就是说,遵循DRY原则(Don’t repeat yourself!)。一旦实现这些逻辑有几个好处:

你不需要花费宝贵的时间致力于那些已经被解决的问题。

有一套统一的方式做消息传递、数据库访问等。这意味着,当开发者需要去阅读和修改其他开发者最初创建的微服务中的代码时,他们很容易找到他们的方式。

关于彼此行为略有不同的业务逻辑或基础设施关注点,你不想有不同的实现。取而代之的是,有一套做正确事情的规范实现。

共享代码的问题

在理论上听起来很棒的东西不会没有自己的问题,而且这些问题可能比你试图用你的库解决的问题更令人痛苦。Stefan Tilkov已经详细解释了为什么你应该避免规范的数据模型。除此之外,让我指出一些其它的问题。

分布式单体

通常,似乎存在一个隐含的假设,将东西放入库意味着你永远不必担心使用错误或过时的实现构成的服务,因为他们只需要更新其对库的依赖关系到最新版本。

每当你依靠通过将所有的微服务更新到同样的新版本库,来对所有微服务的某些行为作出一致的改变时,你就会在它们之间引入强耦合。你失去了微服务的一个主要优点,即它们彼此独立地演进和部署的能力。

我见过这样的案例,所有的服务必须同时部署,以便服务仍能正常工作。如果你达到这种状态,不可否认,你实际上构建了一个分布式的单体。

一个流行的示例是使用代码生成,例如,基于服务API的Swagger描述,以便为你的服务提供一个客户端库。比你想象的更多,开发者可能会滥用此种方式进行重大变更,因为依赖服务“只”需要使用新版本的客户端库。这不是你如何演进一个分布式系统

依赖地狱

库,尤其是那些旨在为基础设施关注点提供通用解决方案的库,往往有个额外的问题:它们会附上它们依赖的一整套额外的库。你的库的传递依赖树越大,它导致俗称为依赖地狱的噩梦的可能性就越高。因为你的微服务可能需要自己的额外的依赖,它们同样具有传递依赖性,直到它们中的某些库间接地拉进一些库的冲突版本,这只是个时间问题,只在不同版本之间选择是不可能的,因为它们是二进制不兼容的。

当然,你的解决方案也许只是提供微服务可能需要的所有库作为你的核心库的依赖。那仍然意味着你的微服务不能独立地演进,例如通过升级到它们依赖的唯一的特定库的更高版本——它们都与核心库的发布周期步调一致。除此之外,为什么你要强制每个服务接受一整堆的依赖,当它们实际上可能只需要依赖中的一些时?

自顶而下的库设计

通常情况下,我见过的库被一个或多个架构师强加于开发者,采用自顶而下的方法进行库设计。

通常,在这种情况下发生的是,由库暴露的API太受限制和不灵活,或者使用了错误的抽象级别,因为它们是由不够熟悉广泛的不同的真实世界用例的人设计的。这样的库经常导致不得不使用它的开发者遭受挫折,以及导致人们试图绕过库的限制。

单语言解决一切

强制使用库的最明显的缺陷之一是,这使得它更难以切换到不同的编程语言(或者平台,比如JVM或.NET),再次失去了微服务架构的一个优势,即选择最适合给定问题的技术的能力。如果你后来意识到,你终究需要这种语言或者平台的多样性,你必须创造各种奇怪的支持。例如,Netflix提出的Prana,一个同时运行非JVM服务的附加件,为他们提供到Netflix技术栈的一套HTTP API。

我们能不能做得更好?

由于所有的问题都是通过库共享代码引入的,最极端的解决方案是根本没有这样的库。如果你这样做,你将不得不做一些复制和粘贴或者为新的微服务提供一个模板项目,以便从前面所述的步调一致中释放你的服务。基础设施代码以及领域模型的共享内核中都可以这么做。事实上,Eric Evans在他的关于领域驱动设计的经典蓝皮书中提到,“通常各个团队只是在各自的内核备份上改动,每隔一定时间才会与其他团队集成”[1]。共享内核不一定要是库。

如果你不喜欢复制和粘贴的想法,那也很好。毕竟,如上所述,通过库共享代码有一定的优势。在这种情况下,这里有一些重要的事情需要考虑:

最少依赖的小型库

尝试将大的共享库分成一组非常小的、高度集中的库,每个库解决一个特定的问题。试着让这些库成为零依赖库,只依靠语言的标准库。是的,仅仅针对语言的标准库来编程并不总是令人愉快的,但是对于你公司的所有团队的巨大好处(甚至超出你的公司,如果你的馆是开源的)显然大于这个微小的不便。

当然,零依赖并不总是可能的,特别是对于基础设施关注点。对于这些,通过你的每个小型库最小化所需的依赖。另外,有时可以独立于库的核心,提供与别的库的绑定或集成作为单独的工件。

留下选择余地

不要指望服务将在特定时间点更新到共享库的最新版本的事实。换句话说,不要强制团队进行库更新,而是让他们可以按照自己的节奏自由更新。这可能需要你以向后和向前兼容的方式修改库,但它会解耦你的服务,不仅给你微服务架构的运营成本,而且还有一些优势。

如果可能,不仅要避免强制库更新,还要使库本身的使用可选。

自底而上的库设计

最后,如果你想拥有共享库,我见过的获得成功的项目是使用自底而上的方法。让你的团队实现他们的微服务,而不是让象牙塔架构师设计在现实世界中几乎不可用的库,而当在多个服务的生产中已经证明它们自己的一些常见模式出现时,将它们提取到库中。

[1] Evans, Eric: Domain-Driven Design: Tackling Complexity in the Heart of Software, p. 355

协议分析器的威力

英文原文:http://arstechnica.com/information-technology/2016/09/the-power-of-protocol-analyzers/

问题发生在错综复杂的网络世界里。但要在一时激动之下确定一种新型问题的确切原因变得有些冒险。在这种情况下,当Google-fu耗尽的时候甚至其他能干的工程师也可能会被迫去依赖试错法。

幸运的是,有个秘密武器等待乐意的工程师去部署——协议分析器。该工具允许你明确地确定几乎任何错误的根源,给你提供在底层协议上自我学习的能力。现在唯一的问题是,许多工程师因为(毫无根据的)恐惧而完全回避它。

什么是协议分析器?

协议分析器,或者数据包嗅探器,是一个用于拦截通信量,存储它们,并以一个已解码的、人类可读的状态呈现它们的工具。现代协议分析器比如Wireshark甚至可以靠自己发现基本的问题,然后使用捕获的数据执行统计分析。

不理会特性,数据包嗅探器都以基本相同的方式工作。它们把自己插入到网络堆栈中,把所有通信量复制到一个缓冲区或文件。大部分还会将网络驱动置于“混杂模式”,该模式从根本上说允许这些工具取回所有进入网络堆栈的通信量,而不是只采集前往系统本身的通信量。

协议分析仪如何帮助

在很多情况下,解决一个困难的网络问题的最难部分是找到和理解问题的根源。这种困难的部分源于这样的事实,你对大多数问题使用的工具不是正确的对底层问题的工具。

如果你是一个系统管理员,很有可能你经常用于数据采集的工具是某种日志和错误消息。通常,这些都是解释工具。这些实体试图把原始数据总结为对非开发者或非工程师有意义的东西。因为解释工具是从应用层的视角提供问题的汇总数据,它们往往不能帮助你解决底层的问题。

例如,一条事件日志消息可以告诉你应用程序无法连接到服务器。它甚至可以告诉你根本原因是超时。但这条消息不大可能告诉你超时是由一个黑洞路由器丢弃一个大帧引起。它不能,因为事件日志消息服务不知道错误为何发生。为了使工具知道那个,它需要预测(不解释)这个非常问题,在MTU稳步减少的情况下发送数据包,直到一个通过。如果一个事件日志消息服务早就被编写好要做那件事,从一开始你就不会有这个问题。

当使用错误的工具时,你可能会在某处花上几个小时甚至几周的时间,直到你侥幸得到解决方案。然而,通过使用协议分析器和历久弥新的ping命令,你可以非常容易地在大约5分钟内诊断这个问题。就像早在高中时我的汽车技术辅导员就告诉我的,它全都是关于对任务使用恰当的工具。

除了确定错误,协议分析器提供为数不多的方法之一去证实问题的根源。以前我在微软的时候,棘手问题在团队间来回穿梭是很常见的,因为每个组误解由解释工具提供的数据。首先,问题可能被发送到Exchange团队,接着它可能被穿梭到Active Directory团队,然后最后到Networking团队。

通常,这是因为在其它团队的能力范围之内一个问题好像是合理的。然而,烫手山芋的游戏往往停止在Networking团队。为什么?因为Networking团队的头号工具是证实问题根源的救世主。

网络,像所有的计算,其核心是完全合乎逻辑的。一旦你了解它在幕后是如何工作的,你就有能力在底层确定问题,不论问题是多么独特。作为协议分析的一个伟大副作用,你也将学到很多关于网络的知识,它们将帮助你解决各种各样的网络问题(即使那些不需要协议分析)。

Wireshark基础

现在,有各种各样的协议分析器可供选择,从免费的和相当功能的微软消息分析器到特性极其丰富但十分昂贵的Savvius Omnipeek。多年来我已经使用过大量的分析器,但我最喜欢的用于常规故障排除的协议分析器是Wireshark。它是免费的,开源的,多平台的具有很多特性的分析器。有个充满活力的社区站在它背后,而且Wireshark也相当容易习惯。这让它成为一个很好的开始的地方。

你可以从 https://www.wireshark.org/ 下载用于你操作系统的Wireshark。安装它没有什么特别的,但如果你是安装在Windows上,确保也安装了捆绑的WinPCAP驱动程序。这允许Wireshark实际上捕获数据包(没有它,你只能观看存档的数据包)。

Wireshark通常将你的NIC置于混杂模式。正常情况下,你的NIC只会保留前往你的MAC或者广播MAC(FF-FF-FF-FF-FF-FF)的帧。启用混杂模式后,不管怎样,你的NIC保留所有它听到的帧。

从理论上讲,这意味着你应该接收所有的在你Ethernet段上的数据包。不过,实际上如今几乎所有的Ethernet网络都是交换网络。如果你想接收所有的通信量,必须多做些工作。

一旦你已经安装了Wireshark,使用它是相当简单的。把它打开,你将看到如下显示的屏幕:

wireshark-network-analyzer

这个屏幕给你展示选择一个在它上面捕获数据包的NIC的选项和输入一个用于只捕获一部分入站数据包的过滤器的选项。如果你选择一个NIC然后点击在文件菜单下面的小鱼翅图标,Wireshark将立即开始捕获数据包。

随着数据包被捕获,Wireshark在主界面中实时地显示它们。当你准备停止时,你只需点击在鱼翅图标旁边的小红方块。

wireshark-main-interface

数据包列表部分显示在这个点捕获的每件事物,按它们被捕获的顺序排序(默认)。(你可以通过点击要作为排序依据的标题任意地排序这些数据包。)

数据包细节部分显示Wireshark解码的在数据包中的每个报头。基本上,Wireshark有几乎今天在用的每个协议的解码器,并且为了显示分成字段的数据,解码器工具自动应用到每个数据包。

举例来说,如下,我已经为一个典型的HTTP数据包增加了Ethernet II报头。

wireshark-http-header

你可以清楚地看到Wireshark已经析出了Destination和Source的MAC地址,以及Type字段,它是0x0800。Type字段指出下个报头应该是一个IPv4报头,Wireshark很方便告诉你这个。

这个解码特性使你不必自己计算字节和解码它(不过,如果你愿意,你仍然还可以在原始字节部分做)。如果对原始字节部分有兴趣:Wireshark同时为所有数据提供ASCII转换,它有时提供令人惊讶的数据。在下图,你可以在ASCII视图里的这个数据包中清楚地看到HTTP请求发送的细节。

wireshark-ascii-view

Wireshark同时也提供一些非常实用的分析和统计特性,包括测量响应时间和往返时间的能力。但到目前为止,最有用的特性是过滤功能。

在数据包列表直接的上方是一个文本框,那里你可以输入显示过滤器。默认不使用过滤器,意味着显示被捕获的所有数据包。然而,你经常最后得到信息过载,而过滤掉噪音是数据包分析的一个非常重要的部分。

Wireshark中的过滤器按照一门简单的语言结合协议字段、比较运算符和逻辑运算符以便过滤掉不匹配条件的数据包。例如,过滤器http将只显示HTTP通信量,而过滤器ip.addr == 192.168.1.10将只显示源或目标的IP地址是192.168.1.10的数据包。

当你是第一次开始的时候,过滤可能会有点令人生畏,但通常在Wireshark中学习过滤器的最简单的方法是使用内建的表达式工具。可以通过点击过滤器文本框右边的Expression按钮访问它。

这个工具允许你寻遍Wireshark本身支持的所有协议,挑选你想过滤的字段而不需要知道过滤器动词或语法。只需选择协议,填写呈现的字段,然后过滤器将会为你而建。这里,我使用表达式工具构建一个仅查找Ethernet广播的过滤器。

wireshark-filter-expression

注意工具如何在底部的绿框中显示最终的过滤器语法。通过注意这个字段,你将最终变得熟悉你的最常用的过滤器。

然而,你不能用表达式工具做的一件事是把过滤器串起来。因为那个原因,你需要学习一些逻辑运算符。Wireshark的基本逻辑运算符是and(&&)、or(||)和not(!)。

and被用于结合过滤器,只有满足所有条件的数据包会被显示。例如,过滤器http && ip.addr == 192.168.1.10将只显示在第7层报头中的HTTP协议和在IP报头中的IP地址192.168.1.10两者都包括的数据包。

or被用于查找两者中的任何一个过滤器,因此满足你输入的任何条件的数据包都会被显示。举例来说,过滤器http || ip.addr == 192.168.1.10将显示在第7层报头中的HTTP协议或在IP报头中的IP地址192.168.1.10的数据包。

not被用于从结果中过滤掉一些东西。例如,过滤器ip.addr == 192.168.1.10 ! http将显示有在IP报头中的IP地址192.168.1.10但没有在第7层报头中的HTTP协议的数据包。

关于基本的Wireshark功能最后要注意的事是,除保存你的原始捕获外,你也有多种多样的选项导出捕获。

首先,你导出当前选择的数据包、所有数据包、你标记的数据包或者一段范围的数据包。在这些选项的每一个中,你可以选择导出所有被捕获的数据包或只是被显示的数据包(考虑当前应用的过滤器)。这些选项让你非常具体地知道你想导出哪些数据包。

wireshark-export-packets

此外,你可以把数据包导出成几乎任何常用的格式。在Wireshark中用于文档和电子邮件转出的最好的特性之一是以纯文本或CSV格式导出解剖数据包(完整的数据包解码)的能力。要做到这个,只需从文件菜单里选择“Export Packet Dissections”。

理解你所看到的

尽管所有这些功能都很好,底线是如果你不明白在每个报头中字段的目的它们都是无用的。幸运的是,除了少量专有协议,你遇到的几乎每个协议的规格说明都是免费在线的。

例如:Ethernet,你可以直接到IEEE下载标准;802.3标准可以在 http://standards.ieee.org/about/get/802/802.3.html 获得。它是免费的直接来自权威人士。如果你在查找802.3 Ethernet帧格式,你将发现真的只有3个感兴趣的字段:目标MAC地址、源MAC地址和类型/长度字段。下图中在Wireshark解剖体左边的是来自IEEE 802.3规格说明Section 1中Part 3.1.1的Figure 3-1:

wireshark-packet-format

如果你想知道preamble和SFD发生了什么,它们在帧从NIC到Wireshark向上传递给栈之前被移除。同样地,你通常不会在末尾看到FCS,因为它在向上传递帧之前被剥去。

在第2层上面,所有TPCTCP/IP协议由IETF管理和由RFCs(请求评论)定义。所有这些RFCs可以在站点 https://www.rfc-editor.org/ 上即刻地免费地获得。虽然它们有点简洁(并且因为这个原因有时难以理解),它们总是正确的,具体问题的澄清可以使用Google快速搜索获得。

举例来说,通常混淆新手的事情之一是大量TCP重置,或者在TCP报头中数据包有打开的RST标记。浏览RFC 793(TCP),你可能会得到RST总是用信号告知一些坏事情的印象。几乎所有的35个左右提到的RST与某种错误条件有关联。

然而,使用关键词“tcp rst from client”的Google快速搜索将让你得到大量的关于这个现象的很好的讨论。也许最好的是来自Wireshark论坛,在那里他们解释说这很平常,因为客户端应用仅仅被编码去重置链接而不是优雅地关闭它。在这种情况下,服务端已经发送一个FIN。作为回复一个FIN/ACK并等待最终的ACK的替换,客户端只需通过发送一个RST并中止会话来优化过程。在下面的示例中这可以清楚地被看到。

wireshark-pcap-example

除了规格说明和Google,另一个学习协议通常如何运转的良好来源是示例跟踪的资料库。这些示例跟踪允许你去查看相当典型的既常见又晦涩的协议操作,以及一些十分罕见的平常可能不会碰到的错误。

一个很好的起点是Wireshark Wiki上的样板捕获:https://wiki.wireshark.org/SampleCaptures 。在这里有大量非常有用的捕获让你去下载以及用过滤和其它Wireshark特性做实验,包括像广播风暴、病毒和攻击套装这样有意思的错误。如果这些还不够,在这个页面上还有一些其它资源的链接去协助你。但是毫无疑问地,变得擅长协议分析的最快方式是仔细观看大量的捕获并试图理解被使用的协议。

如何得到好的捕获

如果不能捕获正确的数据,世界上所有的领悟都无济于事。在最基本的层面,目标是只捕获涉及你试图解决的问题的数据包,有效减少你跟踪里的噪音。

为了做到这点,你可以使用一个捕获过滤器去从捕获中排除那些匹配过滤器外的所有数据。如果你确切地知道你在寻找什么这会工作的很好,但往往这种方法会导致你不能觉察一些重要的事情。大多数时候你只有一个问题是什么的粗略想法,或者你忘了一些潜在的找到错误的关键的过程。如果这种情况发生,使用捕获过滤器就没那么幸运,而且你不能返回和没重设置它就不过滤捕获。

例如,在诊断一个网站的性能问题时,你可能决定使用一个捕获过滤器集从Web服务器自身取得捕获,以便只捕获在其与客户端系统和后台SQL服务器之间往返的数据。然而,这个问题实际上可能仅仅是Web服务器使用的身份验证服务器过载,等待身份验证才是引起整个性能问题的原因。使用你选择的捕获过滤器你将永远不会发现这个问题。

这是我倾向于捕获所有数据并且使用显示过滤器去减少跟踪里的噪音的原因。这不是说捕获过滤器完全不必要。捕获过滤器的一个常见用途是当你有一个非常繁忙的你正在捕获的千兆或万兆连接的时候,捕获过滤器变得有用仅仅是因为大量的数据。不过,你始终需要牢记过滤器的限制。

得到一个好的捕获的第二部分是正确识别你需要捕获的系统。举例来说,在前面关于Web服务器性能问题的例子中,我可能首先会从Web服务器和Web客户端两者取得同时发生的捕获。这样你可以看到两边的正常预期行为的任何偏差,这有助于你将问题隔离到服务器或客户端。

一旦查明延迟是一个与身份验证有关的服务端问题,然后我会从Web服务器和身份验证服务器两者取得其它的跟踪。这样,我可以看到是本地到身份验证服务器的问题还是等待像DNS这样其它服务的问题或者Global Catalog是实际上的罪魁祸首。

得到一个好的捕获的第三步是在成功和失败条件中都使用捕获。举例来说,如果你有一个间歇性的Web服务器性能问题,设法在站点正常和不正常工作时都得到跟踪。这能给你一个好的和坏的比较跟踪,可以使用它去隔离问题。

最后,当处理一个间歇性的问题时,你会发现很难得到一个失败捕获。在这种情况下,Wireshark有一个很重要的特性被称为环形缓冲区,它允许你持续地捕获。

通常,特别在一个繁忙的网络上,持续的捕获将冒填满磁盘的风险。但有了环形缓冲区,Wireshark会写入文件直到它达到指定大小或者经过一段时间,然后它会切换到一个新文件。一旦指定数量的文件已经被写入,程序删除最旧的文件。例如,看看下面我已经定义的设置:

wireshark-capture-interfaces

这个配置告诉Wireshark不管文件大小每10分钟创建一个新文件,并且确保程序保留总计3个文件,根据需要删除最旧的。这确保从错误被通知的时间起我有30分钟去停止捕获。这是一个非常有用的技术用于捕获极其间歇性的问题。

这些是你需要的以便用Wireshark开始故障排除的所有基本技术。使用这些技术和资源,你会发现你经常能用比几乎任何其它技术更短的时间找到和验证网络问题的原因。快乐的故障排除。

如何创建有效的图标

英文原文:http://www.awwwards.com/how-to-create-effective-icons.html

我可能就是你所说的图标爱好者。我喜欢图标,而且我更加喜欢制作它们!作为一个艺术家,我的背景在很大程度上是绘画——我喜欢绘画,并且我已经画了一辈子(甚至远远早于我知道什么是图形设计)。我想,这是我理解创建图标的一个关键。绘画教你看——然后把你所看到的转化成纸上的线条和图形——而这正是如何创建有效的图标。

几何图形

因此,对初学者而言,基本上任何东西都可以用这四种图形组合而成:

effective-icons-1

当我想把某事物转换成一个图标时,我观察它然后尽可能地将其拆分为最简单的图形。例如,水滴可以用一个三角形和一个圆形组成。

effective-icons-2

心形图标可以由两个圆形和一个三角形构成。

effective-icons-3

我每次都是在Adobe Illustrator中创建这些图形。使用矢量图形可以让我控制线条的粗细,以及图形和其锚点的相互作用。Illustrator也可以让我自由地把线条转换成图形,反之亦然。这一切也许看起来十分基础,但它是我用于创建最复杂图标的同样的方法。下面是我最近在做的一个略微更加复杂些的《权利法案》图标的示例,在这里我应用了同样的原则。

effective-icons-4

界面图标

我最近有机会为一款超赞的iPhone应用Parker Planner制作一组图标。我很喜欢做这个项目,这个项目其中最重要的一个方面是创建一组易懂的、私有的、实用的和美观的用户界面图标,可以帮助用户浏览操作这款略微复杂的计划应用。

effective-icons-5

让我们选取这些图标的其中之一分解看看我如何创建它。例如,垃圾桶图标是由三个圆角矩形和三条线构成。

1、选择圆角矩形工具。

effective-icons-6

2、拖动出一个图形。

effective-icons-7

3、调整笔划宽度直到你满意。

effective-icons-8

我通常选择在整组图标中使用一到两种笔划宽度。

effective-icons-9

这使它们看起来更一致和感觉更有整体性。

4、用另一个圆角矩形创建盖子。

effective-icons-10

5、再一个圆角矩形创建盖子的把手。

effective-icons-11

6、擦除圆角矩形的下半部分。

effective-icons-12

7、现在,通过添加三条竖线到桶身上给桶添加条纹。

effective-icons-13

8、然后你就获得了它!一个垃圾桶图标……如果你喜欢,你可以用颜色或线条宽度做进一步调整。

effective-icons-14

我在创建图标时经常使用的一些其它真正有用的工具是Pathfinder,我使用它来剪切、连接和挖空图形。

effective-icons-15

Stroke/Fill工具,它帮助你将图形在填满和笔划间切换。

effective-icons-16

以及我非常喜欢的工具Stroke Panel,它帮助你将拐角和线的末端从直角转换到圆角。

effective-icons-17

当我完成一组图标,我通常将它们全体紧挨着排成一排,看看是否有哪个看起来很奇怪或不到位。然后我会做任何必要的修改。

effective-icons-18

最后,我总是在应用中测试它们以确保它们感觉正确和功能良好。

effective-icons-19

最终,我想说创建优秀图标的方法不仅仅是学习Illustrator技巧,尽管它们也是必需的。最好的做法是练习把你周围看到的事物分解成简单图形。你在这点上越是变得更好,你越是能够成为更高超的图标设计师!加油!

如何使用Flexbox构建新闻站点布局

英文原文:http://webdesign.tutsplus.com/tutorials/how-to-build-a-news-website-layout-with-flexbox--cms-26611

flexbox-preview

在投入并开始之前你没有必要理解Flexbox的每个方面。在这篇教程中,我们将介绍一些Flexbox的特性,同时设计一个类似The Guardian的“新闻布局”。

我们正在使用Flexbox的原因是它提供了非常强大的特性:

  • 我们可以很容易制作响应式列
  • 我们可以使列等高
  • 我们可以把内容推到容器的底部

那么让我们开始吧!

1. 从两个列开始

在CSS中创建列一直是个挑战。长期以来,唯一的选项是使用float或者table,但它们都有它们自己的问题。

Flexbox使这个过程更简单,给予我们:

  • 更简洁的代码:我们只需要一个带有display: flex的容器
  • 不需要清除float,以防止意外的布局行为
  • 语义化的标记
  • 灵活性:我们可以用几行CSS代码来调整列的大小、拉伸或者对齐列

让我们从创建两个列开始:一个是容器宽度的2/3,一个是1/3。

1
2
3
4
5
6
7
8
<div class="columns">
  <div class="column main-column">
    2/3 column
  </div>
  <div class="column">
    1/3 column
  </div>
</div>

这里有两个元素:

  • columns容器
  • 两个column子容器,其中一个带有附加的名为main-column的class,我们将用它来让该子容器更宽
1
2
3
4
5
6
7
8
9
10
11
.columns {
  display: flex;
}

.column {
  flex: 1;
}

.main-column {
  flex: 2;
}

作为有一个flex值为2的主列,它将占用其它列的两倍空间。

通过添加一些附加的视觉样式,这里是我们得到的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
html {
  font-family: sans-serif;
}

.columns {
  display: flex;
}

.column {
  background: #eee;
  border: 5px solid #ccc;
  flex: 1;
  padding: 20px;
}

.main-column {
  flex: 2;
}

2. 让每一列都成为Flexbox容器

这两列中的每一个都将包含若干垂直堆叠的文章,因此我们打算把column元素也转变成Flexbox容器。我们想要:

  • 文章被垂直堆叠
  • 文章拉伸并填充可用空间
1
2
3
4
5
6
7
8
.column {
  display: flex;
  flex-direction: column;  /* 使文章垂直堆叠 */
}

.article {
  flex: 1;  /* 拉伸文章以填补剩余的空间 */
}

容器上的flex-direction: column规则结合在子容器上的flex: 1规则确保文章将填补整个垂直空间,保持最初两列具有相同的高度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="columns">
  <div class="column main-column">
    <article class="article">
      Hello World
    </article>
    <article class="article">
      Hello World
    </article>
  </div>
  <div class="column">
    <article class="article">
      Hello World
    </article>
    <article class="article">
      Hello World<br>
      Foo Bar
    </article>
    <article class="article">
      Hello World
    </article>
  </div>
</div>
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
28
29
html {
  font-family: sans-serif;
}

.columns {
  display: flex;
}

.column {
  background: #eee;
  border: 5px solid #ccc;
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: 10px;
}

.main-column {
  flex: 2;
}

.article {
  background: mediumseagreen;
  border: 5px solid seagreen;
  color: white;
  flex: 1;
  margin: 10px;
  padding: 20px;
}

3. 让每篇文章都成为Flexbox容器

现在,为了给我们额外的控制,让我们把每篇文章也转变成一个Flexbox容器。每篇文章将包含:

  • 一个标题
  • 一个段落
  • 一个带有作者和评论数量的信息栏
  • 一张可选的响应式图片

我们在这里使用Flexbox是为了把信息栏推到底部。我们的目标文章布局如下图所示:

flexbox-card

这里是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<a class="article first-article">
  <figure class="article-image">
    <img src="">
  </figure>
  <div class="article-body">
    <h2 class="article-title">
      <!-- 标题 -->
    </h2>
    <p class="article-content">
      <!-- 内容 -->
    </p>
    <footer class="article-info">
      <!-- 信息 -->
    </footer>
  </div>
</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.article {
  display: flex;
  flex-direction: column;
  flex-basis: auto;  /* 基于其内容设置初始元素大小 */
}

.article-body {
  display: flex;
  flex: 1;
  flex-direction: column;
}

.article-content {
  flex: 1;  /* 这将使内容填补剩余的空间,并因此在底部压入信息栏 */
}

文章元素被垂直放置归功于flex-direction: column规则。

我们把flex: 1应用到article-content元素以便它填补空白空间,并且把article-info推到底部,无论列的高度如何。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<main class="main columns">
  <section class="column main-column">
    <a class="article first-article" href="#">
      <figure class="article-image is-4by3">
        <img src="https://s3.amazonaws.com/cms-assets.tutsplus.com/uploads/users/1366/posts/26611/attachment/image-01.png" alt="">
      </figure>
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
          Maecenas non massa sem.
          Etiam finibus odio quis feugiat facilisis.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Proin ornare magna eros.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>

    <a class="article" href="#">
      <figure class="article-image is-16by9">
        <img src="https://s3.amazonaws.com/cms-assets.tutsplus.com/uploads/users/1366/posts/26611/attachment/image-02.png" alt="">
      </figure>
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
          Maecenas non massa sem.
          Etiam finibus odio quis feugiat facilisis.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
          Maecenas non massa sem.
          Etiam finibus odio quis feugiat facilisis.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
  </section>

  <section class="column">
    <a class="article" href="#">
      <figure class="article-image is-3by2">
        <img src="https://s3.amazonaws.com/cms-assets.tutsplus.com/uploads/users/1366/posts/26611/attachment/image-03.png" alt="">
      </figure>
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
    <a class="article" href="#">
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
    <a class="article" href="#">
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
    <a class="article" href="#">
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
  </section>
</main>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
html {
  background: mediumseagreen;
  font-family: sans-serif;
  font-size: 14px;
}

a {
  text-decoration: none;
}

div, h2, p, figure {
  margin: 0;
  padding: 0;
}

.main {
  margin: 0 auto;
  max-width: 1040px;
  padding: 20px;
}

.columns {
  display: flex;
}

.column {
  display: flex;
  flex: 1;
  flex-direction: column;
}

.main-column {
  flex: 2;
}

.article {
  background: white;
  color: #666;
  display: flex;
  flex: 1;
  flex-direction: column;
  flex-basis: auto;
  margin: 10px;
}

.article-image {
  background: #eee;
  display: block;
  padding-top: 75%;
  position: relative;
  width: 100%;
}

.article-image img {
  display: block;
  height: 100%;
  left: 0;
  position: absolute;
  top: 0;
  width: 100%;
}

.article-image.is-3by2 {
  padding-top: 66.6666%;
}

.article-image.is-16by9 {
  padding-top: 56.25%;
}

.article-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: 20px;
}

.article-title {
  color: #333;
  flex-shrink: 0;
  font-size: 1.4em;
  font-weight: bold;
  font-weight: 700;
  line-height: 1.2;
}

.article-content {
  flex: 1;
  margin-top: 5px;
}

.article-info {
  display: flex;
  font-size: 0.85em;
  justify-content: space-between;
  margin-top: 10px;
}

4. 添加一些嵌套列

在左边的列中,我们真正想要的是另一组列。因此我们将以我们已经用过的同样的columns容器代替第二篇文章。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="columns">
  <div class="column nested-column">
    <a class="article">
      <!-- 文章内容 -->
    </a>
  </div>

  <div class="column">
    <a class="article">
      <!-- 文章内容 -->
    </a>
    <a class="article">
      <!-- 文章内容 -->
    </a>
    <a class="article">
      <!-- 文章内容 -->
    </a>
  </div>
</div>

因为我们想要第一个嵌套列更宽,我们添加一个带有附加样式的名为nested-column的class:

1
2
3
.nested-column {
  flex: 2;
}

这将使新列的宽度是其它列的两倍。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<main class="main columns">
  <section class="column main-column">
    <a class="article first-article" href="#">
      <figure class="article-image is-4by3">
        <img src="https://s3.amazonaws.com/cms-assets.tutsplus.com/uploads/users/1366/posts/26611/attachment/image-01.png" alt="">
      </figure>
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
          Maecenas non massa sem.
          Etiam finibus odio quis feugiat facilisis.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Proin ornare magna eros.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>

    <div class="columns">
      <div class="column nested-column">
        <a class="article" href="#">
          <figure class="article-image is-16by9">
            <img src="https://s3.amazonaws.com/cms-assets.tutsplus.com/uploads/users/1366/posts/26611/attachment/image-02.png" alt="">
          </figure>
          <div class="article-body">
            <h2 class="article-title">
              Hello World
            </h2>
            <p class="article-content">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
              Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
              Maecenas non massa sem.
              Etiam finibus odio quis feugiat facilisis.
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
              Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
              Maecenas non massa sem.
              Etiam finibus odio quis feugiat facilisis.
            </p>
            <footer class="article-info">
              <span>By Joe Smith</span>
              <span>42 comments</span>
            </footer>
          </div>
        </a>
      </div>

      <div class="column">
        <a class="article" href="#">
          <figure class="article-image is-16by9">
            <img src="https://s3.amazonaws.com/cms-assets.tutsplus.com/uploads/users/1366/posts/26611/attachment/image-03.png" alt="">
          </figure>
          <div class="article-body">
            <h2 class="article-title">
              Hello World
            </h2>
            <p class="article-content">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            </p>
            <footer class="article-info">
              <span>By Joe Smith</span>
              <span>42 comments</span>
            </footer>
          </div>
        </a>
        <a class="article" href="#">
          <div class="article-body">
            <h2 class="article-title">
              Hello World
            </h2>
            <p class="article-content">
              Lorem ipsum dolor sit amet.
            </p>
            <footer class="article-info">
              <span>By Joe Smith</span>
              <span>42 comments</span>
            </footer>
          </div>
        </a>
        <a class="article" href="#">
          <div class="article-body">
            <h2 class="article-title">
              Hello World
            </h2>
            <p class="article-content">
              Lorem ipsum dolor sit amet feugiat facilisis.
            </p>
            <footer class="article-info">
              <span>By Joe Smith</span>
              <span>42 comments</span>
            </footer>
          </div>
        </a>
      </div>
    </div>
  </section>

  <section class="column">
    <a class="article" href="#">
      <figure class="article-image is-3by2">
        <img src="https://s3.amazonaws.com/cms-assets.tutsplus.com/uploads/users/1366/posts/26611/attachment/image-04.png" alt="">
      </figure>
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
    <a class="article" href="#">
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
    <a class="article" href="#">
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
    <a class="article" href="#">
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
  </section>
</main>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
html {
  background: mediumseagreen;
  font-family: sans-serif;
  font-size: 14px;
}

a {
  text-decoration: none;
}

div, h2, p, figure {
  margin: 0;
  padding: 0;
}

.main {
  margin: 0 auto;
  max-width: 1040px;
  padding: 20px;
}

.columns {
  display: flex;
}

.column {
  display: flex;
  flex: 1;
  flex-direction: column;
}

.main-column {
  flex: 3;
}

.nested-column {
  flex: 2;
}

.article {
  background: white;
  color: #666;
  display: flex;
  flex: 1;
  flex-direction: column;
  flex-basis: auto;
  margin: 10px;
}

.article-image {
  background: #eee;
  display: block;
  padding-top: 75%;
  position: relative;
  width: 100%;
}

.article-image img {
  display: block;
  height: 100%;
  left: 0;
  position: absolute;
  top: 0;
  width: 100%;
}

.article-image.is-3by2 {
  padding-top: 66.6666%;
}

.article-image.is-16by9 {
  padding-top: 56.25%;
}

.article-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: 20px;
}

.article-title {
  color: #333;
  flex-shrink: 0;
  font-size: 1.4em;
  font-weight: bold;
  font-weight: 700;
  line-height: 1.2;
}

.article-content {
  flex: 1;
  margin-top: 5px;
}

.article-info {
  display: flex;
  font-size: 0.85em;
  justify-content: space-between;
  margin-top: 10px;
}

5. 给第一篇文章一个水平布局

第一篇文章真的很大。为了优化空间的使用,让我们把它的布局切换成水平的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.first-article {
  flex-direction: row;
}

.first-article .article-body {
  flex: 1;
}

.first-article .article-image {
  height: 300px;
  order: 2;
  padding-top: 0;
  width: 400px;
}

这里的order属性是非常有用的,因为它允许我们改变HTML元素的顺序而不影响HTML标记。在标记中article-image实际上出现在article-body的前面,但它表现的好像出现在后面

6. 使布局可响应

这些就是我们想看到的,虽然它有点儿扁平。让我们通过响应式来修复它。

Flexbox一个极好的特性是你只需要移除容器上的display: flex规则就可以完全禁用Flexbox,同时保持所有其它的Flexbox属性(例如align-items或者flex)有效。

结果是,你可以通过仅在某个断点上启用Flexbox来触发一个响应式布局。

我们将从.columns和.column选择器中移除display: flex,而不是用一个Media Query包装它们:

1
2
3
4
5
6
@media screen and (min-width: 800px) {
  .columns,
  .column {
    display: flex;
  }
}

就是这样!在更小的屏幕上,所有的文章都在彼此的上面。超过800px时,它们将会被放置在两列中。

7. 添加最后的润色

为使布局在大屏幕上更具吸引力,让我们添加一些CSS微调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@media screen and (min-width: 1000px) {
  .first-article {
    flex-direction: row;
  }

  .first-article .article-body {
    flex: 1;
  }

  .first-article .article-image {
    height: 300px;
    order: 2;
    padding-top: 0;
    width: 400px;
  }

  .main-column {
    flex: 3;
  }

  .nested-column {
    flex: 2;
  }
}

第一篇文章的内容是水平放置的,文本在左边,图片在右边。而且,主列现在更宽(75%),嵌套列也是(66%)。这里是最终结果!

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
<header class="header">
  <h1>The Envato Tuts+ Report</h1>
  <h2><a href="http://webdesign.tutsplus.com/tutorials/how-to-build-a-news-website-layout-with-flexbox--cms-26611">Read tutorial</a></h2>
</header>

<main class="main columns">
  <section class="column main-column">
    <a class="article first-article" href="#">
      <figure class="article-image is-4by3">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/210284/image-01-lo.jpg" alt="">
      </figure>
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
          Maecenas non massa sem.
          Etiam finibus odio quis feugiat facilisis.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Proin ornare magna eros.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>

    <div class="columns">
      <div class="column nested-column">
        <a class="article" href="#">
          <figure class="article-image is-16by9">
            <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/210284/image-02-lo.jpg" alt="">
          </figure>
          <div class="article-body">
            <h2 class="article-title">
              Hello World
            </h2>
            <p class="article-content">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
              Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
              Maecenas non massa sem.
              Etiam finibus odio quis feugiat facilisis.
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
              Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
              Maecenas non massa sem.
              Etiam finibus odio quis feugiat facilisis.
            </p>
            <footer class="article-info">
              <span>By Joe Smith</span>
              <span>42 comments</span>
            </footer>
          </div>
        </a>
      </div>

      <div class="column">
        <a class="article" href="#">
          <figure class="article-image is-16by9">
            <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/210284/image-03-lo.jpg" alt="">
          </figure>
          <div class="article-body">
            <h2 class="article-title">
              Hello World
            </h2>
            <p class="article-content">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            </p>
            <footer class="article-info">
              <span>By Joe Smith</span>
              <span>42 comments</span>
            </footer>
          </div>
        </a>
        <a class="article" href="#">
          <div class="article-body">
            <h2 class="article-title">
              Hello World
            </h2>
            <p class="article-content">
              Lorem ipsum dolor sit amet.
            </p>
            <footer class="article-info">
              <span>By Joe Smith</span>
              <span>42 comments</span>
            </footer>
          </div>
        </a>
        <a class="article" href="#">
          <div class="article-body">
            <h2 class="article-title">
              Hello World
            </h2>
            <p class="article-content">
              Lorem ipsum dolor sit amet feugiat facilisis.
            </p>
            <footer class="article-info">
              <span>By Joe Smith</span>
              <span>42 comments</span>
            </footer>
          </div>
        </a>
      </div>
    </div>
  </section>

  <section class="column">
    <a class="article" href="#">
      <figure class="article-image is-3by2">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/210284/image-04-lo.jpg" alt="">
      </figure>
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
    <a class="article" href="#">
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
    <a class="article" href="#">
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
    <a class="article" href="#">
      <div class="article-body">
        <h2 class="article-title">
          Hello World
        </h2>
        <p class="article-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <footer class="article-info">
          <span>By Joe Smith</span>
          <span>42 comments</span>
        </footer>
      </div>
    </a>
  </section>
</main>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700);
@import url(https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css);

html {
  background: mediumseagreen;
  font-size: 14px;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  min-width: 300px;
  overflow-x: hidden;
  overflow-y: scroll;
  text-rendering: optimizeLegibility;
}

body {
  color: #666;
  font-family: "Source Sans Pro", "Helvetica", "Arial", sans-serif;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.4;
}

a {
  text-decoration: none;
  transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
}

div, h2, p, figure {
  margin: 0;
  padding: 0;
}

.header {
  color: white;
  padding: 40px 0 20px;
  text-align: center;
}

.header h1 {
  font-size: 40px;
  font-weight: bold;
}

.header h2 a {
  border-bottom: 1px solid rgba(255, 255, 255, 0.5);
  color: white;
  font-size: 20px;
  opacity: 0.5;
}

.header h2 a:hover {
  border-bottom-color: white;
  opacity: 1;
}

.main {
  margin: 0 auto;
  max-width: 1040px;
  padding: 10px;
}

.column {
  flex: 1;
  flex-direction: column;
}

.article {
  background: white;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
  color: #666;
  display: flex;
  flex: 1;
  flex-direction: column;
  flex-basis: auto;
  margin: 10px;
}

.article:hover,
.article:focus {
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
  color: #444;
}

.article-image {
  background: #eee;
  display: block;
  padding-top: 75%;
  position: relative;
  width: 100%;
}

.article-image img {
  display: block;
  height: 100%;
  left: 0;
  position: absolute;
  top: 0;
  width: 100%;
}

.article-image.is-3by2 {
  padding-top: 66.6666%;
}

.article-image.is-16by9 {
  padding-top: 56.25%;
}

.article-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: 20px;
}

.article-title {
  color: #333;
  flex-shrink: 0;
  font-size: 1.4em;
  font-weight: 700;
  line-height: 1.2;
}

.article-content {
  flex: 1;
  margin-top: 5px;
}

.article-info {
  display: flex;
  font-size: 0.85em;
  justify-content: space-between;
  margin-top: 10px;
}

@media screen and (min-width: 800px) {
  .columns,
  .column {
    display: flex;
  }
}

@media screen and (min-width: 1000px) {
  .first-article {
    flex-direction: row;
  }

  .first-article .article-body {
    flex: 1;
  }

  .first-article .article-image {
    height: 300px;
    order: 2;
    padding-top: 0;
    width: 400px;
  }

  .main-column {
    flex: 3;
  }

  .nested-column {
    flex: 2;
  }
}

总结

我希望我已经向你展示在投入和开始使用Flexbox前你不需要理解它的每个方面!响应式新闻布局是一个真正有用的模式。拆解它,把玩它,让我们知道你是如何进展的!