[杂谈] 模块化架构视图分块与视图嵌套的选择Mon Sep 11 2017

目前前端大部分的项目都采用模块化架构的方案,如今,模块化已经成为真正的主流。市面上的模块化方案有很多种, 比如 requirejs , webpack,browserify, seajs等等等,当然,大公司的项目不一定都是用这些方案的。都有自己内部的框架,内部的强人研发的适合本公司业务体系的技术架构.


模块化方案的对比

讨论前先说明一点,这里仅仅只比较 requirejs 和 webpack, seajs这种KPI不靠谱的产物(已经许久不维护了,更新估计只是内部填坑更新) ,我们就先不做讨论了. 这里我们详细对比webpack和requirejs


RequireJS

非常了不起的模块化先驱,也可以说的上是前端模块化的鼻祖级框架,也正是RequireJS将模块化规范引入到了前端开发的流程和概念中。

RequireJs 解决了什么问题?

早期的web开发还没有按需加载的概念,一个页面,对应一个或者多个js,公共方法的调用通常是在全局window下创建一个对象,在里面编写共用方法,这样做既不安全,到后期也难以维护. 没有规范化的模块管理机制导致了页面很多无用的脚本.

RequireJs 正是为了解决这些问题而诞生的, 它遵循AMD规范. 先说优点:

  • 浏览器执行解析,分析依赖,无需提前编译和预处理,所见即所得
  • 在脚本文件未压缩的情况下非常容易定位到错误, 容易维护, 完全无需生成sourcemap
  • 集成了rjs 打包工具,配合自动化工具容易做到发版发布
  • 配置简单,5分钟即可上手

当然,也有缺点:

  • 不安全,在全局暴露了 require 和 requirejs 两个全局对象
  • 打包时没有对模块名称进行加密,导致很容易被全局require对象导出(需要后期处理)
  • 前端执行解析依赖存在一定的消耗,速度一般
  • 使用Uglify 压缩不够彻底.


Webpack

近两年兴起对模块化解决方案,在1.0时期还是挺蛋疼的,直到3.0我在开始真正玩它, 这个时候的webpack基本上已经很稳定了, loader也是比较靠谱,属于后起之秀,非常棒的模块化解决方案,与requirejs的思路不同,webpack对打包的概念更加笼统,也却是更加正确的,我认同的一种打包思路。 很多人认为,使用webpack是因为整合babel一起用来写ES6,其实搞错了,用requirejs一样能写ES6。

Webpack 解决了什么问题

Webpack 的优点:

  • 可以打包AMD,CommonJS,ES6 Import 范式的模块化文件,支持广泛
  • 打包出来的文件已经经过了预处理,定义好了依赖关系,完全无需在浏览器端执行解析消耗
  • 安全性大大提升,全局只有一个webpackJsonp 入口,且模块名,模块顺序加密
  • 打包压缩更加彻底,由于全闭包贮存,性能比 RequireJs 更加优越, 压缩更加彻底
  • 优秀的loader中间件生态,基本上你想得到的loader都有
  • 纯天然对Babel进行整合,使用ES6来开发项目,爽的不要不要的

缺点:

  • 基本配置简单,但打包概念对于新手来说晦涩难懂, 复杂的配置更是需要精心的分块设计
  • 开始时虽然可以配合devServer做热部署开发,也能生成sourcemap进行错误定位,但是如果一旦上了Vue全家桶或者React全家桶,模板错误的追踪就变得极其困难,难以调试. 而且报错信息不准确
  • 打包成多页面需要更多额外的配置,后期不是太好集成自动化工具.


可以说这两种模块化的方案我都有过成熟的项目实践,对比起来,其实我更加倾向于webpack,但是又不得说,requirejs也是非常优秀的,至少概念上通俗易懂,更容易上手,更容易被接受一些。


视图层

如今React和Vue已经很流行了,当然,经过实践的检验,我认为这两种视图都只适合局部使用,不适合直接上全家桶全单页面, 甚至于全站采用(更加不靠谱),踩过了坑,收集了一大波经验的同时,也是经历了不少痛苦开发的过程,经历了组件的封装,到视图的拼装。不得不对业务逻辑进行简化。

两种vdom的视图框架都带来了状态的概念,以至于延伸到了状态管理,父子组件通信等等等的概念,视图层的状态管理其实在早期的MVC软件开发架构中就已经被提及了,在许多场景下,通过状态,数据的改变来触发视图的更新,是非常正确的做法,但是,只是正确,仅此而已.


视图嵌套

视图的嵌套可以表示成以下这种形式

 [ 主视图  [子视图1 [子视图2 [ 子视图3 [  ... [ ... ] ] ] ] ] ]

这种架构可以参照 知乎的React视图结构

从上到下, 层层嵌套, 这样的结构有一个问题在于,一旦功能复杂度很高,一个视图的状态特别多的情况下,则会导致了状态难以管理,同时,父子组件通信直接传递是难以维护的,只能通过注入Redux之类的Big Object来管理。 再者,如果这种结构存在于单页面应用,则是有router和状态存储等等一堆乱七八糟的问题。这也就是为什么很多大型项目都没有采用这种视图嵌套的结构.

也许,本来一个简单的静态表单页面,使用视图嵌套之后就变得极其复杂,完全没有必要而且增加了自己的工作量,当然后期维护就更加不用说了。接手的那个人必定会非常蛋疼,写过的人应该心里很清楚.


视图分块

视图的分块可以表示成以下这种形式

[ 主视图 ] - [ 视图1 ] - [ 视图2 ] - [ 视图 3 ]

当前页面下,视图1和视图2和视图3之间不存在半毛钱关系(结构,单项数据,内部操作逻辑), 只需要单纯的挂在固定根结点即可, 状态的传递可以通过某些介质,例如Backbone的Model, Ax的Model, Redux 中存储,或者是相互emit 视图之间的事件即可。这样的做法的好处在于,状态单一,视图容易管理,且视图逻辑之间互不干扰。

当然之后的项目,我们还是会回到视图分块的路上,并且抛弃视图嵌套的做法。关于Vue和React组件,只要是在body下生成子节点的视图模块我们会使用jQuery或者原生来编写外置的DOM逻辑。从而达到不破坏根视图的效果。具体做法可以参照国内的 Element UI(饿了么UI)

另外,也会根据具体的场景,来分析到底使用React & Vue的视图,还是Ax的视图模版,或者HTML静态+jQuery来实现。