js 模块化

一、什么是模块化?

1.1 定义

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

 1.2 模块化的进化过程

 1.2.1 全局function模式
 * 全局函数模式: 将不同的功能封装成不同的全局函数
 * 问题: Global被污染了, 很容易引起命名冲突
 */

function foo() {
    console.log('foo()')
}
function bar() {
    console.log('bar()')
}

 问题: 污染全局命名空间, 容易引起命名冲突/数据不安全

1.2.2 namespace模式 
/**
 * namespace模式: 简单对象封装
 * 作用: 减少了全局变量
 * 问题: 不安全(数据不是私有的, 外部可以直接修改)
 */
let myModule = {
  data: 'Jerry',
  foo() {
    console.log(`foo() ${this.data}`)
  },
  bar() {
    console.log(`bar() ${this.data}`)
  }
}
  •  减少了Global上的变量数目
  • 本质是对象,一点都不安全
 1.2.3 IIFE模式
/**
 * IIFE模式: 匿名函数自调用(闭包)
 * IIFE : immediately-invoked function expression(立即调用函数表达式)
 * 作用: 数据是私有的, 外部只能通过暴露的方法操作
 * 问题: 如果当前这个模块依赖另一个模块怎么办?
 */
(function (window) {
  //数据
  let data = 'Jerry'

  //操作数据的函数
  function foo() { //用于暴露有函数
    console.log(`foo() ${data}`)
  }

  function bar() {//用于暴露有函数
    console.log(`bar() ${data}`)
    otherFun() //内部调用
  }

  function otherFun() { //内部私有的函数
    console.log('otherFun()')
  }

  //暴露行为
  window.myModule = {foo, bar}
})(window)

  IIFE模式增强:引入依赖

(function (window, $) {
  //数据
  let data = 'Jerry'

  //操作数据的函数
  function foo() { //用于暴露有函数
    console.log(`foo() ${data}`)
    $('body').css('background', 'red')
  }

  function bar() {//用于暴露有函数
    console.log(`bar() ${data}`)
    otherFun() //内部调用
  }

  function otherFun() { //内部私有的函数
    console.log('otherFun()')
  }

  //暴露行为
  window.myModule = {foo, bar}
})(window, jQuery)
  • IIFE : 立即调用函数表达式--->匿名函数自调用
  • 编码: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口
  • 引入依赖: 通过函数形参来引入依赖模块 

1.3 模块化的好处

  • 避免命名冲突(减少命名空间污染)
  • 更好的分离,按需加载
  • 更高复用性
  • 高可维护性
<body>
<!--
  1. 一个页面需要引入多个js文件
  2. 问题:
    1). 请求过多
    2). 依赖模糊
    3). 难以维护
  3. 这些问题可以通过现代模块化编码和项目构建来解决
-->

<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript" src="module2.js"></script>
<script type="text/javascript" src="module4.js"></script>
<script type="text/javascript" src="module3.js"></script>
<script type="text/javascript" src="../04_IIFE模式增强/jquery-1.10.1.js"></script>
<script type="text/javascript" src="../04_IIFE模式增强/test4.js"></script>
<script type="text/javascript">
  module.foo()
</script>
</body>

 二、模块化规范

2.1  CommonJS

Node.js:     服务器端

Browserify :  浏览器端    也称为js的打包工具(CommonJs 浏览器端打包工具 )

// 基本语法:
      // 定义暴露模块 : exports
        exports.xxx = value
        module.exports = value

      // 引入模块 : require
        var module = require('模块名/模块相对路径')

 引入模块发生在什么时候?

  • Node : 运行时, 动态同步引入
  • Browserify : 在运行前对模块进行编译/转译/打包的处理(已经将依赖的模块包含进来了),  运行的是打包生成的js, 运行时不存在需要再从远程引入依赖模块
2.1.1 CommonJS-Node

代码示例:

module1.js   使用module.exports = value向外暴露一个对象

/**
 * 使用module.exports = value向外暴露一个对象
 */
"use strict"
module.exports = {
  foo() {
    console.log('moudle1 foo()')
  }
}

 module2.js     使用module.exports = value向外暴露一个对象

/**
 * 使用module.exports = value向外暴露一个函数
 */
module.exports = function () {
  console.log('module2()')
}

 module3.js      使用exports.xxx = value向外暴露多个对象

/**
 * 使用exports.xxx = value向外暴露多个对象
 */
"use strict"
exports.foo = function () {
  console.log('module3 foo()')
}

exports.bar = function () {
  console.log('module3 bar()')
}

 使用时,用 require() 语法导入

/**
  1. 定义暴露模块:
    module.exports = value;
    exports.xxx = value;
  2. 引入模块:
    var module = require(模块名或模块路径);
 */
"use strict"
//引用模块
let module1 = require('./modules/module1')
let module2 = require('./modules/module2')
let module3 = require('./modules/module3')

let uniq = require('uniq')
let fs = require('fs')

//使用模块
module1.foo()
module2()
module3.foo()
module3.bar()

console.log(uniq([1, 3, 1, 4, 3]))

fs.readFile('app.js', function (error, data) {
  console.log(data.toString())
})
2.1.2 CommonJS-Browserify 

 下载 browserify

  * 全局: npm install browserify -g

  * 局部: npm install browserify --save-dev

打包处理js:

browserify js/src/app.js -o js/dist/bundle.js

页面使用引入:

  <script type="text/javascript" src="js/dist/bundle.js"></script>

2.2 AMD - RequireJS

说明: 专门用于浏览器端,模块的加载是异步的     AMD · amdjs/amdjs-api Wiki · GitHub 

基本语法:

定义暴露模块:

// 定义没有依赖的模块
define(function () {
  let msg = 'atguigu.com'
    
  function getMsg() {
    return msg.toUpperCase()
  }
    
  return {getMsg}
})

// 定义有依赖的模块
define(['dataService', 'jquery'], function (dataService, $) {
  let name = 'Tom2'
    
  function showMsg() {
    $('body').css('background', 'gray')
    alert(dataService.getMsg() + ', ' + name)
  }
    
  return {showMsg}
})

引入使用模块:

//引入使用模块
requirejs( ['alerter'], function(alerter) {
  alerter.showMsg()
})

举例说明:alerter.js 依赖 dataService.js 和 jquery模块,可以在alerter.js中这样写:

/*
 定义有依赖的模块
 */
define(['dataService', 'jquery'], function (dataService, $) {
  let name = 'Tom2'

  function showMsg() {
    $('body').css('background', 'gray')
    alert(dataService.getMsg() + ', ' + name)
  }

  return {showMsg}
})

 在 main.js 中引入依赖

(function () {
  //配置
  require.config({
    //基本路径
    baseUrl: 'js/',
    //映射: 模块标识名: 路径
    paths: {
      //自定义模块
      'alerter': 'modules/alerter',
      'dataService': 'modules/dataService',

      //库模块
      'jquery': 'libs/jquery-1.10.1',
      'angular': 'libs/angular'
      
    },

    //配置不兼容AMD的模块
    shim: {
      angular: {
        exports: 'angular'
      }

    }
  })

  //引入模块使用
  require(['alerter', 'angular'], function (alerter, angular) {
    alerter.showMsg()
    console.log(angular);
  })
})()

在html页面中引入即可:

<!DOCTYPE html>
<html>
<head>
    <title>Modular Demo 2</title>
</head>
<body>
    <script type="text/javascript" src="js/libs/require.js" data-main="js/main.js"></script>
 </body>
</html>

关于 require.js 的详细用法可以参考:

Javascript模块化编程(三):require.js的用法 - 阮一峰的网络日志

2.3 CMD - SeaJS 

首先需要引入 sea.js的库

<script type="text/javascript" src="js/libs/sea.js"></script>
定义导出模块  define()  exports  module.exports

module1.js

define(function (require, exports, module) {
  //内部变量数据
  var data = 'atguigu.com'
  //内部函数
  function show() {
    console.log('module1 show() ' + data)
  }

  //向外暴露
  exports.show = show
})

 module2.js

define(function (require, exports, module) {
  module.exports = {
    msg: 'I Will Back'
  }
})

module3.js

define(function (require, exports, module) {
  const API_KEY = 'abc123'
  exports.API_KEY = API_KEY
})
引入依赖模块   require()

module4.js

define(function (require, exports, module) {
  //引入依赖模块(同步)
  var module2 = require('./module2')

  function show() {
    console.log('module4 show() ' + module2.msg)
  }

  exports.show = show
  //引入依赖模块(异步)
  require.async('./module3', function (m3) {
    console.log('异步引入依赖模块3  ' + m3.API_KEY)
  })
})

 main.js

define(function (require) {
  var m1 = require('./module1')
  var m4 = require('./module4')
  m1.show()
  m4.show()
})
 使用模块  seajs.use()
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <!--
使用seajs:
  1. 引入sea.js库
  2. 如何定义导出模块 : define()   exports   module.exports
  3. 引入依赖模块:   require()
  4. 如何使用模块:   seajs.use()
-->
    <script type="text/javascript" src="js/libs/sea.js"></script>
    <script type="text/javascript">
      seajs.use("./js/modules/main");
    </script>
  </body>
</html>

 2.4 ES6 模块化

依赖模块需要编译打包处理,使用Babel将ES6编译为ES5代码

export:导出模块,引入模块:import

1. 定义package.json 

2. 安装依赖包

安装babel-cli, babel-preset-es2015和browserify

 npm install babel-cli browserify -g 
 npm install babel-preset-es2015 --save-dev 
 presets 预设(将es6转换成es5的所有插件打包)

3. 定义.babelrc文件

{
   "presets": ["es2015"]
}

4. 编码

 module1.js 分别暴露

define(function (require, exports, module) {
  //内部变量数据
  var data = 'atguigu.com'
  //内部函数
  function show() {
    console.log('module1 show() ' + data)
  }

  //向外暴露
  exports.show = show
})

 module2.js  统一暴露

let data = 'module2 data'

function fun1() {
  console.log('module2 fun1() ' + data);
}

function fun2() {
  console.log('module2 fun2() ' + data);
}

export {fun1, fun2}

module3.js  export default

export default {
  name: 'Tom',
  setName: function (name) {
    this.name = name
  }
}

app.js 引入模块 import

import {foo, bar} from './module1'
import {DATA_ARR} from './module1'
import {fun1, fun2} from './module2'
import person from './module3'

import $ from 'jquery'

$('body').css('background', 'red')

foo()
bar()
console.log(DATA_ARR);
fun1()
fun2()

person.setName('JACK')
console.log(person.name);

5. 编译

  * 使用Babel将ES6编译为ES5代码(但包含CommonJS语法) : babel js/src -d js/lib

  * 使用Browserify编译js : browserify js/lib/app.js -o js/lib/bundle.js

6. 页面中测试引入

<script type="text/javascript" src="js/lib/bundle.js"></script>

7. 引入第三方模块(jQuery)

  1). 下载jQuery模块:

     npm install jquery@1 --save

  2). 在app.js中引入并使用

 import $ from 'jquery'

 $('body').css('background', 'red')

    

  • 22
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿小野

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值