JavaScript-模块化开发

javaScript模块化

什么是模块化?

即将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),并用规定语法将其组合在一起,块的内部数据/实现是私有的,只是向外部暴露一些接口(方法)与外部其他模块通信

以前的代码


全局模式

1
2
3
4
5
6
function foo(){

}
function abc(){

}

问题:所有的函数都被绑定在了window上,容易产生命名冲突和产生对window的污染


namespace模式

将函数或者数据绑定在对象上

1
2
3
4
5
6
7
8
9
let obj = {
k: 'asd',
foo(){
console.log(this.k)
},
abc(){

}
}

问题:对象中的数据可以随意被修改,所以数据不安全


IIFE(Immedicately-invoked-function-expression)

该方法就是利用函数的作用域和即使函数的特性

1
2
3
4
5
6
7
8
(function(window){
function foo(){
console.log('ooo')
}
window.module={foo}
})(window);
-----------------------------
module.foo();

或者

1
2
3
4
5
6
7
8
(function(window){
function foo(){
console.log('ooo')
}
window.module=foo
})(window);
-----------------------------
module();

IIFE就是现代化模块化的基石

模块化的好处

  1. 避免命名冲突
  2. 根据功能点进行更好的分离
  3. 更高的复用性
  4. 高可维护性

但是以此同时就出现了问题

  • 一个页面需要引入多个js文件,如果多个文件之间有依赖关系,先后顺序的错误就会导致报错
  • 请求过多
  • 依赖模糊
  • 难以维护

这时候我们就需要引入一种模块化的规范来避免这种问题,下面我们来看看有哪些模块化规范

模块化的规范

  1. CommonJS(nodejs就是基于这种规范)
  2. AMD (用的少)
  3. CMD(阿里的人写的。用的少)
  4. ES6

CommonJS

说明:

  1. 每一个文件都是一个模块
  2. 服务器端:模块的加载是运行时同步加载的
  3. 浏览器端:需要自己编译打包将所有的模块js合并到一个js中

定义模块:

1
2
3
4
5
6
module.exports={}
---
expoets.foo = function(){

}
exports.n = 2

引入模块:

1
2
3
4
let module1 = require(..)
// 这里的..
// 引入第三方的包 直接写包名
// 引入自己写的js 写路径

服务器端例子:

先创建项目结构

1
2
3
4
5
6
-modules
--module1.js
--module2.js
--module3.js
-app.js
-package.json

module1.js

1
2
3
4
5
module.exports = {
foo(){
console.log(`'module1's foo()`)
}
}

module2.js

1
2
3
4
5
6
exports.foo = function(){
console.log(`'module2's foo()`)
}
exports.abc = function(){
console.log(`'module2's abc()`)
}

module3.js

1
2
3
module.exports = function(){
console.log(`'module3's foo()`)
}

app.js

1
2
3
4
5
6
7
8
let module1 = require('./modules/module1.js')
let module2 = require('./modules/module2.js')
let module3 = require('./modules/module3.js')

module1.foo()
module2.foo()
module2.abc()
module3()

package.json

1
2
3
4
{
name: 'applicationname',// 这里写项目名字,不能有大写字母,不能有中文
version: '1.0.0'
}

可以使用npm init生成该文件


客户端例子:

结构树如下

1
2
3
4
5
6
7
8
9
-test
--dist
--src
---module1.js
---module2.js
---module3.js
---app.js
--index.html
--package.json

js代码如上

下载编译工具

1
2
npm install browserify -g								# 全局安装
npm install browserify --save-dev # 开发依赖,在上线阶段不需要此工具了

使用该工具

1
browserify src/app.js -o dist/budle.js

index.html

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<title>title</title>
</head>
<body>
<script src='./dist/budle.js'></script>
</body>
</html>

AMD

说明:AMD((Asynchronous Module Definition))专门用户浏览器端,模块的加载是异步的

此规范依赖于一个Require.js

基本语法:

定义模块:

1
2
3
4
5
6
7
8
// 没有依赖其他模块
define(function(){
return 模块
})
// 定义有依赖的模块
define(['module1','module2'],function(m1,m2){
return 模块
})

引入模块

1
2
3
4
5
6
require(['module1','module2'],function(m1,m2){
使用m1/m2
})
requirejs(['module1','module2'],function(m1,m2){
使用m1/m2
})

例子:

目录结构

1
2
3
4
5
6
7
8
9
10
-test
--js
---libs
----require.js//这是引入的第三方js
---modules
----module1.js
----module2.js
----modlue3.js
---main.js
--index.html

mudule1.js

1
2
3
4
5
6
7
define(function(){
lett msg = 'module1';
function foo(){
console.log(msg)
}
return {foo}
})

module2.js

1
2
3
4
5
6
7
define(['module1'],function(m1){
let msg = 'module2.js'
function foo(){
console.lopg(msg,m1.foo())
}
return {foo}
})

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
(function(){
require.config({
baseUrl: 'js/',
path: {
module1: './modules/module1.js',
module2: './modules/module2.js'
jquery: './libs/jquery.js'//引入第三方库,不是所有的第三方库都支持AMD语法
}
})
requirejs(['module1','module2'],function(m1,m2){
m1.foo();
})
})()

页面中只要引入该标签即可

1
<script data-main="js/main.js" src=js/libs/require.js></script>

ES6规范

说明:该规范也需要编译打包处理,先使用babel将ES6转化为ES5,然后使用browserify将其并为一个js文件

基本语法:

导出模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export function foo(){

}
export function abc(){

}
------------------------------------------------------------------------------------------------------
function foo(){

}
function abc(){

}
export {foo,abc}
------------------------------------------------------------------------------------------------------
export default {
function foo(){

}
function abc(){

}
}

引入模块:

1
2
3
4
5
// 常规导出,这边用对象接
import {} from '路径' //自己的js

// 默认导出,这边用变量来接
impoet module from '路径'

例子:

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
-test
--js
---dist
---build
---src
----module1.js
----module2.js
----module3.js
----main.js
--index.html
--.babelrc
--package.json

安装依赖

1
2
3
4
5
npm install browserify --save-dev

npm install babel-cli -g

npm install babel-preset-es2015 --save-dev

创建.babelrc

rc就是run control运行时控制文件

1
2
3
{
"presets":["es2015"]
}

module1.js

1
2
3
4
5
6
export function foo(){

}
export function abc(){

}

module2.js

1
2
3
4
5
6
7
function f1(){

}
function f2(){

}
export {f1,f2}

module3.js

1
2
3
4
5
6
7
8
export default {
function d1(){

}
function d2(){

}
}

main.js

1
2
3
4
5
6
7
8
9
import {foo,abc} from './module1.js'
import {f1,f2} from './module2.js'
import module3 from './module3.js'

foo();
abc();
f1();
module3.d1();
module3.d2();

es6转es5

1
babel js/src -d js/build

这里如果出现这个错误:

1
babel : 无法加载文件 C:\Users\win\AppData\Roaming\npm\babel.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/ go.micros

那么你输入以下命令一定是这样的

1
2
get-ExecutionPolicy
# restricted

这时候,先使用管理员身份打开Windows Powershell然后输入以下命令

1
2
set-ExecutionPolicy RemoteSigned
然后输入y回车

合并文件

1
browserify js/build/main.js -o js/dist/bundle.js

index.html导入js

1
<script src="js/src/dist/bundle.js"></script>
给作者买杯咖啡吧~~~