对前端工程化的参考资料:https://github.com/fouber/blo...
前端的工程化构建开发不只仅停留在‘压缩’、‘校验’、‘合并’,模块化构建可使项目的扩展性、代码的复用性和可维护性大大提升。在解决基本的开发效率和运行效率以后,前端团队要思考维护效率。模块化是当前前端最流行的开发手段。
模块化是一种处理复杂系统分解为更好的管理模块的方式。它能够经过不一样的组件设定不一样的功能,把一个问题分解成多个小的独立的、相互做用的组件。感受就像是在拼图。
模块化的好处:
提升代码的重用率
提升开发效率、减小沟通成本
下降耦合
更好实现代码的快速迭代
便于代码的维护javascript
css模块化开发基本都是在less、sass、stylus等预处理器的import/mixin特性支持下实现的。css
js模块化方案有AMD/CommonJS/ES6 Module等
使用webpack模块的各类方式的依赖关系:
一、ES2015 import语句
二、CommonJS require()语句
三、AMD define 和 require语句
四、css/sass/less文件的 @import 语句
五、样式 (url(...)) 或 HTML文件(<img src=...>) 中的图片连接(image url)
webpack的优点
一、支持CommonJS 和 AMD 模块。
二、支持模块加载器和插件机制,可对模块灵活定制。babel-loader支持ES6
三、能够经过配置,打包成多个文件。有效的利用浏览器的缓存。
四、将样式文件和图片等静态资源视为模块进行打包。配合loader加载器,对资源进行处理。前端
1、原始写法
模块是实现特定功能的一组方法。java
function m1(){ //'''' } function m2(){ //'''' }
上面的函数m1()和m2(),组成一个模块。使用的时候,直接调用就ok了。
这种作法的缺点就是污染了全局变量,没法保证不与其余模块发生变量名冲突,并且模块成员之间看不出直接联系。
2、对象的写法
将模块写成一个对象,全部的模块成员都在这个对象里。node
var module = new Object({ _count : 0, m1: function(){ console.log(this._count) }, m2:function(){ console.log(this) } }) module.m1() module._count = 5 module.m2()
上面的函数m1()和m2(),都封装在module对象里。使用的时候直接调用这个对象的属性。jquery
module.m1(); 输出:0
可是这样的写法会暴露全部的模块成员,内部状态会被外部状态改写,好比:webpack
module._count = 5 module.m1() 输出:5
3、当即执行函数
使用当即执行函数能够达到不暴露私有成员的目的。git
var module_1 = (function(){ var _count = 1; var m1 = function(){ console.log(_count) }; var m2 = function(){ console.log(this) }; return { m1:m1, m2:m2 } })(); module_1.m1() module_1.m2()
使用上面的写法,外部代码没法读取内部的_count变量。github
JavaScript模块规范共有两种:CommonJS 和 AMD。
为何要有模块?
有了模块,咱们就能够方便的使用别人的代码,想要什么功能,就加载什么模块。
1、CommonJS
用于服务端模块化编程:
一个文件就是一个模块,require方法用来加载模块,该方法读取一个文件并执行,最后返回文件内部的module.exports对象;
require是默认读取 .js 文件,因此require(模块名)能够不写后缀;
同步加载,因为服务端加载的模块通常在本地,因此能够这样;可是客户端若是一个模块过大就会致使页面“假死”;
。
node.js的项目,将JavaScript语言用于服务器编程,在浏览器环境下,没有模块也是能够的。但在服务器端,必定要有模块,与操做系统和其余应用程序互动,否侧就无法编程。
node.js的模块系统,就是参考CommonJS规范实现的。在CommonJS中,有一个全局性的方法require(),用于加载模块。假定有一个数学math.js,就能够像下面这样加载。web
var math = require('math');
而后,就能够调用模块提供的方法;
var math = require('math'); math.add(2,3); //5
module.exports属性表示当前模块对外输出的接口,其余模块文件加载该模块,实际上就是读取module.exports变量;为了方便用exports,exports指向module.exports;即exports = module.exports = {};
exports.xxx至关于在导出的对象上添加属性,该属性对调用模块可见;
exports = 至关于给exports从新赋值,这样就切断了和module.exports的关联,调用模块就不能访问exports的对象及其属性。
在浏览器环境:
CommonJS不使用于浏览器环境。若是在浏览器中运行,会有一个大问题;
var math = require('math'); math.add(2,3)
在第二行math.add(2,3),在第一行require('math')以后运行,所以必须等math.js加载完毕。若是加载时间很长,整个应用就会停在这里。
这对服务器不是问题,由于全部的模块都存在本地硬盘,能够同步加载完成,等待时间就是硬盘的读取时间。可是,在浏览器,由于模块都在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于“假死”状态。
所以,浏览器端的模块,不能采用“同步加载”,只能采用“异步加载”。这就是AMD的产生的背景。
2、AMD
AMD是“Asynchronous Module Definition”的缩写,意思是“异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面的语句的运行。全部依赖这个模块的语句,都定义在一个回调函数中,等到加载完成以后,这个回到函数才会运行。
AMD也采用require()语句加载模块,可是不一样于CommonJS,它要求两个参数,
require([module],callback);
第一个参数[module],是一个数组,里面的成员就是要加载的模块,第二个参数callback,则是加载成功以后的回调函数。若是将前面的代码改写成AMD形式,就是下面的形式:
require(['math'],function(math){ math.add(2,3); })
math.add()与math模块加载不是同步的,浏览器不会发生‘假死’。因此AMD比较适合浏览器环境。
3、require.js的用法:
require.js(前端模块化管理的工具库)实现js文件的异步加载,避免网页失去响应;管理模块之间的依赖性,便于代码的编写和维护。
主模块的写法:
main.js称为是“主模块”,意思是整个网页的入口代码。全部的代码都从这开始运行。主模块依赖于其余模块,这时就使用AMD规范定义的require()函数。
//main.js require(['moduleA','moduleB','moduleC'],function(moduleA, moduleB, moduleC){ //some code here }); require()函数接受两个参数。第一个参数是一个数组,表示所依赖的模块, 上例就是['moduleA','moduleB','moduleC'],即主模块依赖这三个模块; 第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。 加载的模块会以参数形式传入该函数,从而在回调函数内部就可使用这些模块。 require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖的问题。
假定主模块依赖jquery、underscore和backbone三个模块,main.js就能够这样写:
require(['jquery','underscore','backbone'],function($,_,Backbone){
//some code here }) requery.js是先加载jQuery、underscore和backbone,而后在运行回调函数。主模块的代码就写在回调函数中。
4、模块的加载
主模块的依赖模块是['jquery','underscore','backbone']。默认状况下,require.js假定这三个模块于main.js在同一个目录,文件名分别是jquery.js、
underscore和backbone.js,而后自动加载。
使用require.config()方法,咱们能够对模块的加载行为进行自定义。
require.config()就写在主模块(main.js)的头部。参数就是一个对象,
这个对象的paths属性指定各个模块的加载路径。
require.config({ paths:{ "jquery":"jquery.min", "underscore":"underscore.min", "backbone":"backbone.min" } }); 上面的代码路径默认与main.js在同一个目录(js子目录)。若是这些模块在 其余目录,好比js/lib目录,则有两种写法: 一种是逐一指定路径。 require.config({ paths:{ "jquery":"lib/jquery.min", "underscore":"lib/underscore.min", "backbone":"lib/backbone.min" } }); 另外一种则是直接改变基目录(baseUrl) require.config({ baseUrl:"js/lib", paths:{ "jquery":"jquery.min", "underscore":"underscore.min", "backbone":"backbone.min" } }); 若是某个模块在另外一台主机上,也能够直接指定它的网址,好比, require.config({ path:{ "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min" } }) requiry.js要求,每个模块是一个单独的js文件。这样的话,若是加载多个 模块,就会发出屡次HTTP请求,会影响网页的加载速度。所以,require.js提供一个优化工具, 当模块部署完毕之后,能够用这个工具将多个模块合并在一个文件中,减小http请求。