前端工程化发展历史
毕业前对前端工程化一直没有什么切身的体会,现在工作也有半年多了,体会也越来越深,npm
,yarn
,Webpack
,gulp
,Babel
,ESlint
,TypeScript
最近准备一一去深入了解一下,看到一篇不错的关于前端工程化的发展过程,就翻译了一下,How it feels to learn JavaScript in 2016,
https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f
虽然说是 2016
年的文章,但现在看来依旧不过时,也侧面说明了前端也渐渐趋于稳定了。
以下是全文:
hi,我准备写一个网页项目,但是说实话我已经很多年没有碰过代码了,听说现在行业变化很大。你是我们这里最与时俱进的网页开发者(
web dev
)了吧。
对的,但更准确的说我是前端工程师(Front End engineer
)。可视化、音乐播放器、足球游戏,凡是你能想到的都属于前端开发。我刚刚从JS
大会(JsConf
)和 React 大会(ReactConf
)回来,因此我知道创造 Web apps
最新的技术。
太棒了!我现在需要写一个展示用户活动的页面,我需要通过
RESTful
接口获取数据,然后展示到可筛选的表格中。我是不是可以用jQuery
去获取数据和展示?
天哪,不不不,已经没有人再用 jQuery
了。你应该去学习 React
,现在已经 2016
年了!
啊,好吧,
React
是什么呢?
它是由 Facebook
几个大神创造的一个非常 cool
的框架,它能帮助你轻松的控制视图,更好的管理项目,提升性能。
听起来不错,那我能使用
React
去展示来自服务端的数据吗?
可以的,但你首先得在你的页面中引入 React
和 React Dom
这两个库。
啥?为啥是两个库?
React
是它的核心库,而 React Dom
是用来操控 Dom
的,通常你需要用 JSX
去描述 DOM
。
JSX
?JSX
又是啥?
JSX
是一种 JavaScript
的语法扩展,看起来更像 XML
。它是描述 DOM
的一种新的方式,比 HTML
会更好。
HTML
表示很无辜。
孩子,已经 2016
年了,没有人直接去写 HTML
了。
好吧,如果我添加了这两个库,是不是就能使用
React
了?
emmm
,还不太行。你还需要添加 Babel
这个库。
又一个库?
Babel
是啥
Babel
是一个可以帮助你把任意版本的 JavaScript
代码转换成你要的版本。但如果你坚持只使用 ES5
的语法,Babel
也可以不引入。但现实点吧,现在是 2016
年了,你应该向大家一样,使用 ES2016+
的语法了。
ES5
?ES2016+
?我晕了,它们是些什么。
ES5
代表 ECMAScript 5
,它是使用人数最多的一个版本,几乎所有浏览器都支持 ES5
的语法。
ECMAScript
?
它是在 1999
年提出的一个语言规范,JavaScript
属于其中的一种实现。JavaScript
是 1995
年提出的,之前还叫过 Livescript
,仅仅运行在网景的浏览器中。之前这些非常混乱,现在由于有 ECMAScript
的 7
个版本,一切都变得很清晰了,
7 个版本?那
ES5
和ES2016+
属于?
分别是第 5
个和第 7
个版本。
等等,第
6
个版本哪去了?
你的意思是 ES6
?由于每个版本相当于之前版本的超集,所以如果使用 ES2016+
,之前版本 ES6
、ES5
所有的特性你就都可以使用了。
好吧,那我可以用
ES6
来编程吗?
当然可以,但你不能使用一些最新的特性,比如 async
和 await
。你只能通过 ES6
的生成器和协程来执行「同步」的形式异步请求,感兴趣的话可以看一下 co
框架。
完全听不懂你在说什么了,这些名词我都没有听说过。让我理一理,我只想从服务器加载一段数据,过去我是从
CDN
中拿到jQuery
,然后通过AJAX
请求数据就可以了,现在怎么变得那么复杂了?
大哥,已经 2016
年了,没有人再使用 jQuery
了,它只会让你写出意大利面条式的代码。
好吧,所以我需要引入
React
、React Dom
和Babel
这三个库来拉取数据和展示HTML
表格吗?
是的,但你还需要用一个模块管理器把这三个库打包成一个文件。
哦哦,那模块管理器又是啥?
它的定义取决于语境,不过在 Web
中,只要支持 AMD
和 CommonJS
模块的话就可以称为模块管理器了。
等等,
AMD
和CommonJS
是?
按照定义来说,他们是描述不同的 javaScript
的库和类模块如何相互作用的不同规范,也就是常说的模块化。你听过 exports
和 require
吗?你可以通过 AMD
或者 CommonJS
编写不同的 js
模块,然后可以使用 Browserify
把这些文件打包起来。
听起来很有道理,但是
Browserify
是什么?
它是一个可以将我们工程依赖的、由 CommonJS
编写的 js
模块打包起来,使其可以运行在浏览器中的工具。之所以有这个工具,是因为我们所依赖的那些模块往往被发布在 npm registry
中。
npm registry
?
它是一个存放着世界各地的人们编写的代码模块的仓库。
就像是
CDN
?
不太一样。它更像一个中心仓库,人们可以在上边发布和下载模块。你可以把模块下载下来在本地使用,也可以把它们上传至 CDN
上然后使用。
明白了,就像是
Bower
!
是的,但现在是 2016
年了,没有人再使用 Bower
了。
哦哦,明白了,那我用
npm
下载所需要的库文件就行了。
是的,如果你想使用 React
,你只需要下载 React
模块,然后 import
到你的代码中就可以了。你几乎可以使用 npm
下载现在所有流行的 javaScprit
库。
Angular
也在里边吧?
是的,不过 Augular
是 2015
年的事情了。虽然 Augular
现在也还在用,但 2016
年有了 VueJS
或者 RxJS
这些新的库,你要学一学吗?
算了算了,还是用
React
吧,毕竟我们已经谈了这么多了。所以我如果想使用React
,只需要从npm
下载相应的库,然后用Browserify
打包就可以了吧?
是的。
但这看起来很复杂,需要下载那么多库,然后再它们打包起来。
对,所以你需要使用一个任务管理器来自动化的运行 Browserify
,例如 Grunt
、Gulp
或者 Broccoli
,甚至可以使用 Mimosa
。
Grunt
?Gulp
?Broccoli
?Mimosa
?我要疯了,这怎么一下这么多东西。
它们都是任务管理器,但现在看起来一点都不 cool
了。我们在 2015
年的时候使用它们,之后还用过 Makefiles
,但是现在我们通过 Webpack
把所有功能都集成在一起了。
Makefiles
?这些一般用在C/C++
工程中吧?
是的,但是你懂的,在 Web
领域,我们总是喜欢先把事情搞复杂,然后再回归起点。这些年我们总是这样,你等着吧,再过一两年我们肯定就能在 web
上写汇编代码了。
唉,你刚刚讲的
Webpack
是什么呀?
它是另一种浏览器的模块管理器,同时也是一种任务执行器(task runner
)。他更像是 Browserify
的升级版。
好吧,它是比
Browserify
更好吗?
也许吧,它可以帮你更好的管理模块之间的依赖。Webpack
允许你使用不同的模块管理器,除了 CommonJS
类型的模块,最新的 ES6
的模块也是支持的。
我完全被
CommonJS/ES6
这些东西搞晕了。
大家都是这样,但通过 SystemJS
的话你就不用关心它们了。
苍天啊,又一个
js
名词,所以SystemJS
是啥?
和 Browserify
以及 Webpack 1.x
不同,SystemJS
可以动态加载模块,允许你将不同模块打包成不同文件,而不是打包到一个大文件中。
等等,我认为我们就是应该把所有库打包到一个大文件中,然后加载啊。
是的,但由于 HTTP/2
的时代要来临了,它会支持请求多路复用。
等等,所以我们不能只是把
React
依赖的库放到本地??
也不是。我的意思是我们可以把依赖的库作为外部的脚本从 CDN
中加载,但 Babel
库仍然需要加到本地的。
唉,这听起来是不是不太好。
对的,你需要引入整个 babel-core
,对于线上环境来说效率很差。你需要做很多的前置动作才能让项目准备好,压缩资源、混淆代码、内联 css
、延迟加载 js
,还有…
明白了,明白了。所以如果不用
CDN
去加载库的话,你会怎么做?
我会使用 Webpack
+ SystemJS
+ Babel
的组合从 TypeScript
转化。
TypeScript
?我一直以为是用javaScript
写代码。
TypeScript
就是 javaScript
,更准确的说是 javaScript
的超集,或者说更具体点,是 ES6
版本的 javaScript
的超集。
ES2016+
不已经是ES6
的超集了,为什么我们还需要使用这个叫TypeScript
的东西?
因为它允许我们写 javaScript
的时候定义类型,从而减少运行时的错误。现在已经是 2016
年了,是时候在 javaScript
代码中添加类型了。
哈哈,就像它的名字一样,
TypeScript
。
虽然 TypeScript
是 javaScript
的超集,但它还需要编译成 javaScript
才能在浏览器运行。而另一种工具 Flow
就仅仅做类型检查,无需编译。
等等,
Flow
是啥?
它是 Facebook
的几个人开发的一个静态类型检查器,他们使用 OCaml
语言去写的,因为函数式编程看起来很酷。
OCaml
?函数式编程?
这是如今那些 cool kids
使用的,函数式编程、高阶函数、柯里化、纯函数。
哎,我一个也没听过。
没有人一开始就会的。你只需要知道函数式编程比面向对象更好,并且这是 2016
该采取的方式就可以了。
不对吧,我在大学学的是面向对象,这个会更好些吧?
就像 java
被 Oracle
收购前一样好,哈哈,我意思是面向对象过去很辉煌,当然现在依旧很多人在使用。但是现在很多人都意识到修改对象状态是一个太危险的事情了,所以大家都转向了不可变对象和函数式编程。Haskell
语言已经这么做很多年了,但不要和我提 Elm
那些人。幸运的是,原生 javaScript
也可以通过 Ramda
这样的库进行函数式编程。
你不要再罗列名词了,
Ramnda
是什么呀?
不,是 Ramda
,和 Lambda
表达式类似,它是 David Chambers
创建的库。
David
?是谁啊?
David Chambers
,一个大神,喜欢玩 mean Coup game
,是 Ramda
的贡献者之一。如果你想更深入的了解函数式编程,你还需要知道 Erik Meijer
。
Erik Meijer
是?
另一个函数式编程的大神,他有很多演讲抨击过敏捷编程。当然感兴趣的话你还可以去了解 Tj, Jash Kenas, Sindre Sorhus, Paul Irish, Addy Osmani--
等等,对不起打断一下。目前这些对于我来说应该用不到,我只想拉取数据然后展示出来。让我们回到
React
,我怎么用React
从服务器获得数据?
emmm
,你不是用 React
获取数据,你只是用它展示数据。
阿西吧,那你通常用什么
fetch the data
?
你可以用 Fetch
去从服务器 fetch the data
。
啥?用
Fetch
去从服务器fetch the data
?起这个名字的人真够简单粗暴。
Fetch
和 XMLHttpRequests
一样是浏览器的原生实现,是为了从服务器获取数据。
那就是
AJAX
吧?
AJAX
只是基于 XMLHttpRequests
的封装,而 Fetch
可以让你使用 Promise
风格去异步请求数据,从而避免回调地狱。
回调地狱?
就是由于网络请求是异步的,你需要在回调函数里边去获取数据,如果此时又需要网络请求,那就需要在回调函数里再调用网络请求,然后再加回调函数,如果再请求网络…会变得越来越乱。
嗯嗯,我知道这个,所以
promise
可以解决这个问题吗?
是的,通过 promise
你可以更轻松的管理异步请求,写出易于理解的代码,同时调用多个网络请求。
也就是用
Fetch
去写?
是的,但是你得保证你用户的浏览器是最新的,否则你需要 Fetch
的 polyfill
(兼容不能用 Fetch
的浏览器),或者使用 Request
,Bluebird
或者 Axios
。
苍天啊,我到底需要知道多少库,怎么还有啊。
这就是 javaScript
,有成千上万个库去做同样的事情,当然我们可以从中选出一个最好用的。
那你刚才说的那些库是干什么的呀?
它们是基于 XMLHttpRequests
实现的 promise
风格的请求库。
jQuery
的AJAX
方法不是也开始返回promise
了吗?
忘记 jQuery
吧,去用 Fetch
+ polyfill
,或者 Request
,Bluebird
, Axios
。我们可以通过它们在 async
函数中 await
异步请求,就像顺序编程一样。
这是你第三次提到
await
了,但我完全不知道它是干啥的。
await
允许你阻塞异步请求,让你更好的控制异步请求,然后处理数据,大大增强了代码的可读性。这非常方便,但你记得要加 stage-3 preset
的 Babel
,或者通过 transform-async-to-generator
插件使用 syntax-async-functions
。
这也太麻烦了。
不不不,真正麻烦的地方在于首先要编译 Typescript
代码,然后再用 Babel
转化才能让 await
被浏览器认识。
啥,
Typescript
不支持await
吗?
1.7
是不支持的,它只会被编译成 ES6
,预计下一个版本才会支持。所以你只能先把Typescript
编译成 ES6
,然后再通过 Babel
把它转换成 ES5
,以便兼容更多的浏览器。
我不知道我还能说什么。
其实挺简单的。就是用 Typescript
写代码,所有模块都用 Fetch
去请求,加上 Babel
的 stage-3 preset
,然后使用 SystemJS
去加载它们。为了让 Fetch
兼容更多浏览器,记得加 polyfill
,或者使用 Request
,Bluebird
或者 Axios
,并且使用 await
去等待所有的 promise
。
我们对简单的定义可能不太一样,,,所以现在我拿到了数据,我就可以用
React
展示数据了吧?
你的应用要控制所有 state
的变化吗?
我觉得不用,我只是需要展示数据。
那太好了,不然我还得向你解释 Flux
,以及它的一些实现,比如 Flummox, Alt, Fluxible
。但说实话, Redux
会更好用些。
我不想再知道新的名字了,我只是想展示数据。
哦哦,如果只是展示数据,你其实不需要 React
,用一个模版引擎就好了。
你在逗我吗?你觉得这很有趣吗,唉,感情淡了。
我只是想告诉你你能用什么。
那请你不要说了。
其实如果用模版引擎的话,我还是推荐你继续使用 Typescript + SystemJS + Babel
的组合。
那你有推荐的模版吗?
有很多,你之前有用过什么吗?
不太记得名字了,隔的时间太久了。
jTemplates? jQote? PURE?
没有用过,还有吗?
Transparency? JSRender? MarkupJS? KnockoutJS?
这一个支持双向绑定。
还有吗?
PlatesJS? jQuery-tmpl? Handlebars?
这些还有些人在用。
有和最后一个比较像的吗?
PlatesJS? jQuery-tmpl? Handlebars?
甚至 lodash
都有一个模版引擎,但这已经是 2014
年的事情了。
那有没有更新一些的?
Jade? DustJS?
没听说过。
DotJS? EJS?
没听说过。
Nunjucks? ECT?
没听说过。
对的,应该没有人喜欢 Coffeescript
的语法了。那 Jade
?
你不是说过
Jade
了吗?
我的意思是 Pug
,也是 Jade
。现在 Jade
叫 Pug
了。
额,我想不起来我用过啥了,你现在用什么模版引擎?
也许会用 ES6
支持的原生模版字符串。
那我捋捋。只有
ES6
支持?
对的。
那我需要用
Babel
来兼容更多的浏览器。
对的。
我需要从
npm
加载它的核心库?
对的。
我还需要
Browerify
或者Webpack
或者SystemJS
来管理这些模块?
对的。
除非直接用
Webpack
,不然的话我还需要一个任务管理器。
对的。
由于我要用函数式编程以及强类型的语言,我还需要
Typescript
或者Flow
。
对的。
如果要用
await
,Babel
需要进行相应的配置。
对的。
这样我就能使用
Fetch
,Promise
这些神奇的东西了。
对,记得不要忘记给 Fetch
加上 polyfill
,有些浏览器目前还不支持这个特性。
好吧,我疯了,今天到这里吧。我不要再碰
Web
了,不要再和我提javsScript
了。
问题不大,也许未来我们就会使用 Elm
或者 WebAssembly
了。
我还是去写我的后端吧。我觉得我追不上这么多的变化,各种版本号,还有各种编译器和转换器。
javaScript
社区真是太疯狂了,它觉得每个人能跟上这么快的变化吗。
哈哈,你应该去了解一下 Python
社区。
为什么?
听过 Python 3
吗?( python 3
没有向前兼容 pyhon 2
,差异巨大)
总结一下,前端之所以发生这么大的变化,我觉得一个很关键的点就是 Node.js
的出现。它使得 js
可以脱离浏览器去运行,还提供了读写文件的能力。从而可以在本地进行编译、转换 js
文件,将打包完成的文件运行在浏览器中。
我们可以不去考虑浏览器支持的语法,各种模块化、ES
的新特性,放心大胆的用就可以了,大不了最后再转换就可以了。
此外 node.js
也使得 javaScript
可以写一些服务器端的应用,自己只用它写过一些 Web
接口,其他的了解不多。