Web 快速入门

虽然我是原生应用的坚定粉,但是随着微信小程序的出现,还是得了解一下 Web 应用的开发。本文结合 CMU 15417 Web Application Development 这门课,大致梳理一下入门 Web 开发的必备知识及思维方式上需要转变的地方。


更新记录

  • 2016.09.27 更新『优秀学习资源』和一些链接
  • 2016.09.26 更新『Web 发展历程』与『Web 技术演进』
  • 2016.09.25 完成『Web 之难』与『一次点击背后』

Web 之难

对于大多数同学来说,如果没有业余做一些小项目,其实是很难理解 Web 到底是怎么回事儿的,至少我当年就因为『觉得』 Web 很难所以『抗拒』Web 和网络相关的开发。虽然课堂上学过计算机网络,甚至也去实验室捣鼓过路由器交换机,但是真到自己开发的时候,真的是一脸懵逼。现在回过头来看,发现主要有以下原因:

  • 教学内容僵硬,硬件部分只讲硬件,软件部分只讲软件,没有联系起来
  • 实验设计不接地气,为了实验而实验,没有结合具体的场景来复制相关概念的理解

当然,最重要的原因还是在自己的『好奇心』不足,没有追根溯源把事儿弄明白。底层原理和上层应用的有机结合本来就是非常需要花功夫的,以目前国内高校的现状来看,甚至是『吃力不讨好』的。如果体制内没有变革的动力,那不妨通过外部努力进行自下而上的改变。

看到越来越多非常走心的教程以开源的方式出现在网上,我特别开心。这种开放和分享的精神在中文(互联网)世界里生根发芽,信息流动的过程本身就是一种平等。只要有网络和一颗想要学习的心,不需要缴纳高昂的学费,同样可以学到本领。程序员有程序员改变世界的方式,这就是我开博客写文章的初心了。

Web 之难,主要是简单操作背后所隐藏着的复杂体系,需要有比较完善的计算机及网络相关的知识积累,既需要大局观,也需要懂细节,这对于初学者来说可能就像无法逾越的大山,很多东西只知道怎么做,却不知道为什么要这样做。与此同时,遇到问题也没有解决的思路,就很容易有挫败感。(心疼当年的自己一秒)

希望本文能给在困惑中的同学一点帮助,虽然不能面面俱到,但至少能给出一些有用的提示和方法,出了问题不至于束手无策。

一次点击背后

之前在 caoz 的公众号中看到过,他喜欢出的一道面试题是:

从浏览器地址栏输入网址,到网页彻底打开,中间都发生了什么?

这道题看起来很简单,但其实需要答题者对 Web 的工作过程有整体认识。与此同时,面试官也可以根据具体情况选择一到两个细节进行深入的讨论。只有明白了整个系统运行的机制,之后的调试、测试、优化才不是无源之水。而弄懂这些问题本身,也能体现一个人学习的习惯和对技术的追求,这才是面试最需要体现的东西。

我一直认为,即使是做一个小的模块,也应该把自己放到更大的场景中去思考,这个模块在系统中所处的位置,与其他模块的关系与交互方式等等都是需要去思考的,这样才能写出最有价值的代码,并从中找到之后改进的方向。如果让我去面试,具体的技术细节我反而不会去拘泥太多,毕竟聪明的人很快就能学会,我在意的是候选人能不能真正以网络和计算机的方式去思考问题,这才是最重要的。(原文可以在caoz 的梦呓中查看)

仔细读题,我们应该想到的细节问题是:

  • 为什么我们要输入网址而不是其他什么东西?
  • 浏览器做了什么事情?
  • 网址最后去了哪里?
  • 服务器是怎么收到网址的?
  • 服务器做了什么事情?
  • 不同的页面效果是怎么出来的?

化简一下,就是『客户端』、『网络』和『服务端』。每个部分都有各自相关的技术体系,学习的时候一定要弄清楚自己处于哪一块,比如上面几个问题涉及的相关知识点是:

  • 为什么我们要输入网址而不是其他什么东西?
    • DNS 服务器的工作原理。具体每个部分的缓存机制,如何依次更新?
    • CDN 的工作原理。如何在硬件条件限制的情况下提供最佳服务质量?
  • 浏览器做了什么事情?
    • Host 文件的作用。应用扩展与劫持。
  • 网址最后去了哪里?
    • 不同层的网络协议(HTTP, TCP, UDP)
    • 路由规则,传播路径,tracert
  • 服务器是怎么收到网址的?
    • 端口监听
    • 静态资源
    • 动态请求处理
  • 服务器做了什么事情?
    • 防火墙
    • 缓存
    • 数据库
    • 负载均衡
    • 集群
  • 不同的页面效果是怎么出来的?
    • javascript
    • css/html

一个网址请求背后就有这么多的东西,基本上每一个都可以开一门一学期的课程,初学者忽然面对这样概念饱和的轰炸,真的是九死一生。而这么多知识点,真的要样样精通,不太可能,不过如果有一个完整的概念,学习或工作中涉及具体的某个领域,再根据需要深挖,就能逐步触类旁通,找到技术的感觉。

可能前面说的都比较虚,了解系统原理最直接的好处是:出了问题知道怎么排查。比如前一阵子我的博客访问忽然变慢,我做了什么呢?

  1. 梳理架构及流程。我的博客是静态博客,评论系统采用第三方服务,页面部署在 Github 和 Coding 双平台。因为是第三方托管,所以服务器的部分我不需要操心,而客户端的话我在手机和电脑都测试过,也并没有表现出设备相关的特性,于是把问题锁定在网络传输层面。
  2. 因为是静态网站,静态本身已经帮我缩小了可能出现问题的范围,无非是几个可能:页面内容传输、图片传输、字体渲染、图标显示和评论系统加载,逐个进行排查。
  3. 切换 VPN 确定 Github 和 Coding 没有问题后(即页面内容和图片的传输正常),发现字体渲染卡住了,检查后是因为所用的字体库不稳定(为了照顾国内使用了 360 的源),所以切换到了中科大的源。
  4. 问题解决。另外,几天后,360 官方也给出通告,停止了该源的维护,所以我也经历了一次『春江水暖刀先知』了

平时遇到的问题大多是模糊的,只有对系统运行的机制有基本的理解,才能快速找到问题的关键所在,而这就是个人价值的体现。

另一篇类似的文章是技术普及帖:你刚才在淘宝上买了一件东西

Web 的发展历程

通过前面一道面试题,我们大致了解了 Web 的整体框架逻辑,这里我们从历史出发,分门别类介绍一下 Web 相关的技术

  • 1989 - HTML - SGML 的一个子集,由 1960 年代的 GML 衍生而来
  • 1990 - HTTP 协议 - 一开始只有一个方法 GET
  • 1993 - CGI(Common Gateway Interface),允许应用程序动态生成 HTML
  • 1994 - Cookies
  • 1995 - SSL
  • 1996 - JavaScript
  • 1999 - AJAX
  • 2008 - 开始制定 HTML5 标准

HTML 与 CSS

HTML 其实是一个比较笼统的说法,具体的规范有很多,比如 HTML4, XML, XHTML, HTML5 等等,各有各的标准。问题在于这种标准并不是强制性的,所以现实生活中往往不同的厂商有不同的实现方式,目前看来,HTML5 是比较有前途的。

最原始的页面基本都是非常『简陋』的,就是把需要显示的内容显示出来。而随着时代的发展,大家对于外观的要求越来越高,慢慢就出现了样式和内容分离的趋势,也就是 CSS(Cascading Style Sheets) 的出现。

纵观历史,就会发现这样的解耦其实无处不在。为什么每一次解耦都能够带来巨大的进步呢?因为解耦实际上是沟通方式的抽象化,用更加高层的抽象能够表示更多的细节,分工更细,自然挖掘得更深。

DOM - Document Object Model

提到 HTML,就不得不提 DOM 这概念,甚至可以这么说,万变不离其宗,我们看到的五花八门的各种前端技术,实际上都是基于 DOM 这个东西的。DOM 可以认为是一个 HTML 的对象模型,一个表示 HTML 文档的节点树。树中的每个节点可能是元素(element),属性(attribute)或文本(text),我们通过操作不同的节点,就可以展现出不同的页面。

尤其是在动态页面大行其道的今天(但静态仍然有静态的好处,比如本博客就是静态博客),如何看待 DOM,如何操作 DOM 是每个前端框架都必须要回答的问题,比方说 React 提出的 Virtual DOM,就是希望优化传统对 DOM 操作可能带来的各种问题。

Javascript

如何去操作 DOM?如何在页面中展现动态效果呢?这就离不开基于浏览器的脚本语言 - JavaScript 了。看现在的趋势,JavaScript 还真有一统前端天下的感觉,虽然最初设计得很烂(十天搞定),但是架不住用的人多呀。

而随着语言本身的不断进化,也出现了各种各样的『方言』,虽然说最终都会转化成 JavaScript,但如果开发过程中有特定的需求(比方说希望有强类型),还是有用武之地的。

简单描述 JavaScript 就是:

  • 类 C 语法
  • 垃圾回收
  • 基于对象(函数是对象)
  • 对象有具名属性
  • 函数可以是属性
  • 基于原型
  • 解释型
  • 动态类型
  • 在浏览器中运行

好了,相信再多写点大家也看不下去了,总之写的时候多思考下就好。

jQuery

有一定开发经验的同学都知道,没有各种各样的库的支持,开发起来真的是捉襟见肘。对于前端来说,jQuery 虽然现在听起来有些过时,但确实是一代王者。在它的帮助下,我们得以以更加清晰的方式来进行开发。jQuery 提供了:

  • DOM 操作支持(元素选择,遍历等等)
  • 事件支持
  • AJAX 支持
  • 工具支持
  • 插件支持

不过现在各大公司都在推自己的前端技术栈,希望大家在看热闹的同时不要忘记曾经的英雄 - jQuery。

AJAX

前面提到的 AJAX 并不是一种语言,而是一种技术,允许异步 HTTP 调用,识得更具交互性的 web 应用成为可能,因为我们不在需要每次都刷新整个页面,减少服务器负载的同时,增加了灵活性。

具体的原理也很简单,使用 JavaScript 通过 XMLHttpRequest 从服务端获取数据,把数据转换成 DOM 树,再融合到现有的 Dom 树中,就完成了页面的更新。

HTTP 与服务器

除了前面提到的技术,Web 另一部分非常重要的内容就是服务器。这里以 HTTP 服务器为例,简要进行说明。开始之前,先来看一眼服务器的市场份额:

老牌劲旅 Apache 在最近两年和 Microsoft 的缠斗中逐渐失去了王者地位,从最高的 70%(大约是 05-06 年)跌落到现在的不足 30%,而微软倒是不紧不慢地突破了 40%,还有一个值得注意的特点是 nginx 这种反向代理服务器的异军突起。

简单来说,用户通过 URI 指定要访问的资源,向服务器发起请求。传输的协议基于 TCP,默认是服务器的 80 端口进行接收。具体的服务器实现因为 HTTP 规范,所以比较复杂,但基本结构是简单的,就是读取请求 - 处理请求 - 返回响应,如下所示:

while (true) {
request = readHttpRequest(...);
response = processHTTPRequest(request);
sendHttpResponse(..., response);
}

服务器需要照顾的东西非常多,比如:

  • 服务器名称
  • 电子邮件
  • 文档位置
  • IP 地址和端口
  • 超时
  • 最大请求长度
  • 缓存
  • 目录显示
  • 验证与授权
  • 日志与监控
  • 错误处理
  • 系统管理
  • 性能
  • 安全
  • ….

不一而足,这里就暂时不展开了。

Cookies 与 Sessions

前面忘了说,HTTP 其实是无状态的,即不同的请求没有上下文关系,但是很多时候页面上的操作是需要依赖上下文的,比方说登录注册购物车。这时候就是 Cookies 与 Sessions 登场的时候了。

Cookies 有几种类型:

  • Session Cookie - 浏览器关闭就删除
  • Persistent Cookie - 过期才删除
  • 第三方 Cookie - 从其他站点得到的,通常用于追踪,可以在浏览器中设置拒绝

而 Sessions 则可以认为是一个短期的状态暂存,通常用 cookie 来实现。我们保存一个 session id,通过这个 id 来取出服务器上对应的 (name, object),来进行相关操作。

网络地址

回过头来说说网络传输层面,针对不同层级,我们都需要一个方式来区别不同的设备和应用,于是有了:

  • MAC 地址 - 底层 - 硬件地址
  • IP 地址 - 可路由的地址
  • DNS 主机名 - 高层 - 逻辑地址

确定了服务器之后,使用不同的端口来和不同的应用沟通,比如

  • telnet - 23
  • smtp - 25
  • rdp - 3389

在开发和部署中,也有一些惯例,我们在开发的时候最好也遵守这种约定:

  • 部署
    • HTTP - 80
    • SSL - 443
    • MySQL - 3306
  • 开发
    • HTTP - 8080
    • SSL - 8443

Web 技术演进

CGI

虽然现在可能很多人都不知道 CGI(Common Gateway Interface)是啥了,但是从 1993 年出现其,CGI 使得在网页上动态显示内容成为可能。这里的动态并不是指页面在动,而是可以通过 Web 请求动态生成内容。这是怎么做到的呢?说来也简单,CGI 可以认为是 Web 服务器用来执行外部程序的接口,等于把一个 HTTP 请求通过 CGI 转化成了一个函数调用,而具体调用的函数可以由我们自己编写。当年最流行的语言是 Perl, Shell, 和 C,只要符合 CGI 接口标准,就可以把内容发送给 Web 服务器,用户也就能看到了。

借助 CGI 这一层,我们把 Web 请求和处理请求的程序解耦了。当年的网站大概是这样的:

PHP

能显示动态内容是一个巨大的进步,不过很快人们就发现 CGI 没有办法满足需求了,因为每来一个请求,就要启动一个进程来处理。而且需要我们的程序自己输出 HTML 字符串,哪怕只需要一个简单的页面,也不得不输出成吨的 HTML 代码。

程序员就想,那我把不变的部分抽出来,把动态内容留个占位符最后填充进去不就省事儿了吗?于是,我们就有了模板。折腾的人多了,自然就要弄出一个新东西,于是 PHP 就在 1994 年横空出世(毕竟是『最好的』语言)。

之后的 ASP 和 JSP 其实可以认为是类似 PHP 这样的模板引擎,只不过后端的语言不同罢了。后来为了把内容和样式分离,1996 年的时候 W3C 发布了 CSS 1.0 规范。这下好了,Web 终于成了一个可以处理复杂事务的平台了。

J2EE / .Net

随着 Web 应用越来越大,大家又不满足了。分布式要考虑,安全要考虑,事务要考虑,部署方式要考虑。各大公司一看,这是一个可以赚钱的事儿,于是双雄争霸,Java 派以 Servlet, JSP 和 EJB 合体出战,是为 J2EE。而微软甩出 ASP.Net 和 Visual Studio,用拖拽组件方式创建 Web 页面,极大降低了门槛。

框架的兴起

不同的技术方案在发展到一定程度,再次面临了同一个问题,就是如何多人协作,如何组织代码。回答这两个方式有多种答案,但是最流行的答案是 MVC 的方法,即 Model/View/Controller 的套路。这种分类方式按照不同的角色进行代码复用和组织,Web 应用可以以更加灵活的方式进行配置。

有的框架大而全,如果没有特别多个性化需求,几乎可能在很短的时间完成开发工作,但缺点是易学难精,一旦出现问题和稍微复杂且小众的需求,就需要大量的开发工作。

有的框架小而灵活,不过需要使用者对于自己的需求和技术整体有比较清晰的认知,才能挑选出正确的组件完成工作。

不同语言基本都有自己的框架,具体怎么选择就看团队和业务的要求了,如果好奇各个框架的性能,可以看这里,进行查看,比如说:

随着前端的复杂度越来越高,各种各样前端的框架也应运而生,比如 BackboneJS, AngularJS, EmberJS(然而我并不熟悉,就此略过)

框架另外做的事情是 ORM,我们不需要自己去写 SQL 语句,而是像操作对象一样操作数据库中的数据即可。

更多内容可以查看Spring,Django,Rails,Express这些框架技术的出现都是为了解决什么问题,现在这些框架都应用在哪些方面?

RESTful

从原来的静态页面开始,到各种框架,Web 本身几乎已经淹没于各种技术名词中。直到 REST 的提出和 RESTful API 的广泛使用,通过 URL 而不是其他来组织功能,可以看做是 Web 精神的回归。

不过 REST 和 MVC 并不是互斥的关系,经过合理的设计,其实可以真正发挥 Web 开放、简洁且平等的力量。

优秀学习资源

总结

前面走马观花写了这么多,可能还是有很多地方没有讲清楚。之后会以系列文章的形式,把自己学习 Web 相关技术的过程跟大家分享。最后会制作一个较大型的开源 Web 应用,以实例讲解的形式完成这个系列的文章。

参考链接

捧个钱场?