一个 iOS 开发者对 web 的窥探


App 开发大家轻车熟路。但是从一个开发 App 起家的人来说,web 开发是一种既陌生又熟悉的领域。熟悉在于开发 UI 程序的整体要素都是相通的:如何构建 UI,请求网络,变换/存储数据。而实现这些东西的方式却很不一样,这与 iOS / Android 之间的差别大很多。

专注一个方面,构建 UI。它的本质都是告诉所在环境(操作系统/浏览器)在屏幕上应该显示什么内容。这里无论是命令式的,还是声明式的都没有本质差别。对于 app 开发,系统提供的是某种编程语言调用的 API。比如在 iOS 中就是以 ObjC 形式提供的类与方法。而 web 提供的 API 则是对 html 文本(以及 css 文本)的解析能力。这些文本描述了应该展示什么内容,然后被浏览器渲染出来。其实 web 开发完全可以提供类似于 app 的接口,现实中也的确可以实现,比如全部使用 JavaScript 构建 htmlElement 的对象给浏览器(只不过最终还要把 JavaScript 代码用 html 包一层给浏览器)。

而造成以 html 为主体的结果是历史的因素。最开始我们只需要简单的静态页面,用一种 xml 格式的文本就能完全描述。后来功能和样式越来越复杂,就引进了 JavaScript 和 css 两种技术,把它们兼容在已有 html 里面。历史是一个的过程,当时每个合理的选择,累加成我们今天的样子。就像 x86 指令集,为了适应各种变化引入了很多新的东西,同时保持着向前兼容。如果完全推到重来,做出的可能是完全不一样的东西。

从现在的角度看,传统的 web 提供的是一个为专门用途定制化的接口。html 主要负责元素层次关系,css 提供样式,Javascript 主要提供交互。只实现他们的本职工作,会比使用通用的变成语言描述方便。比如只考虑描述一个包含两个子元素的 view,iOS 下使用

let a = UIView()
let b1 = UIView(); let b2 = UIView()
a.addSubview([b1,b2])

而 web 下则是

<div>
	<div class="n1"/>	
	<div class="n2"/>
</div>

不考虑尖括号,后者更优雅一些:没有创建对象的概念,直接以声明的形式描述,并且形式很直观。但这里差异的本质不在于声明式的形式,而是为了常用的需求,定制了方便的接口,同时舍弃了通用性与灵活性。

这种定制化在 view layout 的方式上更为明显。作为一个 app 开发者,我曾经最迷惑 web 怎么用 html + css 描述 view 布局。因为 html 只提供了层级关系,view 显示在什么位置是怎么确定的呢?其实 web 的大体思路是,根据父元素的种类不同,依次纵向或横向向下排,横排的时候以文字折行的方式排布。比如上面例子中的 div 就是一种子元素纵向排列的元素,它的两个子元素就会纵向紧密排列。如果用 css 给两个子元素设置一些 margin 之类的属性,就会从紧贴着变成有一定间距的样子。当然也可以调整 css 中的 position 属性,让元素可以以绝对坐标的形式排布(这就很像 iOS 中手动写 frame 形式的布局了),但这不是一种大量使用的方式。(这几个布局的方式到是和 Android 有些相似。)还有专门实现文字环绕的布局方式。这点就显得很老旧了,为什么文字这么特殊,要给专门的方式。同样的还有吸顶的布局。此外还有新一些的 flexbox,现代一些,它在不同平台都有类似的实现,就不赘诉了。相比之下,iOS 里 frame 或约束的方式布局,说不上很好用,但却很通用,比如在此基础上可以自己实现 flexbox。

然而这种我想要去嘲讽的定制化,却造提升了 web 的效率,这是我写了几天 web 后的感受。当然这里的套路除去工具链和较少的状态变化等差异。一个小例子,在文字形式的 title 中间插入一个 icon,只需把对应的元素写在两个字之间就可以了。这基于 web 对文字元素的「套路」。而 iOS 里文字与 view 并不是相同的概念,之间不能产生关系。对于这种定制性,你会怀疑它能否方便的实现现实中的需求?带着这个问题随便浏览几个网页,就会发现绝大部分的内容都是横平竖直的。开发时候,无脑的累积元素就可以了。

当然技术在不断发展,以 react.js 为例就舍弃了传统的 html,绝大部分内容全部以代码的形式实现,而且已经成为主流。但 react 只是改变了 html 的部分,css 控制的样式却没有触碰。而 flutter for web 则自己实现了 core drawing layer,抹平了平台的差异。