前端模块化和构建工具
Posted H5前端开发大全
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端模块化和构建工具相关的知识,希望对你有一定的参考价值。
以前一直对前端构建工具的理解不深,经过几天的研究特意来总结一下,第一次写博客,有写错的请多多见谅,该文章我也从其他博客拷了一些内容,如果有冒犯之处,请指出。
如今,网页不再是自己编写各种功能了,而是如何把各种不同功能的模块组合起来。
以前使用script引用不但增加网络请求,还会时页面更加臃肿,为了解决这类问题,前端的模块管理应运而生,最早提出解决方法的是AMD,其实践方案是requireJs和curlJs.它不仅实现了js文件的异步加载,还管理了模块之间的倚赖性,(类似于面向对象的Module模式)
requireJs:
main就是主模块,省略了.js后缀,在main.js里面可以配置模块的加载,在其头部写require.config
在实际应用中,往往还需要在服务端,将所有模块合并后,再统一加载,这多出了很多工作量
AMD 通过将模块的实现代码包在匿名函数(即AMD 的工厂方法,factory)中实现作用域的隔离,通过文件路径作为天然的模块ID 实现命名空间的控制,将模块的工厂方法作为参数传入全局的define(由模块加载器事先定义),使得工厂方法的执行时机可控,也就变相模拟出了同步的局部require,因而AMD 的模块可以不经转换地直接在浏览器中执行。 因此,在开发时,AMD 的模块可以直接以原文件的形式在浏览器中加载执行并调试,这也成为RequireJS 方案不多的优点之一。
注意:
然而基于AMD 规范的非javascript 资源加载有着本质的如下缺陷。
1.加载与构建的分离导致plugin 需要分别实现两套逻辑。
2.在构建产物,r.js构建的结果是define(function(){...})的几何,文件的执行倚赖页面上事先引入一个amd模块加载器(如requireJs自身),所以常见的AMD项目线上页面往往存在两个Javascript文件:loader.js及bundle.js。而browserify和webpack的构建结果 是可以执行的js代码,它们都支持通过配置生成符合格式的结果文件,如以umd形式暴露 库的exports,以便其他页面代码调用。后者的这种形式更符合js库的构建
3.浏览器的安全策略决定了绝大多数需要读取文本内容进行解析的静态资源无法被跨域加载(即使是JavaScript 模块本身,也要依靠define 方法包裹,类似于JSONP 原理实现的跨域加载)。
Browserify
其本事不是模块管理器,只是让CommondJs格式的模块可以运行在浏览器端,这意味着它可以使用nodeJs的npm模块管理器。所以,实际上,它等于间接为浏览器提供了npm的功能
上面这个代码使用的是CommondJs格式,无法在浏览器中运行,这是需要用到browserify,将上面代码编译为浏览器脚本
注意:(1). requirejs/seajs: 是一种在线“编译”模块的方案,相当于在页面上加载一个CommonJS/AMD模块格式解释器。这样浏览器就认识了define, exports,module这些东西,也就实现了模块化。
(2).browserify/webpack:是一个预编译模块打包的方案,相比于第一种方案,这个方案更加智能。由于是预编译的,不需要在浏览器中加载解释器。你在本地直接写JS,不管是AMD/CMD/ES6风格的模块化,它都能认识,并且编译成浏览器认识的JS。注意: browerify打包器本身只支持Commonjs模块,如果要打包AMD模块,则需要另外的plugin来实现AMD到CMD的转换!!
browserify不支持异步加载,非js文件加载(img src打包路径问题)
这里插入一下es6和CommonJs的区别:
* 运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”* *
编译时加载: ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,输入时采用静态命令的形式。即在输入时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”
正如我们在前面提到的define 函数的作用,没有define 函数的CommonJS 模块是无法直接在浏览器中执行的——浏览器环境中无法实现同Node.js 环境一样同步的require 方法。同样也因为没有define 与工厂方法,CommonJS 模块书写起来要更简单、干净。在这个显而易见的好处下,越来越多的前端项目开始采用CommonJS 规范的模块书写方式。
Gulp/grunt
Gulp / Grunt 是一种工具,能够优化前端工作流程。比如自动刷新页面、combo、压缩css、js、编译less等等。简单来说,就是使用Gulp/Grunt,然后配置你需要的插件,就可以把以前需要手工做的事情让它帮你做了。
gulp相比grunt简便,借助unix的流操作思想,通过管道pipe来管理task任务,实现前端的优化,例如把Js和css融合在一起并压缩
webpack只是在Loader里面加载的时候,帮我们默认实现了操作,没有gulp操作得透明,gulp只是构建工具,所以如果开发SPA单页面应用的时候,需要用到模块化开发,这时就需要用到模块化的概念,如果想用CommondJs风格,就需要借助browserify来作为转换工作,也许你会发现在gulp使用时就有require('gulp')这样的引用风格,这是npm包管理的模块,是CommondJs的node的风格,是编写gulp这些构建工具时用的,不是我们编写自己的前端模块代码的。所以使不使用模块方案要看项目工程是否庞大决定,如果简单,做个多页面应用,不需要把js或各种资源打包处理,只需要简单的合并,压缩,在页面中引用就好,那就不需要Browserify,webpack。用gulp就够了。使用es6模块规范时,通过babel转以后形成commondJs这样的代码,一般还不能使用,因为浏览器不识别commondJs,所以需要browserify或者webpack将其打包到一个js文件中。
但是相比于npm , grunt和gulp缺点是相当倚赖插件的开发,如果出现新技术时,需要相关人开发插件,如gulp-eslint,当使用时,需要在gulp-eslint文档和eslint文档来回切换,不得不在插件和所抽象的工具之间来回切换上下文。相比于npm这个更大的插件社区,缺点教为明显
构建工具完成的目标基本是:
fis
fis也属于前端构建工具,它的本质是基于静态资源标记和动态解析静态资源表,在模版,js里面使用特殊的标记方法引用前端资源,构建的时候生成一张资源倚赖表,浏览器或者后端模版语言在解析的过程中通过查表得到某个静态资源在不同环境下的引用路径,所以不管是纯前端渲染还是服务端渲染,都容易得到支持,
fis其实是一种静态资源表的思想,在这种思想下,对于php的smarty产生了fis-plus,基于java的便是 jello,
后来fis3都将其纳入了其中
fis3的功能和webpack类似,其优点是静态资源表
fis3设计之初是用在网页构建上,所以后端的文件操作之类不适用,fis3的模块化开发主要是AMD,CMD和自定义扩展commonJs的modJs,三个分别对应于三个插件来实现,由于自己葛璐开发了一种模式,所以导致贡献也少了,fis可以做到完美的解决方案还需要后端的配合,所以也使得其不了了之。
对于最大boss ---------webpack
webpack比较黑盒,用起来很方便,十分适合开发SPA项目,但是却不适合服务端渲染,webpack目前的功能已经非常齐全,也相应的会带来首次加载,打包时间慢,需要对其打包进行优化
它既吸取了大量已有方案的优点与教训,也解决了很多前端开发过程中已存在的痛点,如代码的拆分与异步加载、对非JavaScript 资源的支持等。强大的loader 设计使得它更像是一个构建平台,而不只是一个打包工具。
考虑到AMD 规范与CommonJS 规范各有各的优点,且都有着可观的使用率,webpack 同时支持这两种模块格式,甚至支持二者混用。而且通过使用loader,webpack也可以支持ES6 module(这一特性在即将到来的webpack 2 中原生支持),可以说覆盖了现有的所有主流的 JavaScript 模块化方案。通过特定的插件实现 shim 后,在webpack 中,甚至可以把以最传统全局变量形式暴露的库当作模块require 进来。
webpack.config.js实在node.js中运行的,因此不支持es6的import语法
其实webpack是个大boss,我就不在本文细说了,网上对webpack的使用已经有很多案例了。
感谢你的捧场,有什么错误可以指出,大家多多探讨...
***未完待续***
以上是关于前端模块化和构建工具的主要内容,如果未能解决你的问题,请参考以下文章