FIS3 VS Webpack

Posted by liveipool on September 15, 2016

FIS3 VS Webpack

伴随着移动互联的大潮,当今越来越多的网站已经从网页模式进化到了 Webapp 模式。它们运行在现代的高级浏览器里,使用 HTML5、 CSS3、 ES6 等更新的技术来开发丰富的功能,网页已经不仅仅是完成浏览的基本需求,并且webapp通常是一个单页面应用,每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的 JavaScript 代码,这给前端开发的流程和资源组织带来了巨大的挑战。

前端开发和其他开发工作的主要区别,首先是前端是基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。

以下是我在阅读学习了FIS3和Webpack相关文档后提取出来的其中一些较核心的或者说我觉得比较重要的内容,用于帮助理解FIS3和Webpack设计的思想。

FIS3

FIS3 是面向前端的工程构建工具。解决前端工程中性能优化、资源加载(异步、同步、按需、预加载、依赖管理、合并、内嵌)、模块化开发、自动化工具、开发规范、代码部署等问题。
FIS3 的构建不会修改源码,而是会通过用户设置,将构建结果输出到指定的目录。
看了一下,感觉FIS3还是非常不错的,虽然学习成本有点高,但思路清晰明了,功能齐全强大,插件也丰富,值得一学。

工作原理

首先来看一下FIS3的工作原理,FIS3 是基于文件对象进行构建的,每个进入 FIS3 的文件都会实例化成一个 File 对象,整个构建过程都对这个对象进行操作完成构建任务。

fis3构建流程.jpg
整个 FIS3 的构建流程大题概括分为三个阶段:

  1. 扫描项目目录拿到文件并初始化出一个文件对象列表。
  2. 对文件对象中每一个文件进行单文件编译。
  3. 获取用户设置的 package 插件,进行打包处理。

当一个文件被实例化为一个 File 对象后,会携带上一些文件基本属性,如 filename、realpath 等等,当这个文件被处理时,FIS3 还会把用户自定义的属性也添加到文件对象上。这样在以后处理某个文件的时候就可以根据它的属性来作为指引。

三种核心的编译能力

让一个问题变得简单最好的方法就是真的让它变得简单

以前的一些前端构建框架,或者在以前一些编程的过程中,我们往往会希望这个框架能够比较复杂,因为我们觉得那样它所能实现的功能也就越多。而实际上,我们应该是去找能解决问题的最小规则集,其它的东西反而让事情变得冗杂。
经过百度 FIS 团队不断实践总结,发现支持前端开发所需要的编译能力只有三种 :

  1. 资源定位:获取任何开发中所使用资源的线上路径;
  2. 内容嵌入:把一个文件的内容(文本)或者 base64 编码(图片)嵌入到另一个文件中;
  3. 依赖声明:在一个文本文件内标记对其他资源的依赖关系;

资源定位

资源定位能力,可以有效地分离开发路径与部署路径之间的关系,工程师不再关心资源部署到线上之后去了哪里,变成了什么名字,这些都可以通过配置来指定。而工程师只需要使用相对路径来定位自己的开发资源即可。这样的好处是:资源可以发布到任何静态资源服务器的任何路径上而不用担心线上运行时找不到它们,而且代码具有很强的可移植性,甚至可以从一个产品线移植到另一个产品线而不用担心线上部署不一致的问题。

内容嵌入

嵌入资源即内容嵌入,可以为工程师提供诸如图片base64嵌入到css、js里,前端模板编译到js文件中,将js、css、html拆分成几个文件最后合并到一起的能力。有了这项能力,可以有效的减少http请求数,提升工程的可维护性。

声明依赖

声明依赖能力为工程师提供了声明依赖关系的编译接口。
FIS3 在执行编译的过程中,会扫描这些编译标记,从而建立一张静态资源关系表,资源关系表详细记录了项目内的静态资源id、发布后的线上路径、资源类型以及依赖关系和资源打包等信息。使用FIS3作为编译工具的项目,可以将这张表提交给后端或者前端框架去运行时,根据组件使用情况来按需加载资源或者资源所在的包,从而提升前端页面运行性能。

一些FIS3的特性

配置文件

默认配置文件为fis-conf.js,FIS3 编译的整个流程都是通过配置来控制的。FIS3 定义了一种类似CSS的配置方式,固化了构建流程,让工程构建变得简单。

文件指纹

文件指纹,唯一标识一个文件。在开启强缓存的情况下,如果文件的 URL 不发生变化,无法刷新浏览器缓存。一般都需要通过一些手段来强刷缓存,一种方式是添加时间戳,每次上线更新文件,给这个资源文件的 URL 添加上时间戳(如:img src=”a.png?t=12012121”)。
而FIS3选择的是添加MD5戳,直接修改文件的URL,而不是在其后添加 query(useHash: true)。

压缩资源

为了减少资源网络传输的大小,通过压缩器对 js、css、图片进行压缩是一直以来前端工程优化的选择。在 FIS3 中这个过程非常简单,设置一个optimizer属性即可。

调试

FIS3 内置一个简易 Web Server,可以方便调试构建结果。FIS3 内置的 Server 是常驻的,如果不重启计算机或者调用命令关闭是不会关闭的。但一般还是用node的服务器啦。

发布到远端机器

当我们开发项目后,需要发布到测试机(联调机),一般可以通过如 SMB、FTP 等上传代码。FIS3 默认支持使用 HTTP 上传代码,首先需要在测试机部署上传接收脚本(或者服务)。但部署到服务器这一部分还不怎么会,到时候再学学。

模块化开发

模块化开发是工程实践的最佳手段,分而治之维护上带来了很大的益处。
说到模块化开发,首先很多人都会想到 AMD、CMD,同时会想到 require.js、sea.js 这样的前端模块化框架。主要给 js 提供模块化开发的支持,之后也增加了对 css、前端模板的支持。这些框架就包含了组件依赖分析、保持加载并保持依赖顺序等功能。
但在 FIS 中,依赖本身在构建过程中就已经分析完成,并记录在静态资源映射表中,那么对于线上运行时,模块化框架就可以省掉依赖分析这个步骤了。
可见,fis对模块化的支持还是不错,以后在进行代码模块化等过程的时候,会比较顺手。

fis.media()

这也是我觉得眼前一亮的地方,media能让我们在不同状态(情形)下给文件分配不同属性。 假设我们有如下需求,当在开发阶段资源都不压缩,但是在上线时做压缩,我们可以在fis-conf.js中可以设置不同的属性,在release代码包时,一个media就能进行选择,而其它一些框架是做不到,或者,要花一定的功夫才能将其实现。

Webpack

Webpack 是当下最热门的前端资源模块化管理和打包工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。
Webpack 是一个模块打包器。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源:

webpack.png

模块化

我们期望的模块系统是怎样的?:可以兼容多种模块风格,尽量可以利用已有的代码。
模块的加载和传输,我们首先能想到两种极端的方式,一种是每个模块文件都单独请求,另一种是把所有模块打包成一个文件然后只请求一次。显而易见,每个模块都发起单独的请求造成了请求次数过多,导致应用启动速度慢;一次请求加载所有模块导致流量浪费、初始化过程慢。这两种方式都不是好的解决方案,它们过于简单粗暴。
分块传输,按需进行懒加载,在实际用到某些模块的时候再增量更新,才是较为合理的模块加载方案。
要实现模块的按需加载,就需要一个对整个代码库中的模块进行静态分析、编译打包的过程。

静态分析

在编译的时候,要对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,然后将不同类型的模块提交给适配的加载器来处理。比如一个用 LESS 写的样式模块,可以先用 LESS 加载器将它转成一个CSS 模块,在通过 CSS 模块把他插入到页面的

并且 所有资源都是模块

在上面的分析过程中,我们提到的模块仅仅是指JavaScript模块文件。然而,在前端开发过程中还涉及到样式、图片、字体、HTML 模板等等众多的资源。这些资源还会以各种方言的形式存在,比如 coffeescript、 less、 sass、众多的模板库、多语言系统(i18n)等等。

Webpack 的一些特点

代码拆分

Webpack 有两种组织模块依赖的方式,同步和异步。异步依赖作为分割点,形成一个新的块。在优化了依赖树后,每一个异步区块都作为一个文件被打包。

Loader

Webpack 本身只能处理原生的 JavaScript 模块,但是 loader 转换器可以将各种类型的资源转换成 JavaScript 模块。这样,任何资源都可以成为 Webpack 可以处理的模块。
通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。

Loader的特性:

  • Loader 可以通过管道方式链式调用,每个 loader 可以把资源转换成任意格式并传递给下一个 loader ,但是最后一个 loader 必须返回 JavaScript。
  • Loader 可以同步或异步执行。
  • Loader 运行在 node.js 环境中,所以可以做任何可能的事情。
  • Loader 可以接受参数,以此来传递配置项给 loader。
  • Loader 可以通过文件扩展名(或正则表达式)绑定给不同类型的文件。
  • Loader 可以通过 npm 发布和安装。
  • 除了通过 package.json 的 main 指定,通常的模块也可以导出一个 loader 来使用。
  • Loader 可以访问配置。
  • 插件可以让 loader 拥有更多特性。
  • Loader 可以分发出附加的任意文件。
  • Loader 本身也是运行在 node.js 环境中的 JavaScript 模块,它通常会返回一个函数。大多数情况下,我们通过 npm 来管理 loader,但是你也可以在项目中自己写 loader 模块。

智能解析

Webpack 有一个智能解析器,几乎可以处理任何第三方库,无论它们的模块形式是 CommonJS、 AMD 还是普通的 JS 文件。甚至在加载依赖的时候,允许使用动态表达式 require(“./templates/” + name + “.jade”)。

插件系统

Webpack 还有一个功能丰富的插件系统。大多数内容功能都是基于这个插件系统运行的,还可以开发和使用开源的 Webpack 插件,来满足各式各样的需求。

快速运行

Webpack 使用异步 I/O 和多级缓存提高运行效率,这使得 Webpack 能够以令人难以置信的速度快速增量编译。

配置文件

Webpack 在执行的时候,除了在命令行传入参数,还可以通过指定的配置文件来执行。默认情况下,会搜索当前目录的 webpack.config.js 文件,这个文件是一个 node.js 模块,返回一个 json 格式的配置信息对象,或者通过 –config 选项来指定配置文件。

故障处理

Webpack 的配置比较复杂,很容出现错误,一般情况下,webpack 如果出问题,会打印一些简单的错误信息,比如模块没有找到。我们可以通过参数 –display-error-details 来打印错误详情。

CommonJS 和 AMD

模块为什么重要?因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。
但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!考虑到Javascript模块现在还没有官方规范,这一点就更重要了。
目前,通行的Javascript模块规范共有两种:CommonJS和AMD。而因为Webpack和这两种模块规范联系非常紧密,在这里简单提一下。

CommonJS

2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。这标志”Javascript模块化编程”正式诞生。因为浏览器端,代码是否模块化可能只有简单的差别,但在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。
并且要记住的是:CommonJS 是同步加载模块

AMD

AMD(Asynchronous Module Definition)为浏览器环境设计的,因为 CommonJS 模块系统是同步加载的,当前浏览器环境还没有准备好同步加载模块的条件,AMD 定义了一套 JavaScript 模块依赖异步加载标准,来解决同步加载的问题。

FIS3 和 Webpack的对比

确实,严格的说,拿 FIS3 和 Webpack 直接作比较不是特别恰当,他们虽然都能实现一部分相同的如打包的工作,但这两个工具还是有很大的区别的。
如果把FIS3比作一个糖果工厂里的整个流水线,从最初的原料开始,到接下来多种原料按照比例混合,再将混合后的原料定型成糖果状,接下来,再对糖果用包装纸进行包装,最后,将糖果装进一个个盒子里运送给市场进行销售,这整个过程中,FIS3都是一直参与并作为主导力量,控制着整个流程。
而Webpack呢,它可能只参与了对糖果进行包装的过程,或者最多再参与一些原料混合的部分。
FIS3是一个前端工程的构建工具,而Webpack是一个资源模块化管理和打包工具

FIS3可以自成体系,使用它就有了一切开发一个项目所需要的条件,从0开始,构建一个基础包,再在它内置的服务器上进行调试,运行,最后release到需要到地方,连接服务器上线,有了它一切OK,我们只需要改动一下配置文件,并添加进我们的html、css、js代码即可。 而Webpack,从0开始构建项目不是它的强项,我们往往是在项目中安装Webpack并用它来进行一些工作。它虽然也有如webpack-dev-server等的开发服务,但很少有人这么干。它和node.js有着良好的依附,所以我们往往采取用node来搭建服务器的策略。如果我们把文件代码、Webpack、服务器等看成开发中的一个个大的版块的话,它们就是一个个平等的版块,互相帮助,互相依附组成完整的项目。
而FIS3工具可以看成包括装了所有版块的一个大箱子,无论里面的版块如何变化,FIS3始终是装着所有东西的那个箱子。可见,从这方面来看,FIS3和Webpack是有很大区别。

Webpack最核心的思想就是模块化,而FIS3并没有太过于强调这一点。Webpack将所有的资源都转换成JS模块,并对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,最后按照要求进行打包,这也使得Webpack的打包工作做得精致并且高效。
而模块化显然不是FIS3百度团队的工作人员们最关心的话题,他们关心的是整个构建流程如何更加流畅、合理,而要细致到流程中如打包环节等某个具体步骤,他们没有做得像Webpack那么精致。当然,FIS3也是可以实现模块化的,但它最后实现的效果可能不及Webpack那么好。就好比一家完全独立的汽车制造公司,整辆汽车的所有制作流程、材料都由它们一家公司提供,它们可能更关心的是最后制作出来的整个汽车的效果,整体感觉、整体性能,而如果你要把这辆汽车拆分来看,并且去研究它的轮胎,质量要想比专门制作轮胎的公司更好,基本不可能。

最后,从通过阅读学习这两个工具的相关文档来说,感觉这两个工具都是非常不错,非常有学习价值的。它们各有各的好处,都能很好的完成它们该做的工作。由此看来,无论是FIS3还是Webpack,用来做项目都是比较不错的选择。

赞赏码.jpeg