没有打包构建的日子

nodejs的出现对于构建工具具有重要的意义,在没有nodejs之前,js只能执行在浏览器环境下,所以意味着对发布前的js文件要进行处理,十分局限,没有打包工具,只能用PHP脚本来处理文件,甚至还需要借助一些在线压缩网站,开发体验十分差劲,在史前时代存在以下几个痛点:
1、缺乏文件处理工具,对文件进行编译或其他预处理,进行打包压缩等工作;
2、缺乏文件的模块化,引入第三方库直接用cdn引入,需要处理依赖管理,人为控制脚本的加载顺序,并且存在全局变量命名冲突的问题;
3、缺乏代码校验和自动化测试,在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。

有了打包构建工具的日子

随着nodejs的诞生,我们可以在开发环境下书写nodejs代码脚本,对我们的前端代码做预处理,编译压缩等工作,最初诞生的是grunt和gulp,Grunt和Gulp都属于任务流工具Tast Runner,两者都是通过配置好配置文件,但是相比之下,gulp通过函数式编写配置文件,以及前端人员所熟悉的链式调用,让大家觉得更易懂更易上手,gulp本身借鉴了grunt的经验进行升级和加入一些新特性。正因为流管理多任务配置输出方式的提高,人们逐渐选择使用Gulp而放弃grunt。
有了grunt和gulp,文件压缩处理的工作解决了,代码校验和测试也可以处理了,但是模块化仍没有结果?
其实前端的痛点还远不止模块化那么简单,频繁的DOM节点处理,JS里杂糅了交互逻辑、请求逻辑、数据处理和校验逻辑、DOM操作逻辑,导致JQ书写的代码就更意大利炒大便,呸!意大利炒面一样。在团队开发中,可能你的代码要给别人维护,这就非常痛苦了。

《深入浅出webpack》有感-LMLPHP

webpack诞生记

1、模块化思想

隔离不同的js文件,模块化开发,仅暴露当前模块所需要的其他模块,这是模块化思想想要传递给我们的。nodejs诞生后,后端所采取的模块化思想是commonjs,然而,不同于后端,前端的代码运行在浏览器端,有两点不同之处:
1、没有nodejs执行环境,不支持module.exports的书写格式;
2、后端require一个文件,是读取本地文件的形式,速度极快,而对于前端而言,需要去动态加载一个js文件,存在额外耗时。
于是AMD思想应运而生,对此的相应实现是requireJS,允许你定义好模块名称、模块依赖以及当前的模块代码(function),通过广度优先遍历的方式,递归加载父模块所依赖的子模块,但是这也暴露出了一些问题:
1、通过js加载执行后再去加载其依赖的子模块,这个递归加载过程本身是耗时的;
2、模块化思想提倡我们分隔逻辑,管理好各个js文件内的逻辑,一旦分割的JS文件过多,最终造成前端资源加载压力。
不过不用担心,requireJS提供了r.js来处理发布前的模块合成,帮助你把多个JS文件打包成一个文件。
再到后来,国内出现了CMD的思想,不同于AMD的声明依赖的形式,允许你动态加载依赖,但是其实现以及具体的运行结果被大家诟病。
再到后后来,为了解决这种不同库的模块实现不一致的问题,提出了UMD,其实很简单,只是写一段hack,让你的模块能够兼容不同的模块加载场景,无论是commonjs还是AMD,如果都没有的话就直接声明为一个全局变量的形式。
下面引用一段UMD的代码:

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global.Vue = factory());
}(this, function () {
  'use strict';
  //your code here
}

再到后后后来,未来的大一统,es6中所提出的import和export的形式来统一前后端的模块化加载方式。相比以往的实现,import/export的形式,在模块化加载JS文件的时候,保留动态的执行引用,其次,不允许动态控制加载依赖,使得tree-shaking成为可能。

2、组件化思想

组件化的思想并非前端所特有,在客户端也会面临相同的问题。想象一下,A跟B被同时分配一起开发完成一个首页页面,包括导航栏、轮播图、网站列表数据,登录框等,两人需要如何分工协作?导航栏、列表这种需要在多个页面复用的HTML怎么办?假如在没有组件化处理的情况下:
1、A和B分工困难麻烦,代码提交时会处理大量的冲突;
2、导航栏、列表等多处复用的地方,需要cv大法直接复制粘贴到另一个页面中去使用。
组件化思想,让我们把页面划分为一个个组件,组件内部维护自己的UI展示、交互逻辑,组件间可以进行数据通信,实现一种变相的相互隔离,便不会出现A和B两人一起编辑一段html的难受场景,同时,提高了代码的可维护性和复用性,这是其解决的关键痛点。
引用vue官网的一张有关组件化思想的图:

《深入浅出webpack》有感-LMLPHP

3、MVC框架、MVVM框架的流行

在模块化和组件化的基础上,实现了页面组件之间的隔离和各自维护,但是面临的最大的一个痛点问题,仍然是前面所说的,前端的JS逻辑中杂糅了各种处理逻辑,交互逻辑、请求逻辑、数据处理和校验逻辑、DOM操作逻辑;而其实这一切可以划分为两个层次,一个是数据层,一个是视图层,如何避免重复的书写操作DOM的逻辑,如果说早期的各种模板引擎给了我们初期的解决方案,那么vue、react以及angular就是在模板引擎的基础上的上层建筑。
为了让我们更加专注于数据的处理,MVC框架和MVVM框架帮我们做了以下两件事:
1、监听页面操作事件,触发相应的事件钩子,执行代码逻辑,即V层到M层的过程;
2、执行代码逻辑后,数据层发生修改,帮我们更新渲染页面,即M层到V层的过程;vue中通过vm实现,react中通过触发setState通知。
如此,我们只需要书写一次html,在html中写明绑定或展示的数据,同时绑定好事件监听器,后续便不需要再处理视图层相关的操作,只需要关注于自己的业务逻辑代码、数据层的处理等。
MVVM架构流程图:

《深入浅出webpack》有感-LMLPHP

4、代码打包构建

前面介绍了grunt、gulp打包构建工具,其实webpack本质也是打包构建工具,但是webpack呈现出来的功能更为强大和成熟。对于代码的预处理、模块化加载、代码分割等,webpack具有更大的优势。

webpack诞生!

《深入浅出webpack》有感-LMLPHP

读者读到这里,可能仍有些许疑惑,前面讲了这么多,为啥还是没有介绍到webpack相关的,其实不然,仔细回想一下前面所介绍的思想,以及你平时使用webpack来打包构建的时候,其实webpack正是帮你处理了这些繁杂琐碎的事情。
如果代码预处理压缩足以,那么grunt和gulp已经满足了;
如果说模块化开发足以,那么requireJS和Browserify已经满足了;
如果说组件化开发、MV*框架足以,那么只需要在页面内引入相应的vue或react框架,足矣。
笔者写这边文章,更多是想让大家能够思考工具或者框架背后,所呈现出来的思想,webpack就像是一个巨无霸,集大成者,它解决了打包构建,它处理了模块化开发,它帮助你和其他框架完美融合实现组件化开发;而这几年来MV*框架的流行对于webpack市场的迅速扩展有着不小的贡献。

webpack为我们做了以下这些事:(引自《深入浅出webpack》)
代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。
文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。

其实以小见大,你能窥见的不仅是webpack的思想,更多的是前端的发展,从最初的土法炼钢,不规范,到如今的模块化、组件化、MV*框架,是前端思想的进步。作为一个前端仔,我们应该探索和研究的是如何磨刀、磨好刀,而不是砍柴而已。

谢谢观看~

10-08 18:22