大多同学的前端学习路线:三件套+框架+慢慢延伸到其他,在这个过程中,有一个词出现的频率很高:webpack 。
作为一个很出名的前端构建工具我们在网上随便一搜,就会有各种教程:loader plugin entry吧啦吧啦。
但是为什么会有它,为什么我们离不开它呢? 如何理解"构建工具"四个字?它和"打包工具"又有什么关系呢?用法和api其实很好找,但是这几个问题的答案找起来却有点难度:相关的解释都零零散散,于是决定做这么一个分享,希望能以不一样的角度带给大家新的启发。 以上就是写这篇文章的初衷
目录
何为前端构建(打包)工具
现代的前端构建主要包括两部分:transform 和 bundle
1.transform(代码转换): 的作用就是将 TypeScript/JavaScript/JSX 等高级语法 (es6语法浏览器直接运行可能会不兼容)转化适合浏览器/Node.js 运行的相关标准的 javascript 的过程。
2.bundle(代码打包):从开发者设置的入口出发,分析模块依赖,加载并将各类资源最终打包 成1个或多个文件。
所以,"打包工具"是"构建工具"的一部分。
构建工具发展史
接下来会按照前端发展为时间线来进行讲解,以前端大事件为节点进行分割,来分析讲解当时的前端困境以及对应的构建情况。
1.石器时代
标志性事件:1990年浏览器的诞生。
在这段时间,除了浏览器,还诞生了很多东西:WWW(World Wide Web)的诞生、JavaScript的诞生、W3C、PHP......
此时的前端:那个时候页面基本是纯静态的或者服务端输出 没有专业的前端,页面全是由后端开发 在很长的一段前端历史里,是不存在打包/构建这个说法的。
2.蒸汽时代
2.1标志性事件:2005年AJAX被拾起。
ps:XMLHttpRequest是2000年就有了,但是由于推广以及存在的问题(需要浏览器提供支持,存在跨域问题),导致它刚开始并没有被人们注意,到 2005 年 Ajax 和 Web 2.0 变得流行才被人注意。
为什么说AJAX对前端具有非凡意义呢?
想象一下,之前前端页面展示在浏览器中 是后端 把一整个页面发过来,有数据更改,后端改了之后,把一整个新的页面再发过来(也就指常说的:页面整个刷新)。
有了ajax之后,发过来页面之后,如果有更新,只需要网络交互有更改的地方。
也就是:整个页面刷新---->部分刷新(将页面/呈现和数据分离!交互的粒度变小了)
意味着可以把以前一些服务器负担的工作转嫁到客户端!
js能做的事愈来愈多,前端开发者开始往页面里插入各种库和插件,我们的 js 文件程指数倍的开始增加。
2.2此时遇到的问题:
1.运行环境限制:js作为一门脚本语言,只能在浏览器运行(没有node的时候),只能通过<script>标签 插进html去运行去引那些js文件,还得注意引用顺序和插入的位置,一方面难以维护, 一方面增加了网页加载时的请求数量。
2.代码分割管理的问题:引用一些脚本来存放每个功能;此解决方案很难扩展,因为加载太多脚本会 导致网络瓶颈。
3.可维护性:使用一个包含所有项目代码的大型 .js 文件,但是这会导致作用域、文件大小、 js文件间的文件依赖难以维护,可读性差等问题。
举个例子 :比如说如果要用bootstrap,只需要在 script 标签中引入对应库的 cdn 链接即可, 然而由于 bootstrap 依赖于 jquery,我们必须把引入 jquery 的标签写在 bootstrap 的**《前面》**,不然就会导致报错。
如果一个网站功能很多,我要按照功能划分写15个js文件, 那我就要插入15个<script src=""> 去引那些js文件,还tm得注意引用顺序和插入的位置,一方面难以维护, 一方面增加了网页加载时的请求数量(15个不算多,如果是200个js文件的话,维护起来会多令人崩溃。。。)
2.3当时的解决办法:JS压缩。
压缩主要做了局部变量命名简化、空格/换行/注释消除、自动优化可简化的语法等工作
一般是通过在window上用bat脚本或者mac/linux上的shell脚本来决定合并哪些文件、用什么工具压缩、怎么压缩。
JSMin、YUI Compressor、Closure Compiler、UglifyJS 等js 文件压缩合并工具在当时陆陆续续诞生。
2.4存在的问题:
但是这种解决办法依旧存在问题:
举个例子,当a文件和b文件都引用了c文件的方法时,如果把c文件分别和ab合并, 这样就只有两个文件了这就是最开始的合并方式。 工程化的项目里相互依赖关系变得很是复杂 合并的文件里可能会有不少重复/无用代码。
3.近现代
3.1标志性事件:Node.js的诞生(2009)和随之诞生的各种JS规范(commonJS AMD CMD ECMAscript等)。
之前我们说什么?js只能在浏览器里面执行 node可以在浏览器环境之外的计算机和服务器中使用。 commonjs的require机制为js引入了模块化。
3.2此时遇到的问题:
1.浏览器兼容问题:尽管现如今主流浏览器的最新版本都支持这一特性, 但是在几年前的我们还无法忽略用户存在使用老版本浏览器的 case, 所以我们还需要考虑兼容问题。
2.网络性能问题:模块化的方式划分出来的模块文件过多, 而前端应用又运行在浏览器中,每一个文件都需要单独从服务器请求回来。 零散的模块文件必然会导致浏览器的频繁发送网络请求,由于上文所述的 Http1.x 的能力局限性, 进而会影响应用使用体验。
3.模块化需求:随着应用日益复杂,在前端应用开发过程中不仅仅只有 JavaScript 代码需要模块化,HTML 和 CSS 这些资源文件也会面临需要被模块化的问题。(sass ejs)
4.打包时候的额外工作: ES6 -> ES5 ,TS -> JS, less / scss -> css等
3.3当时的解决办法:颇具代表性的构建工具webpack来了!(2014)
(ps:同时期还有其他也很优秀的构建工具,如rollup等,这里挑的是比较有代表性的webpack来讲解)
我们上文提到,构建工具包含transform 和 bundle两部分,webpack主要负责打包(bundle),同时它那些loader/plugin让他能够去转化代码(transform)。
所以如果我们回看此时的困境,webpack其实差不多都能解决的7788了(webpack有大量的plugin和loader兼容性问题,代码转换问题 以及webpack还引入了异步地加载 chunk 和预取等等来进行性能优化,来弥补网络性能问题等等)
3.4存在的问题:主要是dev环境(即开发环境)下的问题
我们项目中的所有代码最终都被打包到了一起,如果我们应用非常复杂, 模块非常多的话,我们的打包结果就会特别的大,加载时间就会很长。 项目的启动速度也会越来越慢(因为每次都要重新打包)。
为了解决这个问题,我们可以分包,HMR(模块热替换Hot Module Replacement),以及还有其他优化方法,就不多赘述。
4.现代
我们想要的:让我们在生产环境下,更快的构建出对应的产出文件
上文我们提到了dev环境下构建工具存在的缺陷,那么为了解决这个缺陷,一个概念被提出:bundleless
bundleless:顾名思义,就是不打包。
颇具代表性的就是vite。
这里的不打包,侧重的是dev环境下的不打包,生产环境该打还得打(vite打包使用的是RollUp)。
基本原理:依赖浏览器原生支持esm 去解析模块。
为什么快:vite可以不用打包,只需要更新修改过的文件即可
PS:在查资料的时候,觉得vite和和webpack的一个plugin很像:webpack-dev-server。
都是用在开发环境的,提高开发效率的(把代码读取到内存中)。
查了一下:大致过程和webpack-dev-server的差不多,但是vite可以不用打包,只需要更新修改过的文件即可
而和webpack-dev-server最主要区别则是:每次更新都会重新打包编译,所以没有vite快。
回顾
总结一下,不外乎这几种原因催,生了技术一次又一次的革新和发展:
需求,语言越来越多样化
对性能要求越来越大
项目(代码量)复杂度越来越大
其他技术(如硬件等)的发展
。。。。。
最后,希望这次"另辟蹊径"的分享能带给大家不一样的启发。