面对H5零散页面,我为什么放弃了Webpack

相信每一款产品,都有很多零碎的H5页面,有的是用于浏览器打开,有的是用来嵌在终端的APP中。这些页面的大多存在方式,都是互相不关联,是一个又一个的松散组织的零散页面。Webpack是不是这些页面的正确打包、构建方式呢?

原有项目打包的简要介绍

首先有一点是需要提前说明的,就是我们产品和大多数前端架构不一样的地方:

  1. CSS不是由我们来写,也不是由我们来发布,所以针对CSS、Image静态资源的存放是不由我们控制的。
  2. 页面是用Node直出的,所以我们一个页面会有部分的Node服务器代码。(什么是直出
  3. 我们的页面都是用zepto手撸的,并没有用vue等框架。 正是由于我们项目的特殊性,目前没有找到和我们相匹配的构建方式,所以探索过程也并不容易。 ## H5项目目录结构 整个H5项目目录结构大致是:
.
├── common
├── lib
├── modules
│   ├── page1
│   ├── page2
│   ├── page3
│   └── page4
├── tmpl
  • common:业务相关库
  • lib:业务不相关基础设施库
  • modules:各种页面项目存放目录
  • tmpl:直出基础模板目录 > 注:其中对于第三方库我们是另外存储的,直接发布CDN,然后通过script标签引用;其中lib/common库是服务器端、浏览器端通用的同构库。 ## 单页面的目录结构 单个页面的目录结构如下:
.
├── src
│   ├── _config.js      // jsc配置文件
│   ├── ar.js
│   ├── busiConfig.js
│   ├── capacity_purchase.js
│   ├── dom.js
│   ├── mgr.js
│   ├── tmpl
│   │   ├── body.tmpl.html
│   │   └── iap.tmpl.html
│   ├── view.js
│   └── vm.js
├── sync
│   ├── config.js
│   ├── loader.js
│   └── sync.js
├── tmpl.js
├── index.js

src

src目录是异步使用的库,也就是浏览器端代码,是需要被打包的。
其中比较另类的有两个:

  • tmpl文件夹,这个文件夹是存放编译前模板的,编译(编译是指我们自己一个工具先进行预处理)出来成js函数,填入对应数据,输出html字符串,这个是浏览器端和服务器端共用的。
  • vm.js文件,这个是处理数据的工具函数,无状态。也是浏览器和服务器端都使用(直出使用这个来处理拉到的数据,格式化成页面需要用到的展示数据;浏览器端ajax拉到的数据也要用其处理)。 ### sync sync目录存放的是服务器端代码,Node服务器拿到request。先从业务后台拉取需要的数据,填到模板函数中,输出html字符串,然后封装返回response。 ### 编译,jsc jsc是团队内部的一个打包&编译工具,src/_config.js就是它的配置文件,做这样两件事:

  1. 把src目录下面所有的文件都打包到index.js之中,然后在浏览器中加载运行时将拆包推入seajs模块系统中。(没错,加载这个文件之前,要加载seajs,虽然seajs已死。这也是我决心要更新一套构建工具的原因,面向未来编程嘛)

  1. 把src/tmpl目录下的模板文件打包出来一份输出为单独的tmpl.js,以供服务器端代码使用,因为直出渲染时也依靠这个来输出html字符串。

CSS、Image静态资源哪去了?

正如上文所说,样式是由专门的重构同学做的,发布流程目前也不在我们手中。所以我们的工作很纯粹,是纯粹的Javascript程序员。

使用Webpack打包的尝试

为什么首先尝试Webpack?

两个原因:

  1. 装X(有人说为什么不用rollup,那么我就说了,别着急,马上就来尝试)
  2. 用上ES6 module,面向未来就是要面向真正的未来。browsify插件太少,不热,插件少,自然不予考虑。

尝试效果如何

比“差强人意”差一点,这句话没毛病吧?说人话的话,就是妥协太多,最终虽然达到了生产环境的发布要求,但是有太多需要妥协的事情了,而妥协的事情一多,就表明不合适,这和谈恋爱是一个道理。虽然花费了很多时间去探索,但是学经济的人都知道,那都是沉没成本,所以最后我选择不妥协,要再去找个更好的。废话不说那么多,我们来看看哪里妥协了。

改版换血概览

├── build
│   ├── lib.dev.js
│   ├── lib.js
│   ├── vendor.dev.js
│   └── vendor.js
├── lib
├── modules
│    ├── src
│    │   ├── _config.js
│    │   ├── entry.dev.js       // webpack dev build
│    │   ├── entry.js           // webpack production build
│    │   ├── index.js
│    │   └── tmpl
│    │       └── index.tmpl.html
│    └── sync
│        ├── config.js
│        ├── data_processor.js
│        ├── loader.js
│        ├── sync.js
│        └── tmpl.js
│
├── tmpl
├── node_modules                // npm
├── package.json                // npm
├── manifest.dev.json           // webpack
├── manifest.json               // webpack
├── webpack.config.js           // webpack
├── webpack.dirvars.js          // webpack
├── webpack.dll.config.js       // webpack
└── webpack.dll.dev.config.js   // webpack

先从基础库说起

使用了Webpack之后,我的库只划分为两个:vendor(第三方库,如vue、zepto)、lib(common + lib)。

  • 发现改版之后去掉了common库,把common库(业务基础库)和lib库(基础工具库)合在了一起,这就是妥协点一。
    Webpack对于基础库的打包定制能力不强,对于自制库(包括common、lib)只能有一个CommonsChunkPlugin的解决方案。而且这种解决方案的弊病是:组织库的方式是按照引用次数自动归为库文件(比如对a.js文件,项目中有超过5次引用,就认为这个是一个库文件),而非依据目录来定义库。这种约束不强,库文件初期容易经常变更。

  • 第三方库打包方式也不适合这种多页松散的场景。
    第三方库使用DllReferencePlugin这个来实现。简要介绍下这个插件的作用:你可以指定['vue', 'zepto']两个第三方库,然后生成对应的vendor.js以及manifest.json。vendor.js是浏览器中加载的打包好的两个第三方库文件,manifest.json是依赖记录,可以在打包页面的时候告诉页面,vue库已经被包含在vendor库中了,你可以不用把vue打进来了。听起来是很不错,但是问题在于:
    1.这个vendor.js不能热插拔,丢失了的话页面会有报错。所以对于你的每一个页面来说,都必须要加载这个vendor。
    2.打包这个第三方库dll,也要起一个Webpack才行,配上生产环境和开发环境两套配置,你可以看到项目下面全部是Webpack的配置文件。

再说打包好的文件存放目录

不能自定义,对于我这种服务器端和浏览器代码都有的项目结构,理想的存放方式是

├── 项目A
│    ├── 浏览器代码
│    │   ├── 原始src文件
│    │   ├── 打包好的bundle.js
│    └── 服务器代码
│        ├── 原始commonjs模块
│        └── 编译好的tmpl.js

然而Webpack要一股脑扔到build目录下,虽然有奇技淫巧:比如让entry的name是一个路径可以达到存放到相应目录下的效果。但是一是不爽,二是服务器端模板文件需要编译一遍放在服务器端代码目录下面,这个实现不了(具体比较复杂)

再说模板编译

我需要写一个插件,将jsc第二个功能抽离出来,即将模板文件编译成js函数文件。但是这个需要再起一个Webpack,再起一个Webpack!!!
加上页面的、dll的、模板文件编译的,一共需要三个Webpack。光配置文件一大堆,一点都不优雅。

Webpack对于这些页面力不从心的原因

需要作出妥协的几点:

  1. 库打包差强人意,无法层次分明地区分:第三方库vendor、业务基础库common、基础工具库lib。
  2. 库无法热插拔,每个页面必须同时且顺序加载:vendor、lib、entry。
  3. 存放目录无法自定义。
  4. 每个Webpack是从一个页面进入出发的,需要启动多个Webpack才能满足服务器端和浏览器端两种打包or编译需求。

另外几点:

  1. 最强的部分用不到:抽离css、image这种。
  2. 缺点全让我碰上了。

我也自我反思过了,当初确实是不太了解,没有仔细思考这个工具的定位。在我探索之后,我才明白,这个东西完全不适合我的场景。并不是她不优秀,而是我们真的不合适。

未来之路在哪里

Gulp + rollup or
Gulp + Other ES6 module pack tool
皮皮虾,我们走!