1.什么是模块化:
将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),并进行组合在一起块的内部数据/实现是私有的,只是向外部暴露一些接口(方法)与外部其他模块通信。
发展过程:
(1)无模块化(最早是将所有的js代码写在一个文件里,这样不利于阅读查找)
script标签引入文件,相互罗列,但是被依赖的放在前面,否则使用就会报错。
<script src="jquery.js"></script> <script src="jquery_scroller.js"></script> <script src="main.js"></script> <script src="other1.js"></script> <script src="other2.js"></script> <script src="other3.js"></script>
即简单的将所有的js文件统统放在一起,但是这些文件的顺序还不能出错,比如jquery需要先引入,才能引入jquery插件,才能在其他的文件中使用jquery。
缺点:
- 污染全局作用域(global)
- 很容易命名冲突
- 维护成本高
- 依赖关系不明显
- 耦合度高(关联性强,不利于后期维护)(什么是耦合?‘高耦合,牵一发而动全身’:‘低耦合,关联性相对低’)
(2)简单封装:Namespace模式(把一部分数据放在对象里,通过对象.属性操作数据)
特点:减少了global上的变量的数目,但本质是对象,不安全。
(3)匿名闭包:IIFE模式(把一些逻辑写到立即执行函数里),这样相对安全。
(4)引入依赖
2.为什要模块化
(1)降低复杂度,降低耦合性,减少他们之间的复杂性。
(2)部署方便(比如js1模块专门正对轮播图,但这个版块没有轮播图,则这个版块完全没有必要引入)。
3.模块化的好处
(1)避免命名冲突(减少命名空间的污染)
(2)更好的分离,按需加载
(3)更高的复用性
(4)高可维护性(方便维护)
4.页面引入加载script
但现实是:
要加载多个script标签,要发起多次请求,依赖模糊,难以维护。
5.规范
CommonJS(node根据该规范编写)
规范:
(1)每一个文件都可以当做一个模块
(2)在服务器端:模块的加载是运行时同步(会等待,阻塞)加载的
(3)在浏览器端:模块需要提前编译打包处理
语法:
引入模块:require(xxx)
第三方模块:xxx为模块名
自定义模块:xxx为模块文件路径
暴露模块:
(1)module.exports=value(exoports本身是一个空对象,module.exports=value是给exports对象添加属性或方法。)
(2)exports.xxx=value(exports.xxx=value是直接用value新对象来覆盖原来的空对象。)
module.exports={foo:'bar'};//true
module.exports.foo='bar';//true
export={foo:'bar'};//false,相当于重新依赖了exports
问题:暴露的模块本质是 exports对象
优点:
- 解决了依赖、全局变量污染的问题
缺点:
- CommonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,CommonJS不适合浏览器端模块加载,更合理的方案是使用异步加载,比如下边AMD规范。
AMD(专门用于浏览器端的模块化规范,模块的加载是异步的,实现(浏览器端):Require.js)
语法:
定义暴露模块:
//定义没有依赖的模块:
define(function(){
return 模块
})
//定义有依赖的模块:
define(['module1','module2'],function(m1,m2){
return 模块
})
被依赖
模块是异步加载的,而定义的模块是被当作回调函数来执行的,依赖于require.js模块管理工具库。当然,AMD规范不是采用
匿名函数自调用
的方式来封装,我们依然可以利用闭包的原理来实现模块的私有成员和公有成员:
define(['module1', 'module2'], function(m1, m2) { let x = 1; function add() { x += 1; return x; } return { add }; })
引入使用模块:
require(['module1','module2'],function(m1,m2){
使用 m1/m2
})
CMD(专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行,实现(浏览器端):Sea.js)
AMD 推崇依赖前置,CMD 推崇依赖就近。
require
函数同步加载模块时没有HTTP请求过程。
基本语法:
//定义没有依赖的模块:
define(function(require,exports,module){
exports.xxx = value
module.exports = value
})
//定义有依赖的模块:
define(function(require,exports,module){
//引入依赖模块(同步)
var module2 = require("./module2")
//引入依赖模块(异步)
require.async("./module3",function(m3){
})
//暴露模块
exports.xxx = value
})
引入使用模块:
require(function(require){
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})
ES6
说明:依赖模块需要编译打包处理;
特点:
- 模块化规范输出的是一个值的拷贝,ES6模块输出的是值的引用。
- 模块化规范是运行时加载,ES6模块是编译时输出接口。
语法:
(1)导出模块:export xxx
(2)引入模块:import xxx from "url"
实现(浏览器端):
(1)使用Babel将ES6编译为ES5(有些浏览器不兼容ES6)
(2)使用Browserify编译打包js
ES6模块化详:
1.首先页面引用,需要在<script></script>里添加type='module'
2.导出模块分为默认导出和指定导出
let name ='wangwu'; let age=18; let module1 =function(){ console.log(`你好,我是$(name),今年$(age)岁了。`) }; export default module1;//默认导出 export {name,age,modue1}//指定导出
两者的区别主要在于引用的时候:
- 如果是用默认导出export default,接收的时候可以使用任意名字来接收,如下所示,导出的叫module1,被接收的名字叫x:
import x from 'js/module1.js';
- 如果要一次导出多个,需要用 { } 包裹,接收的时必须与导出时同名:
import {name,age,module1} from 'js/module1.js';
- 如果一个模块既有默认导出,又有指定导出
export default module1;//默认导出 export {name,age,module1};//指定导出
接收时可以使用:
import x,{name,age,module1} from 'js/module1.js';//默认导出的依然可以随意命名
3.如果是同级目录,必须添加./
如果有两个模块里导出的内容是一样的,此时console.log,就会报错
import x,{name,age,module1} from './module1.js'; import y,{name,age,module1} from './module2.js'; console.log(name);//此时module1,module2都导出了name报错
import x,{name as name1} from './module1.js'; import y,{name as name2} from './module2.js'; //as相当于重命名 console.log(name1); console.log(name2);
4.只有import会只执行代码,不会执行函数
有一个新的module.js:
console.log('这是module3'); function x(){ console.log('这是module3里面的函数'); }
然后在main.js里面直接引用:
impotr './module3.js';
可以看到只打印了'这是module3',而函数没有执行。
5.集体导入
如果模块里导出的东西太多,又不想引用的时候一个一个的写出来,可以这样:
import * as x from './module1.js';//这里的*可理解为css里的通配符,意思是所有
此时打印出x,可以看到
module1.js里面导出的都在x下了,现在可以用x.age(),x.module1()来调用。
ES6-Babel-Browserify使用教程
1.定义package.json文件
{ "name":"es6-babel-browserify", "version":"1.0.0" }
2.安装babel-cli,babel-preset-es2015和browserify(cli:command line interface)
* npm install babel-cli browserify -g (babel全局安装)
* npm install babel-preset-es2015 --save-dev
* preset 预设(将es6转换成es5的所有插件打包)
3.定义 .babelrc 文件(rc:run control 运行时控制文件,就是运行时要读的文件)
{ "presets":["es2015"] } ...
4.编码
* js/src/module1.js 分别暴露
参考引用原文:https://www.jianshu.com/p/ae4e566212ff
参考引用原文:https://www.imooc.com/article/43781?block_id=tuijian_wz