探索javascript----浅析js模块化
引言:
鸭子类型:
面向对象的编程思想里,有一个有趣的概念,叫鸭子类型:
“一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那它就可以被当做鸭子。也就是说,它不关注对象的类型,而是关注对象具有的行为(方法)”----面向接口的编程
编程思想还讲求单一原则,也就是要解耦,所以我们希望我们编写程序功能的时候,具有单一职责、和面向接口的特点。
模块化其实也是这种思想,我们赋予模块鲜明特点的功能(如jquery就是dom操作的能手),并把它们可使用的方法属性(就是一种接口)暴露出来,当然,从这个角度来说js模块化开发的引入是有点偏颇了,其实框架的出现都是为了解决一类问题产生的,
在前端开发中,众多的script标签和他们之间的依赖关系复杂化,以及全局变量的增加,使得人工的维护变得困难起来,常犯错误。
-----------------------------------------------
模块写法的演进:
多函数分散写法 --> 对象单体的写法 --> 匿名函数立即返回 --> 传入其他模块实现继承,var m1=(function(m1){ ...;return m1 })(window.module1||{})
(全局变量的污染) (暴露成员易被外部修改)
-----------------------------------------------------
规范:
commonJS
commonJS应用于服务端,服务端加载是和硬盘通信,可以轻易实现同步,只需像require,然后直接使用,但浏览器则会造成长时间等待,故适合异步加载。
在客户端:
将多引入回调参数。
AMD,(Asynchronous Module Definition)客户端异步模块定义,是一种requireJS推广过程产生的规范。
CMD, (Common Module Definition)通用模块定义,是一种seaJS推广过程产生的规范。
---------------------------------------------------------------------------------------------------------------------------------------------
解决思路:
全局变量的消除 --- 按一定规则定义的模块 --- 把方法封入define的方法体内:define([id],dependencies,factory);
依赖关系的实现 --- 延迟和按需加载
具体实现:
<script src="js/require.js" data-main="js/main" defer async="true"></script>后两个属性可以实现放在头部但是在页面最后加载 这个main就是加载完require后首先加载和运行的文件,相当于c语言里的main函数
调用环节:
require.config({
baseUrl: "js/lib",//公共路径
paths: {
"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min",//支持在线地址
"underscore": "underscore.min",
"backbone": "backbone.min"
}
});
require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){
//注意:模块和参数要一一对应
});
定义环节:
define(['myLib'], function(myLib){ //用一个数组生明依赖
function foo(){
myLib.doSomething();
}
return { //要返回
foo : foo
};
});
如jquery的末尾有这样的语句:
if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
define( "jquery", [], function () { return jQuery; } );//第一个参数是模块id,为可选参数
}
如果一个库没有通过规则定义,则加载的时候可以通过require.config({});参数对象里添加一个属性:
require.config({
shim: { //如果一个模块 'underscore':{
exports: '_'
}, 'backbone': {
deps: ['underscore', 'jquery'],//生明依赖
exports: 'Backbone'//对外表明身份
} }
});
---------------------------------------------------------------------------------------------------
seaJS
上文require.js有以下特点:对于依赖的的模块,它是提前执行的(虽然从requireJS2.0开始改为延迟执行),而且必须把依赖前置。
而seaJS推崇as lazy as possible,且依赖就近。
define(function(require, exports, module) { //提供三个参数,require实现就近依赖,expotrs实现对外接口,module
var a = require('./a')
a.doSomething()
// 此处略去 100 行
var b = require('./b') // 依赖可以就近书写
b.doSomething()
// ...
})
http://seajs.org/docs/#docs
使用简要:
引入-配置-入口
<script src="../sea-modules/seajs/seajs/2.2.0/sea.js"></script>
seajs.config({ //配置
base: "../sea-modules/", //设定统一目录
alias: { //设定别名
"jquery": "jquery/jquery/1.10.1/jquery.js"
}
});
seajs.use("examples/hello/1.0.0/main");//入口
main模块:
define(function(require) { var Spinning = require('./spinning'); var s = new Spinning('#container');
s.render(); });
spinning模块:
define(function(require, exports, module) { var $ = require('jquery'); function Spinning(container) {
this.container = $(container);
this.icons = this.container.children();
this.spinnings = [];
} module.exports = Spinning; Spinning.prototype.render = function() {
this._init();
this.container.css('background', 'none');
this.icons.show();
this._spin();
} Spinning.prototype._init = function() {
var spinnings = this.spinnings; $(this.icons).each(function(n) {
var startDeg = random(360);
var node = $(this);
var timer; node.css({
top: random(40),
left: n * 50 + random(10),
zIndex: 1000
}).hover(
function() {
node.fadeTo(250, 1)
.css('zIndex', 1001)
.css('transform', 'rotate(0deg)'); },
function() {
node.fadeTo(250, .6).css('zIndex', 1000);
timer && clearTimeout(timer);
timer = setTimeout(spin, Math.ceil(random(10000)));
}
); function spin() {
node.css('transform', 'rotate(' + startDeg + 'deg)');
} spinnings[n] = spin;
}) return this;
} Spinning.prototype._spin = function() { $(this.spinnings).each(function(i, fn) {
setTimeout(fn, Math.ceil(random(3000)));
}); return this;
} function random(x) { return Math.random() * x }; });
具体文档:
https://github.com/seajs/seajs/issues/266
*exports
仅仅是 module.exports
的一个引用。在 factory
内部给 exports
重新赋值时,并不会改变 module.exports
的值。因此给 exports
赋值是无效的,不能用来更改模块接口。
好比:
var a={x:1};b=a;b={x:2};console.log(a);//->{x:1} b和a指向关系以及断裂
------------------------------------------------------------------------------------------------------------------------
requireJS和seaJS两者的主要区别如下:
定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。Sea.js 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 环境中。
遵循的规范不同。RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD (通用模块定义)规范。规范的不同,导致了两者 API 不同。Sea.js 更贴近 CommonJS Modules/1.1 和 Node Modules 规范。
推广理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。Sea.js 不强推,采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
对开发调试的支持有差异。Sea.js 非常关注代码的开发调试,有 nocache、debug 等用于调试的插件。RequireJS 无这方面的明显支持。
插件机制不同。RequireJS 采取的是在源码中预留接口的形式,插件类型比较单一。Sea.js 采取的是通用事件机制,插件类型更丰富。
还有不少差异,涉及具体使用方式和源码实现,欢迎有兴趣者研究并发表看法。
总之,如果说 RequireJS 是 Prototype 类库的话,则 Sea.js 致力于成为 jQuery 类库。
最后,向 RequireJS 致敬!RequireJS 和 Sea.js 是好兄弟,一起努力推广模块化开发思想,这才是最重要的。
------------------------------------------------------------------------------------------
尾巴:
软件危机:1960年代中期开始爆发众所周知的软件危机是指落后的软件生产方式无法满足迅速增长的计算机软件需求,从而导致软件开发与维护过程中出现一系列严重问题的现象。
对于前端,不单是js的模块化极具意义,这个前端的模块化(工程化的实现必经)也极具意义。
没有银弹:
软件危机被提出以后,IBM大型电脑之父Fred Brooks在1987年所发表的一篇关于软件工程的经典论文
《没有银弹》,该论述中强调真正的银弹并不存在(银弹是西方驱魔驱鬼的特效武器),而所谓的没有银弹则是指
没有任何一项技术或方法可以能让软件工程的生产力在十年内提高十倍。
所以,前端工程化是一个复杂的实践,我们不能指望掌握了什么时下很流行的框架或者什么前沿知识就能掌握前端,前端是一个永无止息的动态演进过程,对于我们而言,也永远没有“学成本事”这一说,在此互相勉励大家,学无止境,多多益善。
前段工程化是未来之路,模块化开发是前端工程化的最初实践,我们应该学习这种”分而治之“的思想,故我今天做此分享。
探索javascript----浅析js模块化的更多相关文章
- [JavaScript] 后端js的模块化规范CommonJs
CommonJs概述 主要是单个文件定义的变量,函数,类都是私有的,其他文件不可见,单位的作用域 通过 exports(modules.exports)对外暴露接口,通过 require 加载模块 n ...
- Atitit.js模块化 atiImport 的新特性javascript import
Atitit.js模块化 atiImport 的新特性javascript import 1. 常见的js import规范amd ,cmd ,umd1 1.1. Require更多流行3 2. at ...
- js模块化加载器实现
背景 自es6以前,JavaScript是天生模块化缺失的,即缺少类似后端语言的class, 作用域也只以函数作为区分.这与早期js的语言定位有关, 作为一个只需要在网页中嵌入几十上百行代码来实现一些 ...
- Node.js 模块化你所需要知道的事
一.前言 我们知道,Node.js是基于CommonJS规范进行模块化管理的,模块化是面对复杂的业务场景不可或缺的工具,或许你经常使用它,但却从没有系统的了解过,所以今天我们来聊一聊Node.js模块 ...
- JS模块化开发:使用SeaJs高效构建页面
一.扯淡部分 很久很久以前,也就是刚开始接触前端的那会儿,脑袋里压根没有什么架构.重构.性能这些概念,天真地以为前端===好看的页面,甚至把js都划分到除了用来写一些美美的特效别无它用的阴暗角落里,就 ...
- 从273二手车的M站点初探js模块化编程
前言 这几天在看273M站点时被他们的页面交互方式所吸引,他们的首页是采用三次加载+分页的方式.也就说分为大分页和小分页两种交互.大分页就是通过分页按钮来操作,小分页是通过下拉(向下滑动)时异步加载数 ...
- JS 模块化和打包方案收集
1.这里想讨论的是拆分规则,不是在问哪个工具怎么使用.2.这里没有在想找正确答案,因为感觉这个问题要结合具体业务场景. 随着项目开发越来越大,一开始代码全打包到一个文件的方式,让文件越来越大,接下来要 ...
- 闲聊——浅谈前端js模块化演变
function时代 前端这几年发展太快了,我学习的速度都跟不上演变的速度了(门派太多了,后台都是大牛公司支撑类似于facebook的react.google的angular,angular的1.0还 ...
- js模块化历程
这是一篇关于js模块化历程的长长的流水账,记录js模块化思想的诞生与变迁,展望ES6模块化标准的未来.经历过这段历史的人或许会感到沧桑,没经历过的人也应该知道这段历史. 无模块时代 在ajax还未提出 ...
随机推荐
- C#基础2
namespace 基础 //定义一个命名空间,为基础 { class Program //定义一个名称为Program的类 { static void Main(string[] args) //这 ...
- 机器学习之决策树熵&信息增量求解算法实现
此文不对理论做相关阐述,仅涉及代码实现: 1.熵计算公式: P为正例,Q为反例 Entropy(S) = PLog2(P) - QLog2(Q); 2.信息增量计算: Gain(S,Sv) = E ...
- 为 iTween 指定特定的回调 : onupdate, oncomplete
问题地址:Specifying a delegate for the value of onupdate in iTween 1.找到 void CallBack 2.修改以下代码: void Cal ...
- android SDK下载及中文API地址
中文API:http://wiki.eoeandroid.com/Android_API_Guides Android Dev Tools官网地址:www.androiddevtools.cn 收集整 ...
- svn设置外网访问
1.设置路由器 默认协议为:https 端口号:443 服务器地址:https://主机名/svn/版本库 例:https://mleo-pc/svn/Share/ 也可就主机名用IP地址代替 如:h ...
- mysql分组合并GROUP_CONCAT
SELECT pid, GROUP_CONCAT(field ORDER BY coder desc) 'msg'from product_field GROUP BY pid 分 ...
- Java—网络技术
1 TCP Sockets基础 Sockets是一个编程抽象概念,它是网络上与另一个应用程序通信连接的句柄.Sockets编程将用户代码与TCP/IP协议堆栈的底层实现隔离开,允许用户灵活地实现自己 ...
- Python笔记总结week2
1. 关于Python程序执行原理:
- 10.6 CCPC northeast
1001 Minimum's Revenge 点的编号从 1 到 n ,u v 的边权是 LCM(u,v) ,求这个图的最下生成树 搞成一颗以 1 为 根 的菊花树 ---------------- ...
- 无废话SharePoint入门教程三[创建网站集和网站]
一.前言 前两篇文章讲解了什么是SharePoint,并且介绍了在SharePoint中一些常用的概念.但概念终究是概念,我们还是要脚踏实地的去动手实践.下面的文章对于了解SharePoint的人来说 ...