Please note that this is a back-ported post from 2016.
今天我们的前端工程师告诉我说,postcss 的一个小 bug/feature 花了他很多时间才搞清楚。
Postcss 是我们用的一个处理 CSS 的前端框架,和我们目前也在用的 webpack 可以一起使用。后者负责把各种 JavaScript,CSS 以及图片等资源进行打包,而前者的主要工作就是在此之前把 CSS 代码进行一遍预处理。
比如 Webpack 的配置文件里面可能有这么一段:
// webpack.config.js
module.exports = {
// 省略
postcss: [require("autoprefixer"), require("precss")]
// 省略
};
其中的 postcss
部分是一个数组(array),里面的每一个元素都是一个 postcss 的插件。Postcss 自己做的非常模块化,大部分任务都交给各式各样的插件来完成,通过组合不同的插件来实现多种功能。「Do one thing and do one thing well」,多么 UNIX 的哲学。
但我们的前端工程师遇到问题是,他之前不知道这个定义的顺序是有意义的,还以为只要把所需的功能模块放上去就好了。我说,其实这个和 UNIX 的 pipe 是一样的,或者你如果用过 gulp 也更能清楚这种流式处理(Streamline/Pipeline)系统的工作原理:顺序其实非常重要。他总结说是自己只用过 webpack 的原因。但是我仔细一想,其实这种情形我自己倒是反复遇到过。
「总会有漏洞的抽象层」
记得本科的时候读过 Joel Spolsky 的文章 The Law of Leaky Abstractions,大体讲的是,所有的抽象层都会有漏洞,总会出现一个情况下,你必须绕过抽象层去了解背后的细节。文中举了 TCP 作例子,说很多程序都依赖 TCP 协议,把易错的 IP 层协议封装好,让你发送的数据总是有序、完整和没有冗余的。但是总会出现一些情况,比如老鼠咬断了网线,那时候你还是会观察到抽象层的破碎(TCP 不再正常工作),不得不处理那些意想不到的情况。
抽象是一个超级强大的工具,事实上有句很有名的话就是说:「计算机科学里面,没有什么问题是多加一层抽象不可以解决的」。比如上面 webpack 的配置文件其实就是对流式代码的一种封装和抽象。而且抽象其实不只是计算机科学里面的概念。它是一种人类最最基本的思维工具,可以帮助人脑减少同时要处理的信息量,把不重要的、可重复的细节滤掉,而着眼于目前最重要的东西。抽象大部分情况下都是思维的利器,也是每一个程序员必须掌握的本领。
一切都很美好——直到你第一次碰壁。
「编程很简单」
最近有个好朋友在做零基础编程培训,课程设计的紧凑但很有含金量:他会教所有学员在三周时间内,先学会用 HTML 搭建一个静态网页,然后学习基本的 CSS 知识,用 Twitter Bootstrap 美化页面,最后学习 JavaScript 给页面加上一些动态的元素。
和许多其他给零基础的人群做的培训一样,他希望在很短时间内让大家了解什么是编程,什么是编程思维,也顺便学会如何和程序员同事、朋友沟通共事。我觉得这个目标非常的实际和靠谱——当然,与之相对应的就是许多夸大宣传的所谓程序员培训,希望在短期内让人速成,可以学会数据挖掘,AR/VR,和人机交互等「酷炫」的技能——说到底,最多可以让你走马观花看看而已(虽然这个本身也是好事)。
的确,现在的很多程序语言(比如 Python,JavaScript,Swift)已经比以前(比如 C,C++)好学太多了,不用手动管理内存,不用学习文件系统,甚至可以拖拽式的操作,完成一个基本的「编程」。我个人非常喜欢这些「user friendly」的编程语言,而且无论是 code.org 还是 code academy 都是非常有价值的存在——因为他们都旨在激发大家对编程的兴趣,而编程几乎肯定是未来的必备技能(最多是换一种形式罢了,编程思维才是核心);他们无一例外的都要告诉受众:编程很容易,开始学习吧!
但,其实这不是故事的全部:其实编程很难;它一开始比较容易,之后就会变得比较难;但等你熬过去,它又会变得比较容易。而这个过程下来,你会真正的感觉到受益匪浅。而我认为,这一切背后的原因,很大一部分就是来自于这个无所不在的、总会有漏洞的抽象层。
比如,我的那个朋友在备课的时候,和我探讨过以下几个问题:
- 为了保证学员在最短时间内接触最有意思的东西,编程语言用相对好入手的 JavaScript,因为网页编程是最「所见即所得」的编程方法。但是每个人用的浏览器可能不同,所以最好用 Twitter Bootstrap 这样的 CSS 框架,至少整体页面的 look and feel 不会因为浏览器不同而不同——但是,但是,我和你打赌肯定会有人用 IE 6 的,oops!
- 编程环境也必须得统一,否则就光去解决每个人的配置问题了(想想几种不同的换行符,想想文件编码,想想至少四种不同的 npm 版本)。我提议用 Vagrant,让每个人都用同一个虚拟机镜像,但是这个方案因为中国的网速最后作罢。(不过好在他最后直接找到了 codepen 这一个终极的受控的环境。)
- 为了让大家不受网络连接的问题影响,需要找到靠谱的代理或者 VPN,否则一些 CDN 可能会受影响,也会出现比如 CSS 无法加载的问题,而如何教会大家使用网络工具本身又是一个挑战
你看,虽然已经是一个及其简化的配置(所见即所得,不需要编译),一个极其容错的编程平台(浏览器),也还是到处都是「有漏洞的抽象」。如果一个人因此认为自己掌握了前端编程而信心满满的时候,他下次换个机器,换个浏览器,甚至哪怕忘记打 </script>
里面的 /
的时候,就会感觉到受挫和沮丧。
什么时候变得容易?
在过去的编程经历里,我自己也无数次遇到过这样的「有漏洞的抽象」,而以往的经历往往是这样的:
- 发现一个很好的框架/抽象/编程语言特性,玩起来发现很有意思,可以提高效率,做到以前不能或者不容易做的事情
- 直到你用它来做一些更重要的事情,用到更重要的项目里面,才发现原来里面这么多的「坑」,于是你不得不开始研究它背后的原理,读文档,搜 StackOverflow 的问题,读源代码,用不同的配置方法去调试,直到你搞清楚了它的原理,它究竟解决什么问题,解决这个问题的思路是什么,具体做法是什么
- 这个时候再回过头来看,原来它这层抽象是有这么一个意义,而且比它取而代之的之前方法更简洁、高效,只是唯一的弊端是你之前踩过的那些坑,并且你知道了如何避免,甚至改进它
从这个角度看,其实学习编程的过程也许也可以类比:
- 发现编程很好玩,可以用来写一个简单的程序,可以给朋友炫耀,自己也体会到了动手的快乐
- 直到你想深入学习,才发现踩了大坑,你开始去学习文件系统,学习内存管理,学习好的编程习惯,学习数据管理和数据库,学习数据结构,学习离散数学,学习编译器,你才发现这些东西原来都联系在一起,才发现那个「找不到对象」的错误提示背后指的是数据库没有启动,而不是一个悲伤的故事
- 这个时候再回过头来看,原来编程和编程思维是这样的,和想象中有那么多的不一样。原来 DeepMind 下围棋赢了人类和强人工智能的到来之间没有那么强的因果关系,也不再会被新闻媒体随随便便的忽悠了;最关键的,是你掌握了一整套分析和解决问题的思维框架
不难发现以上都是 2.
最长,这也的确是最漫长的一个过程,甚至还意味着很多次在 123 之间的反复循环。
但是结论也许也是这样的:一切都很美好——直到你第一次碰壁,然后你摸索着沿着墙壁走,逐渐的,找到了门。