原始时代
在很久以前,一个前端页面非常简单,由1个html、1个css、1个script组成。
后来,随着“前端应用”的复杂度越来越高,link
和script
标签的数量也在剧增。举个夸张的例子,如下图:
这个html文件引用了1000个js文件,这会引发两个重大的问题:
- 访问一个页面需要发起1000次http请求!
- 如何有序地管理好这1000个script标签?
针对以上两个问题,我们一般会采取两种策略:
- 将1000个script标签合并成一个js文件;
- 拿个“小本本”记下每个js文件的顺序。
在原始时代,我们采用纯手工的方式实现上面的策略1:用ctrl + c
和ctrl + v
的方式,手动合并所有js文件(task 1);完成合并之后,可能还需要手动地通过Babel
进行转译(task2);转译之后,可能还需要手动地进行压缩,删除多余的空格(task3);等等。
这是一个工作量巨大的任务!想想如果我们每次发布一个小版本,都需要手动地“重复一遍”上述所有的任务,那简直是个噩梦!
我们急需一个“自动化”的工具,来帮我们“一键”完成上述的所有任务!
题外话:现在,很多前端工程师觉得要学的东西越来越多了,比如要学Node、Webpack、Vue/React等各种工具。其根本原因就在于此:前端项目复杂度的剧增!前端已经从1个script就能解决的时代,进入了1000个,甚至更多script标签才能解决的时代,也就是“前端工程化”的时代。而工程化是一门极其高深的学问。
gulp/grunt时代
在前一节的背景下,Gulp和Grunt应运而生了。Grunt官网将Grunt定义为 Task Runner,也就是“任务执行器”。我们事先定义好一系列的task,比如第一步为“拼接”、第二步为“转译”、第三步为“压缩”...,最后只需要一行命令,Grunt就能自动地帮我们完成所有任务!
上述定义task的过程是在gruntfile中进行的,下面是一个gruntfile的简单示例:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// task1
concat: {
// 相关配置
},
// task2
uglify: {
// 相关配置
},
// task3
qunit: {
files: ['test/**/*.html']
},
// task4
jshint: {
// 相关配置
},
// task5
watch: {
// 相关配置
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('test', ['jshint', 'qunit']);
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
};
可以看到,gruntfile注册了两个“任务”:test和default。其中test包含了jshint、qunit两个子任务,default包含了jshint、qunit、concat、uglify四个子任务。
这种配置方式,在现在看来,不免显得有些“繁琐”。
webpack时代
Webpack是前端工程化过程中的一个产物,它的核心思想是“自动化”。
提起工程化,怎么能少得了“模块化”开发呢。随着“模块化”的发展,伴之而来的问题是:如何分析模块与模块之间的依赖关系。举个简单的例子:
如上图,模块a依赖模块b和模块c。我们如何告诉计算机“在加载a之前需要先加载b和c”这种逻辑呢?由此便诞生了Webpack项目,Webpack会以a.js为入口,递归访问其依赖文件,构建成“依赖关系图”,然后根据这个关系图,生成相应的代码。
Webpack最初的目的是为了分析模块之间的依赖关系,后来人们发现它提供的Loader和Plugin非常好用,为它开发了很多好用的插件,发展至今,Webpack已经成为前端开发必备的一个工具了。
题外话:Webpack的配置方式,相比于gulpfile/gruntfile的配置方式,从设计角度而言,有了很大提升。
现状与展望
之前,网络上流传着“Webpack配置工程师”的一个梗,这反映了Webpack配置繁琐的问题。后来便诞生了很多基于Webpack的构建工具,如Vue/React的Cli工具,它们内置了一些常用的功能,比如Dev Server、HMR等,这样确实省却了前端工程师的配置工作。但是在我看来,这种方式“治标不治本”。
最好的“插件配置”设计体系应该像VS Code那样,插件一键安装,点点鼠标就能完成相关配置。