switer / switer.github.io

Personal homepage
https://switer.github.io
5 stars 0 forks source link

腾讯视频前端工程优化之路 — 模块化与构建 #30

Closed switer closed 8 years ago

switer commented 8 years ago

模块化是前端工程中最需要,同时也是最重要的一点。关于使用模块化的好处就不多说了,个人觉得分治是模块化最重要的一个优点。

在腾讯视频接触到的老项目,所使用的模块化解决方案也是挺传统,主要分两种:

  1. 全局命名空间挂载模块
  2. seajs实现模块化

方案1使用命名空间来挂载模块的方式是出于历史包袱的原因;方案2做了模块化的改善。但我们希望更便捷地,就如node.js一样,使用 commonjs 的规范来写模块代码,通过npm安装第三方依赖。

模块代码构建

目前是前端工具大爆发的时期,解决模块化问题的工具也是层出不穷,所以我们可以使用更强大与便捷的工具,来构建模块化的代码,比如大名鼎鼎的 webpack

选用 webpack 原因,第一点在于它的高可定制性方面,个人觉得是在 FIS 之后,可定制性最高的模块化构建工具;第二点是它完善的插件生态,就如gulp/grunt的生态一般旺盛。

由于文章的主题并非 《深入浅出 webpack 》,所以更多关于 webpack 的知识点就不赘述,只是想在此点出:

“我们使用了 webpack 来解决模块依赖与合并问题

系统性构建

webpack 能自然地处理 JS 模块依赖关系与合并问题,但是前端开发中,并不是只有 JS,还有 HTML ,甚至其它静态资源,例如注入资源引用地址到 HTML 中,例如内联资源到 HTML 中。webpack 处理 JS或其它资源 与 HTML 的关系时候,就有点力不从心(纯粹派也是可以将webpack掰弯的)

好的,那就再引入一个更灵活的“胶水”构建工具:gulp

为什么用gulp?为什么不用grunt?

这里数一下gulp的好处:

选用 Gulp 的原因就如上面所列举的,它是一个优秀的任务管理工具。

在结合使用 Gulp 之后,webpack 的职责也是明确的:“负责 JS/CSS 的模块化构建”, 这样通过gulp,我们也就能灵活地配置 webpack 构建的下游任务与工程中其它资源的构建任务。

到此,我们是按照以下流程去构建:

1=> 开始输入:

2=> 启动gulp任务:执行webpack等一系列流程 .......................................

3=> 最后输出:

JS: 已合并/已压缩/添加MD5 ...
CSS: 已合并/已压缩/添加MD5 ...
HTML: 已注入JS/CSS资源依赖路径

通用化集成工具

大多数项目的构建流程是相同的,就算存在差异,那么大部分流程也是相似的,简单描述就类似于:

JS的入口路径 => 构建逻辑 => 输出SourceMap / 压缩代码 / 其它依赖的静态资源

输入很简单,就是一些入口文件路径,但要从输入到吐出指定内容,我们需要不少工作量去处理输出逻辑,特别是开发阶段生产发布的输出结果不一致的情况下特为明显。

为此,我们通过约定,规范工程的目录结构与输出结果,将构建任务通用化,最终把webpack + gulp 的通用流程逻辑,抽象为一个集成的构建工具:vfe

当我们再次使用的时候,只需要一些简单的配置即可得到目标结果,中间逻辑封装在vfe中。

对使用vfe的项目,我们约定的目录结构是这样的:

./
|___/c
| |
| |___/header
| |
| |___header.css
| |
| |___header.js
| |
| |___header.tpl
| |
| |___/images
| |___icon.png
| 
|___gulpfile.js
|___/views
|___index.js
|
|___index.html

/views 存在JS/HTML入口文件,/c是业务模块的根目录,从目录结构上来看,会发现 header.js / header.tpl / header.css 这些文件全挂在一个同名的目录下。嗯,我们在组件模块相关的资源从文件结构上内聚在一起了。因此 vfe 定义了一些方便开发的模块引用规则,例如:

require('header')

这样的规则相当于:

require('path/to/c/header/header.js')
require('path/to/c/header/header.css')

甚至是二级目录:

./components
|
|___/header
|
|___header.css
|
|___header.js

在任何模块内都可以这样引用,而不需要冗长的相对路径:

require('components/header')

自定规则会与commonjs的规范产生冲突,这样模块迁移到其它非使用 vfe 构建的项目中是需要修改的引用路径的。vfe 也是可以定制的,我们使用的时候也通过简单的配置可以去掉这些自定义规则:

vfe({
rule: false,
....
})

工程模版化

在做完大一统的通用化集成后,那么就需要面对架构选型的差异化问题,不同的前端架构对应的工程结构输出结果会存在差异,所以我们又把使用于不同业务场景的工程给模版化了。

大概意思就是,“通过一句命令,生成适用于不同业务场景的工程模版”,使用方式是这样:

vfe init [template]

到目前为止,所积累适用于不同业务类型的模版,如下所示:

使用vfe生成的工程模版后,我们只需要使用三条命令即可完成从初始化到发布的构建任务:

3e55ea01fcd3e2ea5c72f256d0835aa91460473041

工程模版化有效地降低了启动新项目的初始化工作,也为细化的场景定制提供了更大的灵活度,同时也减少了维护构建脚本的工作量,团队成员不用面对接手过来的形态各异的构建脚本,excited !

原创文章,转载请知会