Swift 和函数式编程的遗产

tryswift-rob-napier-swift-legacy-functional-programming
原文地址 by Rob Napier
译文地址 by Alex Tse

Rob Napier 开发过单子(Monad),Functor of Doom。 他接触过 map,flattened 和 lensed。他反复迭代尝试,提出了 Maybe 类型,今后他也将继续致力于对函数式编程的探索。
他从 Haskell 到 Church,可以确定:Swift 不是一个函数编程语言,如果硬把它们当成一回事反而会扰乱 Swift 和破坏 Cocoa。
然而,Swift 却已经从函数编程吸取了一些很不错的经验,虽然值类型可能不是当前所流行的,但它们显然是未来趋势。
Rob 研究着已经有数十年的函数语言是如何对 Swift 产生了影响,以及如何更好的使用这些特性,同时还要保持 Swift 的个性和 Cocoa 的友好,当然少不了要拥抱面向协议编程。


概述

Swift 面世一周后,我写了“Swift并不是一种函数语言”这篇博客。两年过去了,Swift 依旧不是函数语言。这篇文章讨论的是经历了数十载的函数编程,以更 Swifty 方式带到 Swift 里去。

什么是函数编程?

它是单子(monads),函子(functors),Haskell 这类优美的代码。

函数编程不是一种语言或语法,它是一种用来思考问题的方式。函数编程在 Monad 出来的前几十年就有了。 我们会用函数编程来思考如何解决问题,并将它们结构化的组织起来。

用 Swift 写一段非常简单的代码看看:

var persons: [Person] = []
for name in names {
    let person = Person(name: name)
    if person.isValid {
        persons.append(person)
    }
}

这是个实现了两件事的简单循环:用 name 实例 person,然后把有效的 person 插入到数组当中。如此简单的代码,还是做了不少工作。因为要知道这其中发生了什么,必须从头看到尾过一遍。而且你还需要思考,“这个数组是做什么的?”

这段代码其实还可以更简单。我们可以关注点分离(separate the concerns),把事情分开。

比如我们现在有两个循环,每个循环做少量的事。我们从每个简单的循环代码里面找到模式:创建一个数组,遍历一些值,在每个值上面做一些操作,最后把它们插入到另一个数组里。

当你有一些事需要重复执行多次,你可能需要类似一个 extract 函数 – Swift 就有这个函数,叫 “map”。Map 可以将一个列表的东西转换成另一个列表。要注意的是:Map 会包含所有有效或无效的 person 对象。这是个基于 names 的 persons 列表。因此我们不需要从头开始过一遍代码,我们要表达的已经在这里面。

let possiblePersons = names.map(Person.init)
let persons = possiblePersons.filter { $0.isValid }

另一个循环模式也很常见:叫“filter”。Filter 接受一个返回 bool 的闭包函数。Filter 适用这个例子,可以得到有效的 person。我们可以将 map 和 filter 放在一起,把 map 得到的 Person 列表放到 filter 函数当中。

我们从上面的7行代码减少到2行。此外,我们可以重组它们:把这些所有函数通过链式(chaining)放在一起处理。

它十分易读,我们一次就可以读完。

let persons = names
    .map(Person.init)
    .filter { $0.isValid }

这种方式值得好好学习,我们应该适应这种写法(在我看来这很Swift)。在这个例子里无需再写 for 循环。

函数式工具

在1977年,约翰·巴科斯(John Backus)(帮助发明了 FORTRAN 和 ALGOL)赢得了图灵奖,并发表了一场名叫“编程能否从 Von Neumann 风格中解放出来”的演讲,我喜欢这个标题。“Von Neumann 风格”指的就是 FORTRAN 和 ALGOL。

这次演讲是一场道歉,原因是他发明了 FORTRAN 和 ALGOL。他解释说,指令式编程是一步一步的改变一些状态,直到进入你想得到的最终状态。他说“函数”这并不代表今天所说的功能,他鼓励许多函数研究者去学习研究。

我对该演讲很感兴趣,因为有一些东西可以带给 Swift:我们如何将复杂的东西分解成简单的东西。使这些简单的东西变得通用,然后再使用一些规则(如代数)将它们粘合起来。

代数的规则是把东西堆放在一起,然后按条件分离,最后一起转换它们。我们可以想一个用来控制程序的规则,事实上我们已经做到了:将一个循环分解成两个更简单的循环,找到它们惯用的形式,然后用链式(chaining)链在一起。当 Haskell 开发者第一次接触 Swift 的时候,往往会觉得不爽,因为他们一直实践的是 Haskell 的理论。

就如所有函数式语言一样,Haskell 的基础构成单位就是函数。它们都有非常好的方法来组织函数。下面我创建了一个新函数,叫 sum,把 foldr 函数和 + 函数合在一起,并赋予其初始值为 0。你阅读时可能觉得不顺畅,但是一旦运用起来,它的魅力会把你折服。

let sum = foldr (+) 0
  sum [1..10]

你可以用在 Swift 上,但你会发现代码变得丑陋,又不能很好的运行,别用错了单位组合,Swift 可不是函数式。

Swift 的构成单位是类型。你可以用 Classes,structs,enums 和 protocols 来构建(compose)。我们通常会通过一个函数将两种类型结合在一起,使用更简单的方式构建它们。

Swift 另一种常见的构成是将类型添加上下文(context)。最常见的是可选(optionals)类型。可选(optional)是添加上下文的类型,这个上下文可能有值,可能没有。那么这个类型就包含了一个小小的信息:它是否存在?这就是上下文的意思。添加上下文比其他跟踪信息的方式更强大。

extension MyStruct<T>: Sequence {
    func makeIterator() -> AnyIterator<T> {
return ... }
}

我们可以跟踪查看一个值,判断它是不是整数,是否有值,是否为-1,如果是-1那这意味着是无效值。那么如果每个地方都写上这样的判断,你会发现代码开始变得丑陋起来,而且容易出错,甚至编译器也帮不到你。

如果我们把 int 加上可选类型,那么编译器就可以帮助你,这个 int 就有了这样的上下文:“有值,没值,我可以确保你不会忘记它”。如果你曾经使用过-1,而这又很容易忘记检查,那么你的程序就会走到你意想不到的地方。

// No value: magic value
let noValue = -1
let n = 0
if n != noValue { ... }

// No value: context
let n: Int? = 0
if let n = n { ... }

例子

让我们来看一个更复杂的例子。

func login(username: String, password: String,
           completion: (String?, Error?) -> Void)
login(username: "rob", password: "s3cret") {
    (token, error) in
    if let token = token {
        // success
    } else if let error = error {
// failure }
}

这是很常见的 API。我们有一个登录函数,需要用户名密码,同时还会回传一个 token 和一个可能会出现错误的信息。我认为我们可以把上下文(content)处理的更好。

第一个问题completion 回调。这里说的是一个 String,但这个字符串是什么?是一个 token。我可以加上一个 label,但这并没有用。在 Swift 中,label 不是类型。即时这样做了,它依旧是字符串,字符串可以表示很多东西。

Token 有一些规则:它可能是固定长度,或者不能为空。这些都可以用字符串表示,但都不能表示 token。我们想要 token 带有更多的上下文。它是有规则的,所以我们要使用这些规则。既然如此,我们可以给它更多的结构。这就是为什么称它为 struct

struct Token {
    let string: String
}

我把字符串加入到结构当中。这不会产生其他开销,也不会有任何额外内存的使用,但现在我可以把一些规则运用在这里。

在这就只能构建一些指定的字符串,使用扩展还可以创建一些其他字符串。更棒的是这可以添加所有类型,string,dictionary,array,int 等,统统没问题。

当有了这些类型,你就可以把它们添加到一个上下文当中,并在使用它们的地方控制它们。意味着你可以控制它们是什么意思。我不再需要 label 或添加标注(comment),因为这第一眼可以看出是 Token 类型,第一个参数就是令牌(token)

第二个问题是我们传入了一个用户名密码。在大部分的程序中,你总会把它们一起传入进去;但密码本身就很少被使用到。我想要创建一个规则,允许我用“and”把用户名和密码组合起来,所以我需要一个“and”类型。事实上我们已经有一个,那就是刚才用到的 struct。

“AND”类型(积)

struct Credential {
  var username: String
  var password: String
}

Struct 是“and”类型。Credential 是一个用户名密码。“and”类型常用的名字叫“积类型”(product type)。

我鼓励你大声的把看法说出来。例如:“credential 结构是一个用户名和密码。”是否有意义?如果没有意义,这或许是一个错误的类型,又或许是你构建错了。

func login(credential: Credential,
           completion: (Token?, Error?) -> Void)


let credential = Credential(username: "rob",
                            password: "s3cret")
login(credential: credential) { (token, error) in
    if let token = token {
        // success
    } else if let error = error {
        // failure
    }
}

现在我们可以把用户名密码换成 Credential:这也使得我们的验证函数变得更短,更清晰,还开辟了更多不错的可能:对 Credential 进行扩展,或者用其他类型来代替它们。或许我们想要一个 one0time 密码,一个访问令牌(access tokens),或者 Facebook、Google 等第三方登录。现在我不需要修改其他地方的代码,因为这里只需传一个凭证即可。

但这依旧有问题。我们已经传递了这个 (Token?, Error?) 元组(tuple) – 元组是“and”类型。它们都是匿名构造体。我们的意思是“这可能是一个 token?可能是一个 error”吗?所以有四种可能:都是,都不是,要么是 token,要么是 error。但任何场景下,只有其中两种可能性。如果我同时得到一个 token 和 error,怎么办?这是错误的情况吗?需要一个致命的错误码?需要忽略它吗?此时你需要思考一下才能针对性的测试。

“OR”类型(和)

问题是你不需要把所有情况都表示出来 – 你只需表达清楚这是 token 或是 error。那么是否有一个“or”类型提供我们使用吗?

enum Result<Value> {
    case success(Value)
    case failure(Error)
}

这是一个枚举(enum) – 枚举是“or”类型,而结构(struct)是“and”类型。“and”类型是“积类型”(product type),而“or”是“和类型”(sum type)。

func login(credential: Credential,
           completion: (Result<Token>) -> Void)
login(credential: credential) { result in
    switch result {
    case .success(let token): // success
    case .failure(let error): // failure
    }
}

我构建一个 result 类型。result 类型不是内建在 Swift 里,这真的让我很恼火。幸好它很容易构建。我们将赋予 value 更多上下文,从一个普通 value 变成有“登录成功”含义的 value。

我们这里的 error 也有了更多上下文,它更像处理失败情况的 error。如果返回像之前的结果,最终 token 会包含在里面,所有不可能发生的情况都需要编写一次测试。现在我们并不需要担心,因为已经不会发生这种情况了。我可不想为不存在的 bug 编写一个个测试。

我喜欢这个API。我通用一个 credential,它就会返回一个最终的 token。

总结

这是函数式编程真正的遗产,这些也应该带给 Swift:复杂的东西,可以分解成更小,更简单的东西。

我们可以为这些简单的东西找到一些通用的解决办法,并在程序有条理的使用一贯的规则将简单的东西放在一起。这能避免编译器不会出错,而且我认为在70年代的约翰·巴科斯(John Backus)也会认同这个观点。打破它,并重建(Break it down, build it up)。

Better

better-pic

 

一件事要让人留下印象,不是他有多与众不同,而是他有多好。

有多好才算好?比之前更好就算好。

现在有两间卖橙子的店铺:

一间卖长相奇特被称为是来自异国,但吃起来味道很一般,10块钱10个。

一间卖外表普通但吃起来味道更甜,10块钱5个。

这两间店铺,肯定会有人选择奇形怪状的橙子,但这不重要,因为那些人尝过之后,以后再也不会买,甚至再也不相信所谓的异国,哪怕是再给多几个。人们最终还是会选择吃起来更甜的橙子。原因很简单,因为我们在挑水果的时候总会想着挑更好更甜的水果。

甜橙子的店长毫不关心那些长得奇怪的橙子,他只想知道如何把橙子栽培的更甜,他十分清楚这才是种植橙子的本质,其他的都是其次。这个道理在所有“竞争”行业都是共同的,虽然说的有点夸张,但如今有太多的人没发现核心却做了很多不相关的事情,他会很理直气壮的告诉你他知道,可实际行动却是让人沮丧。

甚至还有更多的是“拿来主义”,别人做什么,我也做,今年流行各种奇形怪状的橙子,仿佛看到了希望,看到了机遇,明年我也跟着种。殊不知那些橙子的回头客连10%都不到,跟风的那一批人全亏本经营。

一味的看着别人在做什么是让自己迷失方向最快的方法,因为你只有别人的想法,同时你有不同的竞争者,互联网的今天可以让你每周都有一个不同的竞争者,他们的每一款产品都会让你去“思考”,这些参杂的思维让你无法前进,让人不可思议的是即便是在这样的窘境下你依旧会很自豪的说出自己觉得很有“创意”的20个点子。

是的,没错,20个,里面包含了A的核心功能,B的重点模块,还有让C一夜暴富的流行元素。然后自信满满的和别人说:其实我就想做一个xxx,可以让用户xxx,这是用户需求。在遇到流程有问题的时候会说:看看别人怎么处理的。由始至终都没有为你要做的这样一件事想出点什么,而当你推出这一个“瑞士军刀”,你的竞争对手并没有感到害怕,反而会感谢你,是你证明了他们的这个功能这个需求是正确的。

你可能会说那20个点子当中有他们没有的功能,这才是这个产品的特色,那为什么不抛弃那15个“他们的功能”,只保留5个特色功能?如果这5个需要另外15个来支撑,那么这只不过是各个功能相互之间交错的思维错觉,完全不是特点,甚至谈不上核心。如果连抛弃的勇气都没有,那么这个产品价值还剩什么?

这就回到了刚才的选择题,你会有20个奇形怪状的功能,而不是5个更好的功能。一个人能把一件事情做好已经很了不起了,可现在却要找3个人做20件事?这简直是可笑至极!更甜的橙子不仅需要好的土壤、水源、环境,更需要比别人多两倍时间的无微不至栽培。看似是一件简单的事,可是能做到这一步的人却是很少,那这真的是一件“简单”的事情吗?

所有表现“简单”的事物背后都是复杂的,如同设计,好的设计都是简单的设计,简明的排版,超脱的色彩,设计师为了让如此简单的设计展现出来,背后有数不尽的草稿、原型,有几百张版式,有上千种色彩搭配,只为让它更简单更好。一个细节不容易让人发现,但多个细节的累加这就是让人眼前一亮的作品。

“我们也能做,没什么难度”这恐怕是看到别人作品(功能)之后最常出现的一句话,接下来就是直接拿过来加上自己“创意”进行粗糙的修改然后推向大众,造成市场上某一个热门的产品会有几十款号称和它不一样的半成品。这种复制修改是无法让你知道其更深层的一面,你是不知道他们为了这一项所付出的时间。你只会成为永远的追随者。

如果换另一种方式:“我们要做这个,而且要比它做的更好!”此时会发生意想不到的转变。你开始主导这个思想,排除所有无用的元素,比划出几十种可能性,只为一个肯定。条件虽然非常有限,而恰恰是因为受限才能让人激进、抓狂,让你可以更好的使用现有资源,因为你的想法只有一个“如何才能做到更好”。你由防守转向进攻,直指竞争者:你们做的到底是什么玩意?

找到核心,抓住不变因素,然后想破脑袋让他变的更棒!如同如何种植更甜的橙子已经够你研究上好一阵子了,所有还是别想着让你分心的事了吧,尤其是无关紧要的事。

当其他人为各种新鲜事物所扰乱时,你却能从中发现独特意义。如果你发现了,那么恭喜你!

“The thing is, it’s very easy to be different, but very difficult to be better.”

– Jony Ive

 

一个交互,oAround图片展示

实现了一个动画交互。动画上的UI和设计上有一定的出入,因为迫不及待的想做出这个交互,所以把大部分时间用在了思考怎么实现。最后反复的动作其实是双指在做反复开合动作,由于QuickTime录制iPhone屏幕是没有把手指点在哪个位置(或用了几个手指)的反馈(圆点)显示出来,所以这个可以说明一下。

当然了,还是用Objective-C写的,都不知道自己什么时候可以转到Swift。

 

Photo Share App – oAround

第一次尝试设计,希望自己的一些想法可以通过图形来表达。

这个App就取名叫:oAround,要说含义的话,应该是一切美好的东西,其实就在你周围。

 

两年

太久没写博客了,看着最近发的文章都已经有一年多了。这个月刚好毕业两年,自己又没有什么能力写教育和鸡汤的文章。所以还是说说自己这两年来做过哪些事吧。

我是12年初开始出来实习,准确的说到现在已经工作了两年半了吧。在这段时间,没感觉自己有多大的变化,或者说这些都是潜移默化,自己没有发觉到吧。

唯一值得庆祝的可以说是去年我比较顺利的转向了iOS开发,这一刻我等了3年。这3年的等待实在是太漫长了。那为什么不是3年前就动手呢?是因为那时候也刚接触开发语言也不久,才一年多,很多知识也不太了解,再加上没有Mac,自己装的黑苹果又十分的卡,而且没有驱动,所以一直都是以看Objective-C的书为主。到了毕业,分期付款了一台MacBook Air,买了一些书自学了一小段时间(其实这时候我已经把在学校看的Obj-C已经忘得差不多了)。再直到去年有了一个机会,让我可以真正意义上去接触,去开发。那是一件多么令人兴奋的事。

看了我博客的人都以为我是一名前端开发者,其实不是,前端只是我的其中一个兴趣点,我毕业刚好去了家做网站的公司,而且那时候的自己对前端也不太了解,所以我就利用这么一个机会去了解一下前端,学习JS。翻译Bootstrap也是我一次比较大胆的行为了(我之前已经在准备翻译Bootstrap v3.0,但由于个人的原因,再加上那时正好忙着找新的工作,所以没有进行后续翻译了)

整理下我这两年内看过的书:

简约之上

瞬间之美

演讲之禅

乔布斯:苹果禅

HTML5 Canvas 基础教程

高性能网站建设进阶指南

你不是个玩意儿

皮克斯

JavaScript模式

Effective MySQL之SQL语句最优化

iOS 5应用开发入门经典

设计诗

你的灯亮着吗?

深入浅出iPhone开发

触动人心:设计优秀iPhone应用

超越平凡的平面设计:怎样做好版式

赤裸裸的经济学

Node.js开发指南

设计与死

搞定:无压工作的艺术

深入学习MongoDB

IDEO,设计改变一切

还有部分书暂时只是翻翻,还没有仔细看下去,就不列举出来了。而上面那些书,我都十分推荐阅读的。当然也有几本是入门级的编程书,大神们就可以直接忽略啦。

我阅读设计类的书籍已经在慢慢超过技术类,这是因为我是一个推崇设计(和交互)优先的人。因为一个成功的设计可以使一个产品或者公司注入无限的活力和成就。甚至可以夸张的说,设计可以直接和创意和创新划上等号。

在激辩的讨论做什么,倒不如直接讨论怎么设计。这个东西能做什么,用户并不关心,因为他们不懂,他们不会告诉你他们真正需要的是什么,这就是亨利福特说过的:如果我当年去问顾客他们想要什么,他们肯定会告诉我:“一匹更快的马”。他们不会告诉你他们需要的是有四个轮子的,有引擎的,叫“车”的东西。

当然,这不是叫我们去创造一个全新的、改变世界的东西,这毕竟是百年难遇,甚至这不是常人能做出来的。那么现在已经有了车,全世界的人都对车有了一个共识,那就是一辆可以代替马,甚至可以比马跑得更快,而且比坐在马上面更舒适、更安全的东西。车,无非就是有轮子,有方向盘,有保险杠,有车门,有车玻璃,有空调系统,有喇叭,等等等等。所有的车,都千遍一律,都有这些系统和零部件,那为什么有些贵到几千万美元,有些却便宜到几万人民币。我们先抛开品牌效应这个因素,先拿我这个不懂车的人来说,我不知道车有哪些部件,而且车都是有方向盘,踩刹车就刹车,踩加速就加速,还有一个专门为开车人设计的座位,所以我要的是坐在上面是舒服的、方向盘握持感要好和一具漂亮的外观设计。这就是我要的感觉,没错,是感觉,因为全世界车的功能都一样,塞车的时候照样塞车,难不成还会有一辆在塞车的时候会自动变成飞机车飞在天上?因此在功能都一样的条件下,车的制作商都在从车的用料,做工,外观的设计,甚至使用了许多尖端的科技和人工智能,提高车的舒适度,提升开车和坐车人的感觉。

在开发一款产品同样如此,你说要做一个功能,就可以马上找到上百上千个和你提的功能一样的产品和应用。所以在为了要做哪些功能和不做哪些功能花大量时间紧张激烈的讨论有何意义呢?任何产品,用户的第一也是最为直接的感觉就是设计和交互,当然他们也不懂这些,他们只知道应用有没有给他带来舒适感,使用的过程中有没有给他带来不同的惊奇,等等,而功能才是其次的。从一开始就只知道功能,看到别人做什么,自己就要做什么,不考虑用户感觉,这些东西注定失败。

这就是为什么在我做一件事情的之前,首先会想到的就是如何去设计,如何把交互做到最好。

嗯?这些算不算是这两年来学到的东西?但不管怎样,我确信我现在做的是我热爱的事。

另外做个小广告,在2,3个月之前,我做了一个iOS小游戏《Rush Plane R & B》,也是自己的第一个个人应用(游戏)。欢迎大家下载玩玩。

下载地址

最后用乙川弘文对乔布斯说过的一句话作为结尾吧:

书法可以告诉你关于一个人的很多事,它与其他任何的艺术不同。如果你犹豫了,它就会在纸上显示出来,没有第二次机会。这就是你当下留下的痕迹,希望这也是你想要留下的,因为这就是你的全部了。

 

Bootstrap 2.3.1 改动及文档

Bootstrap 2.3.1在前几天发布出来,主要是修复了2.3版本的一些Bug。而mdo和fat貌似对这次错误的出现表示很不愉快。下面是发布该版本的博客翻译和2.3.1最新文档,原文同样可以点击这里查看

Bootstrap 2.3.1

Bootstrap 2.3作为V3.0前的最后一个版本,就在刚才我们打了一个小补丁来解决一些“贴心”的JavaScript bugs。Bootstrap 3仍在开发,这个过程也相当不错。我们也将有更多的东西和大家分享。

在此之前,让我们来看看2.3.1有什么新的东西:

  • 修复了下拉菜单插件缺少事件的情况
  • 修复了提示框/提示工具委派的data-attr
  • 轮播可以更好的运作
  • 在makefile修正了jshint文献
  • 修复了在没有设置背景的情况下试图移除背景的错误

关于这次变动的更多细节,可以查看2.3.1 pull request

下载 Bootstrap 2.3.1 (主线最新的ZIP)

附注:除了这次的修改,以后或暂未发现的Bug只在3.0处理,或者说该版本已经没有太大问题。而这次遗留的问题是2.3版本发布后这几个星期让我们无法忽视,必须要做出修改。

<3,

@mdo and @fat

Bootstrap 2.3.1

点击进入Bootstrap 2.3.1文档中文翻译版

 

Bootstrap 2.3 文档 中文翻译

Bootstrap 2.3来了!该版本是V2的最后一个版本,所以下一版本会直接过渡到V3。

2.3的改动和未来3.0的一个变化可以查看我这篇博客

该版本的翻译跟上版本修改了一些错误或不足的地方,同时也跪求大家的指正,也希望和大家一起相互学习。

Bootstrap 2.3.0

点击进入Bootstrap 2.3.0文档中文翻译版

最后感谢@mdo@fat的一直努力,把最好的Bootstrap带给我们。

Bootstrap 2.3

下面是官方发布Bootstrap 2.3版本的博客文章,里面的内容包括了2.3版本的改动和3.0版本的变化。原博客文章可点击这里查看

Bootstrap 2.3

久等了朋友们。自我们推出新版本的Bootstrap已经过去了3个月。但不用担心,因为我们从未停止。经过无数次的延期,我们非常高兴地发布 Bootstrap 2.3

包含些什么

Bootstrap 2.3 包含了一些新功能,同样的也修复了一些bug和对文档进行了改进。下面的是重要的地方:

  • 库的变化:
    • 对于makefile和安装过程现在使用本地而非全局的依赖。所以现在开始容易许多了 — 只需运行 npm install
    • 升级至jQuery 1.9。其实是没有更改的需要,但我们的升级也要把最新版本的jQuery包含在里面。
    • 更改了changelog(更改记录),而不是简单的链接到一个wiki页面。
  • 新功能和一些改进:
    • 在轮播组件添加了指示器!
    • 在提示工具添加 container 选项。默认的选项依然是 insertAfter, 但现在你可以把提示工具插入(可选的)container参数指定的容器里面。
    • 提示框(popovers)现在是使用max-width 代替 width,从240px扩大到280px,并如果没有通过CSS设置 :empty 选择器将会自动隐藏标题。
    • 改进了提示工具边缘上的对齐方式 #6713
    • 改进了所有组件的<a>标签。 合并之后#6441,链接停悬状态现在适用于 :focus 状态。同样适用于按钮、导航、下拉菜单等等。
    • 添加了打印属性,在 screenprint 之间显示或隐藏内容。
    • 更新了各个input组件,让它们的行为更像默认的表单控件。添加了 display: inline-block;,改善了 margin-bottom,并且加入了 vertical-align: middle; 以配合 <input> 的样式。
    • 加入 .horizontal-three-colors() 渐变mixin (例子在CSS测试文件)。
    • 加入了 .text-left.text-center, 和 .text-right 属性,让对齐更加容易。
    • 添加了 @ms-viewport,让IE10在多画面(分屏)模式下也可以使用响应式。
  • 文档改变:
    • 添加了一个新的导航示例
    • 添加了一个带有固定导航的粘页脚(Sticky footer)的示例。

和以往一样,你可以在GitHub查看2.3.0 milestone2.3.0 pull request 的一个更加完整的列表。以上未被提及的问题,大多是对CSS轻微的改动和文档的错别字。

下载 Bootstrap 2.3.0 (主线最新的ZIP)

提示工具的注意事项

当我们发布了Bootstrap 2.2.2,我们改变了提示工具和提示框的插入方式。在默认情况使用insertAfter代替追加到<body>的方式。这种变化修复了z-index数量问题,并可以更加容易的控制和修改样式。

不幸的是,这也导致了一些错误的出现,也就是干扰了相邻的CSS选择器,破坏了input。我们并没有修改插入方式,而是加入了一个新的 container 选项。如果你遇到在 insertAfter 情况下不显示,那么在该选项设置最适合你使用的元素。

Bootstrap 3

正如我们以前所提到的,在专注开发3.0版本前,2.3版本是我们最后发布的一个版本。对于最新版本的情况,可跟进Bootstrap 3 pull request当然这里也有的一些“内幕”:

  • Bootstrap 3 将优先支持移动设备。
  • 没有单独的响应式CSS文件,现在整合到一个文件。
  • 放弃对IE7和Firefox 3.x(及以下版本)的支持。
  • 网格(栅格)已彻底修改,更容易使用,并在默认情况为流式布局。
  • 对话框现已支持响应式。
  • 不再支持子菜单。
  • 重新设计了轮播。
  • 重命名所有变量,现使用破折号分割代替驼峰命名。
  • 放弃了图像图标,用字体图标代替。
  • JavaScript事件也将加入命名空间。
  • 文档的改动 – 框架和基础CSS已被合并成一个单一的CSS页面。
  • 添加一个新的画廊页(gallery page)来展示更多更棒的Bootstrap的现实例子。
  • 和其他混乱的变化。

而这仅仅是一部分亮点。同样,进入pull request看最新变化,我们也将保持更新。可以通过任何形式进行反馈,可发表评论,或来到我们的Twitter。

<3,

@mdo and @fat

Bootstrap 中文翻译

Bootstrap,是GitHub最火爆的项目,是著名的前端开发框架之一。

个人认为Bootstrap比其他框架要好的原因之一是他有许多优秀的组件,可以帮助开发者快速开发一个网站(快到可以只导入Bootstrap的CSS代码,直接使用里面的属性,就可以实现一个简洁又灵活的网站)。而且还有许许多多第三方插件和样式的支持。同样的,Bootstrap也包含了响应式的功能。

目前翻译的Bootstrap版本是2.2.2,暂时也是最新版本。那时我翻译的时候还是2.2.1,快翻译完毕时才发现官方已经升级到了2.2.2,还好改动的不大,很快就修改完毕。但官方博客已经说正在规划3.0版本,现在也是可以下载到不完整的3.0版本,大家可以去他们的GitHub下载。

点击进入Bootstrap中文翻译版本 – 2.2.2

如果哪里翻译的不够好,或是有错,跪求指出批评。

最后感谢mdo和fat创作出如此优秀的框架。