前端模块化的四种规范

前言:前端模块化出现的缘由和实现的一些弊端

出现的缘由

为什么会出现前端模块化呢,要想实现功能的复用,就需要把代码引入重复利用,如果不使用模块化,我们之前会使用的方法有哪些呢?
1.直接定义全局function的方式,那么会出现函数命名污染的情况,并且各个部分之间的关系也看不出来。
2.在命名空间(name space)中定义方法,定义一个obj = {function1() {},function2(){}};这样有个问题就是函数内部的成员可以被随意修改,很不安全。
3.通过IIFE模式,也就是函数自调用闭包的方式,暴露给window,但是如果存在模块之间的依赖,就会很麻烦。
4.IIFE增强模式:引入依赖。为了解决上面的问题,就出现了IIFE增强模式,说白了就是传参把其他的模块给传进去,就出现了先后顺序很重要的问题,如果前面的都还没挂载到window上面,此时运行后面的js代码肯定就是undefined。
因此,为了解决1.命名污染。2.提升代码的维护性。3.更好的分离代码,实现按需加载。4提高代码的复用性等,我们需要采用模块化规范,这里就介绍比较常用的四种模块化的规范,分别是commonJS,AMD,CMD和ES6四种。

一、commonJS

1.commonJS-node

node.js原生就支持commonJS的规范,所以在node.js中可以直接使用,不需要引入其他的包了。commonJS是在服务端跑的,所以都是读的本地磁盘中的内容,不存在异步的问题,所以require过来就直接可以用,因为是同步的,所以会有阻塞的情况,但是在浏览器端,一般这些js文件也就是模块,很多是通过网络请求过来的,是异步的所以使用commonJS就不是那么合适。
语法就是exports和require两个。

1.1exports可以一起导出,也可以分开导出,require直接导入

module1.png

上面是module1内部的代码,可以module.exports = {}的方式,exports就是一个对象


module2.png

上面是module2的内部代码,exports对象也可以是一个方法

module3.png

上面是module3的内部代码,通过exports.foo和exports.bar的方式分开添加

app.png

上面是app.js的内部代码,可以看到直接通过require('路径')的方式把模块直接给引入过来了,非常方便

1.2 关于package.json

package.png

package.json是通过npm.init初始化的时候就产生的记录本包的详细信息的文件,包含了name(这个name不能包含中文,也不能有大写,在老版本的npm的时候,不具有自动转小写的功能),version以及author等内容,并且通过npm install uniq -save(运行时依赖,这里的save在npm5之后,不加save也默认就是save)或者npm install uniq --save-dev(开发时依赖),在dependencies中记录

1.3 执行app.js

因为是在node环境中执行,当前webStorm执行也是用的node,不是在浏览器环境中,无论通过node app.js还是右键执行app.js都可以完成。

1.4 commonJS的不足

既然commonJS已经这么方便了,为啥会出现AMD和CMD以及ES6的后续的规范呢,就是因为commonJS是在node中实现的,也就是node环境中可以支持require和exports,但是在浏览器环境中,是不支持的,所以要想在浏览器端使用commonJS的规范的话,就必须使用browserify这种第三方包把代码给转换成浏览器端支持的代码,后面将写commonJS在浏览器端的实现。

2.commonJS-Browserify

由于前文中提到的commonJS规范在浏览器端无法适用的情况,我们需要引入一个第三方包也就是Browserify,把原本只能在node环境中跑的代码给转换成浏览器端可以使用的代码。
browserify的安装(必须全局和局部都安装):
①、全局安装
npm install browserify -g
②、局部安装
npm install broserify --save-dev
安装完成之后,运行browserify js/src/app.js -o js/dist/bundle.js


browserify.png

上述指令中的-o,代表的是output,后面js/dist/bundle.js就是输出的目标位置,没有dist目录的话会自动创建,而且这个命令运行之后,命令行不会有什么打印,不代表没执行成功。(这里其他module1,module2这些都没有变,和前面1当中的代码是一样的,只是在浏览器中执行了)


script标签引入.png

在html中通过script标签引入那个bundle.js就可以了,接下来在浏览器中执行。
浏览器中执行结果.png

上图就是在浏览器环境中执行的结果,如果script的标签src引入的不是这个bundle,直接是app.js的话,require这种语法,浏览器根本就不认识,就会出现下面的这个情况。
不用browserify打包.png

二、AMD规范

AMD(Asynchronous Module Definition异步模块定义)规范相比于CMD来说应用得更加广泛一些。AMD是专门用于浏览器端的,模块的加载是异步的。
AMD规范是通过Require.js实现的,所以需要下载Require.js文件。
基本语法:
①、定义暴露模块:如果是定义没有依赖的模块
define(function(){
return 模块
})
如果是定义有依赖的模块
define(['module1','module2'],function(module1,module2) {
return 模块
})
②、引入使用模块
requirejs(['module1','module2'],function(m1,m2) {
使用m1/m2
})

2.1没有AMD时候的模块化实现方式

alert .png
dataService.png
app.png
html.png

如上图所示,在js文件夹中创建了alert和dataService两个模块,这些模块之间都是通过IIFE也就是立即执行函数来把模块内的方法暴露给window对象的,三者之间存在依赖关系,必须按照先后顺序加入script标签,先dataService,后alert再app.js这三个加载顺序,这就是没有AMD的时候的执行方法,就是暴露给window,但这样的缺陷是非常明显的,就是模块之间的依赖必须要手动去定好,不然一定会存在undefined的情况。

2.2 采用AMD-RequireJS的方式实现模块化

html.png

上图是使用require.js来实现的方式,就是script标签的scr是require.js,但是有一个主js的入口,就是data-main属性,里面指定了主文件的入口。


main.png

上图就是main.js的代码。里面是一个立即执行函数,因为他不需要再向外暴露接口,直接就使用requirejs方法,把需要的模块名字传入,然后后面回调函数进行使用就可以了。


alerter.png

上面是alerter,他引入了dataService模块和jquery(必须小写)模块,回调函数中,就使用了其他的模块。
dataService.png

这个dataService,是不需要依赖其他模块的,所以没有传前面的数组,直接定义好,然后暴露接口就可以了。
层级目录.png

上面是层级目录,记得libs下面有这个require.js


requirejs配置.png

这个部分就是requirejs的配置,必须配置,否则根本不知道你数组里头引入,或者定义的时候,那个模块到底是谁,后面映射了路径。shim用来解决那些不支持AMD规范的js包的,就比如angular,本来就是不支持的,你配置了path也没用,配置shim就可以了,上面也可以配置baseUrl。

三、CMD规范

CMD规范用的相对少一些,是阿里开发的,但现在卖给外国人了,使用起来的语法有点像CommonJS和AMD的结合版,SeaJS实现了CMD规范,所以需要下载这个seajs。
使用语法:
①、定义没有依赖的模块
define(function(require,exports,module) {
exports.xxx = value;
module.exports = value;
//这个和commonJS很像,关键就是那个exports对象
})
②、定义有依赖的模块
define(function(require,exports,module) {
var module2 = require('./module2');
require.async('./module3',function(m3) {
///使用module3,是异步的,到时候module3拿到了就给回调直接执行
})
exports.xxx = value
})
③、引入使用模块
define(function(require) {
var m1 = require('./module1')
var m4 = require('./module4')
//执行...
})


html.png

上图是html里面的使用方法,先引入sea.js,然后seajs.use一下main.js。


mainjs.png

main.js里面因为只是在使用,所以不需要加后面的exports和module,当然你开心也可以加上。
里面的引入方式,还是一样的,就是require就可以了,很像commonjs。
module1.png

上面是module1,接下来以此就是四个module。
module2.png
module3.png

上面是module3和2,都是一个没有依赖的。


module4.png

上面是module4,依赖了2和3。前面mainjs引入了module1和4,执行后发现异步调用的3被放在后面了,说明确实异步了,结果就是1243。


7575.png

上面是seajs的位置,不要忘记。
对比CMD和AMD可以发现,AMD的依赖是前置的,在一个数组里头,直接就先加载了再执行后面的代码,而CMD的依赖是后置的,只有需要的时候,才会去执行模块的加载。

四、ES6

ES6需要安装一些加载插件:babel-cli,babel-preset-es2015和browserify,babel-cli主要是实现命令行,执行babel指令可以运行,后面那个babel-preset-es2015才是把ES6转成ES5的工具,babel不仅可以完成es6转es5,还能转其他的,转啥就下载啥,所以这里就需要下载这个babel-preset-es2015。
1.npm install babel-cli browserify -g
全局安装babel-cli这样我们在任何地方都可以执行babel命令了,browserify我们前面已经安装过了,现在也安装一下吧,反正也不吃亏。
2.npm install babel-preset-es2015 --save-dev
这个很明显是开发时候的依赖,转了到时候运行的时候就不需要再转了嘛。
3.定义.babelrc
不要忘记前面那个. (点),在根目录里面新建一个.babelrc,加了点的时候,webstorm自动把它识别为json文件了。babelrc也能在package.json里面配置
{
"name":'pname',
"babel": {
//config
}
}


配置babelrc.png

我们这里直接在根目录配置babelrc,rc代表的是run control运行控制。
4.编写模块之间的依赖
5.编译
因为前面写的js文件,首先浏览器根本就不认识ES6的语法,因此需要babel来转成es5,另外,需要把它弄到浏览器环境中能执行,所以必须browserify
5.1、babel js/src -d js/lib
-d就是目标是js/lib文件夹
-d前面就是需要编译的整个文件夹路径,里头都需要编译


babel命令.png

5.2 browserify js/lib/main.js -o js/dist/bundle.js
这个就是把他弄到服务器端跑的工具
上面两两步编译之后,浏览器就能够识别并运行了。


成功1.png
成功2.png

6.默认暴露的写法(补充)

默认暴露的module3.png

成功3.png

1.值得注意的一点:package.json中name的值,不要取跟github上常用包一样的名字,否则会死活无法下载下来。
2.安装babel必须安装babel-cli,cli就是command line interface也就是命令行接口,比如node里面的npm为啥可以执行,是因为里面就有cli,存了各种命令,但是babel自身没有,所以需要手动下载。
3.npm install jquery@1指定下载jquery1里面的最新版本,默认包就是import $ from 'jquery'不需要加路径名,而且也是默认暴露的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341