starleejay 2019-07-01
假如你烘焙过蛋糕(哪怕没有亲自做过,但是也应该听说过),除了基本的面粉,鸡蛋等原材料外,或许你还需要一个电动蛋白打发器,一个烤箱。这是现代的做法,那么在打发器和烤箱发明之前,人们怎么烤蛋糕呢?
没有打发器,只能手动打发,这样的弊端是不仅花费时间长,而且你的手可能也要废掉。没有烤箱,用炭烤,这样就会出现温度不好掌握,或许还得有人一直盯着的问题。
我一直爱把软件开发想成做菜,原材料就是编程语言,菜单是算法逻辑,而做菜的这些工具也是我们在软件开发中使用的各种工具。做菜的工具有历史演变的过程,我们软件开发的工具也是如此。
只是有些工具,现在被大家广泛使用,而有些却只能在一些老文章里面见证他们曾经的辉煌。今天就从前端工具演变来聊聊前端的历史发展。
摸鱼警告:本篇没有干货,纯粹闲聊。
1:没有任何工具的纯手工时代
最开始的前端开发,只需要掌握HTML + CSS + JavaScript就好,我们要在一个页面上使用js,除了通过<script>...</script>标签外,还可以把js代码放到一个js文件,通过在HTML文件引用这个js文件的方式:
<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="index.js"></script> </head> <body> </body> </html>
但是,当我们的业务需求变复杂,项目变大,我们通常会把逻辑相关的代码都放同一个js文件里面,这样慢慢就形成了模块的概念,例如你在做一个超市收银系统,项目里有一个(或者几个)js文件是跟价格计算相关,有些js文件是跟时间格式化相关。现在你正在开发收银小票功能模块,毋庸置疑,这个模块肯定需要用到前面的价格计算和时间格式化模块。这里,就产生了模块访问模块的需求。
很多语言都天然地支持在A文件里面引用B文件的功能,但是javaScript一开始却不是这样。因为javaScript一开始是被设计来只在浏览器(准确地说是用户的浏览器)上运行的语言。处于安全性的考虑,它不被赋予访问文件的权利和功能。
在这个阶段我们要做到在A文件使用B文件提供的功能,只能在HTML文件里面引用相关的js文件,通过在全局暴露相关变量,然后这样就能使用到了。
2:包管理(package management)工具
在上面第一点我们提到了模块。在一个稍大的项目中,一般会用到很多前端社区已经成熟的模块和框架。比如,专门用来处理时间格式问题的moment.js,以前可能会用到jQuery等。我们要在项目里面使用他们,得先找到他们在网络上的地址,把相应的文件下载下来,放到我们自己的项目某个目录,然后再在HTML文件里面引用相应的文件路径。
上面的做法很显然是存在效率和不方便的问题,在这种情况下前端的package management工具应运而生。
我们先来了解一下包管理工具为我们做了什么事:
1:帮我们从网络上下载依赖
2:管理依赖的版本
3:有的还具备task runner功能(npm)
这里就要提到几个包管理工具:
bower和npm都是包管理工具,但是又有不同。现在大家听得最多,用的的可能是npm,yarn,bower已经差不多死掉了。
npm vs bowernpm(node package manager)本是nodejs用来管理node依赖的,但是后来也被用于前端的依赖管理。
bower是只被用于管理前端的组件,例如html,css,js的工具,只用在前端。
在依赖结构上二者也不相同。bower是扁平的依赖树,需要用户管理依赖的依赖。而npm采用的是嵌套的依赖树,不需要用户关心依赖的依赖。
bower
bower init //创建bower.json bower install jquery --save //安装jquery依赖
安装成功之后会,bower会在当前目录下创建一个名叫bower_components/的文件夹。所以通过bower安装的依赖都会放到这个目录下。例如上面的jquery安装成功之后,我们能在bower_components/目录下得到jquery相关文件,然后我们就可以在HTML文件里面引用了:
<script src="bower_components/jquery/dist/jquery.min.js"></script>
npm也是类似的:
npm init //创建package.json文件 npm install jquery --save //安装jquery依赖
安装成功之后会,npm会在当前目录下创建一个名叫node_modules/的文件夹。所以通过npm安装的依赖都会放到这个目录下。例如上面的jquery安装成功之后,我们能在node_modules/目录下得到jquery相关文件,然后我们就可以在HTML文件里面引用了:
<script src="node_modules/jquery/dist/jquery.min.js"></script>
3:Module bundler工具
在上面的第二点里面,我们使用了包管理工具,已经不再需要我们自己手动去网上找依赖,下载到项目库里,但是想要使用,依然需要在HTML文件里面一一引用。我们要怎样才能像别的语言一样在一个js文件里面引用另外一个js文件(模块)呢?
当然自从ES6(ES2015)开始,JavaScript已经有这个功能了,我们可以通过import来引入,export来导出。但是在这之前,我们只能依赖Module bundler工具来做到这一点。
当时比较出名和流行的一下2大解决方案:
至今仍然有很多人没有弄明白CommonJS和AMD都是一个协议标准,而不是一个具体的库。CommonJs的核心就是module(模块),定义了JavaScript代码怎样引用和导出代码。实现了CommonJs的最出名的就是node.js和Browserify.js,而对应的实现了AMD的比较是Require.js
大家都知道node.js是运行在服务端的JavaScript,不属于我们本篇的范畴,这里不讲。接下来我们来看看
Browserify.js
如果你打开Browserify的官网,可以看到这样一句话:
Browserify lets you require('modules') in the browser by bundling upBrowserify提供require()方法在一个js文件里面引入另外一个js文件或者一个js文件export的变量。最终Browserify会根据依赖关系,把所有的js代码都整合到一个js文件里,也就是我们常说的bundle文件。之后就只需要在HTML文件里面引用这一个js bundle文件就行了。
接下来看看具体怎么使用:
step1: 全局安装browserify
npm install browserify -g //全局安卓browserify
step2: 在module/目录下创建一个module.js,有如下示例代码:
function getSum(a, b) { return a + b; } module.exports = getSum;
step3: 创建main.js文件,在这里面使用module/module.js
var getSum = require('./module/module'); var sum = getSum(10, 10); console.log('10 + 10 = ', sum);
step4: 利用browserify创建bundle文件:
browserify main.js -o build/bundle.js -d
上面的命令就是:browserify以main.js为源文件,生成bundle文件bundle.js,创建文件夹build/,并把bundle.js放到build/路径下。-o(-output)表示后面跟上输出文件路径, -d表示生成source map.
step5: 在HTMl文件里面引入上一步的bundle.js
<!doctype html> <html> <head> <meta charset="UTF-8"> <script src="build/bundle.js"></script> </head> <body></body> </html>
step6: 验证结果
10 + 10 = 20 //控制台得到我们正确地结果
4:task runner工具
前端的task可能会有:检查代码格式,预编译,跑测试,压缩代码,起server等。Task Runner就是按照用户自定义的任务流自动地完成一系列的task。
一般task runner工具本身并不具体具体执行某项任务的功能,而是每一个任务都有相应的插件来完成。例如曾经流行过的grunt和gulp。
但是现在的前端项目也不会再用到grunt和gulp了。现如今,因为项目上一般会用到npm来做依赖管理,而npm自己的script就可以用来run task, 这样也不用额外再多下一个额外的工具了。