先讲个笑话:我年轻时不爱搭环境,于是放弃java转投了前端。
  没想到两三年的时间过去了,前端工程师在通往“大前端”的路上,又多了一重身份:配置工程师。
  
  一开始的前端是没有搭环境这码事的,把静态资源整理到一个文件夹,开个文本编辑器就能写代码了。为了减少请求数量和大小,我以前也用过一些客户端软件来合并压缩js、优化图片什么的。一些大公司则自己用Python或php写后端脚本,来完成这些工作。那时候前端还没有「工程化」这么一说。
  后来出现了grunt,大家发现只要通过一个命令,就可以自动完成文件合并压缩、上线构建、live-reload等功能,简直太爽了。不过前提是要对grunt进行配置,因为grunt是基于配置来串行调度任务的。
  用grunt的人一开始沉醉于配置任务的乐趣,后来就会发现,随着项目复杂度的增加,光用配置的方式是无法满足需求的,解决问题的办法就只有增加插件的配置项。这样导致的结果就是,配置项越来越多,以至于后来上手的人都不清楚这些配置都是干嘛的,于是grunt就被人诟病“不灵活”。
  这样主打“灵活”的gulp就有了发挥空间,gulp的核心思想是提供文件流的操作,用编程的方式来组建任务,这样我们就不用再进行无止境的配置了,而是像平时写代码一样来编写任务。gulp只提供了5个核心API,其余的都得通过组合各种插件的方式来进行。可以说是相当灵活了。
  事实上,我们公司目前的主力工具就是gulp,得益于插件众多,开发及上线所需的预编译、压缩、优化、构建等任务都能很好完成。
  最近我跟webpack+vue杠上了,其实这两者去年就特别火了,只是我们公司的项目要兼容IE8,没有用到vue,而且目前的gulp方案也运行良好。所以之前只是对webpack和vue大致了解。
  现在手头这个项目只兼容chrome,并且有一点空余时间,这让我相当乐观,决定趟一趟新技术的坑。在搭了一个星期环境之后,我的心路历程大概是这样的:
  •看起来挺简单的嘛
  •好多疑惑啊
  •这玩意对大型项目简直没法搞
  •配置太繁琐了,要疯
  •垃圾玩意,必定被历史淘汰
  •通了!能跑了!
  •试着去理解它的哲学吧(无奈)
  我把我纠结过的问题罗列一下,看看大家有没有类似的疑惑,大家一同体会一下从入门到放弃的心情==!
  1.要单页面还是多页面。 我们的项目比较大,目录又多又深,如果用配合vue-router做成整体的单页面应用,必然会因多级嵌套路由而造成各种困扰。所以最终决定一个功能模块一个页面,各模块自己做成SPA。这个方案也为后面进行多入口打包带来了麻烦。
  2.公共框架如何处理。 所谓公共框架就是像vue、jquery、bootstrap这的第三方框架。 它们基本不会改动,所以肯定是要单独打成一个包的,充分利用缓存。另外一个问题就是,这些框架的代码放哪里好,有人用bower管理,有人用npm管理,或者是像我们之前那样在项目中放一个lib目录,下载好框架代码后就放进去不动了。不过现在业界普遍都推commonjs规范了,所以用npm来管理是个趋势。
  3.项目的公用组件如何打包。 既然是用vue,那肯定会写很多公共的组件了,这些组件肯定不能和第三方库打包到一起,也不要和业务代码打包在一起,因为业务代码总改动,而公共组件相对改动也比较少。最好是把所有的组件打成一个包。然而webpack的设计思想并不是这样,它是完全按照模块的依赖树来分析,根据依赖进行打包。我们的组件之间如果没有依赖,是没法打到一起的。
  4.CommonsChunkPlugin的硬伤。 关于公用组件的处理,其实可以用CommonsChunkPlugin这个插件,它能自动分析出公共模块并打成一个包。但是这个插件有个硬伤,如果我写了一个名为popbox的组件,本意是想给项目公用,但是目前只有一个模块用到了它,那么插件并不能知道它是公用插件,而会把它和业务代码打包到一起。倘若将来有第二个模块用到了popbox,就又会把它打进公共包里,这显然是很弱智的行为。
  5.entry只干入口的事。 我一开始想,把公共组件放entry中打包一下行不行,像这样配置:components: ['loading', 'menu', 'box']。结果证明是不能的,文件虽然能打包出来,但是没法用,就是你标签把文件加到页面也不行,别的模块用require无法引用到。原因就是entry打包出的文件作为入口文件,必须包含直接运行的代码。
  6.webpack-stream是个鸡肋。 一开始我被entry和output的各种配置整的摸不着头脑,因为项目目录多,想要更精细的控制,却发现output总是无法按我的需求输出。后来我看到了有webpack-stream这个东西,而且是官方推荐的。简单来讲,它就是实现了文件流接口,从而能与gulp配合工作,我一看这是个好东西啊。研究了一番,发现也没什么功能,就是能用gulp.src代替entry,用gulp.dest代替output,对于多入口打包,就完全没什么用了,还得在webpack中配置,所以简直就是个鸡肋。直接弃用。
  7.这么多require用哪个。 一开始用webpack构建应用的时候,每当写require的时候我是懵逼的,有commonjs风格的require用法、AMD风格的require加回调用法,以及webpack提供的require.ensure用来打包异步加载的模块。另外还有ES6的import,既然都用vue了,我们肯定得用ES6嘛。这么多引入模块的方法,你得保持头脑清醒了,他们都有什么区别,什么场景下用哪个。
  8.dist目录不是给人看的。 鉴于之前用gulp的习惯,打包后的目录也是有很清晰的结构,而且打包前我们src目录下的代码也是直接可运行的。但是有了webpack之后,首先src下的代码未经打包不能在开发环境运行,其次output选项只能指定一个输出目录,无法再按你的想法再进行组织。唯一有点用的方法是在entry中,把入口文件名字写成这样foo/bar/baz。勉强能在输出目录中新建出文件夹。但是总体来看,dist目录还是一团糟,尤其是异步的chunk文件,只能是id+hash这样的名字。所以我也明白了,要用webpack就别打算去看dist目录了,只能用sourcemap在浏览器看。
  写了这么多,我发现用一篇文章的篇幅根本写不完webpack的坑。。。而且,在与vue以及其他框架整合的过程中,也是各种辛酸泪。所幸的是我已经基本有所了解,度过了抓狂阶段。所以打算之后再写个webpack解惑系列,把每个小点都详细说一说。
  那么,我到底要放不放弃webpack呢?在研究webpack的中间阶段,我曾一度断言,这么难用的东西迟早要被淘汰。但是后来,我觉得态度应该更冷静些,毕竟它是目前最火的打包工具,就是以后要放弃它,起码现在也对它进行一个全面的了解,以模块为核心的打包工具到底是什么特征。所以,尽管难用,也先上手吧,不会有什么损失。
    文/吕大豹