探索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还未提出 ...
随机推荐
- [官方软件] Easy Sysprep v4.3.29.602 【系统封装部署利器】(2016.01.22)--skyfree大神
[官方软件] Easy Sysprep v4.3.29.602 [系统封装部署利器](2016.01.22) Skyfree 发表于 2016-1-22 13:55:55 https://www.it ...
- SSIS学习笔记
SSIS全称(Sql Server Integration Services),是 Microsoft BI 解决方案的一大利器.除了作为ETL的一种工具,在以下方面还有着突出的表现: (1) 系统维 ...
- Jekyll x Liquid 控制文章列表只显示特定类别的Post
使用Liquid按照Category或者Tag过滤Post List 文章首发于szhshp的第三边境研究所(szhshp.org), 转载请注明 前段时间画了一些漫画,考虑把漫画相关的Post放到另 ...
- 项目公共js(vue.js)
var urlHead = "http://hm.runorout.com/";// var urlHead = "/";/*加入跑班相关*/var urlGe ...
- selenium遇到readonly元素的输入
方法:去掉该元素的readonly属性 使用js来去掉 ((JavaScriptExecutor ) driver).executeScript($("input#{放置元素的CLASS}[ ...
- 简单的验证码识别(opecv)
opencv版本: 3.0.0 处理验证码: 纯数字验证码 (颜色不同,有噪音,和带有较多的划痕) 测试时间 : 一天+一晚 效果: 比较挫,可能是由于测试的图片是在太小了的缘故. 原理: 验证码 ...
- 去除 Google 重定向
1. 安装插件 redirector : https://code.google.com/p/chrome-redirector/ 2. 配置规则,如下: 匹配:https?://www\.googl ...
- Tomcat(免安装版)的安装与配置
一.下载Tomcat Tomcat可以从http://tomcat.apache.org/网站下载,选择任意版本,在 Binary Distributions 下的zip包既是. 二.配置Tomcat ...
- 关于磁盘错误disk error
到同事办公室的时候,机器的启动界面就停在磁盘错误disk error上. 首先怀疑的就是硬盘可能坏了,于是就用u盘启动,运行mhdd检测,一直到10%都没有发现错误.于是退出,重启,发现机器能够启动x ...
- 遇到bug怎么办
最近第一个完整的项目的第一期快完成了.期间,我怀疑过无数次人生,给难兄难弟辣椒相互吐槽过.被我师父点播后觉得人和人差距怎么可以这么大数次. 终于!基本功能实现了. 今天不总结具体问题了,说一下调试过程 ...